Это копия, сохраненная 5 августа 2019 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
В нашем треде отвечают почти на все вопросы, только бампайте каждые 5 дней. И не разводите флуд, если вам скучно, лучше сходите погрейте голову на улице, например.
Это тред и для начинающих. Слово "классы" у тебя ассоциируется только со школой, а в аттестате тройка по математике? Ты наш человек.
Предыдущий тред был тут: >>1380485 (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/
- какой-то древний, устаревший, но большой и на русском справочник по веб-разработке, посоветованный аноном: https://starcat.dp.ua/doc/wdh/
Оформляй код аккуратно!!! — например пропусти через 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://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт и найди название для переменной там
- в именах классов используется 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
Заодно я бы хотел разобрать одну интересную задачу из прошлого треда. Есть такая известная задача на ООП про скидки, кто мои задачи про ООП решал, думаю, без проблем справится. Вот тут вот задача: >>1395825 а тут решение анона: https://github.com/nokitakaze/test-programming-task-am-items/
Думаю, прошло много времени, так что прокомментирую решение:
> Правило №4 "стоимость выбранного продукта уменьшается на 5%". Что такое "выбранный продукт"? Покупатель произвольно пальцем тыкнул в любой продукт, и у него цена уменьшилась?
Это товар A из правила.
> Если одновременно выбраны А
Вот он и "выбранный".
Вообще, переставлять правила плохая идея. Да, они могут быть не очень логичными, но в задаче требуется сделать именно как описано. Согласен, впрочем, что формулировка плохая.
https://github.com/nokitakaze/test-programming-task-am-items/blob/master/src/Item.php
Тут странный отступ в 4 пробела. Он не нужен.
> protected $_type;
Не надо использовать подчеркивание в именах свойств. Это было в PHP4.
> @var ItemType[]|ItemType[][]|null
> protected $_only_includes = null;
Вот такой зоопарк типов - это плохо. Тебе при работе с полем придется всегда делать проверку на тип данных, ее легко забыть, итд. Это будет плохой, запутанный код.
И вообще, тут ты имхо переусложнил код. Ты пытался сделать так, чтобы у правил было как можно больше общего кода, чтобы достаточно было передать для разных правил разные массивы, но это лишь привело к переусложнению логики. То есть, думаю, что из базового класса Rule можно вообще убрать эти поля exclude/inlcude.
Тебе не надо было в базовом классе делать поля exclude/include. Они не применимы ко всем видам скидок, и значит, им незачем быть в базовом классе. Также, не стоит полагаться на идею "класс-наследник должен переопределить этот массив", так как она никак не задокументирована и не проверяется. Если ты хочешь что-то требовать от класса-наследника, то используй абстрактные методы.
Также, не стоило объединять правила 1 и 4 в один класс. Так как там другая логика. Или если и объединять, то без потери читабельности кода.
Также, посмотри на объем кода в https://github.com/nokitakaze/test-programming-task-am-items/blob/master/src/Rules/TotalCostSelectedItemsRule.php и сравни с тем, как это правило описано в одном предложении в задаче. Этот код стоит хотя бы разбить на методы с понятными названиями, сейчас он вообще не читаем.
Ну например, один из вариантов - это сделать так:
// Сортируем товары по типу
foreach ($types as $type) {
$itemsByType[$typeName] = $this->pickItemsByType($items, $type);
}
// Определяем число групп
$itemsCount = array_map('count', $itemsByType);
$pairsCount = min($itemsCount);
// Проходимся по группам товаров и даем скидку за каждую
for ($i = 0; $i < $pairsCount; $i++) {
$group = $this->selectGroup($itemsByType, $i);
$this->addDiscountForPair($result, $pair);
}
Вот, основная логика кода занимает небольшой объем и гораздо читабельнее. У тебя же код плохо читабелен, а разделение на функции (calculateIteration) не помогает его понять.
Что касается формата ответа, то думаю, стоило сделать объект, который бы хранил: вид скидки, размер скидки, к чему применяется, за какие товары дана. Тогда мы можем в итоге вывести информацию о примененных скидках. Твой же класс RuleCalculationResponse этого не позволяет.
И еще, ты сделал метод Rule#calculate(), который принимает массив товаров и за один проход ищет все скидки. Но можно было еще сделать по-другому: он принимает массив товаров и возвращает первую найденную скидку. Соответственно, мы вызываем его в цикле, пока все скидки этого типа не найдутся. Плюс в том, что это упрощает код правила. Нам не нужно искать все скидки, достаточно найти одну. Для правила 1, например, код будет совсем простой:
function applyDiscount(array $items)
{
$group = [];
foreach ($this->types as $type) {
$item = $this->findItemByType($items, $type);
if (!$item) {
return;
}
$group[] = $item;
}
// Применить скидку к $group
return new Discount(....);
}
Хотя этот код менее эффективен, но он более читабелен. А если встроить в корзину функцию поиска по типу и оптимизировать ее, то может быть даже более эффективен, чем твой код.
https://github.com/nokitakaze/test-programming-task-am-items/blob/master/src/Rules/RuleGroup.php
В этом классе, наверно, логично было сделать метод вроде applyGroup() или как-то так. А то класс не сильно отличается от просто массива.
https://github.com/nokitakaze/test-programming-task-am-items/blob/master/src/Rules/RuleSet.php
Не очень понятно, зачем этот класс.
https://github.com/nokitakaze/test-programming-task-am-items/blob/master/test/CalculatorTest.php
Тест сложный. Его тяжело прочесть, и понять, а правильный он или нет? В случае падения - непонятно, что именно не так. Обычно тесты пишут для проверки отдельных требований. В твоем случае, можно сделать такие тесты, начиная с тестирования отдельных скидок:
- тест, что скидка на группу товаров применяется, когда она есть
- что скидка применяется несколько раз, если групп несколько
- что она не применяется, если товаров нет
- тесты на другие виды скидок ....
- тест на то, что 2 разных скидки могут применяться к одной корзине
- тест на то, что товар, участвующий в одной скидке, не используется в другой
То есть тестируем отдельные простые требования простыми тестами. То, что у тебя - сложновато выглядит, и это скорее приемочные, а не юнит-тесты. Я бы предпочел набор простых тестов.
И не надо в одном тесте тестировать разные объекты (testprimitives). Надо было сделать 2 отдельных теста.
Заодно я бы хотел разобрать одну интересную задачу из прошлого треда. Есть такая известная задача на ООП про скидки, кто мои задачи про ООП решал, думаю, без проблем справится. Вот тут вот задача: >>1395825 а тут решение анона: https://github.com/nokitakaze/test-programming-task-am-items/
Думаю, прошло много времени, так что прокомментирую решение:
> Правило №4 "стоимость выбранного продукта уменьшается на 5%". Что такое "выбранный продукт"? Покупатель произвольно пальцем тыкнул в любой продукт, и у него цена уменьшилась?
Это товар A из правила.
> Если одновременно выбраны А
Вот он и "выбранный".
Вообще, переставлять правила плохая идея. Да, они могут быть не очень логичными, но в задаче требуется сделать именно как описано. Согласен, впрочем, что формулировка плохая.
https://github.com/nokitakaze/test-programming-task-am-items/blob/master/src/Item.php
Тут странный отступ в 4 пробела. Он не нужен.
> protected $_type;
Не надо использовать подчеркивание в именах свойств. Это было в PHP4.
> @var ItemType[]|ItemType[][]|null
> protected $_only_includes = null;
Вот такой зоопарк типов - это плохо. Тебе при работе с полем придется всегда делать проверку на тип данных, ее легко забыть, итд. Это будет плохой, запутанный код.
И вообще, тут ты имхо переусложнил код. Ты пытался сделать так, чтобы у правил было как можно больше общего кода, чтобы достаточно было передать для разных правил разные массивы, но это лишь привело к переусложнению логики. То есть, думаю, что из базового класса Rule можно вообще убрать эти поля exclude/inlcude.
Тебе не надо было в базовом классе делать поля exclude/include. Они не применимы ко всем видам скидок, и значит, им незачем быть в базовом классе. Также, не стоит полагаться на идею "класс-наследник должен переопределить этот массив", так как она никак не задокументирована и не проверяется. Если ты хочешь что-то требовать от класса-наследника, то используй абстрактные методы.
Также, не стоило объединять правила 1 и 4 в один класс. Так как там другая логика. Или если и объединять, то без потери читабельности кода.
Также, посмотри на объем кода в https://github.com/nokitakaze/test-programming-task-am-items/blob/master/src/Rules/TotalCostSelectedItemsRule.php и сравни с тем, как это правило описано в одном предложении в задаче. Этот код стоит хотя бы разбить на методы с понятными названиями, сейчас он вообще не читаем.
Ну например, один из вариантов - это сделать так:
// Сортируем товары по типу
foreach ($types as $type) {
$itemsByType[$typeName] = $this->pickItemsByType($items, $type);
}
// Определяем число групп
$itemsCount = array_map('count', $itemsByType);
$pairsCount = min($itemsCount);
// Проходимся по группам товаров и даем скидку за каждую
for ($i = 0; $i < $pairsCount; $i++) {
$group = $this->selectGroup($itemsByType, $i);
$this->addDiscountForPair($result, $pair);
}
Вот, основная логика кода занимает небольшой объем и гораздо читабельнее. У тебя же код плохо читабелен, а разделение на функции (calculateIteration) не помогает его понять.
Что касается формата ответа, то думаю, стоило сделать объект, который бы хранил: вид скидки, размер скидки, к чему применяется, за какие товары дана. Тогда мы можем в итоге вывести информацию о примененных скидках. Твой же класс RuleCalculationResponse этого не позволяет.
И еще, ты сделал метод Rule#calculate(), который принимает массив товаров и за один проход ищет все скидки. Но можно было еще сделать по-другому: он принимает массив товаров и возвращает первую найденную скидку. Соответственно, мы вызываем его в цикле, пока все скидки этого типа не найдутся. Плюс в том, что это упрощает код правила. Нам не нужно искать все скидки, достаточно найти одну. Для правила 1, например, код будет совсем простой:
function applyDiscount(array $items)
{
$group = [];
foreach ($this->types as $type) {
$item = $this->findItemByType($items, $type);
if (!$item) {
return;
}
$group[] = $item;
}
// Применить скидку к $group
return new Discount(....);
}
Хотя этот код менее эффективен, но он более читабелен. А если встроить в корзину функцию поиска по типу и оптимизировать ее, то может быть даже более эффективен, чем твой код.
https://github.com/nokitakaze/test-programming-task-am-items/blob/master/src/Rules/RuleGroup.php
В этом классе, наверно, логично было сделать метод вроде applyGroup() или как-то так. А то класс не сильно отличается от просто массива.
https://github.com/nokitakaze/test-programming-task-am-items/blob/master/src/Rules/RuleSet.php
Не очень понятно, зачем этот класс.
https://github.com/nokitakaze/test-programming-task-am-items/blob/master/test/CalculatorTest.php
Тест сложный. Его тяжело прочесть, и понять, а правильный он или нет? В случае падения - непонятно, что именно не так. Обычно тесты пишут для проверки отдельных требований. В твоем случае, можно сделать такие тесты, начиная с тестирования отдельных скидок:
- тест, что скидка на группу товаров применяется, когда она есть
- что скидка применяется несколько раз, если групп несколько
- что она не применяется, если товаров нет
- тесты на другие виды скидок ....
- тест на то, что 2 разных скидки могут применяться к одной корзине
- тест на то, что товар, участвующий в одной скидке, не используется в другой
То есть тестируем отдельные простые требования простыми тестами. То, что у тебя - сложновато выглядит, и это скорее приемочные, а не юнит-тесты. Я бы предпочел набор простых тестов.
И не надо в одном тесте тестировать разные объекты (testprimitives). Надо было сделать 2 отдельных теста.
> переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase
Это же упоминается только применительно к методам.
>Почему PHP? Потому что вакансий море, и учить легко.
Ситуация на рынке не изменилась? Javascript или python в этом вопросе по-прежнему не конкуренты php?
Верно. Но в Симфони так пишут, и ничего плохого, если те, кто не выбрал стиль, возьмут это за основу.
>PSR-2
>4.3. Методы
>Открывающую фигурную скобку НЕОБХОДИМО располагать на отдельной строке
Насколько часто это соблюдается на практике? Я бы не хотел так писать, например.
скажем так, если я вижу, что кто-то в методе написал открывающую скобку на одной строке с сигнатурой, то он либо тупой поскольку не знает стандартов либо долбоеб который не хочет принимать общепринятые условия и в упрек всем пишет "так как ему нравится".
Ты надеюсь понимаешь, что если бы каждый писал так как ему нравится, то у каждого код был бы написан в разном стиле и сложнее было бы понять другого человека, для этого стандарты и придумали
Вместо задавания глупых вопросов сходи на гитхаб и посмотри топовые либы - как они написаны.
>как они написаны
Очень хуёво написаны. Я не хочу делать также, отсюда вопрос: если допустить, что я не буду делать топовые либы для всеобщего пользования, насколько критичным будет игнорирование данного пункта стандартов? Иными словами, в отдельно взятых командах могут быть "свои" стандарты, как мне докладывали. Вопрос в том, насколько часто они идут вразрез с PSR* и насколько вообще будут доёбываться до его несоблюдения.
Алсо, я бы тебя в команду не брал - ты какой-то слишком тупорогий не понимаешь элементарных вещей.
Я не уверен, что у тебя есть вообще какой-то опыт в программировании. Своё мнение можешь в одно место засунуть.
Что ты хочешь-то от меня?
Я задал вопрос, если ты не намерен на него отвечать - просто иди нахуй.
Ясно. Зелень ебаная.
Давай посмотрим объективно: какая ценность в том, что ты пишешь код в другом стиле? Никакой, просто тебе так больше нравится. Это не аргумент, в любой команде предпочтут использовать PSR вместо потакания чьим-то личным вкусам. В open source проекте это приведет к тому, что люди будут задавать вопросы по поводу нестандартного стиля и слать pull request в стиле PSR.
Самым простым и адекватным решением будет не выпендриваться и перейти на PSR. Это не так сложно, как кажется.
в опенсорс проекте просто пошел нахуй если твой код не в том кодстайле что и проект
А на практике никто не особенно не докапывается до кодстайла потому что есть строки и они как правило горят, если ты будешь коллеге закрывать PR потому что у него скобки не так стоят, когда у него прод лежит и ему надо срочно запушить ты навлечешь на себя много нелюбви. Потому PRS крайне желателен, но не всегда обязателен.
А я твоих советов не спрашивал. Иди нахуй.
>просто тебе так больше нравится
Достаточное условие, чтобы начать так делать, если это не создаст никому серьёзных проблем. В чём и был вопрос. Судя по всему, он был задан очень непонятно, хуй его знает.
Не так сложно, безусловно. Но если можно этого не делать - я бы предпочёл не делать.
> В open source проекте
есть гайд для контрибьюторов, где такие вещи, как кодинг стайл, обычно описываются, если они неочевидны. Ну и, конечно же, некоммерческие проекты, которые я сам выбираю, на чём делать, я бы в жизни не выбрал делать на вашей пыхе.
>>15692
Спасибо, буду иметь в виду и класть хуй на стандарты.
>Спасибо, буду иметь в виду и класть хуй на стандарты.
Просто нажимаешь ctrl+alt+l и не имеешь проблем и никому не ебешь мозги
Это норма?
Это холиварный вопрос, потому что у подовляющего большинства уже широкоформатные мониторы, а не квадратные.
Во вторых, если у тебя в методе слишком много параметров то повод задуматься о том что ты что-то не так спроектировал.
> Во вторых, если у тебя в методе слишком много параметров то повод задуматься о том что ты что-то не так спроектировал.
Что поделать, если пехапе не позволяет делать именованные аргументы?
>у подовляющего большинства уже широкоформатные мониторы, а не квадратные
К чему эта реплика вообще?
>если у тебя в методе слишком много параметров
Штуки 3, просто названия длинные.
>Что поделать, если пехапе не позволяет делать именованные аргументы?
1. Идти программировать на питоне.
2. Засунуть все аргументы в какой-то объект и передавать его.
3. Передавать ассоциативный массив как аргумент
>К чему эта реплика вообще?
К тому что это ограничение по длине строки возникло от того что раньше не у всех на монитор эти строки влазили, было неудобно
да еще и потом внутри метода устанешь проверять есть ли такой элемент в массиве
Ты, возможно, неправильно формируешь ссылки и подставляешь в них параметры. Прочти урок про URL и проверь, соответствует ли твой код тому, что там написано и не может ли быть проблема в этом: https://github.com/codedokode/pasta/blob/master/network/urls.md
Особенно внимательно изучи процентное кодирование и функции для работы со ссылками в PHP.
Далее, если это не поможет, то сдампь (echo) получающийся URL, и запости, токен при этом, конечно, надо скрыть. Посмотрим на URL.
Далее, я бы советовал тебе учиться отправлять запросы руками и смотреть, что в ответ приходит. Это можно сделать несколькими методами:
- утилитой curl (не php расширением) в командной строке
- программами с GUI вроде postman для отправки запросов
Мне больше нравится командная строка. Через эти программы ты можешь отправить запрос на сервер и увидеть, что приходит в ответ. Сделай это и тоже напиши.
Благодарю.
Уже написал пост и понял, какой я идиот. Всего-то требовалось 3 дня всматриваться к код и забыть про очевидные вещи. У меня один!! пробел не был url-закодирован. Но почему этот пробел не вызывал ошибок в десятке случаев, а тут вызвал? Это единственное, что мне не ясно. Будь это ошибка везде сразу, я бы быстро нашел, в чём дело.
Тогда другие вопросы задам. Можно отсылать файл multipart/form-data, используя file_get_contents? Или что-то еще, загружающее файл из ссылки в оперативную память и отсылающую её в телеграм оттуда.
Заметь, что в мануале написано: https://www.php.net/manual/ru/function.file-get-contents.php
> Замечание:
> Если вы открываете URI, содержащий спецсимволы, такие как пробел, вам нужно закодировать URI при помощи urlencode().
Надеюсь, что ты освоил теперь кодирование спецсимволов.
Что касается отправки файла. У file_get_contents есть контекст, и в нем можно указывать опции HTTP-запроса. Смотри пример №4 в мануале: https://www.php.net/manual/ru/function.file-get-contents.php
Тут есть документация по опциям HTTP-контекста: https://www.php.net/manual/ru/context.http.php
Увы, судя по ней, тебе надо будет вручную кодировать тело запроса (содержимое файла и нужные заголовки). Справишься? Речь тут именно об отправке файла из оперативной памяти, а не о скачивании.
Я бы советовал освоить нормальную библиотеку вроде Guzzle, где отправка файла делается проще. Ну или расширение curl, хотя Guzzle по моему удобнее.
Сейчас читаю основы регулярок и там попалась задача с выбором корректных номеров. Вроде бы справился, но немного на говнокодил в регулярке и опустился до копирования и спама.
https://ideone.com/b58D3Q
Как можно написать эту регулярку красивее?
(Сори за кровь из глаз)
>Надеюсь, что ты освоил теперь кодирование спецсимволов.
Я и раньше освоил, просто я такой жути наговнокодил, что один пробел без urlencode оставил.
>У file_get_contents есть контекст
Да, я уже указал там ssl и прокси.
>Увы, судя по ней, тебе надо будет вручную кодировать тело запроса (содержимое файла и нужные заголовки). Справишься?
Не думаю. Добавил в контекст Content-Type: audio/mpeg, но файл всё еще не отправляется. Кусок:
'http' => array(
'header' => array("Proxy-Authorization: Basic $auth", 'Content-Type: audio/mpeg'),
Прокси работают, я проверил, открыв с file_get_contents yandex.ru/internet.
>Ну или расширение curl, хотя Guzzle по моему удобнее.
А как это провернуть curl'ом? Мне нужно отправить не ссылку, а файл, при этом не загружая его на сервер, потому что работа с файлами запрещена. Вот и думаю, как загрузить из оперативки.
ОП ты знаешь яву?
Ты зря пренебрегаешь теорией и не изучаешь HTTP. Ты опять неправильно кодируешь тело запроса. Почитай про тип multipart/form-data, раз решил его использовать:
- http://citforum.ru/internet/html40/interact/forms.html#h-17.13.4.2
- https://learn.javascript.ru/xhr-forms#kodirovka-multipart-form-data
Я думаю, что Guzzle будет освоить проще.
Я просто не понимаю, что вообще требуется. Мне не от чего отталкиваться. Нужно увидеть правильный код, чтобы знать, где ошибки нет и где могут они быть. Я обычно так и учусь, от этого отталкиваюсь. Приходится учиться на своих ошибках, но зато запоминается лучше. А теория без этого непонятна, я прочитал 2 твои статьи и они мне ничего не дали.
Вот нужно мне получить файл в переменную $song, чтобы сделать:
$url = "https://api.telegram.org/bot$botapi/sendAudio";
$header = array('Content-Type:multipart/form-data');
$post = array(
'chat_id' => $chat_id,
'audio' => $song
);
curl_get_contents($url, $proxy, 0, $header, $post); //простая функция с curl
И как мне поможет Guzzle, curl или file_get_contents?
>Ты опять неправильно кодируешь тело запроса.
Разве? Это в контексте для file_get_contents, и... А, массивом оно не передается? Сделал так:
'header' => "Proxy-Authorization: Basic $auth\r\n" . "Content-Type: audio/mpeg",
Только я всё еще не могу отправить файл. Я могу отправить его, если просто скачаю в папку, или отправить его по ссылке. Но решить задачу в том виде, в котором она есть, уже не могу.
>Ты опять неправильно кодируешь тело запроса.
А, всё. Я отправляю запрос с заголовком ответа сервера. Прости, я тупой.
Бля, мужик, чё у тебя за проблема запрос организовать с нужными параметрами? Второй день написать не можешь.
Подтягивай матчасть значит.
У меня наоборот обычно - начинать не хочу, а в себя прихожу через пару часов как минимум.
ударение на второй слог
Добро пожаловать в клуб!
Зачем нужны четыре пробела, если есть таубляция? Ладно бы отбивалось одним-двумя пробелами, но визуально-то одно и то же!
Ширину табуляции в любой IDE настроить можно. Ставь хоть с пробел.
Мне вот таб легче нажать, чем как обезьянка пробел дрочить по 4 раза на строку, а то и больше.
В какой-то момент выбора между табами и пробелами вылезли художники-педерасты и начали пронзительно верещать, что они хотят что бы один и тот же код повсюду выглядел одинаково, в то время как табы по разному отображаются в различных IDE/редакторах. К сожалению, им не успели насрать на лицо и поэтому блядские пробелы теперь стандарт.
>табы по разному отображаются в различных IDE/редакторах
Какой-то хуёвый аргумент - они отображаются как настроено.
Что за инвалиды не могут сделать себе как нравится: хоть 1 отступ, хоть 8, если такой любитель? С пробелом такие штуки не получится вытворить.
А ещё IDE обычно с табуляцией немного код подравнивает, в силу своего ума. Пробелы тут тоже сосут.
Потому что ты сидишь на борде для детей.
>Какой-то хуёвый аргумент - они отображаются как настроено.
Вот именно. А пачке педиков было неприятно, что код, который они выравнивали в соответствии со своими художественными вкусами, у кого-то будет выглядеть по другому.
Ещё с табами можно динамически отображать вложенность операторов просто меняя ширину отступа, но не меняя при этом исходников.
Хуйня может получтся если табы с пробелами смешивать. Если ты отбиваешь отступы табами - ты не хочешь чтобы IDE меняло их на пробелы. Соответственно не подравнивай код пробелами иначе у другой макаки может хуита получится у которой ширина таба другая
https://spb.hh.ru/vacancy/31725993
https://spb.hh.ru/vacancy/31636756
кроме собственно языков? Ведь полюбому там необходимы фреймворки, тестирование, package manager, дебаггер, разные БД, потом еще по фронту React, jquery, адаптивная верстка.
Позвони им. Если они связываются со стажерами - значит у них дохуя свободного времени.
На стажера одного пхп не хватит. Лучше иметь общую эрудированность в ИТ, чтобы самостоятельно искать информацию и решать задачи. Задачи тебе посильные найдут а вот нянчится и объяснять тебе никто не будет. Хоть ты и стажер
Ну я понимаю, просто на изучение всех тонкостей PHP уйдет больше всего времени, как мне кажется. А настроить сервер и подключить к нему базу данных можно за пару дней
На ПЧП у тебя уйдет ровно столько времени сколько потребуется у данного работодателя, какой попадется. Так что если знаешь что апач это программа которая запускает интерпритатор пхп и что пхп выполняется на сервере. Что запросы передаются байтами по сети по протоколу хттп. То иди уже стажерь
>Насколько часто это соблюдается на практике?
кем? В любой нормальной фирме с нормальными программистами придерживаются стандартов.
Если ты кодишь для себя и никто с твоим кодом работать не будет кроме тебя, то пиши как хочешь, а в противном случае придерживайся стандартов.
Круто. Порог вхождения низкий, стажировки в наличии. Не то что в этом гнусном фронтенде, откуда я пришел (не получилось вкатиться). Ну ничего, зато пригодятся знания html, css, js.
>Если ты кодишь для себя
Я сначала так и хотел, когда начинал. Теперь только по рекомендациям и пишу - там многие мелочи за меня продумали. Совсем ленивый стал.
Тебя какой-то хуй троллит.
Тебе тестовое вышлют, которое ты провалишь скорее всего. Вряд ли они мимокроков набирают - уж язык знать хорошо должен.
Думаешь ты один такой умный? Придётся конкурировать с теми, кто уже хоть что-то знает.
>>16422
Причина другая. Табы и пробелы визуально неотличимы и если разрешать табы, то файлы превращаются в помойку из смеси тех и других.
Ну и я не понимаю: если тебе хочется поменять величину отступов (странное желание, ну ладно), что тебе мешает делать это с пробелами? Напиши плагин к редактору, который будет при выводе заменять каждые N пробелов на M. И все будет настраиваться без всяких табов.
Ты пишешь "что за инвалиды...", а я не понимаю, что ты за инвалид, который не может написать элементарный плагин для изменения отступов при выводе.
То же самое можно элементарно делать в пробелами. напиши плагин к своему убогому редактору, который будет вместо N пробелов подряд выводить M.
Этому стандарту следуют все ведущие фреймворки вроде Symfony и Laravel, поэтому для единобразия вы тоже обязаны ему следовать, а не лезть со своим уставом в чужой монастырь, иначе будет бардак.
IDE поддерживают PHP-CS-Fixer, так что вы можете одним хоткеем форматировать код по стандарту. Обсуждать тут нечего.
>Напиши плагин к редактору, который будет при выводе заменять каждые N пробелов на M.
Уже есть такой плагин - табуляция.
>Dyno memory:
>Different dyno sizes offer different amounts of maximum RAM:
>free, hobby and standard-1x have 512 MB
>Dyno memory and restarts:
>If the memory size of your dyno keeps growing until it reaches two times its quota (for a standard-1x dyno, 512 MB x 2 = 1 GB), the dyno manager will restart your dyno with an R15 error.
И у меня при этом вылетает ошибка:
>PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 41959424 bytes)
Как увеличить лимит с 128 МБ до максимума?
https://devcenter.heroku.com/articles/php-concurrency
Нашел вот это. Так и не понял, а выше 128 поднять никак нельзя?
Всё, разобрался.
.user.ini, вроде, не работает, но ini_set("memory_limit", "1024M") - вполне.
Ради тебя одного другие не должны испытывать неудобства. Менять величину отступа хочешь ты, потому и плагин писать придется тебе.
Меня пробелы полностью устраивают. Вот мне делать нечего, вместо того, чтобы думать над программой, думать, где я должен табы, а где пробелы ставить.
Ну и насколько я знаю, элита /pr/ использует очень гибко конфигурируемые редакторы, под которые можно писать макросы на элитном языке со скобочками, для них такой плагин сделать, наверно, дело десяти минут.
Создать новую таблицу и импортировать туда инфу со старой, потом удалить старую и переименовать новую.
Ну не хочешь не учи
Ну вообщем да, без копии бд никак не получается, целый день пытался чот измудрить. Если кому нибудь придет что в голову отпишите плз, я мониторю тред.
Сейчас обновил всё до php7.2 и получается очень странная херота, что в некотором проценте случаев php процесс ВП пытается прочитать файлы из системы форума, а форум иногда выдаёт 404 ошибку из-за отсутствия wp-config.php, то есть как-будто их chroot смешиваются, или процессы уходят не туда. При этом все конфиги уже трижды переписаны и в nginx и в php всё по эталонным инструкциям, да и поиск не выдаёт - что у кого-то были подобные проблемы. Что делать?
Что имею\умею - имеется содранный и чуть переделанный MVC фреймворк. Представление реализовано стандартно - html возвращаю шаблонами с разметкой. В принципе я это все понимаю в "принципе", ага.
Сейчас в учебных целях я на свой недофреймворк натягиваю бутстрап-шаблон, в шаблоне соответственно всякие поля для отправки почты и менюшки и прочее. Все это реализовано с помощью джейквери.
Так уж получилось что поверхностно знаю reactjs что то уровня todo могу сделать.
Вот что прошу подсказать:
Я читал о том, что в стандартную страницу можно вставлять отдельные компоненты react, и мне хочется не джейквери использовать в отдельных случаях, а попробовать воткнуть реакт-компонент. Тупо отдельную форму отправки почты.
Как это сделать? С помощью вэб-пака я собираю бандл реакта, в котором допустим код одного компонента, и вставляю его куда нужно в моем шаблоне? Но это я понимаю на столько в общем, что нихуя не понимаю.
У кого нибудь есть подобный опыт? Опыт простенького скрещивания реакта с пхп?
О, Реакт. Моя любимая тема.
Была дом, и в доме срали в подвале. Управдома это заебало и что вы думаете он сделал? Может быть, он натыкал камер в подвале? Или он поставил биотуалет близко к подъезду? Нет, конечно. Он приклеил на подвал табличку Туалет! Вот это React-way.
Когда я увидел ёбаный JSX и охуительные функции render(), мои волосы встали нахуй дыбом. Я сам несколько раз лепил html в js, но я никогда не считал это чем-то хорошим. Я до последнего не мог поверить, что этот кусок кала - признанный на весь мир фреймворк.
в простейшем случае ты его просто затребываешь инлайн прямо на штмл странице как жиквери https://reactjs.org/docs/add-react-to-a-website.html#add-react-in-one-minute
но если хочешь spa то надо https://facebook.github.io/create-react-app/
>>17753
пиши без классов, с хуками так сейчас все и пишут т.к. сет стейт не нужна
алсо
>Я сам несколько раз лепил html в js, но я никогда не считал это чем-то хорошим.
у тебя есть выбор или джс в штмл или штмл в джс, не вижу существенной разницы
>не вижу существенной разницы
А она есть. В случае, когда у тебя нет Реакта, тебе приходится в html время от времени скармливать готовым объектам <?=json_encode($array); ?> и всё. Реакт же сплетает в братских объятиях логику и представление.
Я сталкивался с Реактом на 5 проектах, и каждый раз меня не покидало ощущение, что я сделаю аналогичное говно на нативке страниц на 20, достаточное для конкретного проекта, и при этом избавлюсь от тонны проблем.
Vue вернул мне веру в человечество, это именно то, чем должен быть фронт фреймворк.
Если ты хочешь изучить реакт, то тебе лучше попробовать сделать какое-то нормальное SPA приложение. Реакт нафиг не нужен в форме обратной связи, ты сдурел, тащить полмегабайта яваскрипта и сборщик, который будет постоянно замедлять тебе разработку и отладку, ради формы из трех полей?
Ну например, типичный пример приложения - это бесконечная лента новостей с категориями, комментариями, лайками, работающая (насколько хватает возможностей) в оффлайн-режиме при временном пропадании интернета.
>>в простейшем случае ты его просто затребываешь инлайн прямо на штмл
Да, именно это хочу. Спасибо!
SPA пока чето не хочу.
>>17753
Ну, тут теорема эскобара.
>>17789
Не, именно хочу компонент воткнуть. Естествоиспытательский интерес.
А вообще в принципе есть разница на каком языке бекенд для SPA написан? пхп/нода/питон?
Читал что траблы с асинхронностью есть у пхп. Но честно говоря нихуя не понял. Ноду я знаю немного, и логику работы цикла событий понимаю. А пхп как в данном случае работает?
>А вообще в принципе есть разница на каком языке бекенд для SPA написан?
нет, нету
на ноде пишут т.к. можно все прямо в одной папке проекта писать - и сервер и клиентскую часть, кроме того она довольно быстрая, быстрее питона, пхп и руби
А зачем тебе асинхронность? Это только усложняет написание кода. Плюс, у PHP, в отличие от Node.JS, гораздо лучше реализована буква S (stateless) из REST, и в нем ниже вероятность утечек памяти.
Это очень сильно зависит от ситуации. Да, есть ситуации вроде проблемы 10K, где асинхронность может помочь, но если у тебя приложение просто делает селект из БД и обрабатывает данные, то работать оно будет одинаково, просто в асинхронном коде все будет сложнее и будет куча ненужных в синхронном коде промисов.
> Аноним 14/06/19 Птн 20:42:35 №141656770
>
Не ходи в qsoft, там заставят код на листочке писать, а потом посадят на битрикс. Будешь потом плакать и суицидом все закончится.
Рандом. Нужно тупо ходить по собеседованиям пока не прокнет.
У нас в шапке есть проект TestHub - это наверно немного с запасом, как мне кажется, неплохой проект для начинающего, который может показать его/её навыки.
>>17821
>>17823
Котаны, на волне интереса я решил снова поковырять ноду, сорр что тут спрашиваю, но в js тредах спрашивать про бекенд чет не лежит душа. Когда я изучал ноду, я инвалидно юзал express, абсолютно не понимая зачем нужны мидллвейры. Но я не понимаю и сейчас.
Вот допустим, у меня php MVC фреймворк. Роутинг там классический - регуляркой парсится url, и вызываются соответствующие контроллеры и экшены. Где в данном случае могут быть применены мидлвэйры в нодовском понимании?
Middleware обычно работает с запросом/ответом на уровне протокола HTTP, типичные применения:
- логгирование, статистика
- сжатие передаваемых данных
- шифрование/расшифровка кук
- кеширование на уровне HTTP протокола
- ограничение числа запросов, отсев ботов
Поищи список middleware к нужному фреймворку и посмотри, что там есть.
Почему очень много уважаемых и шарящих в этой теме людей на полном серьезе произносят PHP как ПЭ ХА ПЭ?
Это какой-то мем или им просто похуй?
На самом деле, сейчас, во время, когда существует бешеное количество информации, от которой Сократ сошел бы с ума, лол, главная проблема - 95% инфы - просто лажа, и нужно искать среди терабайтов данных действительно стоящий материал. А это все из-за того, что каждый мудак, который умеет находить последовательность чисел фибоначчи, считает себя супер-пупер гением, и решает запилить обучающий, мать его, контент. Вот и выходит, что интернет - огромная помойка, сайты - лажа, инфа - блевотина бездарей. И чтобы среди этого обезьянника встать на путь становления человеком, нужно понять, что невозможно выучить все, только лишь улучшая понимание каждого топика ежедневно, запоминая и анализируя теорию, практикуя навый код, решая появляющиеся проблемы, мы можем развиваться. Поэтому, ребята, просто удалите все, что не открывали в течении последнего месяца, оно вам не нужно, задайте вопрос себе: "Чего я хочу? Кто я сейчас и что могу?, возьмите одну тему и изучайте ее. Это же структурный подход, пользуйтесь им. Удачи. Кто еще говорил, что это наука, и она не может быть простой и понятной? Кто говорит, что программировать легко - бездари, лжецы, продажники, и прочая челядь, которая срет в уши. Самое молодое направление в науке, как оно может быть простым?
лнр
Надеюсь эти недостатки как-то решаются, с помощью фрейворков или еще чего, или просто с ними не сталкиваешься по работе. А то я не знаю как я буду с этим работать
а ты хорош
Нормальный язык PHP. И что крайне важно - заебенно подходит под свою задачу. Комьюнити годное. И доки отличные. В принципе для вэба среднего масштаба руки лучше ничего нет.
Я сейчас с нодой решил поебаться, но так как подзабыл JS ебусь с прототипами, а после буду ебаться с evemtEmmiter что бы понять что откуда в этом блядстве наследуется.
Классы в пхп гораздо симпатичнее, и это нормальные классы, с нормальной инкапсуляцией.
Работаю php+apache+mysql +phpmyadmin
Сейчас еще кину скрин формы в index.php..
Два дня ебусь, гугл не помог. Молю, с меня нефть
Для начала, исправь уязвимости в своем коде: SQL инъекцию и хранение паролей в открытом виде:
- https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
- https://github.com/codedokode/pasta/blob/master/security/password-hashing.md
Далее, у тебя есть код if ($connection == 0). Открой мануал по mysql_connect и прочитай, что она возвращает. Там должно быть сравнение не с нулем. Используя var_dump(), выведи, что возвращает функция mysqli_connect,
Далее, при использовании mysqli ты должен проверять результат вызова каждой функции, что она не вернула например null или false (что именно, написано в мануале по этой функции). Ты должен ставить if с проверкой после каждого вызова mysqli-функции. В твоем случае, нет if после
mysqli_query.
Ой, вижу, что от инъекции у тебя защита есть. Но хранение паролей все равно стоит переделать.
Также, у тебя скорее всего игнорируются часть предупреждений. Исправь в php.ini или добавь туда директиву
error_reporting = E_ALL
вместо того, что там написано, чтобы не скрывать никакие ошибки.
>>18547
>>18551
Короче прикинь, я понял ошибку.
Оказывается в запросе , к примеру (INSERT INTO `users` (`id`, `login`, `password`) VALUES ('55', 'fdf', 'fdf'));
у users, id, login, password ДОЛЖНЫ СТОЯТЬ другие ковычки ```
А в остальном обычные ''
Воот, как пофиксить эту ебалу? Что бы везде одинаковые ставить стандартные одинарные ковычки?
Может это потому что я на маке?
Косые кавычки (апострофы) по моему располагаются на отдельной клавише. Надо запомнить, где какие используются, использовать нужный вид кавычек. Мак тут не при чем. Косые кавычки используются для идентификаторов: имен таблиц, полей итд. Их можно не использовать, если в имени нет спецсимолов и оно не совпадает с ключевым словом вроде select.
http://www.mysql.ru/docs/man/Legal_names.html
Ща бы доебываться до слов..
Любые другие.
>Прочитал шапку
>Нашел 2 книги
Ты в библиотеку вкатываешься или в разработку? Толку от чтения без практики?
Я бы вообще посоветовал на ютубе посмотреть туторы, для базиса.
В книге все слишком подробно описана каждая вещь поэтому чтоб в целом узнать структуру придется прочесть 500+ страниц
Нужно больше книжек богу книжек
>Посоветуйте что-то объемное, охватывающее большинство ключевых моментов, но при этом понятное
Мне понравилась местная задачка со студентами - практически все аспекты веб-разработки там усвоить придётся. Лучше всяких книжек.
Как вообще можно учить язык без выполнения какой-то задачи я хз.
Хочу что то аналогичное моему php-фреймворку запилить - то есть роутер парсит url, и вызывает соответствующие контроллеры и экшены.
В сети все туторы на express, там это все несколько иначе. Впрочем обработчик запроса в express поддерживает регулярки. Но в JS нет аналога preg_match. В JS нет автозагрузки классов хотя может она не нужна? Ведь класс загружается однажды, и висит в памяти, так?
Сейчас хочу что то типа MVC фреймворка на ноде сделать. Что бы подгружались классы и вызывались экшены.
Не в JS тред пишу по очевидным причинам - они там учатся css динамически менять.
А так, глубже копнув ноду - всеже как же удобен пхп для своих задач. В ноде что бы данные из post вытащить - нужно поебаться.
Да, хорошая задачка. А можете порекомендовать какой-нибудь видеоурок, короткий, но дающий все базовые навыки веб разработки? Можно без фронта
>В JS нет автозагрузки классов
Так язык был придуман специально для динамической смены css. Зачем ему это?
Хочешь современных фич - используй тайп скрипт.
Нет такого урока, да и быть не может. Поначалу полезно всё подряд смотреть, пока сам не подберёшь себе нужный формат и курс.
Пишет ,что скобка лишняя:
Parse error: syntax error, unexpected ';' in /Users/akismac/Sites/bar/includes/registration.inc.php on line 19
А по факту все ок
спасибо
>Обычный джунский код.
Это ты так думаешь
>Чего в нём милого?
У тебя просто нет чувства прекрасного
>Это ты так думаешь
Ну да - я, не ты же. С чего бы мне твои мысли высказывать?
>У тебя просто нет чувства прекрасного
Ты красивее просто ничего не видел.
>Ты красивее просто ничего не видел.
Нет, просто я умею находить прекрасное и красивое в мелочах, вроде твоего "обычного джунского кода".
В каких ещё мелочах? Там даже программирования нет, как явления. Ты просто дёргаешь стандартный функционал - ни обстракций не завезли, ни знаний технологии. Это даже не джун, а скрипт-кидди.
Ещё и семёнишь.
> Но в JS нет аналога preg_match
Есть. Либо /regex/.test(string) либо string.match(/regex/)
> В JS нет автозагрузки классов
В JS нет, в ноде есть модули, которые можно экспортировать и импортировать, а в новой ноде с флагом experimental-modules можно использовать ES6 модули: https://nodejs.org/api/esm.html#esm_code_import_code_statements
Не понимаю, что ты там велосипедить собрался. Поддерживаю чужой проект на ноде третий месяц после нескольких лет Symfony/Doctrine, ощущения как будто пересадили с болида Формулы 1 на одноколёсный велосипед:
- Sequelize вместо нормальной ORM: нет автоматической генерации миграций, на foreign key предыдущие разрабы положили болт, потому что самим лень проставлять, а Sequelize не умеет их добавлять автоматически (Doctrine умеет)
- Нет способа быстро получить CRUD API, для Symfony есть API Platform, который ставится одной командой и позволяет несколькими аннотациями описать CRUD API для сущностей со всеми типами связей, фильтры, пагинацию, поиск по частичному совпадению
- Всё на JS (не TypeScript), автокомплит и go to definition в половине случаев не работают, куча нигде не описанных структур разного уровня вложенности (даже в PHP сейчас принято заворачивать такое в объекты с геттерами, чтобы хоть где-то в коде были описаны такие структуры)
- Про DI контейнер никто не слышал, поэтому норма когда в каждом консольном скрипте копипаста на 100 строк из ручной конфигурации. По этой же причине везде static методы, люди просто не знали как по-другому
Итого: Если нужно, чтобы проект не утонул под тяжестью багов и велосипедов спустя пару лет - нужно брать фреймворки, которые диктуют архитектуру и в которых многие проблемы уже решены, чтобы программисты проявляли меньше творчества. Если бы проект изначально писали мы, то взяли бы PHP и Symfony.
спасиб
> ни обстракций не завезли
Почему все "кодеры" 21 века ,любят напыщенные абстракции и считают это понтом? Причем в больших колличествам.
Вас что всех в универе по одинаковым системам образования прогоняли? Представь себе, раньше вообще небыло ООП, люди писали функциональщину. Ооп просто мода, часто ее пихают туда где от нее нет никакого смысла.
>любят напыщенные абстракции и считают это понтом?
Не понт, а необходимость. Ты бы понял это, если бы не простейшие скрипты писал, а софтверно инженерил.
Не дай бох тебе большой проект - утонешь в своём же говне из копипасты и тысяч if - else.
>раньше вообще небыло ООП
Раньше программы меньше были, и компьютеры больше.
>Ооп просто мода
Это ты сказал?
>пихают туда где от нее нет никакого смысла
Надо тебя спрашивать куда чего пихать?
А почему не Laravel?
У меня там акк с 2014 года. И я срал туда всей хуйней и по работе и свои либы и куча начатых и не законченых проектов и олдовый говнокод - куча говна.
В самом гитхабе нихуя не секу - какие-то ебучие звездочки фоловеры, хуйня-малафья. Юзаю как удаленный репозиторий онли.
Надо удалять все говно и оставить только годноту? Но я ж там с 2014 и вся история потрется((( Завести новый для портфолио? Или забить хуй и расположить новые годные проджекты?
>Не дай бох тебе большой проект - утонешь в своём же говне из копипасты и тысяч if - else.
А чем ооп спасет? Там же тоже надо знать какие у тебя функции в классе, какие нужны параметры для инициализации
А разве там нельзя скрывать репозитории?
Вообще хз - будь я на месте работодателя, то смотрел бы последний код, а не то, что ты 5 лет назад писал?
Браузер В Полном Зависоне
Я Тоже В Нем И К Тому Же Я В Ахуе
Ушел Греть Голову На Улице
Пиздос
У тебя неправильно формируются ссылки для редиректа, а именно, не происходит корректной обработки спецсимволов при подстановке в них параметров. Советую почитать урок и исправить: https://github.com/codedokode/pasta/blob/master/network/urls.md
Также, есть более удобный алгоритм обработки форм, позволяющий исключить лишние редиректы: https://github.com/codedokode/pasta/blob/master/forms.md
Во-первых, заполни информацию о себе. Во-вторых, там вроде есть pinned repositories, которые можно закрепить вверху списка. В-третьих, весь твой гитхаб изучать все равно никто не будет, так как на это нет времени.
Спасибо
>У нас есть свои уроки по основам PHP, они собраны и выложены по адресу http://codedokode.github.io/phpbook
Кто интересно это все писал
Ноунейм какой-то
> с формами, MVC, git, composer, JS,
А зачем php вообще нужен если учить JS? разве Дс не может полностью заменить php?
Ну расскажи, в чем я неправ
Объекты более гибкие чем функции. Их понимать проще, описывать и сочетать. Тупо нагляднее получается.
На Боброчане наверняка запилили. В их стиле.
>разве Дс не может полностью заменить php
Ассемблер может заменить и то и другое.
Вкатывайся в ассемблер.
А лучше вообще сразу байт-код ебаш паяльником.
>>Не понимаю, что ты там велосипедить собрался.
Да я в принципе согласен с тобой. ПХП удобнее. Ноду заюзал что бы просто с иного ракурса взглянуть на разработку в вэб.
Сейчас вот задумался - как реализовать свою цепочку мидлвэйров в пхп, в моем фреймворке. Понятно что не ради продакшена, а ради тупо интереса.
Еще мне импонирует что нода - это отдельное приложение, которое существует в памяти, а пхп это просто последовательность скриптов. Но я не могу из этого факта сделать какие либо отрицательные или положительные выводы, потому что нуб.
Скажи, у тебя есть примеры простого приложения на ноде с MVC паттерном? Потому что пока что в express все похоже на сильно модифицированное if..else, пусть и с вызовом контроллера.
Ты хуйню спросил. Ответили тебе ровно тем же - пиши на ассемблере, ведь он и пхп и жс заменить может. Что тебе не понятно?
Не пхп, а ПИ ЭЙЧ ПИ.
двачую этого
Это неправильно всё конечно, но интерес здорово убивает.
блять чел реально помощи просит, он еще молод и глуп и не видал таких залуп, ептя..ну ответить че переломишься?Себя сначала вспомни как ты начинал, или ты прям с пеленок супер пупер кодером был? ага вставил дискетку в лоб с надписью ООЛ и все охереный сец
нет
Тебе тут никто ничего расжёвывать не будет, пионер. Тем более такому наглому говну как ты.
Если ты этого ждал, то можешь выкатиться из треда вместе со своим говноблоггером.
наглое говно это ты и твой дружок хауди хо, которые на малейшую просьбу о помощи обкладывают хуями и посылают в гугл
но вообще пардон, меня настолько взбесили эти слова нашет "нафиг не спрашивай у меня, матерого программера, вон, гуглить учись" что я автоматом выдал на гора, сам не понял как это у меня получилось..мдя, а в школе вроде 4 по дойчу стояла, а тут прям как чистый берлинец выдал, сам в шоке.
>посылают в гугл
Первое, что учиться любой айтишник, будь то хоть хелпдеском, хоть админом, хоть кодером - ходить в гугл.
Иди в гугл и не задавай тупых вопросов в ИТТ треде.
У Ларавеля отличная документация. Ты от треда чего вообще хочешь? Сходи и сам почитай.
Значит тебе ещё рано такие вещи трогать.
О том тебе и говорять - расжёвывать никто не будет. Самому к этому идти надо.
Откуда тебе знать какая там документация, если ты в ней не разбираешься?
Учи матчасть и не выёбывайся.
Сообщество кодеров вообще очень токсичное.
Ты не вкатишься.
Пришёл васян - расскажите ему быстро как работать с базами на Ларе. Сам не знает нихуя и учить не хочет. Бросается говном в очевидные советы.
Ну ты и дно, дружок.
Петрович, ты?
> Еще мне импонирует что нода - это отдельное приложение, которое существует в памяти, а пхп это просто последовательность скриптов.
Одной ноды для реального приложения недостаточно. Обычно запросы принимает какой-нибудь веб-сервер вроде nginx и проксирует на ноду. Через nginx настраивается HTTPs, отдача статики, сжатие, кеширование. Я ещё настраивал ограничения для разных стран по IP на основе GeoIP. Сама нода запускается через какой-нибудь process manager вроде PM2, чтобы контролировать количество запущенных процессов, перезапускать приложение если какие-то процессы отъедают много памяти. При локальной разработке PM2 удобно использовать для автоматического перезапуска приложения после изменений в файлах. Не будешь же ты после каждого изменения в коде перезапускать сервер? В PHP всё это не нужно, из-за его stateless модели.
В итоге связка Node + Nginx + PM2 не особо удобнее PHP + Nginx + PHP-FPM. В PHP тоже есть встроенный веб-сервер для разработки: https://www.php.net/manual/en/features.commandline.webserver.php
> примеры простого приложения на ноде с MVC паттерном
Будет тоже самое, что и на PHP: https://github.com/codedokode/pasta/blob/master/arch/mvc.md#пример-mvc-приложения
Разве хранят файлы в базе? Не проще их на диск складывать, а в базу только имена посылать?
Я понимаю, что это не лучшая практика.
> База данных должна состоять как минимум из пяти таблиц, причём в таблицах должны быть предусмотрены поля для хранения данных в текстовом формате, формате даты и бинарном формате
А на словах препод сказал, что хранить надо изображения
>препод сказал, что хранить надо изображения
База Данных оптимизирована на работу с данными, а файловая система сервера, внезапно, на работу с файлами.
Ты неправильно инструменты используешь, отсюда и твои проблемы.
Так это вообще реально обойти или проще ограничить максимальный размер файла миллионом байт и забить?
Хз я с таким извращением не встречался. Ищи в доках.
Преподов, наверное, набирали как положено - что с вышкой был, и на зарплату 15 тысяч.
Изящнейшее решение задачи про таблицу умножения.
Покажу своим друганам - они обзавидуются.
Крч, мало ли кому интересно. Почему есть ограничение в миллион байт я так и не нашел и решил проблему разбиением картинки на части и отправкой по частям через хранимую процедуру.
У меня была похожая проблема с системой скуд, не мог целиком вытянуть из её базы картинки.
Что именно я тогда сделал не помню, но скорее всего тебе подойдет то что я нашел по первой ссылке на SO в гугле по запросу "mssql partial image":
mssql_query('SET TEXTSIZE 10000000'); // число в байтах нужно подбирать под твой ожидаемый размер картинок, который так или иначе должен быть ограничен твоими скриптами
И да тебе верно подсказывают что так делать _не_правильно_
Среда разработки — это...среда) Название говорит само за себя. В ней есть все, что нужно + большие возможности кастомизации. Среда сама проверяет твой код, говорит замечания по кодстайлу (можно создать самому или скачать какой-то стандарт), говорит замечания по избыточности, деплоит на сервер, имеет отладчик и дебагер, удобную визуализацию систем контроля версий...и очень много другого в одной программе с единой четкой логикой) Приходить к среде нужно тогда, когда начинаешь понимать, что хочется чего-то большего. Если саблайм кажется идеальным, то пока что не стоит его менять, как мне кажется. Это должно прийти со временем.
Все сказанное выше — чисто ИМХО и может быть ошибочным)
Надо ещё учесть, что профессиональные инструменты не бесплатны. Разве что студентам дают, но толку от этого, да и студенты у нас, вон, файлы целиком из базы тянут. С таким обучением никакая среда не спасёт.
Остальные - не студенты и нужно ебаться с кряками-мокрописьками.
Лучше уже фришные ИДЕ тогда.
Молоток тоже денег стоит. Давайте будем гвозди забивать фришным булыжником!
Очень слабое ООП. Объекты ради объектов, процедурщина на статических классах. Больше по теме читай.
Комментарии - хорошо, что есть. Но как-то перебор их писать в очевидных местах, почти через строку. Ты же сам не запутаешься в этих местах, так почему другие должны путаться?
Форматирование кода - где есть, где слетело.
Схалтурил по архитектуре неслабо - многое проигнорировал. На выходе приложение скорее напоминающее Вордпресс, чем хоть какой-то фреймворк.
Писать HTML внутри echo очень неудобно. Советую освоить операторы с двоеточием и маркер <?=. Почитай урок https://github.com/codedokode/pasta/blob/master/php/templates.md и может быть это поможет тебе сделать код еще аккуратнее.
Спасибо, пошел читать.
Да ты что советуешь-то? Инлайн пхп депрекейтед. Почитай стандарты. Шаблонизаторы и только. Инлайн код залупа полная.
В чём троллинг? Конструкция <?= применима если только в шаблонизаторе. Стандарт явно использовать только <?php и <? теги.
Вроде все выполняется, но меня смущает, что я воткнул в первый цикл какую-то левую переменную $k, чтобы foreach работал. Наверняка есть какой-то хитрый способ обойтись без нее.
ЧЯДНТ?
https://ideone.com/NHXec4
Если бы там еще что-то подобное было написано.
Это и есть встроенный в PHP шаблонизатор. Ты бы на исходный код посмотрел. Для вывода таблицы умножения не требуется устанавливать фреймворк.
>процедурщина на статических классах
Понял
>Объекты ради объектов
Вообще не понял. Я вроде пытался обьекты лишние не плодить
>Но как-то перебор их писать в очевидных местах
Пытался упросить ревью кода, чтобы анонов не напрягать особо.
>Схалтурил по архитектуре неслабо - многое проигнорировал.
В чем схалтурил? Я почитал на гите, хабре статьи, посмотрел урок на ютюбе. Сделал как написано, разделил на Модель, Вид, Контроллер. У каждой страницы своя модель, свой контроллер и html шаблон. Так же есть роутер и фронт контроллер. Что еще реализовать то нужно было?
>>20633
>>20751
Лол. Долго до меня доходило. А надо было всего лишь еще раз прочитать описание функции.
https://ideone.com/7cGGIj
Но ведь для вывода таблицы умножения и инлайн рнр код не нужно использовать.
Инлайн рнр бэд практис. Запомните это. Где-то в своих проектах для себя вы безусловно вольны использовать, что вашей душе угодно.
Ok. Я умываю руки.
foreach ($arr as $k => $v)
$k ты не используешь, так что можно оставить просто
foreach ($arr as $v)
Поправьте если ошибаюсь
Ну да, ты прав, ответ тот же самый получился. Спасибо.
https://ideone.com/t5YRa7
>>21018
Чтобы научиться писать собственные функции. Прохожу слитый курс от WebForMySelf.
Первый пошел.
Знаю, только что это классы ,с помошю которых можно строить структурированные данные , внутри которых всякие плюшки, функции. А потом это все передавать как объект. Знаю ,что это удобно, знаю синтаксис (не оч хорошо).
Ну или своими словами скажите, плиз. С меня тонны нефти (красивые картиночки)
И вот такие еще вопросы
-общее понимание шаблонов проектирования
-общее понимание типов архитектур программных решений (десктоп, клиент-сервер, многозвенные)
Я конечно понимаю суть этих вопросов, но мне кажется что будут задавать формальные вопросы с названиями таких шаблонов. А я не читал книжек по типу "паттерны проэктирования"
Наверное из-за индусов. Стало дофига кодеров маленького и среднего уровня. Они естественно стали меньше получать.
А сеньеров никогда небыло много, они в любом языке стоят дорого
Что за сратапы? Он применяется только криворукими заднеприводными ("backend") обезьянами для своих говносайтиков.
Говноед, ты?
сайты сложнее бложика
>-общее понимание шаблонов проектирования
Их только при хорошем понимании самого ООП можно разбирать. И вообще - смотри в википедии.
--общее понимание типов архитектур программных решений (десктоп, клиент-сервер, многозвенные)
Губу закатай.
>будут задавать формальные вопросы
Нет, не формальные - тебя на знание стека будут проверять.
хуй на
echo intval(11, 8); //11
echo intval('11', 8); //9
Ананасий, объясни мне, пожалуйста, почему так происходит?
В обоих случаях ведь база 8, почему со строкой оно корректно себя ведёт, а в случае c числом принимает базу за 10?
При чём, это ведь не ошибка даже, в мануале так и есть!
>в мануале так и есть!
Мануал надо целиком читать
>The base parameter has no effect unless the var parameter is a string.
Да, Антон, спасибо, я просто привык, что после примеров уже нет смысла скроллить.
>The base parameter has no effect unless the var parameter is a string.
АХАХАХАХАХАХА
Вот это, блядь, дизайн интерфейса!
Вот это, блядь, архитектура!
Ух, бля!
У меня вполне серьезный вопрос:
а как обосновывается использование пхп в новом проекте?
Я могу понять, что если есть легаси, то никуда ты не денешься.
Но, вот, пожалте, надо сделать новую ебу. И принимается решение - будем делать на пхп.
Как это решение обосновывается?
Или теперь начальник-смузихлеб не ебет себе мозги такими вопросами, а просто отстегивает бабло?
Стоимость. В вебе обычно подразумевается какая никакая поддержка проекта. Нужно вносить какие-то изменения, правки. Пхпешники стоят мало и их как говна. В любой момент можно задешево кого-то найти на проект. А если сделать на чем-то другом то можно натолкнуться на ситуацию когда придется отстегивать кучу бабла на дорогого специалиста. Любой разбирающийся в ситуации заказчик будет заказывать разроботку на пхп чтобы потом не ебаться с поиском специалистов для доработки/поддержки.
>>22096
Тут не совсем очевидный нюанс, который я не знаю как нормально объяснить. Смотри, когда у тебя есть число - 11 например - это число всегда будет одинацатью в любой системе исчисления. Но оно может по разному записываться. Одинадцать это всегда одинадцать, но оно по разному записано. Когда ты передаешь в функцию число - функция возвращает тебе это число - потому что одинадцать это всегда одинадцать. Но когда ты передаешь запись числа - тут уже зависит от переданой системы счисления. '11' это запись числа 3 в двоичной, запись девятки в восмеричной и т.п.
>В вебе обычно подразумевается какая никакая поддержка проекта
Именно поэтому я никогда не выберу пхп - там изначально будет спроектировано через хуй, реализовано через хуй, работать будет через хуй.
А когда придет человек со стороны, и ему надо будет разбираться, он поест говна в поисках "откуда вот эта хуета растет". И, в результате, свои доработки сделает через три хуя.
Но спасибо за ответ.
ну пажалуйста, ну выбери пхп!
То есть все разработчики на Питоне разрабатывают одинаково хорошо? И там нельзя принципиально сделать через жопу?
>разработчики на Питоне разрабатывают одинаково хорошо?
На фоне пыхыпыдебилов (тебя) - именно так.
Впрочем, на таком фоне это несложно, хаха.
>Питонодебил кукарекает
>обезьяна на кривом говне без нормальной инкапсуляции кукарекает
>живтоне с разметкой пробелами кудахтает
Дальнейшее развитие нашего диалога будет заключаться в твоих кукареках НЕНУЖНА.
>>>1422266
Ну дай подумать.....
У тебя есть мощный инструмент для веб - разработки, проверенный временем, с кучей готовых и отлаженных библиотек на любой вкус, с развитым сообществом. Может быть, использовать его?
Эхо от сосания хуев долбоебами, которые повелись на Ruby, до сих пор слышно в интернете.
>Эхо от сосания хуев долбоебами, которые повелись на Ruby, до сих пор слышно в интернете.
Поподробней
я понимаю, что сначала может идти плюс или пробел (а может и не идти), потом цифра восемь или семь:
^[+\s][87]
а как мне написать "дальше идут десять цифр и возможные скобки, минусы и пробелы"?
^[+\s][87][0-9]{10}
как мне в последнюю часть к цифрам добавить "сколько угодно скобок, пробелов и минусов"?
звездочка пропала
вот так выглядит
>[+\s]звезда
это съест любую комбинацию из пробельных и плюсов, типа " ++ +++++ +++ ", оно тебе надо?
Зачем ты вообще на него внимание обращаешь? Пора бы привыкнуть, что регулярно тут дауны всякие залетают. Просто игнорируй их.
>Именно поэтому я никогда не выберу пхп - там изначально будет спроектировано через хуй, реализовано через хуй, работать будет через хуй
всегда есть выбор
почему я не могу, например, банально сперва вырезать строковыми функциями все пробелы, скобки, минусы и тире, и после этого уже работать чисто с цифрами? Понятно, что урок с регулярками создан чтобы научиться в нем разбираться, но как дело на практике обстоит? В крупных организациях имеется ввиду. Есть хоть какой то прирост производительности/скорости загрузки, если поставить перед регуляркой строковую функцию с авто-заменой str_replace? Конкретные цифры. Без снобизма и демагогии аля "ну отцы так делали и нам завещали".
>но как дело на практике обстоит?
Обычно регулярками не пользуются. Но какой из тебя программист, если ты шаблон строки накидать не можешь? Сегодня ты регулярки пропустил, а завтра половину ООП не осилил и забил, и не можешь найти работу, потому, что все отлично видят твой уровень.
Понял, спасибо
>Обычно регулярками не пользуются.
У меня часто возникают ситуации, когда проще накидать регулярку, чем ебаться с множественными заменами и поисками.
мимо-битриксоблядь
>этот тред с этими безмозглыми быдлодебилами (тобой)
>с развитым сообществом
Шутку понял.
Смешно.
Ну как бы да - на разных проектах по разному может быть.
Правда, говорят, что они медленные и плохо читаются, поэтому лучше не увлекаться.
>этот тред с этими безмозглыми быдлодебилами
>Спизданул даун с дипломом программиста перфокарт, сосат всем.
>дипломом
>перфокарт
Унижаемый за тупость и в итоге вылетевший студентик ну совсем не палится.
Пикрелейтед средняя разница на 100к сторках, на медленной машине.
Код тут https://ideone.com/CPVWDU
Есть список компаний, нужно из всех названий сформировать группы компаний по похожести. Группы, где совпадают 3/4/5 и тд. слов из названия. Сначала я получу массив, где каждое значение это будет массив слов из названия компаний. Как потом эти массивы слов компоновать по пересечению значений?
Гуглишь расстояние левенштейна и кластерный анализ. Строишь матрицу растояний между названиями и потом обрабатываешь каким-то методом кластеризации. Если тебе нужно именно по совпадающим словам то вместо растояний используешь количество не совпадающих слов в названиях, или что-то типа того.
class JobSeeker {
public static function questClassSearch(): bool {
return someFunc(function (string $c): void {
someFunc(self::class, $c);
});
}
}
$s = new JobSeeker();
assert(
!class_exists('Foo')
&& !class_exists('Bar\Foo')
&& true === JobSeeker::questClassSearch()
&& class_exists('Foo')
&& class_exists('Bar\Foo')
);
Что должно быть на месте someFunc?
Если тебе пофигу на оптимизацию, то алгоритм может быть следующим
Формируешь сначала все возможные пары пересечений, где слово пересечение это ключ. Если в массиве ключ уже есть,
Google => company1, company2
Google2 => company1, company2, company3
Google3 => company1, company2, company3
Google4 => company1, company2, company3
Google5 => company1, company2, company3, company4, company5
Дальше идешь по новому массиву пар, и так же смотришь пересечения, у тебя получится
google_google2 => company1, company2
google4_google5 => company1, company2, company4, company5
И так далее, while пока длина ключа не будет совпадать c количеством слов в самом большом названии
Получаешь массив из названий компаний.
Из него получаешь словарь из отдельных слов каждой компании, подсчитывая сколько встречается каждое слово.
У тебя получится словарь вида "слово:количество совпадений".
Сортируешь его по количеству совпадений в убывающем порядке.
Используя этот словарь распределяешь компании из первого массива по группам.
Это стиль старых пердунов - "the one true brace style". Пошел из С. А так как пхп на С написан то наверное перетек и в сам пхп. Его еще в ядре линкса используют, например, так как тоже на С пишется.
Удвою. Хорошо видно объявление и последующий блок кода.
Утрою.
>а во всех нормальных языках нет
Зато в нормальных языках и функции и замыкания одинаково выглядят, а в ПХП объявление - с новой скобкой, замыкание - без.
Очень дело вкуса. Для меня это говно просто проеб вертикальной строчки на хуйню.
В нормальных языках и в php замыкания обозначают разные сущности.
никто себе конкурентов не хочет. такие вкатывальщики сначала вопросы на дваче задают, а потом приходят к тебе на работу и начинают доёбывать вопросами уже в рабочее время.
Кстати я уже начал встречать js таким же образом оформленный, именно в недавних компонентах/сниппетах
решил написать свой валидатор кода. раскритикуйте, на толкните на что нибудь умное?
- почему не phpmd - потому что слишком долгое время обработки.
Ну тупенький значит.
Не от большого ума ты задаёшь незнакомым людям вопросы - на что въебать тебе последующие 5-10 лет.
Что выучил за месяц, что спрашивали? Какая зарплата?
Ты занимаешься интересным делом, но, увы, немного неправильно. Я сейчас не могу подробно все разобрать, так как у меня много дел до понедельника, но немного прокомментирую.
Ты разбираешь исходный код регулярками тут: https://github.com/ta-tikoma/phpins/blob/master/app/Validators/IDefinedThisVariable.php
Это неправильно. Например, твой разборщик кода "увидит" функцию, даже если она закомментирована или находится внутри строкового литерала:
// function x() {}
$s = 'function x() {}';
Также, твоя регулярка некорректно определяет границы заголовка функций, например:
function[^\(]+\(([^)]+)\)
Тут не учтено, что определение функции может содержать вложенные скобки:
function x($a = array())
Также, у тебя нет поддержки синтаксиса декомпозиции массивов:
[$a, $b] = [1, 2];
И не учитывается то, что некоторые функции принимают аргументы по ссылке и могут таким образом "создавать" переменные:
preg_match("/x/", "x", $m);
Правильный способ разбирать код - это лексический и синтаксический анализ. Лексический анализ - это разбор исходного кода на список лексем (токенов), где каждый токен является неделимым кусочком кода. Например:
if ($a == f($b)) {
echo $x; // yes
}
превратится в: 'if', '(', '$a', '==', 'f', '(', '$b', ')', ')', '{', 'echo', '$x', ';', '}'
На этом этапе удаляются лишние пробелы и комментарии.
Для проведения лексического анализа в PHP есть готовые функции: https://php.net/manual/ru/book.tokenizer.php
Затем производится синтаксический анализ. Он берет список лексем и используя набор правил, воссоздает синтаксическое дерево (AST) исходного кода. Набор правил называется грамматикой, и он описывает все возможные конструкции, которые могут встретиться в коде. Грамматика описывает, как из неделимых кусочков (их еще называют терминалами) собираются сложные конструкции, а из них - еще более сложные.
Вообще, в теории можно использовать синтаксический анализ сразу, без этапа лексического анализа, работая с отдельными символами, а не токенами, но обычно все же это делают в два этапа.
Ну например, правило может выглядеть так:
конструкция if - это: токен if, за ним круглая скобка, за ней выражение, за ним круглая скобка, за ней фигурная, за ней команды, за ней закрывающая фигурная.
или, если записать это правило в Расширенной форме Бэкуса — Наура (EBNF):
if_construct = 'if', '(', expression, ')', '{', 'sentences', '}'
Здесь expression и sentences - это отсылки к правилам, описывающим из чего состоит выражение и набор команд. Выражение можно описать как-то так:
# От порядка записей зависит приоритет операторов
expression = function_call | multiplication | sum | comparison | variable-like | literal
function_call = indentifier, '(', [ parameter, { ",", parameter } ], ')'
multipication = expression, ( '*' | '/' ), expression
sum = expression, ( '+' | '-' ), expression
comparison = expression, ( '<', '>', '==', '===', '!=', '!==', '<=', '>=' ), expression
variable-like = array_element | variable
literal = string | number | boolean_constant
Вообще, конечно, правила PHP чуть сложнее, и они позволяют пропускать фигурные скобки, а также допускают операторы elseif/else, так что запись можно усложнить:
if_construct = 'if', '(', expression, ')', body, { 'elseif', '(', expression, ')', body }, [ 'else', body ]
body = '{', many_sentences, '}' | one_sentence
Если тебе не очень понятен синтаксис EBNF, то погугли. Кроме EBNF, есть еще другие формы записи грамматик: ABNF, BNF. Они есть в википедии. Но общая суть в том, что они описывают сборку сложных конструкций языка из более простых. Программа состоит из определений и команд, определения состоят из более мелких частей, и так до неделимых частей - терминалов.
Парсер, используя правила грамматики, разбирает исходный код и воссоздает синтаксическое дерево (дерево - это структура, состоящая из узлов, и у каждого узла могут быть дочерние узлы, и есть один главный корень, с которого начинается дерево). Корнем дерева тут будет "Программа". Внутри нее будут определения классов, функций, и блоки кода. Внутри функции будет список ее аргументов, и тело функции. Тело будет содержать команды, они могут содержать выражения и вложенные блоки кода, и так далее. Дерево будет выглядеть так:
Программа:
- Определение функции
-- Имя: f1
-- Аргументы:
--- Аргумент
---- Имя: $a
---- Тайп-хинт: int
---- Значение по умолчанию:
----- Константа класса: SomeClass::SOME_CONSTANT
-- Тело:
--- Приваивание:
---- Переменная: $a
---- Значение: 1
Парсер, который занимается синтаксическим разбором, писать не надо - есть готовые генераторы парсеров, которым достаточно дать грамматику языка на вход: PHP-Yacc, PHP-peg, Lemon-PHP.
То есть, тебе достаточно описать грамматику PHP и ты сможешь парсить исходный код в дерево AST. После чего ты уже можешь легко обходить его, искать там узлы определенного типа (например: функции) и внутри них анализировать использование переменных. Правда, описать грамматику PHP довольно сложно. Где взять полный набор правил? Они есть в исходном коде PHP:
https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y
Разработчики PHP использовали сишный генератор парсеров Bison и описали грамматику в нужной ему форме. Полистай файл, и ты увидишь там описание грамматики.
Также, разработчики Facebook пытались составить спецификацию PHP, но похоже, забросили ее: https://github.com/php/php-langspec/blob/a610388d637b67294925d3b1dbbc0a54b7d94428/spec/php-spec-draft.md
Но хорошая новость состоит в том, что и это делать не надо. Есть готовый парсер кода PHP: https://github.com/nikic/PHP-Parser
Соответственно, тебе стоит попробовать взять его за основу. Он даст более качественный результат. С его помощью можно даже делать автоматический рефакторинг кода.
И немного о том, как искать неопределенные переменные. Твой анализатор ведь не учитывает вложенность и порядок выполнения кода, например:
if (...) {
$a = 1;
}
$a = f($a);
echo $a;
Тут переменная $a может быть не определена, и это ошибка. А если мы напишем так:
if (...) {
$a = 1;
} else {
return;
}
$a = f($a);
echo $a;
То ошибки уже нет. Для анализа потока выполнения можно использовать преобразование кода в SSA-форму ( https://ru.wikipedia.org/wiki/SSA ). SSA = static single assignment - это когда значение переменной присваивается только в одной команде, а если в коде это делается несколько раз, то мы делаем несколько копий переменной, добавляя суффиксы ($a_1, $a_2 и тд).
Например, код:
$a = 1;
$a ++;
echo $a;
превращается в:
$a_1 = 1;
$a_2 = $a_1 + 1;
echo $a_2;
Если же переменная определяется внутри цикла или if, то мы используем конструкцию "Ф-функция", которая говорит о том, что переменная может получить одно из нескольких значений:
if (...) {
$a = 1;
} else {
$a = 2;
}
echo $a;
превращается в:
$a_1 = 1;
$a_2 = 2;
$a_3 = Ф($a_1, $a_2);
echo $a_3;
Ну и наконец, вот еще пример цикла:
$a = 1;
while ($a < 10) {
$a ++;
}
echo $a;
Он превратится в:
$a_1 = 1;
$a_2 = Ф($a_1, $a_2) + 1;
echo $a_2;
SSA удобна тем, что мы можем идти по цепочке команд обратно, и анализировать, какие значения могут попасть в ту или иную переменную. Так как каждая переменная уникальна, нам не нужно анализировать порядок выполнения кода, достаточно просто найти строку с ее определением. SSA-форма позволяет, например, определить, значения какого типа могут содержаться в переменной, определить какие-то ограничения (то, что оно целое или что оно неотрицательное). Представь, какие тщательные проверки можно было бы делать (обнаруживать, например, что отрицательные числа передаются в функцию, которая принимает только положительные).
Ты бы мог использовать ее для поиска неопределенных переменных.
В общем. советую почитать и погуглить про:
- лексический анализ
- синтаксический анализ
- AST (abstract syntax tree)
- форма представления кода SSA
- формы записи грамматик BNF/ABNF/EBNF
- https://github.com/nikic/PHP-Parser
На первый взгляд это может выглядеть сложно, но работать с кодом в такой форме будет удобнее. Я когда-то пробовал написать простой анализатор кода для поиска ошибок, как раз на основе синтаксического анализа: https://github.com/codedokode/source-analyzer Там правда тоже есть регулярки, но есть и использование AST: https://github.com/codedokode/source-analyzer/blob/master/src/Common/TypeHintMissingChecker.php
Вообще, если бы ты мог написать преобразователь AST в SSA, это могло бы стать полезной отдельной библиотекой для анализа кода.
Если есть вопросы - задавай.
Ты занимаешься интересным делом, но, увы, немного неправильно. Я сейчас не могу подробно все разобрать, так как у меня много дел до понедельника, но немного прокомментирую.
Ты разбираешь исходный код регулярками тут: https://github.com/ta-tikoma/phpins/blob/master/app/Validators/IDefinedThisVariable.php
Это неправильно. Например, твой разборщик кода "увидит" функцию, даже если она закомментирована или находится внутри строкового литерала:
// function x() {}
$s = 'function x() {}';
Также, твоя регулярка некорректно определяет границы заголовка функций, например:
function[^\(]+\(([^)]+)\)
Тут не учтено, что определение функции может содержать вложенные скобки:
function x($a = array())
Также, у тебя нет поддержки синтаксиса декомпозиции массивов:
[$a, $b] = [1, 2];
И не учитывается то, что некоторые функции принимают аргументы по ссылке и могут таким образом "создавать" переменные:
preg_match("/x/", "x", $m);
Правильный способ разбирать код - это лексический и синтаксический анализ. Лексический анализ - это разбор исходного кода на список лексем (токенов), где каждый токен является неделимым кусочком кода. Например:
if ($a == f($b)) {
echo $x; // yes
}
превратится в: 'if', '(', '$a', '==', 'f', '(', '$b', ')', ')', '{', 'echo', '$x', ';', '}'
На этом этапе удаляются лишние пробелы и комментарии.
Для проведения лексического анализа в PHP есть готовые функции: https://php.net/manual/ru/book.tokenizer.php
Затем производится синтаксический анализ. Он берет список лексем и используя набор правил, воссоздает синтаксическое дерево (AST) исходного кода. Набор правил называется грамматикой, и он описывает все возможные конструкции, которые могут встретиться в коде. Грамматика описывает, как из неделимых кусочков (их еще называют терминалами) собираются сложные конструкции, а из них - еще более сложные.
Вообще, в теории можно использовать синтаксический анализ сразу, без этапа лексического анализа, работая с отдельными символами, а не токенами, но обычно все же это делают в два этапа.
Ну например, правило может выглядеть так:
конструкция if - это: токен if, за ним круглая скобка, за ней выражение, за ним круглая скобка, за ней фигурная, за ней команды, за ней закрывающая фигурная.
или, если записать это правило в Расширенной форме Бэкуса — Наура (EBNF):
if_construct = 'if', '(', expression, ')', '{', 'sentences', '}'
Здесь expression и sentences - это отсылки к правилам, описывающим из чего состоит выражение и набор команд. Выражение можно описать как-то так:
# От порядка записей зависит приоритет операторов
expression = function_call | multiplication | sum | comparison | variable-like | literal
function_call = indentifier, '(', [ parameter, { ",", parameter } ], ')'
multipication = expression, ( '*' | '/' ), expression
sum = expression, ( '+' | '-' ), expression
comparison = expression, ( '<', '>', '==', '===', '!=', '!==', '<=', '>=' ), expression
variable-like = array_element | variable
literal = string | number | boolean_constant
Вообще, конечно, правила PHP чуть сложнее, и они позволяют пропускать фигурные скобки, а также допускают операторы elseif/else, так что запись можно усложнить:
if_construct = 'if', '(', expression, ')', body, { 'elseif', '(', expression, ')', body }, [ 'else', body ]
body = '{', many_sentences, '}' | one_sentence
Если тебе не очень понятен синтаксис EBNF, то погугли. Кроме EBNF, есть еще другие формы записи грамматик: ABNF, BNF. Они есть в википедии. Но общая суть в том, что они описывают сборку сложных конструкций языка из более простых. Программа состоит из определений и команд, определения состоят из более мелких частей, и так до неделимых частей - терминалов.
Парсер, используя правила грамматики, разбирает исходный код и воссоздает синтаксическое дерево (дерево - это структура, состоящая из узлов, и у каждого узла могут быть дочерние узлы, и есть один главный корень, с которого начинается дерево). Корнем дерева тут будет "Программа". Внутри нее будут определения классов, функций, и блоки кода. Внутри функции будет список ее аргументов, и тело функции. Тело будет содержать команды, они могут содержать выражения и вложенные блоки кода, и так далее. Дерево будет выглядеть так:
Программа:
- Определение функции
-- Имя: f1
-- Аргументы:
--- Аргумент
---- Имя: $a
---- Тайп-хинт: int
---- Значение по умолчанию:
----- Константа класса: SomeClass::SOME_CONSTANT
-- Тело:
--- Приваивание:
---- Переменная: $a
---- Значение: 1
Парсер, который занимается синтаксическим разбором, писать не надо - есть готовые генераторы парсеров, которым достаточно дать грамматику языка на вход: PHP-Yacc, PHP-peg, Lemon-PHP.
То есть, тебе достаточно описать грамматику PHP и ты сможешь парсить исходный код в дерево AST. После чего ты уже можешь легко обходить его, искать там узлы определенного типа (например: функции) и внутри них анализировать использование переменных. Правда, описать грамматику PHP довольно сложно. Где взять полный набор правил? Они есть в исходном коде PHP:
https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y
Разработчики PHP использовали сишный генератор парсеров Bison и описали грамматику в нужной ему форме. Полистай файл, и ты увидишь там описание грамматики.
Также, разработчики Facebook пытались составить спецификацию PHP, но похоже, забросили ее: https://github.com/php/php-langspec/blob/a610388d637b67294925d3b1dbbc0a54b7d94428/spec/php-spec-draft.md
Но хорошая новость состоит в том, что и это делать не надо. Есть готовый парсер кода PHP: https://github.com/nikic/PHP-Parser
Соответственно, тебе стоит попробовать взять его за основу. Он даст более качественный результат. С его помощью можно даже делать автоматический рефакторинг кода.
И немного о том, как искать неопределенные переменные. Твой анализатор ведь не учитывает вложенность и порядок выполнения кода, например:
if (...) {
$a = 1;
}
$a = f($a);
echo $a;
Тут переменная $a может быть не определена, и это ошибка. А если мы напишем так:
if (...) {
$a = 1;
} else {
return;
}
$a = f($a);
echo $a;
То ошибки уже нет. Для анализа потока выполнения можно использовать преобразование кода в SSA-форму ( https://ru.wikipedia.org/wiki/SSA ). SSA = static single assignment - это когда значение переменной присваивается только в одной команде, а если в коде это делается несколько раз, то мы делаем несколько копий переменной, добавляя суффиксы ($a_1, $a_2 и тд).
Например, код:
$a = 1;
$a ++;
echo $a;
превращается в:
$a_1 = 1;
$a_2 = $a_1 + 1;
echo $a_2;
Если же переменная определяется внутри цикла или if, то мы используем конструкцию "Ф-функция", которая говорит о том, что переменная может получить одно из нескольких значений:
if (...) {
$a = 1;
} else {
$a = 2;
}
echo $a;
превращается в:
$a_1 = 1;
$a_2 = 2;
$a_3 = Ф($a_1, $a_2);
echo $a_3;
Ну и наконец, вот еще пример цикла:
$a = 1;
while ($a < 10) {
$a ++;
}
echo $a;
Он превратится в:
$a_1 = 1;
$a_2 = Ф($a_1, $a_2) + 1;
echo $a_2;
SSA удобна тем, что мы можем идти по цепочке команд обратно, и анализировать, какие значения могут попасть в ту или иную переменную. Так как каждая переменная уникальна, нам не нужно анализировать порядок выполнения кода, достаточно просто найти строку с ее определением. SSA-форма позволяет, например, определить, значения какого типа могут содержаться в переменной, определить какие-то ограничения (то, что оно целое или что оно неотрицательное). Представь, какие тщательные проверки можно было бы делать (обнаруживать, например, что отрицательные числа передаются в функцию, которая принимает только положительные).
Ты бы мог использовать ее для поиска неопределенных переменных.
В общем. советую почитать и погуглить про:
- лексический анализ
- синтаксический анализ
- AST (abstract syntax tree)
- форма представления кода SSA
- формы записи грамматик BNF/ABNF/EBNF
- https://github.com/nikic/PHP-Parser
На первый взгляд это может выглядеть сложно, но работать с кодом в такой форме будет удобнее. Я когда-то пробовал написать простой анализатор кода для поиска ошибок, как раз на основе синтаксического анализа: https://github.com/codedokode/source-analyzer Там правда тоже есть регулярки, но есть и использование AST: https://github.com/codedokode/source-analyzer/blob/master/src/Common/TypeHintMissingChecker.php
Вообще, если бы ты мог написать преобразователь AST в SSA, это могло бы стать полезной отдельной библиотекой для анализа кода.
Если есть вопросы - задавай.
спасибо за столь подробный ответ.
>Это неправильно. Например, твой разборщик кода "увидит" функцию, даже если она закомментирована или находится внутри строкового литерала:
>
>// function x() {}
>$s = 'function x() {}';
я предварительно очищаю файл от строк и комментариев. Так что при необходимости валидатор может забрать "стерильное" содержимое.
> сишный генератор парсеров Bison
помню раньше тоже им баловался
> Есть готовый парсер кода PHP: https://github.com/nikic/PHP-Parser
в процессе подбора инструментов так же встречал эту библиотеку, но отмел её наверное из-за лени разбираться а оправдал себя тем что на регулярках быстрее отработает(введу их небольшого количества), но вероятно ты прав и стоит вернуться к ней. Основной целью ставил для себя именно быстроту выполнения - да бы при сохранении файла(а возможно и в процессе его написания) мгновенно видеть результат.
Хотел ввести и di чтоб ввести возможность автоподгрузки валидаторов как сторонних пакетов. но все суммарно упиралось в мысли на сколько мне это урежет скорость.
Рандомный как в дешевом хостеле
Проще всего создать ssh ключ и в composer.json
"repositories": [
{
"type": "git",
"url": "
}
],
"require": {
"<nikname>/<projectname>": "dev-master"
}
Понял.
Спасибо.
Кто-нибудь пояснит что конкретно делают эти точки, про них ничего нету в гайде, а без них код не выполняется??
> ТОЧКА $x $x ТОЧКА "\n\
<?php
for ($x=1; $x<=10; $x++ ){
echo "$x$x=".$x*$x."\n";
}
Спасибо, почитаю.
Там регуляркой ещё валидировать можно.
Там ещё была задача - написать генерацию броска игральной кости.
Он мёртв?
Давай читать шапку вместе:
>С чего начать
У нас есть свои уроки по основам PHP, они собраны и выложены по адресу http://codedokode.github.io/phpbook (вас отредиректит на другой домен, не читайте, не сохраняйте, не запоминайте его, он временный). Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь...
делаю уроки про регулярки, конкретно - про исправление ошибок в предложении: поставить пробелы после запятых, точек, восклицательных знаков. Это все сделал через обычный preg_replace и ассоциативный массив правил. Но уперся в следующее "поменять маленькие буквы, с которых начинается предложение, на большие". Скажи, анон, я могу это сделать через чистый preg_replace? Получается только через preg_replace_callback, потому что надо возвращать mb_strtoupper.
Как мне сказать обычному preg_replacу "Подними пожалуйста $1 в верхний регистр"?
почему?
Спасибо
Вчера ещё всё нормально работало, на хостинге тоже всё отлично, а на локальной машине - хттп еррор 500. Ни копию с хостинга не берёт, ни старую свою.
Ну что за параша?
Убедись что ошибка именно там, а не в коком-то скрипте (заменяй их на пустые файлы где возможно).
Давааай
На сколько возьмут.
Анону без опыта работы, с парой самопильных проектов "для себя" и хорошим знанием laravel/doctrine паттернов, предлагали от 55 до 65 в Питере.
oh shit
echo $param;
echo $two;
}
fn("???");
Поясните мне, как правильно указывать дефолтные значения параметров таким образом, чтобы их можно было скипнуть?
В JS вроде как есть undefined, если его передать в качестве аргумента, то используется дефолтный. А как быть в PHP, если функция с двумя аргументами и я хочу указать второй, а у первого использовать дефолтное значение?
Ставить дефолтом null и самостоятельно городить if (is_null())?
Если неясно выразился, уточню. Что мне нужно написать, чтобы вывелось default3? Конечно же, вариант fn("default", "3"); не предлагать. Неужели единственный вариант
if (is_null($param)) {$param = "default"}
и передавать null?
$param = $param ?? 'default' ?
А, блядь, ну так ты всё правильно сделал.
public function __construct($display_errors = 1, $error_reporting = E_ALL) {
$this->display_errors = $display_errors;
$this->error_reporting = $error_reporting;
}
Если я в конструктор нихуя не передам - создастся с этими значениями по умолчанию. Просто нихуя не делай и всё
Я из мухосрани. Но работаю удаленно. Хз в питере я бы ниже 80 даже не рассматривал наверное. Даже 80 - дно, это так перебиться
Проверять на null не самая лучшая мысль т.к. нуль это по своему тоже значение.
Может быть ситуация где дефолтное значение нуль, а пользователь твоего апи хочет давать не нуль а именно положиться на дефолтный вариант.
Ты можешь брать аргументом массив с именоваными ключами и в описании функции хинтить его структуру; вместо просто использования аргументов тебе придется разборку массива делать
но зато теперь ты можешь делать array_key_eixsts, и на сто процентов отличать нуль и желание именно скипнуть параметр.
Другой вариант использовать dto + builder (опционально fluid interface) в качестве аргумента твоей функции, обязательные параметры требуешь в конструкторе, необязательные чекать через isSet (внутри dto придется хранить флаги).
В качестве финальной альтернативы пересмотри свой подход и подумай действительно ли тебе необходим подобный опциональный аргумент не в хвосте их списка (как ты понимаешь в хвосте можно иметь настоящие опциональные аргументы). Возможно, твой подход к построению апи не самый прозрачный и пользоваться им будет неудобно.
>fn(, '23'); //23
А не пиздите ли вы часом, молодой человек? Моя IDE говорит, что в этой строке ошибка и , не может идти после нихуя.
Не обязательно использовать null. Можно условиться, что "FUCKIN_DEFAULT_VALUE" - магическое слово для подстановки дефолтного значения. Хоть класс под это дело сделать можно. Просто мне кажется это странным: в js я могу сделать fn(undefined, undefined, true, false). В PHP - нет.
Суть там в следующем. Есть класс c1, в нём метод c1m с 4 параметрами: tA, tB, f1, f2. Этот метод не имеет абсолютно никакого смысла без указания tA и tB, поэтому им заданы стандартные значения прямо в методе c1m.
При этом, есть класс c0. Сейчас положение дел таково, что c0 будто "родительский" (РЕЧЬ НЕ О НАСЛЕДОВАНИИ) класс для c1. Если говорить прямо: c0 - список сообщений, c1 - непосредственно сообщение.
То есть, внутри c0 находится массив с кучей объектов класса c1.
При этом, c0 имеет метод, который дёргает у каждого объекта c1 метод c1m. И весьма ожидаема ситуация, в которой пользователь захочет изменить значение tB, но оставить дефолтное tA. Спрашивается, как быть?
Я могу перенести объявление стандартных значений из метода c1m в класс c0. Тогда я смогу сделать так: c1m (a1, a2, a3, a4). Изначально в a1 лежит дефолтное значение tA, в a2 tB и т. д. Соответственно, если кто-то хочет заменить tB, достаточно просто изменить a2, а a1 оставить как было.
Но мне не кажется это хорошей идеей. Ведь, повторюсь, метод c1m не имеет смысла без указания tA и tB. Значит если так случится, что c1->c1m попробуют вызвать откуда либо кроме c0 - дефолтных параметров не будет.
Сейчас я остановился на дублировании. То есть:
1. Дефолтные tA, tB, f1, f2 указаны в объявлении c1m
2. В c0 заданы a1, a2, a3, a4 с теми же самыми дефолтными значениями, c1m вызывается как c1m(a1, a2, a3, a4). Соответственно, если мы хотим изменить tB, а остальное оставить дефолтом - меняем свойство a2 в классе c0.
Но это явно кривое решение. Ведь мы не в самом деле используем дефолтные значения, а просто дублируем их.
Обычно необязательные аргументы просто указывают в конце. Какого-то специального способа нету.
Опции в PHP обычно делают через массив:
function x($a, $b, array $options = [])
В твоем случае это именно опции.
Складывается ощущение что ЗП больше зависит от того на сколько программист подскакивает кабанчиком, а не от скилла в известных пределах.
Звучит как оправдание. А не в том ли проблема, что некоторые программисты слишком увлекаются изобретением архитектур и лишним прилизыванием кода вместо быстрого выполнения задач бизнеса?
Поэтому бэк интереснее.
PHPMailer - библиотека для PHP, которая умеет отправлять письма как через SMTP, так и через mail(). Например, у меня в одном проекте было так: создаётся объект PHPMailer, в него выставляются все настройки, кроме метода отправки. А дальше ставится email, если не отправилось - SMTP (чтобы лишний раз не грузить SMTP сервер, но и слишком полагаться на mail() тоже не стоит).
SMTP - протокол для передачи почты. Соответственно, SMTP-сервер - почтовый сервер, который поддерживает SMTP (ни разу не встречал публичный сервер, который не поддерживал бы).
Дефолтные почтовики (yandex, gmail, mail) имеют свои SMTP-сервера. То есть, для отправки почты через свой скрипт ты можешь завести почту на яндексе и слать письма через их SMTP-сервер. Единственное что: иногда они выёбываются и закрывают доступ свежерегам, но это легко решается. Просто пишешь в поддержку письмо, дескать так и так, я использую эту почту для отправки заказов с сайта на свой ящик (или что там у тебя), розбаньте.
Разумеется, бесплатные сервера имеют кучу ограничений. Если у тебя дай бог сотня писем в день - ты с ними не столкнёшься. Но если собираешься строчить по 10 писем в минуту, то, скорее всего, тебя попросят.
Повторюсь, SMTP - протокол. Этот же самый протокол используется в почтовых клиентах. То есть, яндекс не видит особой разницы между PHPMailer и thunderbird (ну, конечно, может вычислить по ip/заголовкам и т. д, но сейчас речь не об этом).
Еще для отправки писем есть такие писечки https://www.mailgun.com/pricing
Чёт не дописалось немного. Писечка эта бесплатна, если меньше 10k писем и меньше 100 получателей в месяц вроде как.
Для себя подведу итог : smtp - протокол.
PHPMailer - реализация этого стандарта - то есть самодостаточный smtp-сервер, с него могу отправлять тыщи писем Бузовой.
Но так же я могу воспользоваться публичными серверами или же smtp - серверами, которые ставятся как отдельное ПО на мой сервер, но мне это не нужно, потому что есть у меня PHPMailer.
Я все правильно понял?
Это я сам для себя разжевываю что бы понимать .
Начнём с того, что я сам не весь из себя знаток данного вопроса, поэтому в деталях могу что-то напутать. Но всё же
>smtp - протокол
Верно. Даже в википедии об этом написано.
>PHPMailer - реализация этого стандарта
Можно сказать и так, но
>то есть самодостаточный smtp-сервер
НЕТ.
Приведу простую аналогию. Твой браузер работает по протоколу HTTP. Можно сказать, что в браузер - реализация HTTP (ну, по крайней мере, браузер умеет его читать и понимать). Значит ли это, что твой хром - веб-сервер? Нет.
Также и здесь. PHPMailer - библиотека для подключения к SMTP-серверам. Клиент, если угодно.
Если у тебя шаред-хостинг, то там, как правило, всё уже настроено и ты можешь отправлять письма через функцию mail. Насчёт неё я подробностей не знаю: в linux она дёргает sendmail, как в windows работает хз.
Если ты попытаешься с локалхоста отправить письмо через mail(), то, скорее всего, соснёшь хуйцов. Тем не менее, ты можешь используя библиотеку PHPMailer подключиться к чужому SMTP-серверу и послать письмо через него.
Вообще, почта - тема относительно сложная. Настроить веб-сервер - элементарно, а вот правильно настроить почтовый сервер - так еще задача. Поэтому либо используй mail(), если есть возможность, либо чужой SMTP. Ну, либо гугл в зубы и учись настраивать-админстрировать почтовые сервера.
С 4к и 40к вроде работает нормально, но если ставить 1к, как в примере, то всё идёт по пизде.
Так же последний месяц не считает, а дублирует.
ссылка https://ideone.com/04Pa5z
Хо решения правильный или я совсем хуйню сделал?
Анон, я сам новичок, но ты все правильно и красиво сделал. Единственно что - у тебя нижняя проверка, где
>если баланс отрицательный - хватит считать
всегда будет в конце выполняться, поэтому и дублирование идет
Попробуй чуть-чуть изменить код, вообще не сильно привязывайся к коду, вон там даже в учебнике написано что есть намеренные ошибки
>Подсказка: код, который дан как образец, содержит намеренно внесенные ошибки.
вот например
https://pastebin.com/gRj4H8MX
>>Integrated SMTP support - send without a local mail server
То есть PHPmailer самостоятельно может формировать согласно протоколу smtp и отправлять письма, но если винда ( в ней с помощью mail() нельзя отправить по дефолту) и нет локального smtp -сервера то отправить не сможет. Либо цепляться к smtp -серверу (локальному или в сети), и уже сервер отправляет/принимает письма.
Так?
На дот нете, на жабе, на жоэс, даже на питоне че-то там пишут, но пых мертв.
Работы нет
Ты бы вакансиями пруфнул
Крякни да и все, что как маленький-то. Заработаешь копейку с помощью шторма - купишь лицензию, все по-честному. Вообще это оффтоп, соре
в Швятой живее всех
Лёха, успокойся
Нет, крякать не буду, это гавно.
Ты старый, видимо?
До 40 лет ето юноша?
Я тоже не могу решить задачи, очень сложно идет.
>>27264
Два дебила - это сила.
А потом местные дурачки удивляются почему это сложилось мнение что пыхыпы для олигофренов.
Вот именно поэтому.
Нет, не тролю, рили не понел, что нужно сделать. Полагаю задать диапазон от 1 к 6 (задача на выпадения кубика). Но как это сделать не ебу.
>>27292 (Del)
Вашу шутку я оценил.
Иди на node.js напиши.А мы посмотрим как тебя асинхронность и промисы в жопу выебут. А в рот тебя выебет модуль http.
я сам на ноде с рубой пишу, но скорее всего - за работу с базами данных. Но это у нас - у королей
switch ($order) {
case 'id':
$result .= 'id';
break;
case 'name':
$result .= 'name';
break;
case 'surname':
$result .= 'surname';
break;
case 'group':
$result .= '`group`';
break;
case 'mail':
$result .= 'mail';
break;
case 'rate':
$result .= 'rate DESC';
break;
case 'year':
$result .= 'year';
break;
case 'city':
$result .= 'city';
break;
Или пиздец какой толстяк, или пиздец какой дебил.
или оба пункта одновременно
switch ($order) {
case 'id':
$result .= 'id';
break;
case 'name':
$result .= 'name';
break;
case 'surname':
$result .= 'surname';
break;
case 'group':
$result .= '`group`';
break;
case 'mail':
$result .= 'mail';
break;
case 'rate':
$result .= 'rate DESC';
break;
case 'year':
$result .= 'year';
break;
case 'city':
$result .= 'city';
break;
Или пиздец какой толстяк, или пиздец какой дебил.
или оба пункта одновременно
Это не троллинг. Это статьи для дегенератов-вкатывальщиков. Которые ничерта кроме курсов и не делали толкового. И не сделают.
Я нуб, и "знаю" как php так и node, и на мой взгляд понимание общей структуры и деталей вэб-разработки как таковой,стандартов, паттернов и т.д важнее чем технология. Потому что написать нормальный, правильный интернет-магазин это гораздо больше чем знать пхп/питон/нода.
nodejs асинхронен, ой блядь ужас, невероятная сложность. Я промифицировать модуль fs за вечер научился, и теперь не ебусь с колбеками. И при этом зачем тебе морочиться с этим все, если на пхп ты реализуешь тот же самое приложение, но проще? Зачем заказчику продукт на более сложной и реже используемой технологии, если более простая справляется и так отлично?
Уверен нормальному прогеру с опытом вообще похуй на платформу. ПХП предоставляет удобный инструментарий для работы с вэб-ом. Если ты пишешь среднестатистический вэб-магазин/портал, то я не вижу преимуществ ноды или питона перед пхп ну вообще - может я конечно и нуб.
Так что все эти сопли - это для людей которые думают что "вкатываются", а на самом деле хуи пинают. Это от инфантильности индустрии. Впрочем это все помирает, программирование как профессия - стареет естественным образом. Через десять лет в профессии будет толпа людей за 40, с опытом работы в 20-25 лет, и они и будут делать индустрию.
Это вкусовщина. К вопросу популярности в прикладных задачах это отношения не имеет вообще.
>>27783
П.С.
Все что я написал - это конечно мое мнение. Но дополню вот чем:
я начинал вкатываться именно с ноды, и нода сука не милосердна, ее документация не проста, сама нода не проста. Я тогда не знал sql, читал только мантры про MongoBD. И что более важно я вообще не понимал принципов построения- приложения: МVC , сессии, авторизация, регистрация, роли доступа, взаимодействие с БД - все это в кашу, непонятно где и как в ноде использовать. MVC? Где в экспресс реализовывать модели, контроллеры и представления? Я тогда не понимал вообще. И вот самое важное - обучающих материалов нормального уровня на русском по ноде крайне мало.
Ну и еще по ноде - для новичка по ноде (как и в питоне) работы - в принципе нихуя от слова вообще. На пхп можно найти ИП "Красивые-сайты" и за 15 в месяц ковыряться получая реальный опыт работы, а не маня мечтания о том как тебя с умением отдавать на ноде три странички ждут в Калифорнийских стартапах. Ведь нода - это прогресс.О да.
Короче я взялся за пхп, с ее отличной документацией, большим комьюнити и очень большим количеством всяких туторов. Даже на дваче тред по пхп адекватный. По ютубу я пошагово по строчке запили три MVC фреймворка, от простых к посложнее, по ходу пьесы разобравшись с использованием классов и ООП. Изучил sql. И по итогу имею вполне представление о разработке. Самостоятельно с ноля могу запилить что то типа блога/не сложного магазина. Ток в пхп я получил полное представление о том как должно выглядеть вэб-приложение. И сейчас вернулся параллельно к ноде, уже с более серьезным пониманием что и как делать. Реально полсе пхп мне с нодой гораздо проще стало.
В этом контексте у пхп очень серьезное преимущество.
Обычная фильтрация строки по белому списку чтобы в базу ничего лишнего не попало.
работа есть потому что нужно поддержтвать все то дерьмо,которое накодили в эпоху становления и развития веба
Это и есть жирный троллинг для вкатывальщиков.
<?php
error_reporting(-1);
$anonDice1 = mt_rand(1,6);
$anonDice2 = mt_rand(1,6);
$compDice1 = mt_rand(1,6);
$compDice2 = mt_rand(1,6);
echo "U Anonche vypalo ($anonDice1) and ($anonDice2) \nU compa vypalo ($compDice1) and ($compDice2)\n";
$anonSum = ($anonDice1 + $anonDice2);
$compSum = ($compDice1 + $compDice2);
if (($anonDice1 == $anonDice2) && ($compDice1 == $compDice2)) {
echo "Dva Dabla - Zaebis tema!\n";
exit();
}
if ($anonSum > $compSum) {
echo "Anon pobedil ebana!\n";
} else($anonSum < $compSum) {
echo "Plastmasoviy mir pobedil!\n";
exit();
}
Подскажите, а то не хочется перескакивать не поняв...
<?php
error_reporting(-1);
$anonDice1 = mt_rand(1,6);
$anonDice2 = mt_rand(1,6);
$compDice1 = mt_rand(1,6);
$compDice2 = mt_rand(1,6);
echo "U Anonche vypalo ($anonDice1) and ($anonDice2) \nU compa vypalo ($compDice1) and ($compDice2)\n";
$anonSum = ($anonDice1 + $anonDice2);
$compSum = ($compDice1 + $compDice2);
if (($anonDice1 == $anonDice2) && ($compDice1 == $compDice2)) {
echo "Dva Dabla - Zaebis tema!\n";
exit();
}
if ($anonSum > $compSum) {
echo "Anon pobedil ebana!\n";
} else($anonSum < $compSum) {
echo "Plastmasoviy mir pobedil!\n";
exit();
}
Подскажите, а то не хочется перескакивать не поняв...
В последним if/else ошибка. потерял if.
https://www.php.net/manual/ru/control-structures.elseif.php
+ нужно добавить условие для ничьей. когда выбросили одинаково.
Да
if ($anonSum > $compSum) {
echo "Anon pobedil ebana!\n";
} elseif ($anonSum < $compSum) {
echo "Plastmasoviy mir pobedil!\n";
} else ($anonSum == $compSum) {
echo "Pobedila druzhba!\n"
exit();
}
ну чет опять не то, пздц
Может быть if (condition) и elseif (condition)
else (condition) в языке нет.
ты пишешь else ($anonSum == $compSum) и происходит ошибка. пиши просто else {echo "blabla"; exit();}
В последнем echo нет точки с запятой. Ну это так, просто заметил =)
да, дописал elseif в последнем условии и заработало. Но я пздц долго сидел думал и ток перед сном допёр. За ответ всё равно спасибо, надеюсь больше так не буду тупить..
После получаса мучений снова выхожу на связь. Как после равно вставить результат произведения (квадрат). Отдельной переменной не смог, хз как, куда... Кто обладает помогите
Всё норм, ребят. Понадобилось просто ещё пол часика. Извините за засирание треда, просто у кого ещё спросить кроме вас
Каких задачек из шапки может хватить для знаний?
Не хотелось бы проебать единственную стажировку в городе, поэтому не могу пока самому проверить.
>php, mysql, js, html, css, иногда jQuery
А если вдруг это все нужно, то ты тогда это все выучишь за один день - неделю и успеешь не проебать единственную стажировку в городе?
Я в любом случае это все учу потихоньку, просто мне нужно знать, в какой момент мне вкатываться кабанчиком.
вот задача про кредит. окей, как в примере делаю долг в минус уходит и как бы збсь. А как сделать чтобы не платил больше чем надо за последний месяц? Я написал такую херню:
http://ideone.com/c2OUMk#stdin
В какую сторону думать?
class c1 {
public function __construct () {
$obj = new c2();
$obj->fn = function () use ($obj) {
var_dump ($obj);
}
($obj->fn)()
}
}
class c2 {
public $fn;
}
Можно ли как-нибудь добиться того, чтобы вместо var_dump($obj) я мог написать var_dump($this)? То есть, я хочу в коде анонимной функции использовать $this таким образом, чтобы это $this указывало на $this экземпляра класса с2, а не c1.
Или так не бывает? Конечно, чисто функционально меня и сейчас всё устраивает ($this, $obj - какая разница), но мне просто кажется несколько всратым и странным то, что я таким образом могу из кода, который вроде как пишется для класса c2, обращаться к приватным свойствам c1.
Дублирую код скрином для читабельности.
Или, может, я вообще не в ту сторону думаю?
Задача в следующем, есть два класса: c1 и c2. В рамках c1 мне необходимо задать код метода c2. c2 - класс, на основе которого будет сделано штук 10 объектов, каждый из которых имеет одинаковые свойства, но у каждого из этих объектов совершенно разный код в методе должен быть написан. Чтобы не наследовать 10 классов, решил воспользоваться колбэками. Но эта непонятка с $this напрягает немного.
Объявляй его статическим $obj->fn = static function () use ($obj) {};
Тебя самого не смущает тот факт, что ты даже внятно объяснить не можешь - за каким хуем у тебя один объект реализует функционал другого? А как ты писать это примешься, так вообще хоть вешайся.
Колбэки у него блядь. Иди азы читай, макака.
Не можешь нормально, аргументированно ответить - лучше ничего не пиши.
>>29084
> Можно ли как-нибудь добиться того, чтобы вместо var_dump($obj) я мог написать var_dump($this)? То есть, я хочу в коде анонимной функции использовать $this таким образом, чтобы это $this указывало на $this экземпляра класса с2, а не c1
Замыкание в PHP - это объект класса Closure, а у него есть метод bind(). Но твой подход в общем неправильный. Если тебе надо обратиться к $obj, то ты можешь просто передать его в замыкание, и обращаться напрямую, а не придумывать костыли.
> но мне просто кажется несколько всратым и странным то, что я таким образом могу из кода, который вроде как пишется для класса c2, обращаться к приватным свойствам c1.
Ты плохо понимаешь смысл приватного поля. Приватное поле доступно в коде начиная от открывающей скобки класса и заканчивая закрывающей скобкой. То есть не важно, в каком конкретно методе или классе обращение, откуда он вызван, а важно, в каком месте кода находится обращение. Идея в том, что если у нас есть человек, ответственный за написание данного класса, то приватным полем может распоряжаться только он. Твой коллбек находится внутри класса и следовательно имеет право доступа к приватному полю. Так и должно быть.
> Задача в следующем, есть два класса: c1 и c2. В рамках c1 мне необходимо задать код метода c2. c2 - класс, на основе которого будет сделано штук 10 объектов, каждый из которых имеет одинаковые свойства, но у каждого из этих объектов совершенно разный код в методе должен быть написан. Чтобы не наследовать 10 классов, решил воспользоваться колбэками.
Это неправильный подход, который ухудшает чтение и поддержку кода. Либо делай 10 классов, если по смыслу это разные классы, либо сделай один класс со свитчем. Так как я не знаю суть задачи, то не могу подсказать. Но сама идея, что класс c1 решает, какой метод должен быть в классе c2 - неправильная.
Вот более адекватные примеры использования коллбеков. Допустим, у нас есть класс, представляющий коллекцию объектов, и мы хотим искать их по произвольному условию. Тут подойдет коллбек, который получает на вход сущность и возвращает true/false, подходит ли она под условие:
$londoners = $userList->select(function ($u) { return $u->getCity() == 'London'; });
Вот другой пример. Класс-валидатор, который проверяет правильность значения по произвольному условию. Условие описывается коллбеком:
$pinCodeValidator = new CallbackValidator(function ($value) {
return preg_match("/^\d{4}$/", $value);
});
$isValid = $pinCodeValidator->validate($code);
То есть обычно используется подход "передаем функцию в конструктор". Это логично, так как не позволяет создать объект без передачи функции. Твой код же позволяет не задавать функцию, и позволяет записать в поле fn любой мусор. Увы, этот подход не позволяет классу описать и проверить тип функции (например, сколько у нее должно быть аргументов и каких типов).
Так как такая ситуация встречается довольно часто, для неё придумано другое решение: анонимные классы: https://www.php.net/manual/ru/language.oop5.anonymous.php . Оно позволяет сделать более строгий контроль типов. Перепишем валидатор выше на анонимный класс. Сначала сделаем базовый класс валидатора и пометим его как абстрактный (недоделанный), так как он неработоспособен без нашей функции:
abstract class Validator {
abstract public function validate(string $value): bool;
// .. другие поля и методы
}
Теперь создадим анонимный класс, в котором переопределен метод validate и сразу же его объект:
$pinCodeValidator = new class extends Validator {
public function validate(string $value): bool
{
return preg_match("/^\d{4}$/", $value);
}
}
Это решение имеет плюс: мы контролируем тип функции. Не получится указать функцию с другим набором аргументов или возвращаемым типом. Также, переданная нами функция становится полноценным методом класса и имеет доступ к его полям и методам. Также, вместо абстрактного класса можно использовать интерфейс.
Не можешь нормально, аргументированно ответить - лучше ничего не пиши.
>>29084
> Можно ли как-нибудь добиться того, чтобы вместо var_dump($obj) я мог написать var_dump($this)? То есть, я хочу в коде анонимной функции использовать $this таким образом, чтобы это $this указывало на $this экземпляра класса с2, а не c1
Замыкание в PHP - это объект класса Closure, а у него есть метод bind(). Но твой подход в общем неправильный. Если тебе надо обратиться к $obj, то ты можешь просто передать его в замыкание, и обращаться напрямую, а не придумывать костыли.
> но мне просто кажется несколько всратым и странным то, что я таким образом могу из кода, который вроде как пишется для класса c2, обращаться к приватным свойствам c1.
Ты плохо понимаешь смысл приватного поля. Приватное поле доступно в коде начиная от открывающей скобки класса и заканчивая закрывающей скобкой. То есть не важно, в каком конкретно методе или классе обращение, откуда он вызван, а важно, в каком месте кода находится обращение. Идея в том, что если у нас есть человек, ответственный за написание данного класса, то приватным полем может распоряжаться только он. Твой коллбек находится внутри класса и следовательно имеет право доступа к приватному полю. Так и должно быть.
> Задача в следующем, есть два класса: c1 и c2. В рамках c1 мне необходимо задать код метода c2. c2 - класс, на основе которого будет сделано штук 10 объектов, каждый из которых имеет одинаковые свойства, но у каждого из этих объектов совершенно разный код в методе должен быть написан. Чтобы не наследовать 10 классов, решил воспользоваться колбэками.
Это неправильный подход, который ухудшает чтение и поддержку кода. Либо делай 10 классов, если по смыслу это разные классы, либо сделай один класс со свитчем. Так как я не знаю суть задачи, то не могу подсказать. Но сама идея, что класс c1 решает, какой метод должен быть в классе c2 - неправильная.
Вот более адекватные примеры использования коллбеков. Допустим, у нас есть класс, представляющий коллекцию объектов, и мы хотим искать их по произвольному условию. Тут подойдет коллбек, который получает на вход сущность и возвращает true/false, подходит ли она под условие:
$londoners = $userList->select(function ($u) { return $u->getCity() == 'London'; });
Вот другой пример. Класс-валидатор, который проверяет правильность значения по произвольному условию. Условие описывается коллбеком:
$pinCodeValidator = new CallbackValidator(function ($value) {
return preg_match("/^\d{4}$/", $value);
});
$isValid = $pinCodeValidator->validate($code);
То есть обычно используется подход "передаем функцию в конструктор". Это логично, так как не позволяет создать объект без передачи функции. Твой код же позволяет не задавать функцию, и позволяет записать в поле fn любой мусор. Увы, этот подход не позволяет классу описать и проверить тип функции (например, сколько у нее должно быть аргументов и каких типов).
Так как такая ситуация встречается довольно часто, для неё придумано другое решение: анонимные классы: https://www.php.net/manual/ru/language.oop5.anonymous.php . Оно позволяет сделать более строгий контроль типов. Перепишем валидатор выше на анонимный класс. Сначала сделаем базовый класс валидатора и пометим его как абстрактный (недоделанный), так как он неработоспособен без нашей функции:
abstract class Validator {
abstract public function validate(string $value): bool;
// .. другие поля и методы
}
Теперь создадим анонимный класс, в котором переопределен метод validate и сразу же его объект:
$pinCodeValidator = new class extends Validator {
public function validate(string $value): bool
{
return preg_match("/^\d{4}$/", $value);
}
}
Это решение имеет плюс: мы контролируем тип функции. Не получится указать функцию с другим набором аргументов или возвращаемым типом. Также, переданная нами функция становится полноценным методом класса и имеет доступ к его полям и методам. Также, вместо абстрактного класса можно использовать интерфейс.
Не надо выёбываться.
Спасибо за развёрнутый ответ.
Если говорить более конкретно, то суть такая.
Этот PHP-скрипт пишется для консоли и рассчитан на то, что он будет работать в виде "демона", то есть, 24/7 крутится в фоне (пока не упадёт). Сделано это через рекурсию и sleep(). Может быть, я пидор и php для таких целей - худшее решение, но сейчас не об этом.
Обработкой всего этого дела занимается класс srv. Этот класс имеет поле $workers, которое представляет из себя массив объектов класса worker.
У всех worker'ов есть общие методы и поля: метод, который отвечает true/false на вопрос того, пришло ли время данному worker'у выполнить свой код. Поле, которое хранит время следующего планового исполнения. И т. д.
При этом, у каждого worker'а есть некий "основной метод", который выполняет определённую работу. И у каждого worker'а он свой. Кто-то обновляет сессию, кто-то получает список новых сообщений и т. д. Потому что сессию мне нужно обновлять пару раз в день, а новые сообщения дёргать раз в пару минут.
Как это я решил сейчас - описано в моём, первом посте. Класс srv создаёт объекты класса worker и каждому из них передаёт колбэк, который содержит код, который данный worker должен выполнять.
Лепить ради этого дела 10 отдельных классов мне показалось избыточным. Но как я понял из твоего поста - нужно не выёбываться и всё-таки налепить их, верно?
Зачем тебе рекурсия, бесконечный цикл сделай. И отнаследовать десять классов нормальная идея - ты можешь выдать каждому классу понятное имя в зависимости от того что он делает.
Вообще нет, но рекурсия жрет больше ресурсов - так как в пхп не оптимизируется хвостовая рекурсия то с этим нихуя не поделать.
Глубина рекурсии в php ограничена в раене 256. Вроде можно менять эту настройку, но я дропнул эту затею из-за подводных камней. Т.е. скрипт упадет с ошибкой через 8 часов если плодить новый вызов каждые 2 минуты.
>>29604
Сейчас погуглил про рекурсию дополнительно - да, пожалуй, нужно заменить на бесконечный цикл. Информация про глубину в 256, правда, действительности не соответствует совсем никак, у меня уже до глубины 600 дошло.
Еще такой вопрос. Как я уже сказал - у меня cli-скрипт, который рассчитан на псевдобесконечную работу. Я могу как-нибудь выполнить некий код перед его завершением? Деструктор не срабатывает, если прибить скрипт через ctrl + c (SIGINT) в консоли.
Нагуглил это https://www.php.net/manual/ru/function.pcntl-signal.php , но как я понимаю, у меня нет возможности обработать все сигналы разом.
Есть какой-нибудь более надёжный и универсальный вариант?
Это prepared statement вместо того чтобы прямым текстом подставлять значение логина и т.п. они подставляются (биндятся) отдельной операций. У тебя это наверное дальше по тексту идет. Это способ борьбы с разного рода робертами-брось-таблицу (SQL Injection).
<div A>
<div C></div C>
</div A>
<div B>
<div D></div D>
</div B>
Как задать в CSS свойство div D через div C? Нужно что-то типа (div A > div C) + (div B > div C)
Понял, спасибо.
Тогда я хз в чем ошибка, у меня sqlerror
может sql запрос не верный
Вот мой код
https://github.com/InspektorGadjet/FileSharing
Насколько все плохо ?
Если не изменяет память - никак, CSS-селекторы не могут указывать "на уровень выше".
>$container['uploadDirectory'] = __DIR__ . '\uploads';
>$container['copyDirectory'] = __DIR__ . '\copyes';
Кажись удобнее будет это расположить в $container['settings'], в каком-нить массиве ['directories']. Просто по логике искать такое именно в настройках надо, а не в контейнере с зависимостями.
Я бы ещё вынес создание конфигурации в другой файл - не хотелось бы спалить данные подключения БД кому-нить если у хостера модуль пхп глюканёт или ещё что.
В такой ситуации правильнее будет сделать базовый класс или интерфейс воркера, а конкретных воркеров реализовать либо как анонимные классы либо как обычные классы-наследники.
Ну и еще , я не очень понимаю, зачем ты решил переизобрести крон. В линуксе уже есть демон, который запускает задачи по расписанию, и может быть он лучше подойдет. А также, есть готовые очереди задач.
Ну и перечитай мой пост еще раз. Я там прямо описал возможные решения твоей задачи, и любое из них лучше твоего странного решения с засовыванием коллбеков в публичное поле объекта.
Зачем спрашиваешь?
Есть задача:
>W5.2 Некто кладет в банк 10000 р. Банк начисляет 10% годовых (то есть, каждый год на счету становится на 10% больше, чем в прошлом году). Напиши программу, считающую, через сколько лет в банке будет миллион? Сколько лет будет этому некто? Доживет ли некто до этого дня, если сегодня ему 16 лет?
Есть кусок кода, который должен посчитать количество лет:
и эта падла не работает.
<?php
error_reporting(-1);
for ($x=10000; $x==1000000; $x+=1000) {
echo "{$x} - покажи мне че нить";
}
?>
Там по хорошему, цикл должен повторится 49 раз, и как то отобразится в консоле, но нихуя, вопрос что я не так делаю?
> <?php
error_reporting(-1);
for ($x=10000; $x==1000000; $x+=1000) {
echo "{$x} - покажи мне че нить";
}
?>
Это частая ошибка. Там надо указать условие продолжения цикла (цикл выполняется, пока условие верно), а ты написал условие прекращения цикла.
>>29738
Ты неправильно написал код. При возникновении ошибки ты делаешь редирект, но никуда не сообщаешь о подробностях ошибки. Пользователю, конечно, их знать незачем, а тебе нужны подробности, чтобы понять, как отладить приложение. А ты эти подробности приячешь и сам же себе усложняешь жизнь. Гадать по скриншоту, в чем проблема - это бессмысленная трата времени, надо просто сделать вывод подробностей.
Потому надо сделать так:
- в режиме разработки ошибку можно выводить на экран
- в режиме продакшена ошибка должна идти строго в лог, который ты раз в сутки будешь просматривать на наличие ошибок
- выбор между режимами задается стандартной опцией display_errors в php.ini (мануал: https://www.php.net/manual/ru/errorfunc.configuration.php#ini.display-errors )
Казалось бы, нам придется сейчас писать сложный код, определяющий значение display_errors, с разными вариантами выполнения, но есть способ проще. В PHP есть готовая функция, чтобы сообщить об ошибке, и она сама выведет ее на экран или в лог - это trigger_error: https://www.php.net/manual/ru/function.trigger-error.php
Используется она так, чтобы сообщить об ошибке:
trigger_error("Cannot prepare SQL query", E_USER_ERROR);
То есть при возникновении ошибки ты должен вызывать эту функцию, передав ей подробности. Где их взять? Откроем мануал по mysqli и посмотрим, как сделано там: https://www.php.net/manual/ru/mysqli.examples-basic.php
Там есть такой код:
// О нет!! переменная connect_errno существует, а это значит, что соединение не было успешным!
if ($mysqli->connect_errno) {
// Соединение не удалось. Что нужно делать в этом случае?
// Можно отправить письмо администратору, отразить ошибку в журнале,
// информировать пользователя об ошибке на экране и т.п.
// Вам не нужно при этом раскрывать конфиденциальную информацию, поэтому
// просто попробуем так:
echo "Извините, возникла проблема на сайте";
// На реальном сайте этого делать не следует, но в качестве примера мы покажем
// как распечатывать информацию о подробностях возникшей ошибки MySQL
echo "Ошибка: Не удалась создать соединение с базой MySQL и вот почему: \n";
echo "Номер ошибки: " . $mysqli->connect_errno . "\n";
echo "Ошибка: " . $mysqli->connect_error . "\n";
}
Этот код получает информацию об ошибке при соединении. И такой код, который получает информацию об ошибке при вызове функций:
if (!$result = $mysqli->query($sql)) {
// О нет! запрос не удался.
echo "Извините, возникла проблема в работе сайта.";
// И снова: не делайте этого на реальном сайте, но в этом примере мы покажем,
// как получить информацию об ошибке:
echo "Ошибка: Наш запрос не удался и вот почему: \n";
echo "Запрос: " . $sql . "\n";
echo "Номер ошибки: " . $mysqli->errno . "\n";
echo "Ошибка: " . $mysqli->error . "\n";
exit;
}
Тебе надо сделать так же, но объединить все подробности в одну строку и заменить echo на trigger_error(). То есть отдать информацию об ошибке PHP, и он ей распорядится, а заодно остановит выполнение твоей кривой программы, пока ты ее не исправишь.
То есть после каждой функции mysqli у тебя должен стоять if, проверяющий наличие ошибки и передающий ее подробности в trigger_error(). Заодно перечитай мануал и примеры кода в нем по тем ссылкам, что я дал.
Позже, я советую изучить исключения и параметр report_mode, если его правильно настроить, то mysqli сама будет при ошибке выбрасывать исключения, которые будут выводиться на экран или идти в лог, и if'ы будут не нужны: https://www.php.net/manual/ru/mysqli-driver.report-mode.php . Но это можно сделать потом.
Кроме того, что ты скрываешь ошибки, у тебя в коде есть и другие проблемы, которые надо исправить, без этого тебя нельзя подпускать к написанию настоящего кода:
- не надо делать огромную вложенность if друг в друга. Если ты сделал exit, то программа дальше не выполняется, и блок else не нужен.
- надо проверять результат всех mysqli-функций вроде mysqli_stmt_execute() на ошибки, а ты это не делаешь
- нельзя подставлять email в ссылку напрямую, надо его экранировать как описано в моем уроке: https://github.com/codedokode/pasta/blob/master/network/urls.md (процентное кодирование)
- лучше использовать такой алгоритм работы с формами, который не требует редиректа при ошибке. Он описан тут: https://github.com/codedokode/pasta/blob/master/forms.md
Задавай вопросы, если непонятно. Если понятно - делай все, что я перечислил и читай все статьи по ссылкам.
>>29738
Ты неправильно написал код. При возникновении ошибки ты делаешь редирект, но никуда не сообщаешь о подробностях ошибки. Пользователю, конечно, их знать незачем, а тебе нужны подробности, чтобы понять, как отладить приложение. А ты эти подробности приячешь и сам же себе усложняешь жизнь. Гадать по скриншоту, в чем проблема - это бессмысленная трата времени, надо просто сделать вывод подробностей.
Потому надо сделать так:
- в режиме разработки ошибку можно выводить на экран
- в режиме продакшена ошибка должна идти строго в лог, который ты раз в сутки будешь просматривать на наличие ошибок
- выбор между режимами задается стандартной опцией display_errors в php.ini (мануал: https://www.php.net/manual/ru/errorfunc.configuration.php#ini.display-errors )
Казалось бы, нам придется сейчас писать сложный код, определяющий значение display_errors, с разными вариантами выполнения, но есть способ проще. В PHP есть готовая функция, чтобы сообщить об ошибке, и она сама выведет ее на экран или в лог - это trigger_error: https://www.php.net/manual/ru/function.trigger-error.php
Используется она так, чтобы сообщить об ошибке:
trigger_error("Cannot prepare SQL query", E_USER_ERROR);
То есть при возникновении ошибки ты должен вызывать эту функцию, передав ей подробности. Где их взять? Откроем мануал по mysqli и посмотрим, как сделано там: https://www.php.net/manual/ru/mysqli.examples-basic.php
Там есть такой код:
// О нет!! переменная connect_errno существует, а это значит, что соединение не было успешным!
if ($mysqli->connect_errno) {
// Соединение не удалось. Что нужно делать в этом случае?
// Можно отправить письмо администратору, отразить ошибку в журнале,
// информировать пользователя об ошибке на экране и т.п.
// Вам не нужно при этом раскрывать конфиденциальную информацию, поэтому
// просто попробуем так:
echo "Извините, возникла проблема на сайте";
// На реальном сайте этого делать не следует, но в качестве примера мы покажем
// как распечатывать информацию о подробностях возникшей ошибки MySQL
echo "Ошибка: Не удалась создать соединение с базой MySQL и вот почему: \n";
echo "Номер ошибки: " . $mysqli->connect_errno . "\n";
echo "Ошибка: " . $mysqli->connect_error . "\n";
}
Этот код получает информацию об ошибке при соединении. И такой код, который получает информацию об ошибке при вызове функций:
if (!$result = $mysqli->query($sql)) {
// О нет! запрос не удался.
echo "Извините, возникла проблема в работе сайта.";
// И снова: не делайте этого на реальном сайте, но в этом примере мы покажем,
// как получить информацию об ошибке:
echo "Ошибка: Наш запрос не удался и вот почему: \n";
echo "Запрос: " . $sql . "\n";
echo "Номер ошибки: " . $mysqli->errno . "\n";
echo "Ошибка: " . $mysqli->error . "\n";
exit;
}
Тебе надо сделать так же, но объединить все подробности в одну строку и заменить echo на trigger_error(). То есть отдать информацию об ошибке PHP, и он ей распорядится, а заодно остановит выполнение твоей кривой программы, пока ты ее не исправишь.
То есть после каждой функции mysqli у тебя должен стоять if, проверяющий наличие ошибки и передающий ее подробности в trigger_error(). Заодно перечитай мануал и примеры кода в нем по тем ссылкам, что я дал.
Позже, я советую изучить исключения и параметр report_mode, если его правильно настроить, то mysqli сама будет при ошибке выбрасывать исключения, которые будут выводиться на экран или идти в лог, и if'ы будут не нужны: https://www.php.net/manual/ru/mysqli-driver.report-mode.php . Но это можно сделать потом.
Кроме того, что ты скрываешь ошибки, у тебя в коде есть и другие проблемы, которые надо исправить, без этого тебя нельзя подпускать к написанию настоящего кода:
- не надо делать огромную вложенность if друг в друга. Если ты сделал exit, то программа дальше не выполняется, и блок else не нужен.
- надо проверять результат всех mysqli-функций вроде mysqli_stmt_execute() на ошибки, а ты это не делаешь
- нельзя подставлять email в ссылку напрямую, надо его экранировать как описано в моем уроке: https://github.com/codedokode/pasta/blob/master/network/urls.md (процентное кодирование)
- лучше использовать такой алгоритм работы с формами, который не требует редиректа при ошибке. Он описан тут: https://github.com/codedokode/pasta/blob/master/forms.md
Задавай вопросы, если непонятно. Если понятно - делай все, что я перечислил и читай все статьи по ссылкам.
Ок, вот алгоритм решения:
- заведи переменную, положи в нее ноль
- сделай цикл, который будет перебирать массив с учениками
- на каждом шаге цикла ты смотришь, если рост текущего ученика выше определенного значения, то увеличиваем переменную на один
- в итоге мы получим искомое число
> Как эту ебучую проверку сделать? If писать или этот foreach?
И то, и другое. foreach будет перебирать массив, а if - делать проверку.
> Куда писать, в каком месте, скобки
Это вроде объясняется в предыдущих уроках. Если ты пропустил какие-то задачи в них, надо вернуться, перечитать и попробовать решить сначала их. Или тебе непонятен foreach?
Справшивай, если что-то непонятно.
>>29888
В настройки стоит выносить только то, что можно менять - например, пароль от БД. Расположение директорий, наверно, менять особого смысла нет. А лишние настройки раздувают конфиг и усложняют код. Я бы советовал минимизировать их количество.
>>29708
Прочитай мануал (на русском) https://www.php.net/manual/ru/mysqli.quickstart.prepared-statements.php
Затем мой урок по SQL-инъекциям: https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
>>29663
Более надежным способом может быть иметь родительский скрипт, который будет убирать за ребенком при его завершении. Так как при серьезной проблеме ребенок может быть в неработающем состоянии. Ну или ловить SIGINT, если тебя интерсует только прерывание по Ctrl + C.
Ок, вот алгоритм решения:
- заведи переменную, положи в нее ноль
- сделай цикл, который будет перебирать массив с учениками
- на каждом шаге цикла ты смотришь, если рост текущего ученика выше определенного значения, то увеличиваем переменную на один
- в итоге мы получим искомое число
> Как эту ебучую проверку сделать? If писать или этот foreach?
И то, и другое. foreach будет перебирать массив, а if - делать проверку.
> Куда писать, в каком месте, скобки
Это вроде объясняется в предыдущих уроках. Если ты пропустил какие-то задачи в них, надо вернуться, перечитать и попробовать решить сначала их. Или тебе непонятен foreach?
Справшивай, если что-то непонятно.
>>29888
В настройки стоит выносить только то, что можно менять - например, пароль от БД. Расположение директорий, наверно, менять особого смысла нет. А лишние настройки раздувают конфиг и усложняют код. Я бы советовал минимизировать их количество.
>>29708
Прочитай мануал (на русском) https://www.php.net/manual/ru/mysqli.quickstart.prepared-statements.php
Затем мой урок по SQL-инъекциям: https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
>>29663
Более надежным способом может быть иметь родительский скрипт, который будет убирать за ребенком при его завершении. Так как при серьезной проблеме ребенок может быть в неработающем состоянии. Ну или ловить SIGINT, если тебя интерсует только прерывание по Ctrl + C.
Попробуй переписать код внутри цикла примерно так:
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
«Платим» здесь значит уменьшаем долг и увеличиваем общую сумму выплаченного.
>>28825
>>28084
А в мануале по SimpleXML ответа нет? https://www.php.net/manual/ru/simplexml.examples-basic.php
>>27825
Можно сделать чуть проще, сделать массив разрешенных значений, и проверять с помощью in_array(), что значение находится в массиве. Косые кавычки подставлять всегда, а DESC приписывать с помощью if.
>>27807
В случае с нодой - я думаю, ее (как и многие новые технологии вроде Го) пишут для себя и используют в первую очередь крутые программисты с 15+ годами опыта, которые все эти архитектуры знают назубок, и их отсутствие документации мало беспокоит. Аналогично, Го - это улучшенный Си, который рассчитан в первую очередь на опытных сишников, а не на переучивающегося менеджера по продажам.
Что касается асинхронности, то до async/await писать на промисах работу с БД - это по-моему боль, и синхронный код тут гораздо проще подходит.
> С появлением Node и производных фреймворков, работающих асинхронно, необходимость в PHP полностью отпала.
Странно только, что она не отпала с появлением питоновского асинхронного фреймворка twisted, который появился раньше. А так, статья по ссылке - тонкий троллинг, о чем например говорит пункт про "утечки памяти" - видимо автору знакома эта проблема. Или местные "специалисты" по Ноде не поняли намек?
>>27394
Модель - это "модель" предметной сущности, как правило оформленная в виде класса. Например, ты делаешь приложение-магазин и в нем есть класс "Товар", объекты которого являются моделью реального товара в коде и имеют свойства вроде "цена", "название" и тд. Иногда на модель навешивают еще работу с БД, при использовании паттернов вроде Active Record, но это не обязательно.
>>27289
В PHP есть функция mt_rand(), которая "возвращает" случайное число. Вот пример:
echo mt_rand(1, 100); // выводит случайное число от 1 до 100
Здесь мы с помощью команды echo выводим число, которое вернула mt_rand(). Тебе же надо не сразу вывести это число, а записать в переменную. Для этого мы пишем код вроде
$x = mt_rand(....);
Оператор "равно" обозначает "взять значение справа и записать в переменную слева".
Попробуй переписать код внутри цикла примерно так:
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
«Платим» здесь значит уменьшаем долг и увеличиваем общую сумму выплаченного.
>>28825
>>28084
А в мануале по SimpleXML ответа нет? https://www.php.net/manual/ru/simplexml.examples-basic.php
>>27825
Можно сделать чуть проще, сделать массив разрешенных значений, и проверять с помощью in_array(), что значение находится в массиве. Косые кавычки подставлять всегда, а DESC приписывать с помощью if.
>>27807
В случае с нодой - я думаю, ее (как и многие новые технологии вроде Го) пишут для себя и используют в первую очередь крутые программисты с 15+ годами опыта, которые все эти архитектуры знают назубок, и их отсутствие документации мало беспокоит. Аналогично, Го - это улучшенный Си, который рассчитан в первую очередь на опытных сишников, а не на переучивающегося менеджера по продажам.
Что касается асинхронности, то до async/await писать на промисах работу с БД - это по-моему боль, и синхронный код тут гораздо проще подходит.
> С появлением Node и производных фреймворков, работающих асинхронно, необходимость в PHP полностью отпала.
Странно только, что она не отпала с появлением питоновского асинхронного фреймворка twisted, который появился раньше. А так, статья по ссылке - тонкий троллинг, о чем например говорит пункт про "утечки памяти" - видимо автору знакома эта проблема. Или местные "специалисты" по Ноде не поняли намек?
>>27394
Модель - это "модель" предметной сущности, как правило оформленная в виде класса. Например, ты делаешь приложение-магазин и в нем есть класс "Товар", объекты которого являются моделью реального товара в коде и имеют свойства вроде "цена", "название" и тд. Иногда на модель навешивают еще работу с БД, при использовании паттернов вроде Active Record, но это не обязательно.
>>27289
В PHP есть функция mt_rand(), которая "возвращает" случайное число. Вот пример:
echo mt_rand(1, 100); // выводит случайное число от 1 до 100
Здесь мы с помощью команды echo выводим число, которое вернула mt_rand(). Тебе же надо не сразу вывести это число, а записать в переменную. Для этого мы пишем код вроде
$x = mt_rand(....);
Оператор "равно" обозначает "взять значение справа и записать в переменную слева".
В PHP есть функция mt_rand(), которая "возвращает" случайное число. Вот пример:
echo mt_rand(1, 100); // выводит случайное число от 1 до 100
Здесь мы с помощью команды echo выводим число, которое вернула mt_rand(). Тебе же надо не сразу вывести это число, а записать в переменную. Для этого мы пишем код вроде
$x = mt_rand(....);
Оператор "равно" обозначает "взять значение справа и записать в переменную слева".
>>27310
Диапазон указывается при вызове функции mt_rand в скобках, тут есть примеры: https://php.net/manual/ru/function.mt-rand.php
>>27125
Да, либо ты поднимаешь свой SMTP-сервер, либо используешь готовый (они могут предоставляться за плату компаниями-рассыльщиками). Если у тебя нет денег, то можно просто зарегать бесплатный аккаунт почты и посмотреть параметры SMTP в ее настройках, но почтовые сервисы прибивают аккаунты, если писем будет отправляться много. Это не решение для продакшена.
Функция mail() под Windows умеет в SMTP, но видимо без шифрования: https://www.php.net/manual/ru/mail.configuration.php#ini.smtp В линуксе mail() просто вызывает программу sendmail и передает ей письмо (она добавляет письмо в очередь для локально поднятного почтового сервера).
В теории есть еще вариант, чтобы твоя программа содержала в себе "сервер", то есть при отправке письма на
Вообще, алгоритм примерно такой:
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
«Платим» здесь значит уменьшаем долг и увеличиваем общую сумму выплаченного.
>>426895
Не так. Отправка письма на
Потому обычно ты поднимаешь свой почтовый сервер или арендуешь чужой, передаешь ему письмо по протоколу SMTP и дальше он уже решает все проблемы (связывается с почтовым сервером получателя, делает повторные попытки итд). Если ты хочешь поднять свой сервер, то придется настроить технологии вроде настройки DNS для твоего IP адреса. Это называется MTA, или mail relay - сервер, который пересылает письма к получателю.
SMTP - протокол отдачи письма, либо непосредсвенно от тебя к MTA, либо от одного MTA к другому. Когда-то были "открытые релеи", которые принимали письма от любого, но через них слали спам и их позакрывали. Сейчас ты должен отдавать письмо своему серверу.
Приведу пример. Допустим, ты хочешь отправить письмо на
Там указано, что это сервер mx.yandex.ru. Значит, письмо надо передать ему.
PHPMailer делает две вещи:
- поддерживает протокол вроде SMTP
- поддерживает создание сложных писем с HTML, вложениями итд
Функционал полноценного почтового-сервера-доставщика (Mail Transfer Agent) он не выполняет. Он не ищет, кто отвечает за данный домен, а всегда шлет письма на один и тот же SMTP-сервер, на твой сервер, сервер отправителя. Также, он является SMTP-клиентом (тот, кто подсоединяется к серверу), а не сервером.
Вообще, алгоритм примерно такой:
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
«Платим» здесь значит уменьшаем долг и увеличиваем общую сумму выплаченного.
>>426895
Не так. Отправка письма на
Потому обычно ты поднимаешь свой почтовый сервер или арендуешь чужой, передаешь ему письмо по протоколу SMTP и дальше он уже решает все проблемы (связывается с почтовым сервером получателя, делает повторные попытки итд). Если ты хочешь поднять свой сервер, то придется настроить технологии вроде настройки DNS для твоего IP адреса. Это называется MTA, или mail relay - сервер, который пересылает письма к получателю.
SMTP - протокол отдачи письма, либо непосредсвенно от тебя к MTA, либо от одного MTA к другому. Когда-то были "открытые релеи", которые принимали письма от любого, но через них слали спам и их позакрывали. Сейчас ты должен отдавать письмо своему серверу.
Приведу пример. Допустим, ты хочешь отправить письмо на
Там указано, что это сервер mx.yandex.ru. Значит, письмо надо передать ему.
PHPMailer делает две вещи:
- поддерживает протокол вроде SMTP
- поддерживает создание сложных писем с HTML, вложениями итд
Функционал полноценного почтового-сервера-доставщика (Mail Transfer Agent) он не выполняет. Он не ищет, кто отвечает за данный домен, а всегда шлет письма на один и тот же SMTP-сервер, на твой сервер, сервер отправителя. Также, он является SMTP-клиентом (тот, кто подсоединяется к серверу), а не сервером.
неправильно пошёл. Я недавно решал, выше задавал такие же тупые вопросы. Ниже пролистай задачу и там будет пример кода следующей. Вот оттуда я подглядел и кой чего подправил. Щас ебусь с массивами...
хули заводить? там уже по условию переменная $number с нулем идет, ну и вот единственное до чего я додумался: http://ideone.com/LtprDn
и то он мне ошибку выдаёт. Я заебался блять, я не понимаю как это писать
Ты предыдущие задачи, вроде задач про кубики решал? Если нет, то надо вернуться и решить. В if пишется одно условие, а не три разных выражения. Не переставляй символы наугад, а разберись, что ты хочешь сделать.
ну ебать, попробовал через for с разными условиями - тоже нихуя. Всм разберись? Переменная равна нулю, если рост больше 169, то переменная увеличивается на 1. Как я это блять с помощью if должен написать?
да, и от меня тебе тоже спасибо, хоть я нихуя и не понимаю и горю
Обратно возврщайся по программе - ты не понимаешь что делаешь, судя по всему. Это пиздос.
Рабочий код невозможно писать наугад.
да ёбаный в рот...
>лишние настройки раздувают конфиг и усложняют код
Тесты писать заебись будет потом на таком коде - всё железно прописано и хуй чего подсунешь. Можно прямо в классах писать, круто будет потом в них копаться при минимальных изменениях конфигурации.
http://ideone.com/LtprDn#stdin
короче смог только так. Я хуй знает, у меня уже голова раскалывается
а ну всё, скобочку просто не там поставил. Заебись, решил...
>круто будет потом в них копаться при минимальных изменениях конфигурации
А уж постороннему сисадмину\девопсу вообще по-кайфу этим заниматься будет, особенно если он языка не знает.
в 16 строчке пишу $random= array_rand($answers);
дальше я не знаю. если сделать $answer = $random то он конечно же выведет только число. А как сделать чтобы выводил значение?
array_rand возвращает случайный ключ. Вот и возьми значение по этому ключу $answers[$random]
функция array_random возвращает КЛЮЧ. То-есть число перед стрелочкой. А тебе надо вывести число ПОСЛЕ стрелочки. Они связаны друг с другом. То-есть тебе надо сказать массиву, массив, напиши, пожалуйста, мне значение, которое хранится под вот этим вот ключом, записанным в переменную рандом, то-есть
echo $answers[$random];
или в твоем случае
$answer = $answers[$random];
Проще всего будет найти какой контроллер/екшен дергается при сабмите формы и туда дописать отправку в телеграм чего нужно.
Спасибо, буду искать. Я думал там челиком через вьюхи отрабатывается. Еще нормального коммьюнити по кейку нет похоже нихуя, одни бразильцы да индусы
анош, у меня мало опыта, но я подразумеваю, что если в вакансии требуется непосредственное взаимодействие с конкретной технологией, то тебе так и напишут типа ляравель, уи. А если это вакансия джуна, денег платят мало, то, скорее всего, базовые навыки сгодятся аля знаешь слово "массив" - ты уже король. А вообще все индивидуально. Те, кому нужен работник на конкретную работу, они сразу пишут будешь работать на такой языке с таким фреймворком. А если это вакансия "контент-менеджера", плюс сисадмина, плюс дезайнера, и приветствуется знание php, то тут пшп не ключевое
А если это вакансия стажёра в рогах и копытах? По идее требования должны быть на уровне "иметь базовые знания и уметь написать круд"?
>Knowledge of PHP (OOP, MVC), HTML, CSS and JavaScript
Я нихуя из этого никогда не изучал (учил шарп, питон, джаву и т.д.), но принципы ООП знаю. Реально ли,по-вашему, за ~ неделю получить какую-то базу знаний по сабжу, чтобы пройти интерном(там прям сказано, что работа для студентов, но необходимая глубина знаний не указана) ? Так я знаю основы программирования и ООП, но в вебе - мало чего.
За неделю ты только в вёрстке можешь разобраться базово. Сразу предупреждаю, халявы тут нет, конкурс большой и даже на интерна просят достаточно много знаний. Минимум несколько месяцев на все уйдет.
>в шарпотреде сказали,что вы говно и вообще чернь
если даже сишники обращаются к нам на ВЫ, то думай сам, кто тут уважаемый авторитет доски
держу вас в курсе
тоже пытался поставить synthwave тему?
По первому варианту боюсь что зря проебу время. Если в будущем пхп вымрет (есть такие слухи), то это зря потраченные силы на обучение. С другой стороны, в пхп проще вкатиться (дохуя гайдов, большой мануал), а в энтерпрайзные языки берут уже с некоторой базой (знание парадигм, матана, всяких докеров/хуекеров), и ее долго подтягивать, думаю на пхп работе буду с таким работать и учить на ходу.
Куда двигаться короче?
есть еще третий стул - остаться в пхп, смотрю по мидлам вилка зп хорошая. Но здесь главный косяк, что пхп может исчезнуть рано или поздно
анон, ты же понимаешь, что это как с иностранными языками - потратив на один время, второй ты выучишь в разы быстрее.
Да, но допустим я на php дойду до мидла. А потом перейду на Java, мне тогда на junior переходить?
Нахуй переходи.
У тебя ноль коммерческого опыта, какой мидл?
Более того - слишком высокого мнения о своих скиллах, при нулевом опыте.
Петов, вангую, нет ни одного - даже показать ничего не сможешь нормального.
На таких "мастеров" нынче везде насмотрелись.
Тебе перезвонят.
Ты петух, тебя даже стажёром не возьмут
>Да, но допустим я на php дойду до мидла. А потом перейду на Java, мне тогда на junior переходить?
Анон, с тем багажом знаний, который у тебя будет после изучений пшп, с полученным опытом разработки и вращения в программистской среде велика вероятность, что ты джуниора джавовского тупо проскочишь и пойдешь выше. Тебе главное опыт получить
какое еще высокое мнение, я просто планирую на будущее. Через сколько-то лет стану мидлом же? Вот, и как дальше пойдут события думаю
т.е. на будущее, если несколько лет прокодил на php, то при переходе на Java уже можно не на джуна пойти? Хотя наверное зависит от вакансии типа на пхп только для сайтиков бек писал, а на яве какие-нибудь микросервисы надо делать
Ну кто тебя возьмёт java миддлом без опыта этого самого java? Хорошо, если на джуна возьмут. Сейчас даже на джаву стажера требования ниибические
ну и нахуй так жить? Предположим всю жизнь ебашил десктоп на delphi ебаном, потом делфи рынок сдулся. Переучился на c# для десктопа, а там только стажером возьмут? Лол
Да, как-то так) Сурово в айти нынче
Ну технологии же разные, совсем не родственные, пусть и конкурирующие. Опыт в соседней отрасли, конечно, будет небольшим плюсом к резюме, но энивей все решает опыт в специальности и техническое интервью.
Потому что это и есть запах пыха. Я к нему уже привык
Переделал схему наследования БД на Single Table Inheritance.
Перешел с Node.js на PHP.
Реализовал простой текстовый чат (без шифрования) и конфернеции между двумя пользователями.
Сообщения/конференции сохраняются в IndexeDB и извлекаются реактивно (https://rxdb.info/).
Проверять не обязательно, думаю я могу всё сделать самостоятельно. Но если есть какие-то интересные мысли или замечания и советы, всегда рад. Скорее всего у меня повсюду не вынесен код.
У меня есть вопросы:
1. Когда отправляется первое сообщение пользователю, то открывается сначала форма для отправки сообщения, а потом делается редирект в только что созданную конференцию, после отправки сообщения.pic-1 Это сделалось потому что нельзя изначально открыть конференцию, которая ещё не существует.
Было бы лучше если бы приватная конференция имела такой же id как и пользователь. И пользователь сам по себе как бы представлялися как приватная конференция. Тогда можно будет открывать диалог между двумя пользователями без создания отдельной конференция для них.
А ссылки на сообщения как раз помогут в том, чтобы чужие сообщения не отображались кому не надо.
Вот схема:
Conference:
uuid
type
name //может быть как имя пользователя так и имя конференции
...
Conference_Reference:
user
conference
updated
unread
Message:
...
conference
Message_Reference:
user
message
То есть пользователь получает только свои ссылки на сообщения, и чужие сообщения, из одной и той же приватной конференции, представляющая пользователя, он не получит.
И сами приватные конференции создаются при регистрации
Вопрос прост и короток: Ничего страшного если uuid приватных конференций будут такие же как у пользователя?
2. Вопрос связанный с плохим знанием Питона или более специфичный для используемой мною платформы WAMP.
Платформа которую я использую может использовать компоненты, которые запускаются вместе с ней и могут быть использованы для обработки того или иного действия. Эти компоненты определяются в конфиге ( https://github.com/someApprentice/Crypter/blob/master/wamp/.crossbar/config.json#L101-L120 ).
И я сталкнулся с такой проблемой, что каждый из этих компонентов вынужден для себя добавлять путь к модулям, и этот путь добавляется снова и снова с каждым модулем.
https://github.com/someApprentice/Crypter/blob/master/wamp/AuthenticatorSession.py#L4
https://github.com/someApprentice/Crypter/blob/master/wamp/AuthorizerSession.py#L5
https://github.com/someApprentice/Crypter/blob/master/wamp/MessengerSession.py#L4
И я слышал, что такой подход добавления модулей не очень хороший...
И заострю внимание на том, что эти компоненты запускаются самой платформой при её запуске в консоле или точнее сказать как процесс.
Вопрос в том - правильно ли добавляю пути к модулям и как это исправить если нет? И не является ли этот вопрос скорее специфичным для этой платформы и лучше бы ему быть заданным на её форуме?
3. Должно быть правильно шифровать любой контент который передается в сообщении будь то голосовое сообщение, изображение или любой другой файл. Библиотека шифрования может шифровать Uint8Array ( https://github.com/openpgpjs/openpgpjs#encrypt-and-decrypt-uint8array-data-with-a-password ).
Можно ли перекодировать исходный файл в Uint8Array а затем обратно в файл, для его сохранения на диск/хранилище?
Я поискал ответы в интернете и нашел такие ответы
https://stackoverflow.com/questions/37134433/convert-input-file-to-byte-array
https://stackoverflow.com/questions/25354313/saving-a-uint8array-to-a-binary-file
которые предлагают такой подход
var reader = FileReader(file);
reader.onloadend = (e) => {
let a = new Uint8Array(evt.target.result);
//encrypt
let blob = new Blob([a], { type: mimeType });
...
}
Но я не могу понять, Blob и File принимают в конструктор array of ArrayBuffer... Uint8Array является ArrayBuffer'ом?
В документации сказано, что нельзя создать ArrayBuffer сам по себе, вместо этого, нужно создать TypedArray, которым является Uint8Array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
И Uint8Array тоже принимает в конструктор ArrayBuffer
Значит, можно обойтись без FileReader'а и написать напрямую
var buffer = async file.arrayBuffer()
var a = Uint8Array(buffer);
//encrypt
file = new File([encrypted], { type: ... });
Это верно?
Переделал схему наследования БД на Single Table Inheritance.
Перешел с Node.js на PHP.
Реализовал простой текстовый чат (без шифрования) и конфернеции между двумя пользователями.
Сообщения/конференции сохраняются в IndexeDB и извлекаются реактивно (https://rxdb.info/).
Проверять не обязательно, думаю я могу всё сделать самостоятельно. Но если есть какие-то интересные мысли или замечания и советы, всегда рад. Скорее всего у меня повсюду не вынесен код.
У меня есть вопросы:
1. Когда отправляется первое сообщение пользователю, то открывается сначала форма для отправки сообщения, а потом делается редирект в только что созданную конференцию, после отправки сообщения.pic-1 Это сделалось потому что нельзя изначально открыть конференцию, которая ещё не существует.
Было бы лучше если бы приватная конференция имела такой же id как и пользователь. И пользователь сам по себе как бы представлялися как приватная конференция. Тогда можно будет открывать диалог между двумя пользователями без создания отдельной конференция для них.
А ссылки на сообщения как раз помогут в том, чтобы чужие сообщения не отображались кому не надо.
Вот схема:
Conference:
uuid
type
name //может быть как имя пользователя так и имя конференции
...
Conference_Reference:
user
conference
updated
unread
Message:
...
conference
Message_Reference:
user
message
То есть пользователь получает только свои ссылки на сообщения, и чужие сообщения, из одной и той же приватной конференции, представляющая пользователя, он не получит.
И сами приватные конференции создаются при регистрации
Вопрос прост и короток: Ничего страшного если uuid приватных конференций будут такие же как у пользователя?
2. Вопрос связанный с плохим знанием Питона или более специфичный для используемой мною платформы WAMP.
Платформа которую я использую может использовать компоненты, которые запускаются вместе с ней и могут быть использованы для обработки того или иного действия. Эти компоненты определяются в конфиге ( https://github.com/someApprentice/Crypter/blob/master/wamp/.crossbar/config.json#L101-L120 ).
И я сталкнулся с такой проблемой, что каждый из этих компонентов вынужден для себя добавлять путь к модулям, и этот путь добавляется снова и снова с каждым модулем.
https://github.com/someApprentice/Crypter/blob/master/wamp/AuthenticatorSession.py#L4
https://github.com/someApprentice/Crypter/blob/master/wamp/AuthorizerSession.py#L5
https://github.com/someApprentice/Crypter/blob/master/wamp/MessengerSession.py#L4
И я слышал, что такой подход добавления модулей не очень хороший...
И заострю внимание на том, что эти компоненты запускаются самой платформой при её запуске в консоле или точнее сказать как процесс.
Вопрос в том - правильно ли добавляю пути к модулям и как это исправить если нет? И не является ли этот вопрос скорее специфичным для этой платформы и лучше бы ему быть заданным на её форуме?
3. Должно быть правильно шифровать любой контент который передается в сообщении будь то голосовое сообщение, изображение или любой другой файл. Библиотека шифрования может шифровать Uint8Array ( https://github.com/openpgpjs/openpgpjs#encrypt-and-decrypt-uint8array-data-with-a-password ).
Можно ли перекодировать исходный файл в Uint8Array а затем обратно в файл, для его сохранения на диск/хранилище?
Я поискал ответы в интернете и нашел такие ответы
https://stackoverflow.com/questions/37134433/convert-input-file-to-byte-array
https://stackoverflow.com/questions/25354313/saving-a-uint8array-to-a-binary-file
которые предлагают такой подход
var reader = FileReader(file);
reader.onloadend = (e) => {
let a = new Uint8Array(evt.target.result);
//encrypt
let blob = new Blob([a], { type: mimeType });
...
}
Но я не могу понять, Blob и File принимают в конструктор array of ArrayBuffer... Uint8Array является ArrayBuffer'ом?
В документации сказано, что нельзя создать ArrayBuffer сам по себе, вместо этого, нужно создать TypedArray, которым является Uint8Array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
И Uint8Array тоже принимает в конструктор ArrayBuffer
Значит, можно обойтись без FileReader'а и написать напрямую
var buffer = async file.arrayBuffer()
var a = Uint8Array(buffer);
//encrypt
file = new File([encrypted], { type: ... });
Это верно?
переходи на питон
>куда легче
Бутылки собирать легче всего.
Если ты нулевой, то тебе везде медленно и трудно будет.
По идее, во фронтенд вкатиться чуть быстрее и проще, чем в бек. Но это только если у тебя все отлично с вёрсткой.
Но на самом деле вкатиться проще и легче туда, где больше стажировок в твоём городе.
Это именно та ситуация, где нужно выбрать, что тебе по душе, и не работает фишка "похуй куда, главное быстрее уже начать деньги зарабатывать". Что тебе больше нравится, то и легче пойдёт.
Пыха в данный момент очень неплохо развивается, и, учитывая, что она всегда лидировала, ещё минимум 10 лет можно за неё не беспокоиться.
Для вката она вообще идеальна, простая и лёгкая со всех точек зрения, при этом куча вакух и стажировок с низкими требованиями. В то же время, она не такая престижная, как фронтенд(спасибо дурачкам, кричащим что пыха говно и нинужна), соответственно и конкуренция не такая дикая.
Тогда считаешь количество стажировок и джуновских вакансий в твоем городе. Где больше, туда и вкатываешься.
если авторизацию делаем по куке, тогда после закрытия браузера возможность редактирования навсегда теряется? Зачем так делать?
Можно ссылку на идеальное решение этой задачки? Потом хочу сверить.
>если авторизацию делаем по куке, тогда после закрытия браузера возможность редактирования навсегда теряется
анон, я до нее еще не дошел, сам новичок, но разве кукисы хранятся не столько времени, сколько ты сам им задашь? Там в задании вроде написано "укажите срок жизни - год". Разве куки после закрытия браузера не сохраняются?
Куки сохраняются. Потеряется если ты будешь передавать данные пользователя в адресной строке (методом GET если не ошибаюсь). Либо если укажишь время жизни сессии/куки (не уверен, можно ли устанавливать время жизни куки, но сессии точно, но все равно одно без другого жизнеспособно лишь частным случаем передачи самого идентификатора браузера или передачей через get в url), как написал этот анон >>35093.
Что данные сессии, что данные куки, всего лишь информация, которую ты записал в 1ом случае на стороне сервера или во 2ом на стороне клиента. Если ты не используешь сессии и куки, то аутентификация (узнай про разницу между авторизацией и аутентификацией, а она есть) проходит по информации из куки, метода get или идентификатора браузера (думаю своеобразный отпечаток, хотя не особо понимаю).
>Если ты не используешь куки, то аутентификация (узнай про разницу между авторизацией и аутентификацией, а она есть) проходит по информации из метода get или идентификатора браузера (думаю своеобразный отпечаток, хотя не особо понимаю).
Фикс
Куки в передаче через url не задействовано. Без них можно передавать данные посредством url. После прохождения авторизации создается сессия и ее идентификатор передается посредством get запроса. Вроде такую функцию можно настроить в php.ini
Ты, к примеру, можешь выключить в своем браузере куки и никак не получишь доступ к сайту без них. Слышал только про идентификатор браузера, но сам плохо понимаю что это, да и боюсь небезопасно.
Вообщем не мучайся, если интересно, то погугли. А так это один из способов, который почти нигде не используется уже. Но именно в этом способе пропадает связь если ты закрываешь вкладку или браузер.
Суть проблемы в том, что я не могу во фротенд. Начальник не заебует - я пилю бэк, а фронт обычно либо отдается другому разрабу, либо от меня требуется что-то примитивное добавь каких-то кнопок шоб не сильно всрато и можно было нажать и оно работало, если не выйдет - отдадим верстале.
Фронт ненавижу всем сердцем. Но решил укатывать в ДС, искать новую работу и просматриваю вакансии - везде требуются какие-то ангуляры, хуиляры и пр. В душе не ебу что оно такое, в связи с чем вопрос: как можно вкатиться во фронт и какой уровень знания должен быть для бекендера? Что учить?
Синтаксис и логику этих залуп знаю: HTML, CSS, JS, Jquery, но учил я все это мягко говоря лет 5 назад и ниибу что там щас.
Например сейчас клиент попросил сделать инпут куда надо вбивать цифры, после чего появляется возможность добавить еще один инпут, еще и еще и так до бесконечности. Весьма вероятно, что есть какие-то готовые решения, но я сижу пердолюсь изобретая колесо.
>Например сейчас клиент попросил сделать инпут куда надо вбивать цифры, после чего появляется возможность добавить еще один инпут, еще и еще и так до бесконечности.
слющай, анон, у меня мало опыта, но там наверное надо джикверри делать аля
$('#контейнер инпутов').append('<p><input type="мой инпут" name="инпут['+айдишник айдишник инпута+']" /></p>');
можно, конечно, тупо десять штук полей понаделать невидимых, а потом показывать их при заполнении предыдущего, но так наверное не делают
Ну я так и делаю, и также рассуждаю "но так наверное не делают". Вопрос не в том как сделать, в данном случае похуй на это - главное шоб работало, но в теории хотелось бы вкатиться в правильное делание
Да, иди котлеты жарь в маке.
>Для поиска по всем колонкам можно применить оператор LIKE к соединенным через пробел значениям столбцов.
? Разве можно в sql искать по соединению столбцов?
Попробуй vue, там доки можно за час-два прочитать и уже делать адовые вещи
вот например твоя задача https://pastebin.com/ijXJXpHt
Несколько месяцев, зависит от твоего текущего уровня и знания верстки.
Час доки почитать и сделать todo приложуху, готово, синтаксис у жс си-подобный поэтому после пыхи я думаю никаких траблов не будет на интуиции писать
Самая большая проблема наверное это понять как инициализировать приложение, есть вариант использовать рантайм-онли как и с жквери, но удобнее конечно использовать компоненты (каждый компонент это отдельный файл), а там и новый синтаксис и вот это всё. У симфони(webpack-encore) и ларавел(laravel-mix) есть свои тулзы для этого, можешь посмотреть что удобнее.
Тханкс. Еще такой вопрос: во фронт я вообще никак перекатываться не планирую, насколько вероятно что через 3 секунды как я задрочу vue - он станет неактуальной парашей? знаю что во фронте каждую наносекунду устаревает весь стек
строка 15
for ($m=1; $m>=20; $m++)
второй оператор в скобках: при каком условии цикл выполняется. У тебя наизнанку сделано.
for ($m=1; $m<=20; $m++) попробуй, а потом дальше чини.
На следующий день просто перечитай урок и все получится. Если бы ты был тупым, то ты бы не смог браузер запустить. А ты даже в тред нужный зашел, так что в прогеры годишься
<?php
$money=10000;
$age=16;
$count=0;
$procent=10;
for ($money; $money < 1000000; $money= $money+$procentMoney){
$procentMoney = ($money*$procent)/100;
$age++;
$count++;
}
echo "$age -возраст, $count - время, $procentMoney - сумма\n";
?>
Ответ частично правильный:
65 -возраст, 49 - время, 97017.233784872 - сумма, а в ответе написано по другому.
Почитай же еще этих интересных уроков про переменные, да повтори циклы.
Двачую.
Вообще кто кричит что пыха говно, это какие то раки которые никогда не кодили. Они могут сказать что python лучше, только потому что у нее нет скобочек или знака $ на переменной.
Но они не понимают, что скобочка дает возможность проебаться с пробелами . А значок бакса у переменной дает php интерпретатору возможность более быстро работать ,за счет понимания что это переменная и не тратя время на это.
Заебись. Вообще я Пыху почти выучил и js учу, и уже сейчас я могу динамический сайт запилить. Мб ноду выучу для пущего эффекта
Там учить-то нечего, просто хороший, годный набор инструментов и местный композер впридачу. Веб там по тем же технологиям работает.
А че за тема, я слышал, что можно на ноде делать десктоп приложение это правда?
Хз я на её удаленке использую.
Анон, ты молодец, все хорошо зделол, только внизу выводи не процент, а сумму, то-есть
echo "$age -возраст, $count - время, $money - сумма\n";
тебе же надо общую сумму вывести. В этом контексте она больше подойдет
foreach ($db->fetchQuery() as $kek)
то метод будет вызван для каждого элемента, или результат кэшируется и для обхода используется итератор по закэшированному значению?
Наизусть выучил? По уроку хауди хо "учим верстку за час" недеюсь?
Спасибо большое анон
Сначала вызывается функция, она возвращает какое-то значение, с этим значением уже и работает foreach, у всего свой evaluation order так сказатб
Я использую PDO, запрос примерно так выглядит:
$stmt = pdo->prepare($sql);
$stmt->execute($params)
то есть у объекта pdo я вызываю метод prepare.
Но почему в документации это написано таким образом :
PDO::prepare
т.е. к статический метод класса.
https://www.php.net/manual/ru/class.pdo.php
Почему в документации они так пишут?
Интересный вопрос
>Но почему в документации это написано таким образом :
>PDO::prepare
я вроде у Зандстры читал, что часто в руководствах пишут эту конструкцию с точками чтобы показать не реальный способ вывода, а дать читателю понять откуда что берется. Хотя, возможно, это мои фантазии, я тот еще пиздабол
Возможно что указатель на БД в PDO работает как синглтон, поэтому нет смысла делать объектом.
Минимум "на уровне чтения документации" в любом приличном месте. Начинай учить прямо сейчас. Иди в языкач, там пацаны пояснят что и как.
При любом выборе ты пидор, сорр
Создал CRUD на Gii для постов пользователей и с представления на сайте зарендерил update.php
Что нужно сделать чтобы форма update в которой _form.php заработала? Или я пошел не по тому пути? Хочу чтобы там где показывался пост, там и редактировать его
Laravel выбирай, самый модный, легкий
Лара - самый топ. На ней и пишу. Нравится. Когдато знакомился с yii2 но было это сто лет назад и я ниче не помню. Больше ни с чем не знаком.
Все чаще слышу о symfony, насколько понял - это более сложный и крутой фреймворк для альфачей, соответственно и с большей зарплатой.
Хз насколько я прав, но если прав то выбор таков: лара - если хочешь больше вакансий и более легкий вкот. Симфони - если хочешь больше зп, но вакансий поменьше будет и сам фреймворк посложнее
У тебя весь код выложен в публичную папку сервера. Это плохо, так как можно обратиться к любому файлу, прочитать конфиги, какие-то логи, если они там будут. Нужно выделить отдельную папку public, и сделать ее корневой папкой веб-сервера, а остальные файлы разместить за ее пределами.
Далее, у тебя приложен какой-то Си-файл в папке vendor, но нет ни слова в ридми про него. Скорее всего, ты забыл добавить папку vendor в gitignore, чтобы она не выгружалась на гитхаб. Сторонний код не надо сохранять в репозиторий, так как любой может установить сторонние библиотеки через композер.
Вообще, тебе стоило бы почитать комментарии к задаче про студентов, там это описано, как и многое другое: https://github.com/codedokode/pasta/blob/master/student-list.md#скрытие-файлов-из-репозитория
Папку src/Project стоило назвать лучше - например, src/FileSharing.
В README стоило бы добавить краткую инструкцию по разворачиванию проекта (запустите такую-то команду, отредактируйте такой-то конфиг).
Конфиг у тебя закоммичен в гит. Если другой разработчик скачает проект, исправит конфиг, то его данные закоммитятся в гит и у тебя отвалится доступ к БД. Для решения этой проблемы обычно сам конфиг добавляют в gitignore, а в репозиторий кладут образец конфига с комментариями (config.php.dist), который надо скопировать и отредактировать при установке проекта.
> $view = new \Slim\Views\Twig('./templates', [
> 'cache' => false
Кеш твига лучше бы настроить правильно, а не отключать, иначе без него твигу приходится неспешно парсить шаблоны и преобразовывать в PHP-код каждый раз.
Инициализацию Слима и контейнера стоит вынести в отдельный файл. Иначе ты захочешь сделать какую-то консольную команду, и тебе придется копипастить весь этот код. Вообще, я советую тебе попробовать сделать консольную команду для загрузки произвольного файла, чтобы проверить, насколько гибок твой код. Команда должна работать так. Ты запускаешь ее, указывая какой-то файл:
php ./bin/upload.php /tmp/image.png
Она загружает файл и выводит ссылку на него. Или же ошибку, если что-то пошло не так. В случае ошибки возвращается ненулевой код возврата ( https://ru.wikipedia.org/wiki/Код_возврата ), при успехе - нулевой.
> use \Psr\Http\Message\ServerRequestInterface as Request;
Хотя это не противоречит никаким стандартам и так можно делать, но меня это немного сбивает с толку, что в заголовке функции класс подписан одним именем, а на самом деле там интерфейс с другим именем.
> PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES UTF8",
Почитай про отличия utf8 и utf8mb4. utf8 - это не совсем полноценный utf-8.
> PDO::ATTR_ERRMODE=>TRUE
Тут должна быть не true, а одна из констант PDO::ERRMODE_...
> if (empty($files['newfile']->file)) {
> header("Location: /");
Молча редиректить без вывода сообщения пользователю - плохая идея. Тут правильнее было бы показать страницу ошибки или добавить сообщение об ошибке при редиректе.
Далее, если ты используешь контроллеры, то лучше сразу указывать их как обработчик запроса:
$app->post('/upload', контроллер);
У тебя же получается дублирование: часть кода контроллера ты пишешь в обработчике, часть в классе-контроллере. Какой в этом смысл? Выбери что-то одно, либо обработчик-функция, либо контроллер. А не оба варианта сразу:
$app->post('/upload', function (Request $request, Response $response, array $args) {
...
$controller = new \Project\Controllers\MainController($newfile, $directory, $copyDirectory);
Возможно, ты пытался сделать сервис, чтобы можно было повторно исопльзовать код и вызывать его из других мест? Тогда называй его сервис, а не контроллер.
Также, ты используешь странный способ передачи аргументов. Параметры операции (имя файла, итд) ты передаешь в конструктор, а зависимость (PDO) - в метод. Обычно делают ровно наоборот.
На всякий случай можешь глянуть урок по MVC, там есть сервисы и пример кода: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Стоит как-то связать названия шаблонов с названиями контроллеров или обработчиков, так будет проще. У тебя же в URL /files, а шаблон называется чуть по-другому - file_list.php.
> $app->get('/delete/{filename}',
Метод GET предназначен для получения данных и не должен менять данные на сервере. Он не подходит для удаления.
> $fileRecorder = new \Project\Models\FilesDataGateway($pdo);
> $fileManager = new \Project\Models\FileManager();
Вот эти сервисы стоило бы поместить в контейнер (а не создавать руками) и внедрять через конструктор контроллера с помощью Dependency Injection.
> public function getInfoAboutFile(string $directory, string $serverName)
Тут наверно логичнее просто передавать полный путь к файлу.
> $fileParameters['filesize'] = $fileManager->getFileSize($info['filesize']);
Вообще, в базу правильнее сохранять размер как число, а не форматированную строку. Так как ты не сможешь например посчитать общий объем файлов в БД.
> if($diff < 3600) {
> return floor($diff / 60) . ' минут назад';
Надо бы полноценно спрягать слова: 1 минуту, 2 минуты, 5 минут. Ну и showDate может быть имеет смысл вынести в utility-класс, эта функция к файлам имеет мало отношения.
> $function = "imagecreatefrom$typestr";
Собирать имя функции по частям плохая идея, так как такое имя нельзя, например, найти поиском. Лучше было сделать так:
case IMAGETYPE_JPEG:
$decoder = 'imagecreatefromjpeg';
$encoder = 'imagejpg';
break;
То есть писать имя функции как есть.
> if ($type = 2) { # jpeg
Надо использовать константу IMAGETYPE...
Непонятно, зачем в функции createToken() сделан цикл.
> $file->author = $this->filesDataGateway->checkAuthor($this->checkCookie(), $file->serverName);
Здесь ты используешь объект как массив. У тебя нет класса, нет методов, нет определенного списка полей. Не логичнее ли тогда использовать массив? Для них хотя бы разные полезные функции есть.
Также, информацию об авторстве можно было бы получать из БД одним запросом, вместе с данными о файле (получать список токенов и сравнивать их с кукой).
Поле createdAt можно вообще не добавлять, а просто вызывать метод форматирования времени из шаблона.
> https://github.com/InspektorGadjet/FileSharing/blob/master/src/Project/Models/Downloader.php#L14
> header('Content-Disposition: attachment; filename=' . $realFileName);
В заголовках можно использовать только кодировку ASCII (кириллицу нельзя). Надо либо применять специальный способ кодирования, либо заканчивать URL именем файла. Ты можешь глянуть, как это сделано в библиотеке Symfony HTTP Foundation: https://github.com/symfony/http-foundation/blob/master/BinaryFileResponse.php#L147 (хотя они зря используют mb_detect_encoding - она не работает).
Здесь нет проверки условия, что такой файл найден. А вдруг его нет?
> if(!empty($_POST['comment'])) {
Если там есть объект Request, то правильнее использовать его.
> $info['id'] = $file->id;
> $info['name'] = $file->realName;
Тут проще передать в шаблон сам объект $file, чем копировать все его поля.
> https://github.com/InspektorGadjet/FileSharing/blob/master/src/Project/Interfaces/CheckCookie.php
Это очень странный интерфейс, и я не понимаю, что он обозначает и как используется.
https://github.com/InspektorGadjet/FileSharing/blob/master/src/Project/Models/Downloader.php
Здесь не указано, что делать, если файла нет.
https://github.com/InspektorGadjet/FileSharing/blob/master/src/Project/Models/FileManager.php#L31
Здесь правильно добавить в условие while проверку что position не выходит за границы массива.
> if($dayDiff < 60) {
> return 'в прошлом месяце';
Ну вообще, это неверно. Если сейчас 1 мая, и прошло 59 дней, то это будет позапрошлый месяц. А если сейчас 1 марта, то это может быть и прошлый год.
В проекте нет SQL-дампа.
> $('#mask').css({'width':maskWidth,'height':maskHeight});
> $(id).css('top', winH/2-$(id).height()/2);
А это нельзя сделать средствами CSS? Абсолютным или фикс. позиционированием?
> $('.comment_form').submit(function() {
> if($('textarea[name=comment]').val() == '') {
> $('.messenger').html('Вы не ввели текст');
> event.preventDefault();
Ты обращаешься к глобальной переменной event, а правильнее использовать то, что передано в обработчик.
> alert('Ваш комментарий успешно добавлен');
Слишком рано. Он еще не добавлен.
> href="/view/{{ item.serverName }}"
> src="../copyes/{{ info.copy }}"
Возможно, имеет смысл сделать функции или класс-помощник для генерации ссылок и путей. Также, в слиме есть генератор ссылок, надо только добавить названия к роутам.
В функции удаления я не вижу проверки, что пользователь имеет на это право. Также, не вижу защиты от CSRF.
> <p><h2>Комментарий автора:</h2></p>
По правитам HTML внутри p не может быть заголовков. Вот описание тега p из HTML5.2: https://www.w3.org/TR/html52/grouping-content.html#the-p-element
> Content model:
> Phrasing content.
(если кликнуть, будет подробное описание).
Браузер, увидев h2, закроет тег p. Попробуй отправить свой HTML код на валидацию в валидатор: https://html5.validator.nu/ Он должен указать на такие моменты.
> <div class="commets-list">
Тут опечатка в слове comments
У тебя весь код выложен в публичную папку сервера. Это плохо, так как можно обратиться к любому файлу, прочитать конфиги, какие-то логи, если они там будут. Нужно выделить отдельную папку public, и сделать ее корневой папкой веб-сервера, а остальные файлы разместить за ее пределами.
Далее, у тебя приложен какой-то Си-файл в папке vendor, но нет ни слова в ридми про него. Скорее всего, ты забыл добавить папку vendor в gitignore, чтобы она не выгружалась на гитхаб. Сторонний код не надо сохранять в репозиторий, так как любой может установить сторонние библиотеки через композер.
Вообще, тебе стоило бы почитать комментарии к задаче про студентов, там это описано, как и многое другое: https://github.com/codedokode/pasta/blob/master/student-list.md#скрытие-файлов-из-репозитория
Папку src/Project стоило назвать лучше - например, src/FileSharing.
В README стоило бы добавить краткую инструкцию по разворачиванию проекта (запустите такую-то команду, отредактируйте такой-то конфиг).
Конфиг у тебя закоммичен в гит. Если другой разработчик скачает проект, исправит конфиг, то его данные закоммитятся в гит и у тебя отвалится доступ к БД. Для решения этой проблемы обычно сам конфиг добавляют в gitignore, а в репозиторий кладут образец конфига с комментариями (config.php.dist), который надо скопировать и отредактировать при установке проекта.
> $view = new \Slim\Views\Twig('./templates', [
> 'cache' => false
Кеш твига лучше бы настроить правильно, а не отключать, иначе без него твигу приходится неспешно парсить шаблоны и преобразовывать в PHP-код каждый раз.
Инициализацию Слима и контейнера стоит вынести в отдельный файл. Иначе ты захочешь сделать какую-то консольную команду, и тебе придется копипастить весь этот код. Вообще, я советую тебе попробовать сделать консольную команду для загрузки произвольного файла, чтобы проверить, насколько гибок твой код. Команда должна работать так. Ты запускаешь ее, указывая какой-то файл:
php ./bin/upload.php /tmp/image.png
Она загружает файл и выводит ссылку на него. Или же ошибку, если что-то пошло не так. В случае ошибки возвращается ненулевой код возврата ( https://ru.wikipedia.org/wiki/Код_возврата ), при успехе - нулевой.
> use \Psr\Http\Message\ServerRequestInterface as Request;
Хотя это не противоречит никаким стандартам и так можно делать, но меня это немного сбивает с толку, что в заголовке функции класс подписан одним именем, а на самом деле там интерфейс с другим именем.
> PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES UTF8",
Почитай про отличия utf8 и utf8mb4. utf8 - это не совсем полноценный utf-8.
> PDO::ATTR_ERRMODE=>TRUE
Тут должна быть не true, а одна из констант PDO::ERRMODE_...
> if (empty($files['newfile']->file)) {
> header("Location: /");
Молча редиректить без вывода сообщения пользователю - плохая идея. Тут правильнее было бы показать страницу ошибки или добавить сообщение об ошибке при редиректе.
Далее, если ты используешь контроллеры, то лучше сразу указывать их как обработчик запроса:
$app->post('/upload', контроллер);
У тебя же получается дублирование: часть кода контроллера ты пишешь в обработчике, часть в классе-контроллере. Какой в этом смысл? Выбери что-то одно, либо обработчик-функция, либо контроллер. А не оба варианта сразу:
$app->post('/upload', function (Request $request, Response $response, array $args) {
...
$controller = new \Project\Controllers\MainController($newfile, $directory, $copyDirectory);
Возможно, ты пытался сделать сервис, чтобы можно было повторно исопльзовать код и вызывать его из других мест? Тогда называй его сервис, а не контроллер.
Также, ты используешь странный способ передачи аргументов. Параметры операции (имя файла, итд) ты передаешь в конструктор, а зависимость (PDO) - в метод. Обычно делают ровно наоборот.
На всякий случай можешь глянуть урок по MVC, там есть сервисы и пример кода: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Стоит как-то связать названия шаблонов с названиями контроллеров или обработчиков, так будет проще. У тебя же в URL /files, а шаблон называется чуть по-другому - file_list.php.
> $app->get('/delete/{filename}',
Метод GET предназначен для получения данных и не должен менять данные на сервере. Он не подходит для удаления.
> $fileRecorder = new \Project\Models\FilesDataGateway($pdo);
> $fileManager = new \Project\Models\FileManager();
Вот эти сервисы стоило бы поместить в контейнер (а не создавать руками) и внедрять через конструктор контроллера с помощью Dependency Injection.
> public function getInfoAboutFile(string $directory, string $serverName)
Тут наверно логичнее просто передавать полный путь к файлу.
> $fileParameters['filesize'] = $fileManager->getFileSize($info['filesize']);
Вообще, в базу правильнее сохранять размер как число, а не форматированную строку. Так как ты не сможешь например посчитать общий объем файлов в БД.
> if($diff < 3600) {
> return floor($diff / 60) . ' минут назад';
Надо бы полноценно спрягать слова: 1 минуту, 2 минуты, 5 минут. Ну и showDate может быть имеет смысл вынести в utility-класс, эта функция к файлам имеет мало отношения.
> $function = "imagecreatefrom$typestr";
Собирать имя функции по частям плохая идея, так как такое имя нельзя, например, найти поиском. Лучше было сделать так:
case IMAGETYPE_JPEG:
$decoder = 'imagecreatefromjpeg';
$encoder = 'imagejpg';
break;
То есть писать имя функции как есть.
> if ($type = 2) { # jpeg
Надо использовать константу IMAGETYPE...
Непонятно, зачем в функции createToken() сделан цикл.
> $file->author = $this->filesDataGateway->checkAuthor($this->checkCookie(), $file->serverName);
Здесь ты используешь объект как массив. У тебя нет класса, нет методов, нет определенного списка полей. Не логичнее ли тогда использовать массив? Для них хотя бы разные полезные функции есть.
Также, информацию об авторстве можно было бы получать из БД одним запросом, вместе с данными о файле (получать список токенов и сравнивать их с кукой).
Поле createdAt можно вообще не добавлять, а просто вызывать метод форматирования времени из шаблона.
> https://github.com/InspektorGadjet/FileSharing/blob/master/src/Project/Models/Downloader.php#L14
> header('Content-Disposition: attachment; filename=' . $realFileName);
В заголовках можно использовать только кодировку ASCII (кириллицу нельзя). Надо либо применять специальный способ кодирования, либо заканчивать URL именем файла. Ты можешь глянуть, как это сделано в библиотеке Symfony HTTP Foundation: https://github.com/symfony/http-foundation/blob/master/BinaryFileResponse.php#L147 (хотя они зря используют mb_detect_encoding - она не работает).
Здесь нет проверки условия, что такой файл найден. А вдруг его нет?
> if(!empty($_POST['comment'])) {
Если там есть объект Request, то правильнее использовать его.
> $info['id'] = $file->id;
> $info['name'] = $file->realName;
Тут проще передать в шаблон сам объект $file, чем копировать все его поля.
> https://github.com/InspektorGadjet/FileSharing/blob/master/src/Project/Interfaces/CheckCookie.php
Это очень странный интерфейс, и я не понимаю, что он обозначает и как используется.
https://github.com/InspektorGadjet/FileSharing/blob/master/src/Project/Models/Downloader.php
Здесь не указано, что делать, если файла нет.
https://github.com/InspektorGadjet/FileSharing/blob/master/src/Project/Models/FileManager.php#L31
Здесь правильно добавить в условие while проверку что position не выходит за границы массива.
> if($dayDiff < 60) {
> return 'в прошлом месяце';
Ну вообще, это неверно. Если сейчас 1 мая, и прошло 59 дней, то это будет позапрошлый месяц. А если сейчас 1 марта, то это может быть и прошлый год.
В проекте нет SQL-дампа.
> $('#mask').css({'width':maskWidth,'height':maskHeight});
> $(id).css('top', winH/2-$(id).height()/2);
А это нельзя сделать средствами CSS? Абсолютным или фикс. позиционированием?
> $('.comment_form').submit(function() {
> if($('textarea[name=comment]').val() == '') {
> $('.messenger').html('Вы не ввели текст');
> event.preventDefault();
Ты обращаешься к глобальной переменной event, а правильнее использовать то, что передано в обработчик.
> alert('Ваш комментарий успешно добавлен');
Слишком рано. Он еще не добавлен.
> href="/view/{{ item.serverName }}"
> src="../copyes/{{ info.copy }}"
Возможно, имеет смысл сделать функции или класс-помощник для генерации ссылок и путей. Также, в слиме есть генератор ссылок, надо только добавить названия к роутам.
В функции удаления я не вижу проверки, что пользователь имеет на это право. Также, не вижу защиты от CSRF.
> <p><h2>Комментарий автора:</h2></p>
По правитам HTML внутри p не может быть заголовков. Вот описание тега p из HTML5.2: https://www.w3.org/TR/html52/grouping-content.html#the-p-element
> Content model:
> Phrasing content.
(если кликнуть, будет подробное описание).
Браузер, увидев h2, закроет тег p. Попробуй отправить свой HTML код на валидацию в валидатор: https://html5.validator.nu/ Он должен указать на такие моменты.
> <div class="commets-list">
Тут опечатка в слове comments
Может стоит хотя-бы начальные знания в с++ получить?
>Может стоит хотя-бы начальные знания в с++ получить?
Когда начнёшь учить C++ смотри чтобы мыслей выучить ассемблер не появилось, а то ты так до ручной пайки транзисторов докатишься.
Первое веб-приложение ты годам к 95 напишешь.
Но это не отменяет изучения самого программирования как скилла, безотносительно платформы(php\js\С++)
>Когда начнёшь учить C++ смотри чтобы мыслей выучить ассемблер не появилось, а то ты так до ручной пайки транзисторов докатишься.
Хотеть! Но времени нет, да и старый я уже. То есть можно выдрачивать стек не опасаясь на всегда окуклиться в вебе?
Чтобы мое поделие не сжирало 8 гигов оперативной памяти, чтоб быть не как макака с магическим мышлением, а чтоб на кончиках пальцах максимально осмысленно писать код
Не умею верстать. Знаю только основы html, с гуглом.
Знаю только основы JS, с гуглом. Делал некоторые костыли на jquery уровня "нажать на кнопку по событию". В душе не ебу как работает какой-нибуль React и прочие вебпуки.
Зато в похапе я знаю гораздо больше. Сейчас пишу бек для апи на laravel. Раньше юзал Yii, Code Igniter.
Знаю докер и линух немного. Могу на голом серваке поднять окружение из контейнеров, где будет стоять все говно, что требуется для работоспособности проекта.
Еще у меня такая фишка, что я походу научился писать оче большие приложения + рефакторить. Могу залезть в говнокод проекта и улучшить его (но бля, видит бог, я не хочу таким заниматься). Знаю, как написать архитектуру приложения, чтобы потом не потребовалось рефакторить или же он был как можно проще.
Хуй знает че делать. Сейчас век жс-блядства, чтобы веб-разраб не знал жиэс - это нонсенс.
Плюс пых сам по себе становится все больше никому нахуй не нужен.
Еще у меня есть зайчатки умений в Java и Selenium, могу писать всякую консольную поебень, но, думаю, всем на это похуй
>есть у меня такая фишка - ниче не знаю, но все могу
Ты охуенен, когда я выросту. хочу быть какты
Ну как-то так, да
$('form').on('beforeSubmit', function(e){
var form = $('form')[0];
var formData = new FormData(form);
console.log(formData);
$.ajax({
url:"$url",
type: 'POST',
data: formData,
cache: false,
processData: false,
contentType: false,
error: function(){
alert('Error!');
}
});
В Yii2 ajax не передает 2 input'a, в одном текст, в другом файл, с контроллером, моделью все хорошо, проверял, здесь выдает Error
Ты не привел пример кода, трудно подсказать. Обычно путают условие в for и пишут вместо условия продолжения цикла условие остановки (противоположное условие).
>>34558
Вопросы по схеме БД:
- зачем нужна таблица participant, если есть conference_reference? Я не понимаю, чем таблица participant отличается от conference_reference.
- не стоит ли сделать тип ENUM для message.type, чтобы ограничить список допустимых значений?
- не стоит ли сделать message_attachment.type типом ENUM?
- что за URL указывается в поле message.content? URL на стороннем сервере? На своем? Если на своем, не логичнее ли вместо URL указать внешних ключ на таблицу файлов, либо какой-то идентификатор файла, из которого строится URL? Идентификатор удобен тем, что позволяет в будущем менять вид URL файла.
- возможно, стоит в conference_reference убрать id и использовать в качестве ключа пару (conference_uuid, user_uuid). Например, я вижу, что message_reference уже использует пару (conference, user).
И еще, ничего, что тут приватный ключ лежит? https://github.com/someApprentice/Crypter/blob/master/wamp/.crossbar/key.priv
И еще, не отдаем ли мы тут детали ошибки на сервере пользователю: https://github.com/someApprentice/Crypter/blob/master/wamp/AuthenticatorSession.py#L55
Тут, в тестах, возможно стоило сделать вспомогательную функцию для генерации токенов, так как в них легко опечататься и долго гадать, потому тест не работает: https://github.com/someApprentice/Crypter/blob/master/wamp/tests/authenticator_test.py
Еще, я у тебя вижу комментарии:
> TypeError: exceptions must be old-style classes or derived from BaseException, not <class 'module'>
скорее всего ты неправильно сделал импорт и указываешь не объект, представляющий класс, а объект импортированного модуля. То есть тебе надо разобраться с модулями и импортами.
Я бы советовал добавить в тесты проверку, что нельзя подписаться на чужие сообщения или получить их.
Также, немного неудобно, что приходится поддерживать 2 набора сущностей: в Питоне и в PHP.
Что касается исключений при авторизации, мне кажется, их правильнее ловить в обработчике авторизации, а не тут: https://github.com/someApprentice/Crypter/blob/master/api/src/EventListener/ExceptionListener.php Также, не стоит отдавать подробности исключения клиенту, они могут содержать важные внутренние данные.
Тут (https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php ) есть такой код:
$client->request(
$method = 'POST',
$uri = '/api/auth/login',
Так писать не стоит, это не keyword arguments из Питона и имена переменных не учитываются никак.
В тестах авторизации ( https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php ) ты проверяешь что тебе выдана кука с определенным именем. Ты полагаешься на знание внутреннего устройства механизма авторизации. И наличие куки не гарантирует авторизации. Правильнее запрашивать какую-то страницу для проверки доступа к ней. Можно даже сделать специальный тестовый метод /test/whoami для этого. Также, не надо копипастить огромные полотна кода, можно было сделать вспомогательную функцию для отправки запросов.
Соответственно тесты будут вида canLoginWithValidPassword, cannotLoginWithWrongPassword.
Далее, это ненадежный способ проверки, ведь речь тут о безопасности:
> https://github.com/someApprentice/Crypter/blob/master/wamp/AuthorizerSession.py#L40
> if 'private.message.to.' in uri:
Не лучше ли использовать str.startswith() ?
> regex = re.compile('^private\.message\.to\.([a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12})$')
Микрооптимизация: регулярку можно скомпилировать один раз в начале скрипта и использовать скомпилированную версию. Также, для регулярок в питоне есть специальный литерал r'....'.
Также, я тут подумал, что для огромных конференций обновление через websocket может потребовать отдельную схему. У тебя, как я понял, при отправке сообщения в групповой чат на 1000 пользователей будет отправлено 1000 уведомлений private.message.to... и conference.updated.for.... Это будет нагружать вебсокет-демон. Логичнее для огромных чатов просто сделать канал updates.{conference_uuid} и слать туда одно уведомление. А клиент пусть отдельным запросом выясняет, что поменялось. Можно конечно слать и само сообщение, но надо убедиться, что кикнутые из чата, но не отписавшиеся от веб-сокет канала пользователи не смогут его получить.
В общем, один подход для всех плохо работает и для больших групп выгоднее использовать другой подход. То же самое с шифрованием, возможно для больших групп выгоднее не делать тысячу зашифрованных копий, а сделать ключ группы и шифровать им. При бане пользователя - менять ключ и пересылать его оставшимся участникам.
Также, я хочу напомнить, что вебсокет - это ненадежный канал, он имеет право терять сообщения (пользователь отсоединился и пропустил сообщение) и потому стоит проектировать его как вспомогательный канал для быстрого уведомления об обновлениях, но не как единственный или основной канал доставки данных.
> Когда отправляется первое сообщение пользователю, то открывается сначала форма для отправки сообщения, а потом делается редирект в только что созданную конференцию ...
> Было бы лучше если бы приватная конференция имела такой же id как и пользователь. И пользователь сам по себе как бы представлялися как приватная конференция. Тогда можно будет открывать диалог между двумя пользователями без создания отдельной конференция для них.
Этот вопрос можно решить без изменений в БД, на уровне интерфейса. Мы можем, например, сделать URL вида /talk/{userId} и при его открытии смотреть: есть приватный диалог с этим пользователем или нет. Если есть - подгружать данные (либо редиректить на URL с id конференции), если нет - показать пустую страницу. То есть тебе не надо создавать что-то в БД только потому, что пользователь нажал на имя контакта.
Для этого нам надо иметь либо список всех конференций пользователя локально, либо метод API, который примет пару id пользователей и вернет id конференции между ними. Если мы используем API, то появляется задержка на время поиска конференции. Однако, мы можем разрешить пользователю писать и отправлять сообщение, не дожидаясь поиска конференции и подгрузки истории. Для этого надо доработать API приема сообщений так, чтобы в него можно было передавать либо id конференции, либо пару id пользователей. Это немного усложнит код. При желании, можно доработать аналогично и другие API, принимающие id конференции.
Твой вариант подразумевает, что если у пользователей A и B есть контакт C, то у них будет общая conference, но четыре разных conference_reference (A + C, C + A, B + C, C + B). Это не очень удачно, на мой взгляд. Не очень понятно, зачем тогда вообще нужна сущность conference и не проще ли из conference_reference, соответствующего диалогу, ссылаться напрямую на пользователя. И это будет создавать путаницу.
Если хочется, чтобы id конференции совпадал с пользовательским, тогда логичнее было бы в качестве id конференции брать отсортированную пару id пользователей. То есть конференция пользователей A и B может иметь идентификатор A_B. А в таблице можно использовать составной ключ:
TABLE conference (
lower_user_id UUID REFERENCES users (id),
higher_user_id UUID REFERENCES users (id),
PRIMARY KEY (lower_user_id, higher_user_id)
)
Но тут есть минус: нам придется все внешние ключи на конференцию делать парой uuid, что неудобно. Плюс, непонятно, какие uuid записывать в случае группового чата: первых двух присоединившихся пользователей? Что, если один или оба пользователя выйдут из чата?
На мой взгляд, эти проблемы можно попробовать решить, используя для конференции свой id, но указывая id участвующих юзеров для приватных диалогов. Это позволяет ссылаться на конференцию по id, а также искать приватный диалог по id участников:
TABLE conference (
id UUID PRIMARY KEY,
-- Заполняются только для приватных диалогов из 2 человек
lower_dialog_user UUID DEFAULT NULL REFERENCES users (id),
higher_dialog_user UUID DEFAULT NULL REFERENCES users (id),
UNIQUE KEY(lower_dialog_user, higher_dialog_user)
)
Есть ли какие-то недостатки у такого подхода?
Ты не привел пример кода, трудно подсказать. Обычно путают условие в for и пишут вместо условия продолжения цикла условие остановки (противоположное условие).
>>34558
Вопросы по схеме БД:
- зачем нужна таблица participant, если есть conference_reference? Я не понимаю, чем таблица participant отличается от conference_reference.
- не стоит ли сделать тип ENUM для message.type, чтобы ограничить список допустимых значений?
- не стоит ли сделать message_attachment.type типом ENUM?
- что за URL указывается в поле message.content? URL на стороннем сервере? На своем? Если на своем, не логичнее ли вместо URL указать внешних ключ на таблицу файлов, либо какой-то идентификатор файла, из которого строится URL? Идентификатор удобен тем, что позволяет в будущем менять вид URL файла.
- возможно, стоит в conference_reference убрать id и использовать в качестве ключа пару (conference_uuid, user_uuid). Например, я вижу, что message_reference уже использует пару (conference, user).
И еще, ничего, что тут приватный ключ лежит? https://github.com/someApprentice/Crypter/blob/master/wamp/.crossbar/key.priv
И еще, не отдаем ли мы тут детали ошибки на сервере пользователю: https://github.com/someApprentice/Crypter/blob/master/wamp/AuthenticatorSession.py#L55
Тут, в тестах, возможно стоило сделать вспомогательную функцию для генерации токенов, так как в них легко опечататься и долго гадать, потому тест не работает: https://github.com/someApprentice/Crypter/blob/master/wamp/tests/authenticator_test.py
Еще, я у тебя вижу комментарии:
> TypeError: exceptions must be old-style classes or derived from BaseException, not <class 'module'>
скорее всего ты неправильно сделал импорт и указываешь не объект, представляющий класс, а объект импортированного модуля. То есть тебе надо разобраться с модулями и импортами.
Я бы советовал добавить в тесты проверку, что нельзя подписаться на чужие сообщения или получить их.
Также, немного неудобно, что приходится поддерживать 2 набора сущностей: в Питоне и в PHP.
Что касается исключений при авторизации, мне кажется, их правильнее ловить в обработчике авторизации, а не тут: https://github.com/someApprentice/Crypter/blob/master/api/src/EventListener/ExceptionListener.php Также, не стоит отдавать подробности исключения клиенту, они могут содержать важные внутренние данные.
Тут (https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php ) есть такой код:
$client->request(
$method = 'POST',
$uri = '/api/auth/login',
Так писать не стоит, это не keyword arguments из Питона и имена переменных не учитываются никак.
В тестах авторизации ( https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php ) ты проверяешь что тебе выдана кука с определенным именем. Ты полагаешься на знание внутреннего устройства механизма авторизации. И наличие куки не гарантирует авторизации. Правильнее запрашивать какую-то страницу для проверки доступа к ней. Можно даже сделать специальный тестовый метод /test/whoami для этого. Также, не надо копипастить огромные полотна кода, можно было сделать вспомогательную функцию для отправки запросов.
Соответственно тесты будут вида canLoginWithValidPassword, cannotLoginWithWrongPassword.
Далее, это ненадежный способ проверки, ведь речь тут о безопасности:
> https://github.com/someApprentice/Crypter/blob/master/wamp/AuthorizerSession.py#L40
> if 'private.message.to.' in uri:
Не лучше ли использовать str.startswith() ?
> regex = re.compile('^private\.message\.to\.([a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12})$')
Микрооптимизация: регулярку можно скомпилировать один раз в начале скрипта и использовать скомпилированную версию. Также, для регулярок в питоне есть специальный литерал r'....'.
Также, я тут подумал, что для огромных конференций обновление через websocket может потребовать отдельную схему. У тебя, как я понял, при отправке сообщения в групповой чат на 1000 пользователей будет отправлено 1000 уведомлений private.message.to... и conference.updated.for.... Это будет нагружать вебсокет-демон. Логичнее для огромных чатов просто сделать канал updates.{conference_uuid} и слать туда одно уведомление. А клиент пусть отдельным запросом выясняет, что поменялось. Можно конечно слать и само сообщение, но надо убедиться, что кикнутые из чата, но не отписавшиеся от веб-сокет канала пользователи не смогут его получить.
В общем, один подход для всех плохо работает и для больших групп выгоднее использовать другой подход. То же самое с шифрованием, возможно для больших групп выгоднее не делать тысячу зашифрованных копий, а сделать ключ группы и шифровать им. При бане пользователя - менять ключ и пересылать его оставшимся участникам.
Также, я хочу напомнить, что вебсокет - это ненадежный канал, он имеет право терять сообщения (пользователь отсоединился и пропустил сообщение) и потому стоит проектировать его как вспомогательный канал для быстрого уведомления об обновлениях, но не как единственный или основной канал доставки данных.
> Когда отправляется первое сообщение пользователю, то открывается сначала форма для отправки сообщения, а потом делается редирект в только что созданную конференцию ...
> Было бы лучше если бы приватная конференция имела такой же id как и пользователь. И пользователь сам по себе как бы представлялися как приватная конференция. Тогда можно будет открывать диалог между двумя пользователями без создания отдельной конференция для них.
Этот вопрос можно решить без изменений в БД, на уровне интерфейса. Мы можем, например, сделать URL вида /talk/{userId} и при его открытии смотреть: есть приватный диалог с этим пользователем или нет. Если есть - подгружать данные (либо редиректить на URL с id конференции), если нет - показать пустую страницу. То есть тебе не надо создавать что-то в БД только потому, что пользователь нажал на имя контакта.
Для этого нам надо иметь либо список всех конференций пользователя локально, либо метод API, который примет пару id пользователей и вернет id конференции между ними. Если мы используем API, то появляется задержка на время поиска конференции. Однако, мы можем разрешить пользователю писать и отправлять сообщение, не дожидаясь поиска конференции и подгрузки истории. Для этого надо доработать API приема сообщений так, чтобы в него можно было передавать либо id конференции, либо пару id пользователей. Это немного усложнит код. При желании, можно доработать аналогично и другие API, принимающие id конференции.
Твой вариант подразумевает, что если у пользователей A и B есть контакт C, то у них будет общая conference, но четыре разных conference_reference (A + C, C + A, B + C, C + B). Это не очень удачно, на мой взгляд. Не очень понятно, зачем тогда вообще нужна сущность conference и не проще ли из conference_reference, соответствующего диалогу, ссылаться напрямую на пользователя. И это будет создавать путаницу.
Если хочется, чтобы id конференции совпадал с пользовательским, тогда логичнее было бы в качестве id конференции брать отсортированную пару id пользователей. То есть конференция пользователей A и B может иметь идентификатор A_B. А в таблице можно использовать составной ключ:
TABLE conference (
lower_user_id UUID REFERENCES users (id),
higher_user_id UUID REFERENCES users (id),
PRIMARY KEY (lower_user_id, higher_user_id)
)
Но тут есть минус: нам придется все внешние ключи на конференцию делать парой uuid, что неудобно. Плюс, непонятно, какие uuid записывать в случае группового чата: первых двух присоединившихся пользователей? Что, если один или оба пользователя выйдут из чата?
На мой взгляд, эти проблемы можно попробовать решить, используя для конференции свой id, но указывая id участвующих юзеров для приватных диалогов. Это позволяет ссылаться на конференцию по id, а также искать приватный диалог по id участников:
TABLE conference (
id UUID PRIMARY KEY,
-- Заполняются только для приватных диалогов из 2 человек
lower_dialog_user UUID DEFAULT NULL REFERENCES users (id),
higher_dialog_user UUID DEFAULT NULL REFERENCES users (id),
UNIQUE KEY(lower_dialog_user, higher_dialog_user)
)
Есть ли какие-то недостатки у такого подхода?
> И я сталкнулся с такой проблемой, что каждый из этих компонентов вынужден для себя добавлять путь к модулям, и этот путь добавляется снова и снова с каждым модулем.
Есть возможность в конфиге указать дополнения к pythonpath: https://crossbar.io/docs/Native-Worker-Options/ . Не подойдет ли это тут? Единственное, я не советую привязываться к текущей директории, чтобы ничего не ломалось, когда текущая директория меняется. А привязываться к расположению файла. Либо использовать вспомогательный bash-скрипт, который задаст правильную текущую директорию перед запуском сервера.
Также, ты не очень удачно назвал директорию src, надо было назвать как-то вроде wampserver.
Если ты добавил корень всего в pythonpath, то далее ты просто пишешь что-то вроде: import database from src
Я советую почитать какую-нибудь статью про модули и пакеты, хотя бы эту: https://realpython.com/absolute-vs-relative-python-imports/
Там еще есть relative imports, но мне кажется, тут можно обойтись без них. То есть разберись с модуляим и пакетами в python и тем, как они ищутся. Добавь корневую директорию в pythonpath, и импортируй стандартным способом.
> Можно ли перекодировать исходный файл в Uint8Array а затем обратно в файл, для его сохранения на диск/хранилище?
Я не очень понимаю, зачем ты хочешь это сделать, но думаю, что можно. Забивать хранилище огромными файлами, затрудняя поиск информации в нем, не очень логично, а зачем их хранить на диске в зашифрованном виде, вообще непонятно.
>В документации сказано, что нельзя создать ArrayBuffer сам по себе, вместо этого, нужно создать TypedArray, которым является Uint8Array.
Если мы посмотрим определения, то увидим, что:
- ArrayBuffer - просто представляет набор байт в памяти фиксированной длины. Он не позволяет с ними что-то делать. Чтобы что-то делать с данными, нужно создать представление для доступа к ним: DataView или TypedArray
- DataView - позволяет читать/записывать числа разных размеров в ArrayBuffer
- TypedArray (Unit8Array и другие) - позволяет работать с данными в буфере, как будто это массив чисел определенного типа. Это именно представление для буфера, даже если ты не передаешь буфер или передаешь в конструктор не-буфер, он создает его сам. Как я понял, за TypedArray всегда прячется какой-то буфер, даже если ты его явно не создавал.
Blob - это абстрактная штука, из которой можно читать данные, оптимизированная под огромный объем данных (A Blob object represents a file-like object of immutable, raw data.). Он отличается от ArrayBuffer следующими особенностями:
1) он может собираться из нескольких отдельных буферов, позволяя избежать выделения памяти и копирования данных в одну общую область памяти. Вместо копирования он просто хранит ссылки на исходные буферы. И когда ты делаешь blob.slice(), он просто создает новый блоб со ссылками на куски буферов без выделения памяти и копирования каких-то данных. ArrayBuffer же представляет собой одну последовательную область памяти.
2) он может представлять данные, которые пока не загружены в память, или которых так много, что они в нее не поместятся, или которые потребуют много времени для чтения, и читать их кусками или потоком (методы stream(), slice()). Я подозреваю, что когда ты делаешь slice(), он реально ничего не читает в память и не меняет, а просто создает пометку "взять байты с X по Y из низлежащего блоба".
3) он может содержать информацию о MIME-типе и виде символов конца строки
4) он иммутабелен
5) для него можно создать URL (внутри браузера, он имеет вид blob://12345678) и пользователь может скачать блоб как файл по этому URL.
File это расширение Blob с доп. атрибутами и он представляет существующий файл на диске.
Ты читаешь файл в память целиком. Это работает только для маленьких файлов (порядка 1 Мб). Большие файлы надо уметь обрабатывать по кускам по следующим причинам:
- не тратить зря память
- быстрее начать передачу, не дожидаясь полной загрузки с диска в память
- отправка на сервер огромного файла может прерваться, потому его логичнее резать на куски и загружать кусками, а при ошибке пересылать заново только один кусок
Соответственно, твой подход работает только для маленьких файлов. Зачем ты хочешь их сохранять после шифрования, я не понимаю. Если ты пересылаешь файл контакту, то ты читаешь его целиком или частично, шифруешь, шлешь на сервер. Если ты принимаешь файл, то расшифровываешь и пишешь на диск. Если принимаешь файл кусочками, то расшифровываешь каждый кусочек, пишешь во временный файл, в конце переименовываешь.
Не забывай после окончания работы с файлом закрывать все и освобождать, чтобы данные не висели в памяти.
Если тебе пока не хочется возиться с кусочками, то стоит поставить ограничение по размеру на несколько мегабайт. Если ты хочешь сделать качественную передачу файлов, придется учиться резать их на кусочки по несколько десятков или сотен килобайт.
> Но я не могу понять, Blob и File принимают в конструктор array of ArrayBuffer...
Они принимают массив буферов, а не один буфер, чтобы можно было собрать большой файл из кусочков, разбросанных по разным местам памяти, без копирования их в общую область памяти. И я подозреваю, он позволяет даже собирать блоб из кусочков данных в памяти и тех, что не сейчас в памяти.
> Uint8Array является ArrayBuffer'ом?
Нет, это представление для записи/чтения данных из буфера по одному числу типа uint8 за раз.
Зачем нужен FileReader, когда есть blob.stream(), я не очень понимаю, я думаю, он просто исторически появился раньше (в FF3.6), чем Blob.stream() (это новая экспериментальная технология).
> И я сталкнулся с такой проблемой, что каждый из этих компонентов вынужден для себя добавлять путь к модулям, и этот путь добавляется снова и снова с каждым модулем.
Есть возможность в конфиге указать дополнения к pythonpath: https://crossbar.io/docs/Native-Worker-Options/ . Не подойдет ли это тут? Единственное, я не советую привязываться к текущей директории, чтобы ничего не ломалось, когда текущая директория меняется. А привязываться к расположению файла. Либо использовать вспомогательный bash-скрипт, который задаст правильную текущую директорию перед запуском сервера.
Также, ты не очень удачно назвал директорию src, надо было назвать как-то вроде wampserver.
Если ты добавил корень всего в pythonpath, то далее ты просто пишешь что-то вроде: import database from src
Я советую почитать какую-нибудь статью про модули и пакеты, хотя бы эту: https://realpython.com/absolute-vs-relative-python-imports/
Там еще есть relative imports, но мне кажется, тут можно обойтись без них. То есть разберись с модуляим и пакетами в python и тем, как они ищутся. Добавь корневую директорию в pythonpath, и импортируй стандартным способом.
> Можно ли перекодировать исходный файл в Uint8Array а затем обратно в файл, для его сохранения на диск/хранилище?
Я не очень понимаю, зачем ты хочешь это сделать, но думаю, что можно. Забивать хранилище огромными файлами, затрудняя поиск информации в нем, не очень логично, а зачем их хранить на диске в зашифрованном виде, вообще непонятно.
>В документации сказано, что нельзя создать ArrayBuffer сам по себе, вместо этого, нужно создать TypedArray, которым является Uint8Array.
Если мы посмотрим определения, то увидим, что:
- ArrayBuffer - просто представляет набор байт в памяти фиксированной длины. Он не позволяет с ними что-то делать. Чтобы что-то делать с данными, нужно создать представление для доступа к ним: DataView или TypedArray
- DataView - позволяет читать/записывать числа разных размеров в ArrayBuffer
- TypedArray (Unit8Array и другие) - позволяет работать с данными в буфере, как будто это массив чисел определенного типа. Это именно представление для буфера, даже если ты не передаешь буфер или передаешь в конструктор не-буфер, он создает его сам. Как я понял, за TypedArray всегда прячется какой-то буфер, даже если ты его явно не создавал.
Blob - это абстрактная штука, из которой можно читать данные, оптимизированная под огромный объем данных (A Blob object represents a file-like object of immutable, raw data.). Он отличается от ArrayBuffer следующими особенностями:
1) он может собираться из нескольких отдельных буферов, позволяя избежать выделения памяти и копирования данных в одну общую область памяти. Вместо копирования он просто хранит ссылки на исходные буферы. И когда ты делаешь blob.slice(), он просто создает новый блоб со ссылками на куски буферов без выделения памяти и копирования каких-то данных. ArrayBuffer же представляет собой одну последовательную область памяти.
2) он может представлять данные, которые пока не загружены в память, или которых так много, что они в нее не поместятся, или которые потребуют много времени для чтения, и читать их кусками или потоком (методы stream(), slice()). Я подозреваю, что когда ты делаешь slice(), он реально ничего не читает в память и не меняет, а просто создает пометку "взять байты с X по Y из низлежащего блоба".
3) он может содержать информацию о MIME-типе и виде символов конца строки
4) он иммутабелен
5) для него можно создать URL (внутри браузера, он имеет вид blob://12345678) и пользователь может скачать блоб как файл по этому URL.
File это расширение Blob с доп. атрибутами и он представляет существующий файл на диске.
Ты читаешь файл в память целиком. Это работает только для маленьких файлов (порядка 1 Мб). Большие файлы надо уметь обрабатывать по кускам по следующим причинам:
- не тратить зря память
- быстрее начать передачу, не дожидаясь полной загрузки с диска в память
- отправка на сервер огромного файла может прерваться, потому его логичнее резать на куски и загружать кусками, а при ошибке пересылать заново только один кусок
Соответственно, твой подход работает только для маленьких файлов. Зачем ты хочешь их сохранять после шифрования, я не понимаю. Если ты пересылаешь файл контакту, то ты читаешь его целиком или частично, шифруешь, шлешь на сервер. Если ты принимаешь файл, то расшифровываешь и пишешь на диск. Если принимаешь файл кусочками, то расшифровываешь каждый кусочек, пишешь во временный файл, в конце переименовываешь.
Не забывай после окончания работы с файлом закрывать все и освобождать, чтобы данные не висели в памяти.
Если тебе пока не хочется возиться с кусочками, то стоит поставить ограничение по размеру на несколько мегабайт. Если ты хочешь сделать качественную передачу файлов, придется учиться резать их на кусочки по несколько десятков или сотен килобайт.
> Но я не могу понять, Blob и File принимают в конструктор array of ArrayBuffer...
Они принимают массив буферов, а не один буфер, чтобы можно было собрать большой файл из кусочков, разбросанных по разным местам памяти, без копирования их в общую область памяти. И я подозреваю, он позволяет даже собирать блоб из кусочков данных в памяти и тех, что не сейчас в памяти.
> Uint8Array является ArrayBuffer'ом?
Нет, это представление для записи/чтения данных из буфера по одному числу типа uint8 за раз.
Зачем нужен FileReader, когда есть blob.stream(), я не очень понимаю, я думаю, он просто исторически появился раньше (в FF3.6), чем Blob.stream() (это новая экспериментальная технология).
Идея в том, что мы не хотим заставлять пользователя проходить полноценную регистрацию и тратить время, но хотим дать возможность исправить данные, если он указал их с ошибкой. Потому выбран такой способ. При желании ты можешь добавить поле ввода пароля и добавить возможность полноценной регистрации.
Кука хранится столько, сколько укажешь. Однако, если ты не укажешь время жизни, то она будет храниться до закрытия браузера (это называется сессионная кука, сессия - имеется в виду сеанс работы с браузером, а не PHP-сессия).
>>35520
Изучать придется с основ верстки, CSS, JS. У нас есть задачи в ОП-посте, но только по самым основам.
>>35842
WHERE CONCAT(name, ' ', surname) LIKE '%иван%'
>>35867
Честно говоря для добавления поля в форму делать полноценное приложение с вебпаком, npm, моделями, вью и полмегабайтом кода выглядит как оверкилл.
>>36875
for ($money; ...
Здесь выражение $money ничего не делает (получает значение переменной и выбрасывает его). Надо писать либо так:
for ( ; $money < 1000000; ...
Либо так:
for ($money = 10000; $money < 1000000; ...
>>37180
Надо бы старые вопросы проверить еще.
>>37182
Вообще, я думаю, что там небольшая разница в скорости парсинга кода. Более важные отличия - это наличие библиотек, фреймворков, CMS, простота освоения, наличие инструментов, отладчиков, профайлеров, скорость выполнения кода, как язык развивается. Ну и например, в PHP есть типизация для аргументов функций, а в Питоне - нет. Для кого-то еще важно число вакансий и требуемый уровень знаний, насколько легко найти разработчиков.
Я сам Питон использую, например, в Jupyter Notebook, хороший язык, у него много плюсов, хотя конечно редактировать код в этом браузерном недоредакторе не очень удобно.
И напомню, что мы тут никого учить именно PHP не заставляем, не хотите - не учите, или учите оба языка параллельно.
>>37199
Да, гугли Electron. Но чтобы сделать качественное приложение на нем, конечно, надо постараться.
>>37526
Функция вызовется один раз.
>>38092
Потому что у них так принято. Мне больше нравится писать в такой ситуации PDO#prepare().
Возможно, это навеяно Си++, там ты описываешь тело функции класса похожей конструкцией:
void PDO::prepare() { ... }
(вызов статического метода в Си++ делается как Class.someMethod(), потому путаницы нет).
>>39606
Если тебе хочется разобраться в более низкоуровневых деталях, я советую почитать про:
- работу процессора (как устроена память, байты, машинные коды, регистры, стек, ассемблер). Учить весь ассемблер не надо, просто понять общий принцип
- после этого можно почитать про язык Си, указатели, выделение памяти, куча, компиляторы, препроцессоры, makefile
Я думаю, после этого тебе все станет более-менее понятно. Так как PHP написан на Си и ты сможешь читать его код.
Си++ не очень нужен, можешь изучить, но это просто добавление ООП и шаблонов в Си.
>>40218
Посмотри документацию jquery.ajax, что разрешено передавать в качестве data.
Идея в том, что мы не хотим заставлять пользователя проходить полноценную регистрацию и тратить время, но хотим дать возможность исправить данные, если он указал их с ошибкой. Потому выбран такой способ. При желании ты можешь добавить поле ввода пароля и добавить возможность полноценной регистрации.
Кука хранится столько, сколько укажешь. Однако, если ты не укажешь время жизни, то она будет храниться до закрытия браузера (это называется сессионная кука, сессия - имеется в виду сеанс работы с браузером, а не PHP-сессия).
>>35520
Изучать придется с основ верстки, CSS, JS. У нас есть задачи в ОП-посте, но только по самым основам.
>>35842
WHERE CONCAT(name, ' ', surname) LIKE '%иван%'
>>35867
Честно говоря для добавления поля в форму делать полноценное приложение с вебпаком, npm, моделями, вью и полмегабайтом кода выглядит как оверкилл.
>>36875
for ($money; ...
Здесь выражение $money ничего не делает (получает значение переменной и выбрасывает его). Надо писать либо так:
for ( ; $money < 1000000; ...
Либо так:
for ($money = 10000; $money < 1000000; ...
>>37180
Надо бы старые вопросы проверить еще.
>>37182
Вообще, я думаю, что там небольшая разница в скорости парсинга кода. Более важные отличия - это наличие библиотек, фреймворков, CMS, простота освоения, наличие инструментов, отладчиков, профайлеров, скорость выполнения кода, как язык развивается. Ну и например, в PHP есть типизация для аргументов функций, а в Питоне - нет. Для кого-то еще важно число вакансий и требуемый уровень знаний, насколько легко найти разработчиков.
Я сам Питон использую, например, в Jupyter Notebook, хороший язык, у него много плюсов, хотя конечно редактировать код в этом браузерном недоредакторе не очень удобно.
И напомню, что мы тут никого учить именно PHP не заставляем, не хотите - не учите, или учите оба языка параллельно.
>>37199
Да, гугли Electron. Но чтобы сделать качественное приложение на нем, конечно, надо постараться.
>>37526
Функция вызовется один раз.
>>38092
Потому что у них так принято. Мне больше нравится писать в такой ситуации PDO#prepare().
Возможно, это навеяно Си++, там ты описываешь тело функции класса похожей конструкцией:
void PDO::prepare() { ... }
(вызов статического метода в Си++ делается как Class.someMethod(), потому путаницы нет).
>>39606
Если тебе хочется разобраться в более низкоуровневых деталях, я советую почитать про:
- работу процессора (как устроена память, байты, машинные коды, регистры, стек, ассемблер). Учить весь ассемблер не надо, просто понять общий принцип
- после этого можно почитать про язык Си, указатели, выделение памяти, куча, компиляторы, препроцессоры, makefile
Я думаю, после этого тебе все станет более-менее понятно. Так как PHP написан на Си и ты сможешь читать его код.
Си++ не очень нужен, можешь изучить, но это просто добавление ООП и шаблонов в Си.
>>40218
Посмотри документацию jquery.ajax, что разрешено передавать в качестве data.
А как выводить из базы данных текст что бы лежал в центре моего новостного блога , без пользовательских авторизаций,
то есть все оффлайн, открывают мой сайт и там сразу все новости по датам .
Я правильно понял, что
Это надо отдельную базу данных делать к которому нет пароля и можно вытягивать без пароля? Или же с паролем, но этот пароль всегда будет лежать в переменной ,в моем php коде? (безопасно ли это?) Ведь новые новости тоже надо будет заносить
Аношка, я сам новичок, но попробую объяснить, остальные поправят. Твой движок/фреймворк коннектится к базе данных через пароль и логин. После того, как он законнектился, твой движок посылает sql-запрос к базе "выбрать все новости". Движок получает ответ от базы, и, если ответ не пустой, выводит новости на страницу. Пароль посетителя/администратора сайта и пароль к базе данных mysql вообще никак не связаны, это разные пароли. Условно говоря, область видимости новостей, которые ты показываешь посетителю, регламентируется не базой данных и ее паролем, а движком/фреймворком/скриптом. Пароль к базе данных всегда есть. Пароль пользователя сайта может быть, а может и не быть.
Ты можешь в своем движке прописать так: скрипт, просто показывай новости всем. А можешь прописать так: скрипт, показывай новости только тем, у кого есть особые куки/сессия/еще что-нибудь. По этому же принципу организовано добавление новостей, добавлять новости через сайт может только тот, у кого есть пароли/куки/особая сессия - это и есть администратор сайта.
Не ну получается подключение к mysql всегда скрип выполняет? Для всех (оффлайн) Например mysqli(localhost, dbname, dbpass) и тому подобное.
Регестрация и авторизация через другую таблицу т.е уже новое подключение mysqli(localhost, dbname, dbpass) и т.д . Или как
Спасибо, в остальном исчерпывающе
Просто если бд подключена всегда, то когда злоумышленик достанет наш php код, то сможет испоганить всю бд
Кидай сюбда
>Пароль посетителя/администратора сайта и пароль к базе данных mysql вообще никак не связаны, это разные пароли
Да, но разве пароли админки не лежат внутри бд? Пусть даже в хэш-виде, И если у злоумышленника будет пароль от бд то и будет пароли админа , пусть даже в захешированом виде
Часто в БД ставят ограничение, с каких IP можно к ней коннектиться. Например, пишут 127.0.0.1 - только с того же компьютера, где расположена БД, а снаружи нельзя. И злоумышленнику тогда надо сначала получить доступ к этому компьютеру, а не только пароль.
Или фаерволлом закрывают доступ.
session_start() работает так:
- если у человека есть кука вроде PHPSESSION (не помню название), и идентификатор в ней указывает на существующий файл сессии, то загрузить данные из него в массив $_SESSION
- иначе сгенерировать новый ид, создать новый пустой файл, создать пустой массив $_SESSION, попросить браузер создать куку с этим id
А по завершению скрипта или при вызове session_write_close данные из $_SESSION сбрасываются в файл.
Также на время работы скрипта файл блокируется от доступа для параллельно выполняющихся скриптов. чтобы два скрипта не могли одновременно работать с одной сессией.
То есть сессия это файл на сервере в определенной папке, а кука содержит его имя (конечно, это можно поменять, чтобы например это была запись в БД или в редисе, или в шареной памяти итд).
Если на твой сайт зайти браузером или роботом без поддержки кук, то новая сессия будет создаваться при каждом обращении к странице с session_start().
Некоторые пишут свою обертку поверх сессий, которая сохраняет данные только при попытке что-то записать в сессию, и не сохраняет пустые сессии без данных.
>Часто в БД ставят ограничение, с каких IP можно к ней коннектиться.
Тогда нужно отдельную бд для админа и отдельную для остальных? А то закроется всем доступ к регемтрации
Да. Во-первых, чтобы создавать пользователей на уровне БД, нужно было бы выдать скрипту регистрации права администратора БД. Во-вторых, БД не очень рассчитаны на работу с миллионами пользователей. В-третьих, во многих БД разграничение доступа идет на уровне таблиц, и нельзя сделать права доступа на отдельную строку.
Потому обычно скрипт подсоединяется к БД под одним и тем же пользователем, который указан в конфиге.
Но ты можешь попробовать реализовать свою модель с разными пользователями. Только попробуй начать с проектирования и продумывания разных ситуаций вроде разграничения доступа (только автор имеет право редактировать комментарий, но все имеют право его читать), предоставления доступа другому пользователю, смены пароля, регистрации и тд.
Нет, ты видимо не понял)
Запросы идут от пользователей на твой сервер - где есть подключение к БД, снаружи нельзя подключиться к БД как пользователь - к примеру mysql -uroot -p, если ты делал mysql_secure_installation, там при настройке можно запретить remote access (удаленный доступ)
>Честно говоря для добавления поля в форму делать полноценное приложение с вебпаком, npm, моделями, вью и полмегабайтом кода выглядит как оверкилл.
Так ведь вью тем и хорош что это не реакт, его как и jquery можно просто подключить через скрипт, весит это всё дело 30кб, вроде даже меньше чем jquery. Конечно для той задачи опа вообще никакие библиотеки не нужны, а в реальных проектах (на фрилансе например) 95% времени уже будет подключен jquery поэтому лишнее тянуть явно нет смысла.
>Да, но разве пароли админки не лежат внутри бд?
да, но они обычно лежат в зашифрованном виде
>И если у злоумышленника будет пароль от бд то и будет пароли админа
скажу больше, если у тебя, как у злоумышленника, есть пароль к бд, тебе не нужно расшифровывать/менять на свои пароли админа. Ты уже можешь все делать через базу, например менять контент. Идешь в таблицу, где хранится текст страниц, и дописываешь к каждому предложение матерное слово или вставляешь картинку с пенисом.
Другое дело, что альтернативы-то нет. Какую ты можешь предложить альтернативу хранения паролей админа, если не в базе? Тебе верно сказали - можно ограничить доступ по айпи. Еще можно дополнительно в хтакссессе прописать пароль на доступ в папку с админкой. Можно хранить пароль в коде. Но тогда любой, кто чудом достанет твой пароль к фтп, сможет его поменять. Плюс он из файла с конфигом возьмет пароль для доступа к базе.
А зачем усложнять? Если у тебя обычный сайт с новостями, то, во-первых, ты серьезным взломщикам вообще не нужен. Что у тебя воровать? "Ольга Бузова шокировала Анапу голыми прелестями?" Кому эта Бузова нужна-то. Во-вторых, если тебя взломают, ты тупо заходишь в панель управления хостингом, меняешь пароль к фтп, к базе, потом восстанавливаешь базу и все. На все максимум десять минут. Большинство хостеров делают бекапы периодически, так что если у тебя даже резерва нет, просто говоришь им - восстановите мне, и они скидывают архив. Вот если у тебя серьезный магазин с клиентской базой и кредитками клиентов - это да, но там уже другой масштаб
спс
Аноны, иду на вакансию НЕ PHP программиста, но требуют:
Знание PHP на базовом уровне
А какой это уровень? Что я должен знать?
Если это вакансия админа/эникейщика, то глубоких знаний в пхп с тебя скорее всего не спросят. В любом случае скажешь, типа, готов совершенствоваться заради вас, дорогой начальник Махмуд Сверхпланович, и самообучаться. А если вакансия на синьера php за 150 тыщ муленов, то, скорее всего, там php чуть-чуть знать да надо
Смотри чтобы внезапно не выяснилось, что тебе там ещё серваки настраивать и полы мыть надо.
Я б не совался к таким.
>можно ограничить доступ по айпи
Как? На всю базу?
А нету метода запросить айди через php код?
Типо if(your ip == 127.996.66){
авторизация в качестве админа = true;
}
в настройках гридвью где колонки добавь для действия update онклик на жс функцию которая будет открывать модалку
>Что у тебя воровать?
Ну если у тебя опозиционны сайт новостей типо медузы,кому то приятно просто будет всю базу тебе сломать.
хотя для этого существуют бэкапы
Но я же не только буду делать именно новостные сайты, верно?
Ну блин даже не знаю что и сказать, человеку который проработал 3 года. Я вот джуник и вообще не работал, но даже я думаю что тебе достаточно взять лень в кулак и начать учить что тебе не понятно (этот самый ангуля реакт и т.д) И уж темболее не думаю, что это сложно будет для тебя ,человека у которого база 3 года
Нет. открывай другие сайты, уроки по циклам
Пример - не мог несколько часов решить задачу - дан массив с числами, проверьте есть ли в нем одинаковые , последовательные числа
Arr = [1,1,1,3,52]
И вот эту чухню я решал часа два , я совсем говно или можно что то ещё сделать ? Если можно подскажите что
FTP (англ. File Transfer Protocol) — протокол передачи файлов по сети.
Тебе надо пройти по массиву, каждый раз сохранять предыдущее число, сравнивая его с текущим. Если оно не равно, то можно добавить его в результирующий массив.
Как-то надо решать эту задачу.
Да проблема в том что я долго решаю детские задачи и не дорубаю логику , есть варианты как то это исправить или это тупо с опытом приходит?
с опытом. лучше каждый день по чуть-чуть, чем запоями с большими перерывами.
Антон, пойми все что ты делаешь, приходит с опытом. Ты когда маленький был, все время падал. Сейчас ты этого не помнишь, но тогда это было, и было У ВСЕХ.
Если мудрый антоша решает задачу за 5 минут, а ты два часа, это только потому-что мудрый антоша всю свою жизнь сталкивался с математикой, но поверь, когда он начинал, он делал такие же ошибки.
Преимущество "гениев" и мудрых антош только в том, что когда они были маленькие, они были очень близко со схожей тематикой. Эти ребята решали схожие задачи, но в упрощенном стиле (грубо говоря им не тонну песка лопатой перекидать надо было, а всего пару ведер. Тоже напряг, но не такой сильный)И когда выросли их мозг уже привык работать в подобной "логике". Но когда мудрый антоша начинал, он тоже был глупым и долго и трудно все делал.
Главная ошибка всех обучающихся, бросить то, что тяжело дается.
Любой навык, любое действие должно быть выстрадано.
Твой мозг, это адаптивная машина. Если ты захочешь забивать гвозди пяткой, ты научишься держать молоток пяткой. Как не важно. Но ты решишь эту задачу.
Посмотри на друзей левшей. Посмотри на одноруких, одноногих, кто не сломался, адаптировался.
Каждое повторение любого действия, приведет тебя к божественному результату.
Единственное, что от тебя требуется, это каждый раз, не через большие промежутки времени (лучше каждый день), по чуть чуть, пытаться решить задачу . Полчаса, час, но каждый день. И ты научишься всему.
Хорошая речь, жаль не все прислушаются
Очень хорошо написал все, мотивирует заебись!
- на нём работает 80? веба ( https://w3techs.com/technologies/overview/programming_language/all ). Это означает широкую поддержку и массу вакансий
- никогда не зависает, потому что каждый скрипт завершается вместе с запросом
- очень быстрый, начиная с версии 7.1 гораздо быстрее ноды и питона, в том числе благодаря технологиям оптимизации (memcache, opcache, etc)
- в версии 8 (уже осенью) появится предзагрузка постоянных данных и JIT- компиляция, что сделает его ещё быстрее
- поддержка всех современных СУБД, от Postgres и MS SQL до Redis и Mongo
- при лаконичном Си-подобном синтаксисе, инструментарий для разработки практически не уступает Java и C#
- поддерживает машинное обучение даже на виртуальном хостинге
- написан на святом C++, можно дописывать свои расширения
- при необходимости, легко реализуема асинхронность и очереди событий
Только причём тут веб 3.0?
по прежнему чудеса, бился несколько часов, написал на двач и за минуту все заработало, всего лишь заключил все переменные запроса в скобки. Но все равно нихуя не понял так как пробовал уже такой способ.
Все юзают линукс в итт?
Не все конечно, по любому есть еще пользователи MacOS и некоторое количество пользователей Windows. Я когда начал изучать программирование, тоже много слышал про Линуксы, что они лучше подходят для этого дела. В какой то момент решил попробовать, и с тех пор Линукс единственная ОС которую я использую. Как минимум стоит попробовать. Ничего не потеряешь, получишь новый опыт, и возможно поймешь что это твое.
Ну я пользовался лет 5, мне просто интересно стало, много ли его юзают конкретно программисты.
нет
плойка
Я на маке щас, оч годная ос.
Но я хочу винду, по одной простой причине - игры.
Так уж вышло что монополист виндовс захватил основную массу потребителей и все почти игроделы пилят именно на этой платформе.
Играть вполне можно... Ну и есть дуалбут в конце концов!
Лучше конечно работать в какой-то виртуальной среде где уже все зависимости установлены, но если у тебя < 8 гб памяти то сразу мимо. Мне в каком-то проекте нужно было sass компилить, и тут нода, которая в виртуалке, начала высирать ошибки про симлинк и т.п., там какие-то траблы с тем как винда лочит файлы и вот это всё, я ничего не понял короче и просто установил ноду на шиндовс, и она уже компили прям с винды мне эти sass файлы. После этого вспоминаю дни когда я пытался на какой-то некроноут аудио дрова поставить.
Скорее да чем нет.
Видяра говно сама по себе, глюченое, ядро плодит мусор.
Из плюсов всякий софт типо фотошопа, и еще парочки годных редких программ для архитекторов\видеомейкеров\3д мейкеров.
Все, на этом плюсы кончаются.
Линкус = идеальная скорость, качественное ядро. Минусы: нехватка нескольких ПО-гигантов . И иногда немного ебабинга с настройками ( для новичков)
>маст хэв?
Знать - обязан. Почти весь веб на этом работает.
Нравится - не нравится, это уже никого не касается. так-то вполне даже отличная ОСь только не десктоп-версии для пердоликов
>только не десктоп-версии для пердоликов
Ну как рабочую станцию для работы с кодом и серфить инет вполне себе, а запускать игоры с фш через вайн\вулкан , реально хуйня.
>Кроме обычных команд консоли
Это какие?
>что именно надо знать
Мочь софт поставить\собрать, веб-сервер поднять LAMP тот же, конфиги и софт основные, права как раскидать. Ну базовые для пользователя вещи - ты ж не админ.
>Это какие?
Ну там перемещение по каталогам, копирование одной папки в другое место, скачать \ распоковать\ установить,
установить права с chmod
>Мочь софт поставить\собрать,
По сути это все фигня, хотя бывает что надо собрать из исходников прогу(это уже позаебистее, но почти всегда есть пошаговые инструкции) я раньше мог ,но щас забыл. Все гуглится, просто где то надо прям долго читааать гугл пока найдешь что надо.
>веб-сервер поднять LAMP тот же, конфиги и софт основные
Та же фигня, сложность бывает только из-за конфиликтов версикй, когда внезапно твой мак линух апает версию, а LAMP ты скачал какой-то старый и открыл инстуркцию , сделал , а все не работает. Тогда ищешь другие версии\инструкции.
Это все я уже делал, но щас благополучно забыл . Думаю потом с гуглом разберусь, если нужно будет
Как работать с пакетами, с файловой системой, с конфигурированием, чтобы мог установить и настроить базу данных, веб-сервер, автоматизацию настроить.
Сейчас есть докер, поэтому собирать что-то вообще не нужно. Да и в нормальных дистрах все уже собрано. Плюс есть всякие brew и прочее.
команда > файл.txt. Можно дописывать в файл новые строки не удаляя всего содержимого, нужно использовать >>. В KDE, в стандартной утилите konsole можно сохранить через меню 'файл'.
Да я вопрос не так сформулировал, там всё сложнее. Но я уже разобрался.
Ну, помимо того что нет защиты от инъекций.
Может ли это быть потому что и в 1 коде и 2 перед установкой куки и считыванием есть строки с echo?
Да чет ты кажется все спутал.
Где подкобючение к бд?
connection = new Mysqli(параметры бд..)
И зачем ты создал глобальную переменную скл и не вставил туда ничего? А потом проверяешь ее на содержимое? Вместо нее как раз должна быть подключение к бд и передаваться в mysqli_query(connection, zapros)
Первый пошел.
Я намеренно использую такое название, потому что $request и $queue неприятно писать.
Все это есть в отдельном require_once(data-control/connect.php) и там вроде все правильно. Вообще, я хотел обратить ваше внимание на безопасность.
Ну, Буду считать что я все сделал правильно. И в том числе что подорвал пердак одному PSR-4-эстету
>Может ли это быть потому что и в 1 коде и 2 перед установкой куки и считыванием есть строки с echo?
Скорее всего.
Ну добавь себе это
https://www.php.net/manual/ru/function.htmlentities.php
когда получаешь значение из POST и после фильтровки записывай в переменные. И уже потом работай с ними.
Вот это можешь добавить.
https://www.php.net/manual/ru/mysqli.prepare.php
Унес.
Когда ловишь значения из POST , фильтруешь их этой функцией, а потом присваиваешь их своим переменным. С которыми уже работаешь . (вместо того, что бы работать сразу с $_POST['name'] ...)
Блин. Просил выложить весь код а ты выложил только два файла.
Ладно, из того что знаю могу посоветовать:
1) Сделать куки доступным для всего домена и поставить таймер побольше.
setcookie('name', $login , time()+3600, "/");
2) Проверить инклуды и рекваиры.
3) Проверить куки в index.php через print_r или var_dump. Так-же проверь его еще где-нибудь, например перед самым его созданием.
4) Научись кодить красиво.
> Проверить куки в index.php через print_r или var_dump. Так-же проверь его еще где-нибудь, например перед самым его созданием.
Print_r($_COOKIE); выдает это : Array ( [PHPSESSID] => 31f87f3b000efea42132e9c69e9dd4a3 )
> Просил выложить весь код а ты выложил только два файла.
Там просто остальные файлы не свзаны с куки
>4) Научись кодить красиво.
Постараюсь.
Спс.
Почему? Я ведь когда устанавливаю куки так же рядом в строчке вывожу эхо. Значит код дошел до этого момента.
Все, заработало.
В 1 раз я куки установил в конце файла, поэтому echo сбивал мне его.
Потом я поставил его в начало файла, и не заметил что POST 'login' у меня иниицируется после. Сейчас поставил сразу после лолви POST'login'
да здраствует руби!
вот же ебучий пиздабол. пугает вкатывальщиков бедных. иди нахуй сын дерьма. работы дохуя, так шо вкатывайтесь ребятишки
Бывает клиентский и серверный рендеринг. Так просто это не объяснишь. Фреймворки попробуй, в них это реализовано.
Жмякаешь создать тред, посылается запрос по http+ssl,
сервер смотрит что ты хочешь . ему приходит мол вот такой вот айпиадрес хочет тред создать , чекают тя по бан листу , ты чист? тогда сервер выдает ответ тебе. Мол тред создан. Ну и их бд заполняется твоим новым тредом и он виден всем после обновления стр. Или в реалтайме если там есть js скрипт
Самое смешное, что на php больше всего ваканский среди всех языков.
мимо чекер hh ru 2-3 раза в неделю в течении года.
Но во всем интернете все почему то верят что php мертв, видимо это какой-то сломанный телефон.
Тому кто с Си-подобного языка перекатывается будет оч легко и даже приятно
Пишет следующее: unexpected '$anonDice1' (T_VARIABLE) in /home/byLiQ5/prog.php on line 5
https://ideone.com/rUS82j
В 3 строке точку с запятой забыл. Всегда смотри на предыдущие строки, ошибка может быть там!
Спасибо, там действительно ошибка. Дальше пока сам посмотрю.
https://github.com/Onethity/studlist
Если кому не лень, то гляньте пожалуйста косяки.
Раньше я думал, что у меня есть файлы проекта. Мне не нужен сервер, мне не нужен компосер, есть архив с самописным движком. Я меняю там шаблоны под заказчика, прописываю пароли в конфиге, копирую дамп базы, поднимаю базу там - у провайдера, потом копирую файлы сайта к хостеру и все, сайт работает.
Но уроки Ларавеля сбили меня с толку. Я могу получить на входе просто файлы? Просто папку с файлами и дамп базы? Или мне нужно будет и у провайдера на сервере компосер устанавливать и делать кучу сложных вещей, вместо того чтобы просто взять-скопировать на рабочий сервер готовую копию, которую мне дал Ларавель, и счастливо засмеяться?
Пока есть свободная минута. Первое что бросается в глаза:
1. 1 класс 1 файл. Без исключений. Сделай папку Exception и туда сложи все кастомные исключения.
2. Не смешивай стили именования переменных и функций.
Читай PSR-1/2/4: https://www.php-fig.org/psr/
3. Ты бросаешь исключения в никуда. Нужен обработчик, который будет писать в лог хотя бы. https://github.com/codedokode/pasta/blob/master/php/exceptions.md
>>43983
Symfony + ApiPlatform -> Laravel (опционально) если времени много. Laravel (Eloquent -> Doctrine) -> Symfony если нужно быстрей искать работу. хУй под конкретные вакансии в мелких городах.
Спасибо, добра тебе
>что я получу на выходе и как это вообще установить на работающий сервер к хостинг-провайдеру?
Представим что тебе не повезло и хостер дает только доступ по фтп, а ты пишешь не на вордпресе а на каком-то фреймоврке. Руками прокидываешь папку vendor, папку node_modules(если есть, удачи это говно по фтп отправить) - готово
Представим что можно подключиться по ssh, а заказчик настолько продвинутый что юзает гит и на проде никто ничего не лезет блять трогать.
подключаешься по ssh, git clone/pull, composer install - готово
спасибо, пойду разбираться
тебя ждет хардкор
САП, пхп-товарищи, есть вопрос.
Дано: кун-вкатывальщик, основной язык - питон (работа с сетью, джанго), сейчас активно изучается html/css/js, есть опыт работы с гит, убунточкой, сикуэлем.
Задача: сделать тестовое:
"Развернуть на laravel 5.2 приложение, со следующими частями:
1) Пользователи - могут регистрироваться, авторизовываться и писать друг-другу сообщения (не обязательно чатик в реальном времени, просто возможность послать сообщение другому пользователю и прочитать сообщение)
2) База - postgresql
3) Система - ubuntu 14.04 lts (если есть возможность - развернуть через virtualbox + vagrant на windows-машине)
4) Если хватит времени - можно написать unit-тесты (не обязательно)"
Если бы задача была на Джанге, в принципе, я бы сделал.
Вопрос: есть ли годная книжка по ларавель/пхп или мануалы, которые помогут за неделю-две влезть до уровня, на котором это задание можно решить с учетом предыдущего опыта?
Для php гугли на трэкерах profit php. Полистай на быстрой промотке основные моменты.
Для Ларавеля: laracasts и "Let's Build A Forum with Laravel and TDD" (там за несколько первых уроков будет все что нужно для твоего задания).
Раньше я, как честный долбоеб, хуячил все прямо на продакшн и горя не знал.
Потом изобрел деплой через GIT.
На тестовый серв хуячу все через ftp, проверяю, когда фича сделана - комичу в гит, а потом на продакшене делаю pull.
Точнее у меня там небольшой bash-скрипт, но суть такая.
И бля, это вполне себе заебись работает.
Апдейт в одну кнопку буквально. Продакшн никогда не падает теперь.
Какие есть еще более профессиональные варианты?
Тупой вопрос, все зависит на 100% от архитектуры твоей БД.
Где-то инфа о количестве записей доступна по-дефолту, где-то она получается быстро, где-то медленно и имеет смысл делать отдельный счетчик.
Все по-разному.
https://github.com/asdasdasdasddasasdasdas/StudentList
Чекните список студентов пожалуйста.
И ещё вопрос по поводу 16 строки. Почему в этом гайде при выпадении даблов игра заканчивается? Ведь даблы могут выпасть разные, а не одни и те же. Соответственно будет победитель и проигравший. Собственно по скрину видно что выиграл компьютер.
Автор специально допустил эту ошибку?
На второй пикче я сделала игру честной. Правильно надеюсь?
И ещё вопрос по поводу того как сделал эту же задачу этот >>43518 анон. Он же наебался, разве нет? В 16 строке у него может вылезти "1 и 1" и "6 и 6" при этом ему напишут что у него ничья. А в 19 строке у него может быть "1 и 1" и "5 и 6" при этом ему напишут что победил "1 и 1". Разве нет?
Не ругайтесь только если я чего то не понимаю, изучаю пхп всего час и возможно чего то не поняла.
>просто "else" то у меня возникает ошибка?
Смотри синтаксис языка - условные операторы. Как-то так называется.
>Автор специально допустил эту ошибку?
Не баг, а фича.
Так надо.
>в 19 строке у него может быть "1 и 1" и "5 и 6" при этом ему напишут что победил "1 и 1". Разве нет?
Дабл сильнее суммы. Это вроде как в покере.
Спасибо
>Дабл сильнее суммы. Это вроде как в покере.
Но ведь смысл игры в том, что у кого больше сумма чисел, тот и побеждает...
Сумма чисел у тебя есть всегда, какие бы кубики не выпали, а дабл бывает несколько реже.
В любом случае, у того анона сначала бы написало, что победило "1 и 1", потому что даблы побеждают недаблы, а чуть ниже написало бы что победило "5 и 6", потому что сумма чисел больше. Потому я и написала что у него ошибка.
Ну там порядочно так if - else, в которых легко запутаться. Слишком много их городить не стоит в коде и лучше выносить в функции.
else значит "иначе" и после него не пишут условие. elseif значит "иначе, если ..." и после него пишут условие.
> И ещё вопрос по поводу 16 строки. Почему в этом гайде при выпадении даблов игра заканчивается?
Потому что если выпали даблы у обоих игроков, то это ничья и уже не важно, у кого больше очков. Такие условия. Если только у одного - то это ничего не значит. Там можно вместо exit просто сделать один большой блок if/esleif с четырьмя ветками.
Анон из >>43518 сделал игру немного по другим правилам.
Во втором примере кода не нужен exit и можно последний elseif заменить на else.
>>44357
А где composer.json/composer.lock? Надо убрать их из gitignore, и добавить на гитхаб, иначе как скачавший установит зависимости? То же самое с .htaccess, его надо добавить на гитхаб.
Про конфиг надо написать инструкцию в README.
Когда добавишь, пожалуйста, скинь ссылку на гитхаб снова в новом треде, чтобы я её не потерял.
>>44319
Как правило, имеет, так как выгода от быстрого получения цифры превышает затраты на реализацию этой фичи. Иначе придется делать запросы, обходящие большое число записей в поисках непрочитанных.
Я видел в основном самописные системы на bash-скриптах. Либо при коммите в мастер запускается баш-скрипт, либо вручную запускается при деплое.
Есть такой подход, как "бесшовный деплой". Если ты вывалил в корень веб-сервера гит-репозиторий, и делаешь там pull, то он обновляется не мгновенно, а файл за файлом по очереди. И возможно такое, что в какой-то момент там будет смесь старых и новых файлов или вообще наполовину перезаписанный файл. Надо либо блокировать веб-сервер на это время, либо каждый раз создавать новую папку, делать в нее деплой и атомарно переключать веб-сервер на нее.
Кто-то использует контейнеры и делает деплой контейнерами. Плюс - ты деплоишь не только код, но все используемые им программы (nginx, php и тд), не нужен админ для установки какой-то программы или расширения к PHP. Минус - это сложно и куча проблем, которые придется решать самому.
Кто-то пишет скрипты на ansible. Это полезно, когда у тебя будет больше одного сервера и надо массово выполнять на них какие-то команды.
Также, обычно на продакшен сразу не деплоят. Собирают пакет изменений, деплоят на предпродакшен (стейдж), прогоняют автоматические тесты, затем тестировщик тщательно проверяет весь функционал, дает добро и только тогда деплоят на продакшен.
>>44025
Там правильно пишут, если композера нет на сервере, просто ставишь пакеты у себя локально и закачиваешь туда папку vendor.
При этом Ларавель может потребовать поменять какие-то настройки в php.ini или установить расширения к PHP, если хостер это не позволяет, то он не подходит.
>>43518
По правилам даблы влияют на игру, только если они выпали у обоих. Если дабл у одного игрока, то он ничего не решает.
Также, твой код оформлен не по PSR-1 и PSR-2. Скобки ставятся так:
if (...) {
...
} else {
...
}
Тяжело читать нестандартное форматирование.
exit в конце не требуется.
Я видел в основном самописные системы на bash-скриптах. Либо при коммите в мастер запускается баш-скрипт, либо вручную запускается при деплое.
Есть такой подход, как "бесшовный деплой". Если ты вывалил в корень веб-сервера гит-репозиторий, и делаешь там pull, то он обновляется не мгновенно, а файл за файлом по очереди. И возможно такое, что в какой-то момент там будет смесь старых и новых файлов или вообще наполовину перезаписанный файл. Надо либо блокировать веб-сервер на это время, либо каждый раз создавать новую папку, делать в нее деплой и атомарно переключать веб-сервер на нее.
Кто-то использует контейнеры и делает деплой контейнерами. Плюс - ты деплоишь не только код, но все используемые им программы (nginx, php и тд), не нужен админ для установки какой-то программы или расширения к PHP. Минус - это сложно и куча проблем, которые придется решать самому.
Кто-то пишет скрипты на ansible. Это полезно, когда у тебя будет больше одного сервера и надо массово выполнять на них какие-то команды.
Также, обычно на продакшен сразу не деплоят. Собирают пакет изменений, деплоят на предпродакшен (стейдж), прогоняют автоматические тесты, затем тестировщик тщательно проверяет весь функционал, дает добро и только тогда деплоят на продакшен.
>>44025
Там правильно пишут, если композера нет на сервере, просто ставишь пакеты у себя локально и закачиваешь туда папку vendor.
При этом Ларавель может потребовать поменять какие-то настройки в php.ini или установить расширения к PHP, если хостер это не позволяет, то он не подходит.
>>43518
По правилам даблы влияют на игру, только если они выпали у обоих. Если дабл у одного игрока, то он ничего не решает.
Также, твой код оформлен не по PSR-1 и PSR-2. Скобки ставятся так:
if (...) {
...
} else {
...
}
Тяжело читать нестандартное форматирование.
exit в конце не требуется.
Браузер соединяется с сервером и отправляет на него HTTP-запрос (просьбу предоставить страницу по определенному URL). Сервер обрабатывает его и дает HTTP-ответ, содержащий тело страницы (обычно это текст с разметкой HTML), а браузер отображает страницу на экране. Если страница содержит дополнительные элементы (например, картинки, видеофайлы, таблицы стилей CSS, программы на языке яваскрипт), то браузер для каждой делает отдельный HTTP-запрос по такому же принципу.
HTTP - это по сути протокол для скачивания файлов с сервера. При этом сервер может как отдавать готовые HTML-файлы с диска, так и генерировать их на лету в ответ на запрос. Оба варианта актуальны и используются сегодня.
Подробнее описано в моем уроке, в разделе "статические и динамические сайты": https://github.com/codedokode/pasta/blob/master/soft/web-server.md#статические-и-динамические-страницы
>>42877
При выводе в шаблоне. А вообще, есть шаблонизаторы, которые это делают сами.
При записи в БД делать это неудобно, так как, например, кодирование меняет длину строки и затрудняет её поиск (так как символы могут быть закодированы). Ну представь, что тебе надо найти в БД строки длинее 15 символов. Тебе придется извлечь все строки из БД, раскодировать их, и сделать проверку. А если бы они были в исходном виде, хватило бы SQL-запроса WHERE LENGTH(x) > 15.
Тебе надо постоянно помнить, где у тебя в коде закодированная, а где раскодированная строка. Это сложно и неудобно.
>>42930
Ты даешь вредные советы. Так делать очень неудобно. Посмотри, что я написал выше по этому поводу.
Ты можешь посмотреть параметры куки в инструментах браузера. Открой их (F12 или Ctrl + Shift + I) на вкладке Resources, там открой список кук и посмотри. Также, ты можешь увидеть установку куки, если откроешь вкладку Network, обновишь страницу, посмотришь заголовки ответа сервера и заголовок Set-Cookie.
>>42915
Да, стоит так сделать, если используемый тобой фреймворк этого не требует. Это называется "естественный первичный ключ" - когда ключ уже содержится в самих данных. А id - это искуственный ключ.
>>42754
Качество кода пока низкое. Нужно доделывать.
Нет, у тебя SQL инъекция. Читай, например, мой урок: https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
В запросе надо явно указать названия полей: INSERT INTO table (f1, f2, f3) VALUES ..., иначе при добавлении колонок в середину таблицы этот код сломается.
Глобальные переменные в общем плохая идея.
При использовании mysqli надо писать в лог подробности ошибок, иначе ты потом о них не узнаешь. Сейчас у тебя просто отдается сообщение пользователю, а в чем была причина не фиксируется. Смотри пример кода тут: https://www.php.net/manual/ru/mysqli.quickstart.prepared-statements.php
В примере кода 2 нет проверки функций mysqli на то, что они вернули ошибку.
Только echo там надо заменить на trigger_error(текст, E_USER_ERROR) для передачи ошибки в стандартный обработчик ошибок PHP.
Твоя авторизация ненадежная - любой может поставить себе куку с любым логином и залогиниться под кем угодно.
Вместо цифровых кодов ошибок надо сделать константы - для читабельности, сравни свой код и это:
const AUTH_INVALID_PASS = 2;
....
showDeniedPage(AUTH_INVALID_PASS);
Имена функций принято начинать с глагола. Код стоит оформлять по рекомендациям PSR-1 и PSR-2.
Ты можешь посмотреть параметры куки в инструментах браузера. Открой их (F12 или Ctrl + Shift + I) на вкладке Resources, там открой список кук и посмотри. Также, ты можешь увидеть установку куки, если откроешь вкладку Network, обновишь страницу, посмотришь заголовки ответа сервера и заголовок Set-Cookie.
>>42915
Да, стоит так сделать, если используемый тобой фреймворк этого не требует. Это называется "естественный первичный ключ" - когда ключ уже содержится в самих данных. А id - это искуственный ключ.
>>42754
Качество кода пока низкое. Нужно доделывать.
Нет, у тебя SQL инъекция. Читай, например, мой урок: https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
В запросе надо явно указать названия полей: INSERT INTO table (f1, f2, f3) VALUES ..., иначе при добавлении колонок в середину таблицы этот код сломается.
Глобальные переменные в общем плохая идея.
При использовании mysqli надо писать в лог подробности ошибок, иначе ты потом о них не узнаешь. Сейчас у тебя просто отдается сообщение пользователю, а в чем была причина не фиксируется. Смотри пример кода тут: https://www.php.net/manual/ru/mysqli.quickstart.prepared-statements.php
В примере кода 2 нет проверки функций mysqli на то, что они вернули ошибку.
Только echo там надо заменить на trigger_error(текст, E_USER_ERROR) для передачи ошибки в стандартный обработчик ошибок PHP.
Твоя авторизация ненадежная - любой может поставить себе куку с любым логином и залогиниться под кем угодно.
Вместо цифровых кодов ошибок надо сделать константы - для читабельности, сравни свой код и это:
const AUTH_INVALID_PASS = 2;
....
showDeniedPage(AUTH_INVALID_PASS);
Имена функций принято начинать с глагола. Код стоит оформлять по рекомендациям PSR-1 и PSR-2.
Для проверки кода очевидно.
>>42815
Ты даешь вредные советы. htmlspecialchars используют в шаблоне при выводе.
>>42827
Не надо так писать, если не можешь помочь, то лучше ничего не пиши.
>>41841
Без кода не помочь.
>>41436
Самые популярные варианты - это либо сессия, либо куки.
В случае с сессией авторизация (при логине) делается установкой параметра в сессию (например: user=1000), а проверка авторизации - проверкой наличия параметра в сессии. Так как сессия хранится на сервере, пользователь никак ей манипулирвать не может. Минусы: сессии обычно недолго живут и если не заходить день на сайт, тебя может разлогинить (можно продлить срок жизни, но тогда будет больше заброшенных сессий).
Второй вариант - куки. При авторизации пишем в куки какой-то трудно подбираемый токен из базы. Например: при регистрации генерируем токен из 32 случайных символов и сохраняем в БД, а при логине - пишем его в куки. Другой вариант - пишем в куки хеш пароля и id пользователя. При проверке извлекаем этот токен и проверяем по базе, что он есть и какому пользователю он соответствует.
Как сделать БД пользователей - это просто таблица с данными о них. При регистрации в нее вставляется новая запись. Пароли стоит хранить в безопасной форме: https://github.com/codedokode/pasta/blob/master/security/password-hashing.md
Тебе надо просто больше решать задач. Постепенно ты начнешь понимать основные приемы, которые используются при их решении. В данной задаче - поиск идущих подряд чисел - надо просто завести переменную, и хранить в ней предыдущее число (в начале кладем в нее нулевой элемент массива, и делаем цикл с первого до последнего элемента, на каждом шаге сравнивая).
Могу посоветовать сайт https://codeforces.com/problemset Там есть очень сложные задачи, потому начинать советую с тех задач, которые решили большое число человек (пример: https://codeforces.com/problemset/problem/1183/A ). Если захочешь решать более сложные, придется погуглить про решение олимпиадных задач.
Тут нет ничего невозможного, надо начинать с легких задач и идти постепенно, а не браться сразу за сложное.
>>35784
Надо показать код, спросить совета и попросить дополнительные задачи на циклы. Раз уж мы заговорили про циклы, вот тебе задача. Дан массив со списком букв вроде такого: ['a', 'b', 'd', 'c', 'a', 'b']. Найди в нем все буквы, которые встречаются не 2 раза (то есть 1 или более 2 раз), убери из них повторяющиеся и выведи на экран.
В MySQL можно ставить ограничения по отдельным пользователям и указывать IP, с которого может заходить пользователь. На практике это неудобно, так как у пользователя может отключиться интернет, он зайдет через мобильный интернет, а там динамические IP адреса. Плюс, это небезопасно, так как протокол связи с БД может быть незашифрован и провайдер с государством будут видеть твои данные и пароли (и в случае РФ, сохранять их на диск по закону Яровой).
Потому обычно просто разрешают заходить только с локальной машины, а для доступа снаружи ты заходишь по шифрованному протоколу ssh на эту машину и с нее логинишься в базу данных.
>>40866
В теории не должен. На практике тебя могут попросить поправить PHP код, так как программист в отпуске, а надо срочно, или разобраться с багом.
Так что если ты прочтешь хотя бы начало мануала по PHP, это поможет.
>>40667
Я имел в виду, миллион пользователей БД. Потому делают одного пользователя БД, под которым к ней подсоединяется PHP скрипт независимо от залогиненного на сайте пользователя.
https://ideone.com/vsl4kT
я какой то тупой ублюдок
https://ideone.com/QfUSde
Так работает? Надо флаг u добавить, чтобы с русским языком все нормально ловилось.
$i у тебя нигде не изменяется. Перед первой итерацией ему присвается $a * $a и все.
$i разве не должно само рассчитываться из умножения $a ?
Как тогда сделать чтобы и $i изменялось?
Первый аргумент задаёт лишь начальное значение, и оно выполняется один раз, все последующие итерации выполняет третий аргумент
Как лично я делал, после кавычек написал действие, которое ты написал в переменных $a через конкатенацию
спс
https://ospanel.io/download/
Есть бесплатный вариант, но он долго грузится. Вполне удобная штука, но можно и другое что попробовать.
Спасибо, аношка. А что из альтернатив предложишь? Мне бы такую штуку, чтобы на флешке был сервер с моими двумя хеловордскими сайтами, и я бы его мог показать на собеседовании в макдак
Или клади на хостинг или показывай с лэптора. Никто в адекватной канторе флешкой тыкать не даст.
Amazon дает год триала AWS. Нужна карта для привязки, с которой спишут 1 эуро. Если ты еще юн для своей карты, то батя или мамка просто мечтают завести там аккаунт (главное не забыть отменить перед концом триала). Еще можно купить дешманский домен (1.6 бакса в год вроде) на namecheap и прикрутить к aws тогда будут списывать 0.5-1 бакса в месяц, зависит от использования. ssl сертификат дают бесплатно на letsencrypt.
"Я задеплоил свои пет проекты на aws, зайдите на vasya-super.xyz/pet-project-one/" звучит лучше чем: "Дайте я вам тут своей сифилитичной флешкой потыкаю".
>на флешке был сервер
Лучше не стоит, тем более нести это на собес. Если веб, то веб и никаких флешек.
>мог показать на собеседовании
Просто возьми любой бесплатный хостинг. Он ограничен обычно, но для портфолио - за глаза.
Сыры за 500.
Лол.
Я пришел, сказал хочу у вас работать, нихуя не знаю, но научусь. Сижу вот теперь пилю бэкенды для мобилок.
Открыл для себя полгода назад. И бля, это просто охуенно.
Это горизонтальное наследование.
Теперь повсеместно использую, очень удобно.
Например. Есть приложение на фреймворке, зависимости подключаются через IoC.
Есть набор классов, которые использовать надо в разных других классах.
Я делаю трейт вида HuiDepenendencies, там подключаю зависимости и делаю геттеры.
Далее в других разных классах включаю этот трейд и вуаля - у меня зависимости описаны в одном месте и я получаю их везде, где это нужно в 1 строку.
Еще пизже то, что трейты могут задавать абстрактные методы в качестве требования к родительскому классу.
Ты можешь написать какую-то логику в трейте, задать там абстрактные методы.
Далее, когда ты включаешь его - ты реализуешь эти методы и получаешь в нужном классе эту фичу.
Фишка в том, что трейт вообще ни от чего не зависит, это очень удобно.
Также это именно горизонтальное расширение класса.
Я могу включить трейт куда угодно, это же не наследование.
Еще пизже то, что трейтов можно включать множество. И 2 трейта могут задавать один и тот же абстрактный метод как зависимость.
Короче, ты получаешь как бы универсальные кирпичики, которые можешь использовать где угодно, при этом сами кирпичики ДРУГ ОТ ДРУГА ВООБЩЕ НЕ ЗАВИСЯТ.
Еще ты можешь использовать трейты, чтобы "разгружать" жирные классы и выносить связанные блоки логики в кирпичики-трейты.
Например, я так разгрузил класс в 1000 строк, раскидав по трейтам связанные методы.
Сейчас даже в ахуе, почему такого удобного механизма в других популярных языках нет.
Вы можете возразить: "Да то же самое можно делать, и тупо включая один класс в другой как член" -ну, может быть, вот только нихуя блять.
Во-первых, это порождает гораздо больше пердолинга, чем с трейтами.
Во-вторых, трейт != класс, это упрощенная штука, которой на уровне языка запрещено много чего делать, что могут классы, и это заебись, ибо гарантированно защищает от проблем.
В-третьих, методы трейда включаются в класс НЕПОСРЕДСТВЕННО, тебе не нужно писать лишние костыли вида $class->secondClass->method(), ты пишешь просто $class->method().
В-четвертых, для стороннего кода методы из трейта являются полностью методами самого класса.
Открыл для себя полгода назад. И бля, это просто охуенно.
Это горизонтальное наследование.
Теперь повсеместно использую, очень удобно.
Например. Есть приложение на фреймворке, зависимости подключаются через IoC.
Есть набор классов, которые использовать надо в разных других классах.
Я делаю трейт вида HuiDepenendencies, там подключаю зависимости и делаю геттеры.
Далее в других разных классах включаю этот трейд и вуаля - у меня зависимости описаны в одном месте и я получаю их везде, где это нужно в 1 строку.
Еще пизже то, что трейты могут задавать абстрактные методы в качестве требования к родительскому классу.
Ты можешь написать какую-то логику в трейте, задать там абстрактные методы.
Далее, когда ты включаешь его - ты реализуешь эти методы и получаешь в нужном классе эту фичу.
Фишка в том, что трейт вообще ни от чего не зависит, это очень удобно.
Также это именно горизонтальное расширение класса.
Я могу включить трейт куда угодно, это же не наследование.
Еще пизже то, что трейтов можно включать множество. И 2 трейта могут задавать один и тот же абстрактный метод как зависимость.
Короче, ты получаешь как бы универсальные кирпичики, которые можешь использовать где угодно, при этом сами кирпичики ДРУГ ОТ ДРУГА ВООБЩЕ НЕ ЗАВИСЯТ.
Еще ты можешь использовать трейты, чтобы "разгружать" жирные классы и выносить связанные блоки логики в кирпичики-трейты.
Например, я так разгрузил класс в 1000 строк, раскидав по трейтам связанные методы.
Сейчас даже в ахуе, почему такого удобного механизма в других популярных языках нет.
Вы можете возразить: "Да то же самое можно делать, и тупо включая один класс в другой как член" -ну, может быть, вот только нихуя блять.
Во-первых, это порождает гораздо больше пердолинга, чем с трейтами.
Во-вторых, трейт != класс, это упрощенная штука, которой на уровне языка запрещено много чего делать, что могут классы, и это заебись, ибо гарантированно защищает от проблем.
В-третьих, методы трейда включаются в класс НЕПОСРЕДСТВЕННО, тебе не нужно писать лишние костыли вида $class->secondClass->method(), ты пишешь просто $class->method().
В-четвертых, для стороннего кода методы из трейта являются полностью методами самого класса.
Я так другу-говнокодеру сайт снес
>Аноны, а используете ли вы в работе такую штуку как ТРЕЙТЫ
Дальше не читал. Как завещено, использую дерево if-ов, верстаю на таблицах. Все остальное - от лукавого
Мало, мухосрань же.
Бамп годному треду
Бамп за ваше здоровье.
Передрочил кучу IDE и могу с уверенностью сказать, что из бесплатных, лучше Netbeans нет нихуя. Придется правда напильником поработать, чтобы плагинки поставить всякие. Единственный минус - отсутствует поддержка некоторых форматов например шаблонизатор блейд или vueJS.
А вообще чеб пхпсторм не крякнуть?
Шоб оно все работало, надо написать в терминале: npm run dev
Как это автоматизируется на практике? Например на линуксовом сервере?
т.е. нельзя без костылей добавить в сервисы как и любую другую хуйню? Надо пердолиться?
>>46396
>А вообще чеб пхпсторм не крякнуть?
Спасибо за ответы, аноны. Да думал крякнуть, но внутри что-то сжимается, его же такие же двачеры писали, а у своих западло воровать. Там правда на сайте пишут, что для студентов и прочих дармоедов они бесплатно дают версию, но не понятно, нужно ли доказывать, слать им фото шараги или студ-билет
> но не понятно, нужно ли доказывать, слать им фото шараги или студ-билет
Не-а. Нужно подтвердить почту с доменом сайта твоего вуза. Да, оказывается у каждого студента есть свой ящик на сайте вуза. Я пару месяцев назад так получил эту лицензию. Но phpstorm все равно не пользуюсь, привык к vscode уже.
спасибо за информацию анон
Ну хз, я уже не сперматозоид. В мое время крякать софт воспринималось как обыденный этап установки любой софтины. Сейчас смотрю на зумерков и шаблон трещит.
Я бы не платил даже еслибы у меня безлимит денег был, мне просто впадлу даже узнавать как там вся эта оплата происходит
Крякнешь ты софт или не будешь его использовать - в любом из этих вариантов они денег не получат. Так что результат одинаков
"browscap.ini directive not set in <путь>"
Скачал browscap.ini и скинул в нужную папку.
Прописал путь до нее в php.ini следующим образом
[browscap]
browscap = "F:\OpenServer\new_edition\OSPanel\modules\php\PHP_7.3-x64\browscap.ini" (без ковычек тоже пробовал)
сохранил. Перезапустил openserver. обновляю страницу - опять та же самая ошибка.
Подскажите тугодуму, что он делает не так.
Вектор: https://ideone.com/pEHFGH
Не знаю, может и не лучше. Просто привык к нему уже за несколько лет, менять не хочется.
Когда же вы перестанете выдумывать оправдания, халявщики. Ведете себя как дети. Почему нельзя просто взять и гордо сказать «Я ПИРАТ!»?
Я туда только сервисы и внешние либы ставлю. Какой смысл прописывать туда внутренние модели самого приложения?
Понял, спасибо!
Я не знаю ответа, но ты не пробовал посмотреть документацию? Возможно, придется еще и по ВП почитать документацию тоже.
В вордпрессе верстка более-менее отделена от логики, там ты просто вставляешь в страницу функции, которые выводят какие-то полезные вещи.
>>45025
Ну давай разберем твой код. В шапке цикла пишется 3 действия, надеюсь ты сам помнишь, за что каждое из них отвечает.
Перед циклом один раз выполнится $i = $a * $a. Ок, пусть выполнится.
Далее, перед каждым шагом цикла проверяется условие $i <= 100. Ок, пусть проверяется.
Затем выполняется тело цикла, выводится 1 x 1 = 1. И затем команда $a++ увеличивает $a на 1. И снова проверяется условие, выполняется тело цикла. Но значение $i никто не обновляет. Ты на каждом шаге увеличиваешь только $a, но значение $i заново не вычисляешь и оно так и остается равным единице.
Тебе проще всего поставить вычисление $i внутрь тела цикла.
>>45503
Ищи бесплатный хостинг без рекламы.
>>45620
Вряд ли. Про инъекции у меня есть урок: https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
Потомучто, если я так скажу - это будет означать, что я под этим подразумеваю какуют деятельность и считаю это чем-то важным. А мне просто похуй
Спасибо
>целый сайт, где разжёвано по каждому компоненту с примерами
>доки редкостное дерьмо
У меня для тебя плохие новости.
1280x720, 0:34
>- зачем нужна таблица participant, если есть conference_reference? Я не понимаю, чем таблица participant отличается от conference_reference.
Это денормализация чтобы получить конференцию по собеседнику, в случае приватных конференций. Таблица participant нужна скорее для публичных конференций где количество собеседников может быть больше двух. Поскольку изначально предполагалось что приватные конференции будут представлены как конференции из двух человек, это используется и для приватных конференций.
Это подход обсуждался нами ранее:
https://phpclub.tech/pr/chain/1394587/#1389598
>>Когда пользователь впервые отправляет приватное сообщение другому пользователю, создаётся конференция, пара получателей и "копии" диалога для каждого пользователя. Но прежде чем это сделать, нужно проверить, действительно ли сообщение отправляется впервые. Для этого нужно проверить, что конференции между этими пользователями не существует. То есть нужно найти двух Получателей с одинаковым Диалогом и чтобы этот Диалог был приватным. Нужно сделать это (желательно) за один запрос.
>Такие запросы с джойнами будут плохо работать на больших нагрузках. Они же почти не оптимизируются индексами никак и требуют перебор строк. Возможно, тут придется сделать денормализацию, например, добавить в Dialog либо в Participant дополнительные поля.
>- не стоит ли сделать тип ENUM для message.type, чтобы ограничить список допустимых значений?
>- не стоит ли сделать message_attachment.type типом ENUM?
Да, я не знал про этот тип. Исправлю.
>- что за URL указывается в поле message.content? URL на стороннем сервере? На своем? Если на своем, не логичнее ли вместо URL указать внешних ключ на таблицу файлов, либо какой-то идентификатор файла, из которого строится URL? Идентификатор удобен тем, что позволяет в будущем менять вид URL файла.
Исправлю.
>- возможно, стоит в conference_reference убрать id и использовать в качестве ключа пару (conference_uuid, user_uuid). Например, я вижу, что message_reference уже использует пару (conference, user).
Да, id не как не используются для этих сущностный и не нужны. Вроде есть такой шаблон проектирования, когда создаются вспомогательные таблицы, которые ссылаются на основные, как бы соединяя их, и там нету поля id. Как он называется? Чтобы быть уверенным что я делаю.
>И еще, ничего, что тут приватный ключ лежит? https://github.com/someApprentice/Crypter/blob/master/wamp/.crossbar/key.priv
Удалю.
>И еще, не отдаем ли мы тут детали ошибки на сервере пользователю: https://github.com/someApprentice/Crypter/blob/master/wamp/AuthenticatorSession.py#L55
На ту строчку которую вы указали выдаётся ошибка о том что не предоставлен Bearer token. Об этом нужно выдать ошибку пользователю. А вот ниже там действительно обрабатывается любая ошибка, но это только потому что я не знаю как обработать отдельные ошибки https://github.com/someApprentice/Crypter/blob/master/wamp/AuthenticatorSession.py#L77-L78
>Тут, в тестах, возможно стоило сделать вспомогательную функцию для генерации токенов, так как в них легко опечататься и долго гадать, потому тест не работает: https://github.com/someApprentice/Crypter/blob/master/wamp/tests/authenticator_test.py
Тогда пришлось бы генерировать токен так как он генерируется в функции регистрации/логина. Не будет ли это положением теста на знание о том как генерируется токен?
>Также, немного неудобно, что приходится поддерживать 2 набора сущностей: в Питоне и в PHP.
Я думал об этой проблеме и пришел к выводу, что это не важно что у разных языков одинаковый набор сущностей - конечным результатом должен быть рабочий код, а не количество дублирующихся сущностей. Даже если это важно, то нет способа это решить, поэтому проблема сама не имеет смысла.
>Что касается исключений при авторизации, мне кажется, их правильнее ловить в обработчике авторизации, а не тут: https://github.com/someApprentice/Crypter/blob/master/api/src/EventListener/ExceptionListener.php
Делать блок try/catch?
>Также, не стоит отдавать подробности исключения клиенту, они могут содержать важные внутренние данные.
Забыл изменить перед коммитом. Мне нужно сделать рефакторинг обработчика, чтобы в девелопменте они как раз выдавались.
>Тут (https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php ) есть такой код:
>
>$client->request(
>$method = 'POST',
>$uri = '/api/auth/login',
>
>Так писать не стоит, это не keyword arguments из Питона и имена переменных не учитываются никак.
А как понимать что за аргумент был передан? Иногда нужно передать пустое значение, чтобы отправить следующий аргумент, и потом когда читаешь код не понимаешь что там должно быть. Нужно тогда заранее определять переменные и передавать их?
>В тестах авторизации ( https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php ) ты проверяешь что тебе выдана кука с определенным именем. Ты полагаешься на знание внутреннего устройства механизма авторизации. И наличие куки не гарантирует авторизации. Правильнее запрашивать какую-то страницу для проверки доступа к ней. Можно даже сделать специальный тестовый метод /test/whoami для этого.
Разве не нужно проверить что выдаются кукисы? Они ведь нужно для правильной работы приложения.
Проверка авторизации выполняется тестом проверки доступа к разлогиниванию https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php#L232-L242
>Также, не надо копипастить огромные полотна кода, можно было сделать вспомогательную функцию для отправки запросов.
Я как раз хотел спросить где хранить вспомогательные для тестов функции делать класс tests/Utils.php?
>Соответственно тесты будут вида canLoginWithValidPassword, cannotLoginWithWrongPassword.
Нужно разбить одну функцию на две?
>Далее, это ненадежный способ проверки, ведь речь тут о безопасности:
>
>> https://github.com/someApprentice/Crypter/blob/master/wamp/AuthorizerSession.py#L40
>> if 'private.message.to.' in uri:
>
>Не лучше ли использовать str.startswith() ?
Я не знал про эту функцию. Почему тот метод не надёжный?
>> regex = re.compile('^private\.message\.to\.([a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12})$')
>Микрооптимизация: регулярку можно скомпилировать один раз в начале скрипта и использовать скомпилированную версию.
Зачем компилировать регулярку в начале скрипта? Нужны разные компиляции регулировок в зависимости от uri по которому нужна авторизация. Их все нужно делать в начале скрипта?
>Также, я тут подумал, что для огромных конференций обновление через websocket может потребовать отдельную схему. У тебя, как я понял, при отправке сообщения в групповой чат на 1000 пользователей будет отправлено 1000 уведомлений private.message.to... и conference.updated.for.... Это будет нагружать вебсокет-демон. Логичнее для огромных чатов просто сделать канал updates.{conference_uuid} и слать туда одно уведомление. А клиент пусть отдельным запросом выясняет, что поменялось. Можно конечно слать и само сообщение, но надо убедиться, что кикнутые из чата, но не отписавшиеся от веб-сокет канала пользователи не смогут его получить.
М да, для группового чата так и нужно сделать. Вообще отправка 1000 уведомлений сделано, потому что, представим, пользователь очень популярен и каждый день получает 100 а может и 1000 новых сообщений от уникальных пользователей, и в итоге ему на всех нужно подписываться, что перенагрузит клиентское приложение. Гораздо лучше перенести эту нагрузку на серверную часть.
>В общем, один подход для всех плохо работает и для больших групп выгоднее использовать другой подход. То же самое с шифрованием, возможно для больших групп выгоднее не делать тысячу зашифрованных копий, а сделать ключ группы и шифровать им. При бане пользователя - менять ключ и пересылать его оставшимся участникам.
Так и планируется сделать.
Только мне кажется что менять ключ не обязательно, потому что пользователь всё равно не сможет ни получить ни отправить сообщения, потому что API/WAMP его не авторизует для этого.
1280x720, 0:34
>- зачем нужна таблица participant, если есть conference_reference? Я не понимаю, чем таблица participant отличается от conference_reference.
Это денормализация чтобы получить конференцию по собеседнику, в случае приватных конференций. Таблица participant нужна скорее для публичных конференций где количество собеседников может быть больше двух. Поскольку изначально предполагалось что приватные конференции будут представлены как конференции из двух человек, это используется и для приватных конференций.
Это подход обсуждался нами ранее:
https://phpclub.tech/pr/chain/1394587/#1389598
>>Когда пользователь впервые отправляет приватное сообщение другому пользователю, создаётся конференция, пара получателей и "копии" диалога для каждого пользователя. Но прежде чем это сделать, нужно проверить, действительно ли сообщение отправляется впервые. Для этого нужно проверить, что конференции между этими пользователями не существует. То есть нужно найти двух Получателей с одинаковым Диалогом и чтобы этот Диалог был приватным. Нужно сделать это (желательно) за один запрос.
>Такие запросы с джойнами будут плохо работать на больших нагрузках. Они же почти не оптимизируются индексами никак и требуют перебор строк. Возможно, тут придется сделать денормализацию, например, добавить в Dialog либо в Participant дополнительные поля.
>- не стоит ли сделать тип ENUM для message.type, чтобы ограничить список допустимых значений?
>- не стоит ли сделать message_attachment.type типом ENUM?
Да, я не знал про этот тип. Исправлю.
>- что за URL указывается в поле message.content? URL на стороннем сервере? На своем? Если на своем, не логичнее ли вместо URL указать внешних ключ на таблицу файлов, либо какой-то идентификатор файла, из которого строится URL? Идентификатор удобен тем, что позволяет в будущем менять вид URL файла.
Исправлю.
>- возможно, стоит в conference_reference убрать id и использовать в качестве ключа пару (conference_uuid, user_uuid). Например, я вижу, что message_reference уже использует пару (conference, user).
Да, id не как не используются для этих сущностный и не нужны. Вроде есть такой шаблон проектирования, когда создаются вспомогательные таблицы, которые ссылаются на основные, как бы соединяя их, и там нету поля id. Как он называется? Чтобы быть уверенным что я делаю.
>И еще, ничего, что тут приватный ключ лежит? https://github.com/someApprentice/Crypter/blob/master/wamp/.crossbar/key.priv
Удалю.
>И еще, не отдаем ли мы тут детали ошибки на сервере пользователю: https://github.com/someApprentice/Crypter/blob/master/wamp/AuthenticatorSession.py#L55
На ту строчку которую вы указали выдаётся ошибка о том что не предоставлен Bearer token. Об этом нужно выдать ошибку пользователю. А вот ниже там действительно обрабатывается любая ошибка, но это только потому что я не знаю как обработать отдельные ошибки https://github.com/someApprentice/Crypter/blob/master/wamp/AuthenticatorSession.py#L77-L78
>Тут, в тестах, возможно стоило сделать вспомогательную функцию для генерации токенов, так как в них легко опечататься и долго гадать, потому тест не работает: https://github.com/someApprentice/Crypter/blob/master/wamp/tests/authenticator_test.py
Тогда пришлось бы генерировать токен так как он генерируется в функции регистрации/логина. Не будет ли это положением теста на знание о том как генерируется токен?
>Также, немного неудобно, что приходится поддерживать 2 набора сущностей: в Питоне и в PHP.
Я думал об этой проблеме и пришел к выводу, что это не важно что у разных языков одинаковый набор сущностей - конечным результатом должен быть рабочий код, а не количество дублирующихся сущностей. Даже если это важно, то нет способа это решить, поэтому проблема сама не имеет смысла.
>Что касается исключений при авторизации, мне кажется, их правильнее ловить в обработчике авторизации, а не тут: https://github.com/someApprentice/Crypter/blob/master/api/src/EventListener/ExceptionListener.php
Делать блок try/catch?
>Также, не стоит отдавать подробности исключения клиенту, они могут содержать важные внутренние данные.
Забыл изменить перед коммитом. Мне нужно сделать рефакторинг обработчика, чтобы в девелопменте они как раз выдавались.
>Тут (https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php ) есть такой код:
>
>$client->request(
>$method = 'POST',
>$uri = '/api/auth/login',
>
>Так писать не стоит, это не keyword arguments из Питона и имена переменных не учитываются никак.
А как понимать что за аргумент был передан? Иногда нужно передать пустое значение, чтобы отправить следующий аргумент, и потом когда читаешь код не понимаешь что там должно быть. Нужно тогда заранее определять переменные и передавать их?
>В тестах авторизации ( https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php ) ты проверяешь что тебе выдана кука с определенным именем. Ты полагаешься на знание внутреннего устройства механизма авторизации. И наличие куки не гарантирует авторизации. Правильнее запрашивать какую-то страницу для проверки доступа к ней. Можно даже сделать специальный тестовый метод /test/whoami для этого.
Разве не нужно проверить что выдаются кукисы? Они ведь нужно для правильной работы приложения.
Проверка авторизации выполняется тестом проверки доступа к разлогиниванию https://github.com/someApprentice/Crypter/blob/master/api/tests/Controller/AuthControllerTest.php#L232-L242
>Также, не надо копипастить огромные полотна кода, можно было сделать вспомогательную функцию для отправки запросов.
Я как раз хотел спросить где хранить вспомогательные для тестов функции делать класс tests/Utils.php?
>Соответственно тесты будут вида canLoginWithValidPassword, cannotLoginWithWrongPassword.
Нужно разбить одну функцию на две?
>Далее, это ненадежный способ проверки, ведь речь тут о безопасности:
>
>> https://github.com/someApprentice/Crypter/blob/master/wamp/AuthorizerSession.py#L40
>> if 'private.message.to.' in uri:
>
>Не лучше ли использовать str.startswith() ?
Я не знал про эту функцию. Почему тот метод не надёжный?
>> regex = re.compile('^private\.message\.to\.([a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12})$')
>Микрооптимизация: регулярку можно скомпилировать один раз в начале скрипта и использовать скомпилированную версию.
Зачем компилировать регулярку в начале скрипта? Нужны разные компиляции регулировок в зависимости от uri по которому нужна авторизация. Их все нужно делать в начале скрипта?
>Также, я тут подумал, что для огромных конференций обновление через websocket может потребовать отдельную схему. У тебя, как я понял, при отправке сообщения в групповой чат на 1000 пользователей будет отправлено 1000 уведомлений private.message.to... и conference.updated.for.... Это будет нагружать вебсокет-демон. Логичнее для огромных чатов просто сделать канал updates.{conference_uuid} и слать туда одно уведомление. А клиент пусть отдельным запросом выясняет, что поменялось. Можно конечно слать и само сообщение, но надо убедиться, что кикнутые из чата, но не отписавшиеся от веб-сокет канала пользователи не смогут его получить.
М да, для группового чата так и нужно сделать. Вообще отправка 1000 уведомлений сделано, потому что, представим, пользователь очень популярен и каждый день получает 100 а может и 1000 новых сообщений от уникальных пользователей, и в итоге ему на всех нужно подписываться, что перенагрузит клиентское приложение. Гораздо лучше перенести эту нагрузку на серверную часть.
>В общем, один подход для всех плохо работает и для больших групп выгоднее использовать другой подход. То же самое с шифрованием, возможно для больших групп выгоднее не делать тысячу зашифрованных копий, а сделать ключ группы и шифровать им. При бане пользователя - менять ключ и пересылать его оставшимся участникам.
Так и планируется сделать.
Только мне кажется что менять ключ не обязательно, потому что пользователь всё равно не сможет ни получить ни отправить сообщения, потому что API/WAMP его не авторизует для этого.
>Также, я хочу напомнить, что вебсокет - это ненадежный канал, он имеет право терять сообщения (пользователь отсоединился и пропустил сообщение) и потому стоит проектировать его как вспомогательный канал для быстрого уведомления об обновлениях, но не как единственный или основной канал доставки данных.
>пользователь отсоединился и пропустил сообщение
Да, об этой проблеме я уже осведомлён. Например, пользователь отсоединился на долгое время (несколько месяцев или год) и какой-то его собеседник или многие собеседники решили отредактировать или прочесть большое количество сообщений. И когда пользователь вновь откроет клиент, нужно обновить все эти сообщения. Очевидно, что нельзя подхватывать всё это большое количество сообщений сразу, и нужно подгружать их sequential, т.е. стримить.
Это касается только обновленных сообщений. Старые сообщения и новые сообщения в офлайне подхватываются с помощью API.
В чем ещё вебсокет может быть ненадёжен - я не знаю.
>> Когда отправляется первое сообщение пользователю, то открывается сначала форма для отправки сообщения, а потом делается редирект в только что созданную конференцию ...
>> Было бы лучше если бы приватная конференция имела такой же id как и пользователь. И пользователь сам по себе как бы представлялися как приватная конференция. Тогда можно будет открывать диалог между двумя пользователями без создания отдельной конференция для них.
>
>Этот вопрос можно решить без изменений в БД, на уровне интерфейса. Мы можем, например, сделать URL вида /talk/{userId} и при его открытии смотреть: есть приватный диалог с этим пользователем или нет. Если есть - подгружать данные (либо редиректить на URL с id конференции), если нет - показать пустую страницу. То есть тебе не надо создавать что-то в БД только потому, что пользователь нажал на имя контакта.
>
>Есть ли какие-то недостатки у такого подхода?
Да, это неудобно когда что-то редиректиться или выдается пустая страница. Гораздо привычней и соответственно удобней когда сразу открывается диалог и уже в него можно писать.
Я возможно неправильно изложил проблему, проблема была не составлении URL и не в получении записи конференции, а в том что при открытии диалога конференции впервые её просто не существует. И я решил сделать приватные конференции предопределенными, и в качестве их идентификатора/первичного ключа использовать дублированный uuid пользователя, так как предполагал, что переходя по URL с uuid пользователя будет удобно получить эту конференцию. Но после того как я прочитал ваш ответ, я понял одну критическую проблему. То что у двух пользователей будет две конференции с их uuid а сообщения должны находится в какой-то одной. Спасибо за это замечание.
Эту проблему действительно нужно решать на уровне интерфейса.
Нужно отдельный алгоритм для обработки приватных конференций и отдельный для публичных, а не пытаться всё привести к одному как пытался сделать я.
Всё очень просто:
Для приватной конференции мы используем URL 'u-{user_uuid}' и делаем метод API getConferenceByParticipant($user_uuid), и если её ещё не существует, то мы просто не запрашиваем сообщения, а после отправки сообщения и её создания нас уведомит об этом WAMP и мы её реактивно подхватим.
Для публичной конференции всё остаётся без изменений только открываем мы её по URL 'c-{conference_uuid}'.
По сути, это будет тоже самое что и ваша идея, т.к. conference_reference содержит идентификаторы и пользователя, и собеседника и самой конференции.
>>40221
>> И я сталкнулся с такой проблемой, что каждый из этих компонентов вынужден для себя добавлять путь к модулям, и этот путь добавляется снова и снова с каждым модулем.
>
>Есть возможность в конфиге указать дополнения к pythonpath: https://crossbar.io/docs/Native-Worker-Options/ . Не подойдет ли это тут?
Наверно подойдёт. Я не знал про это опцию.
>Единственное, я не советую привязываться к текущей директории, чтобы ничего не ломалось, когда текущая директория меняется. А привязываться к расположению файла. Либо использовать вспомогательный bash-скрипт, который задаст правильную текущую директорию перед запуском сервера.
Текущая директория это какая? Я бы указал относительный путь к ./wamp/src
>> Можно ли перекодировать исходный файл в Uint8Array а затем обратно в файл, для его сохранения на диск/хранилище?
>
>Я не очень понимаю, зачем ты хочешь это сделать
Чтобы зашифровать голосовое/видео сообщение и сохранить его на сервер. Или не только сам контент сообщения, но и приложения к нему, например фотографию. Любая информация должна быть зашифрована.
>Забивать хранилище огромными файлами, затрудняя поиск информации в нем, не очень логично, а зачем их хранить на диске в зашифрованном виде, вообще непонятно.
Зашифрованные файлы весят больше?
Я поискал в интернете и вроде не очень.
https://security.stackexchange.com/questions/76568/does-encrypting-a-file-make-it-larger
https://security.stackexchange.com/questions/8245/gpg-file-size-with-multiple-recipients
>Также, я хочу напомнить, что вебсокет - это ненадежный канал, он имеет право терять сообщения (пользователь отсоединился и пропустил сообщение) и потому стоит проектировать его как вспомогательный канал для быстрого уведомления об обновлениях, но не как единственный или основной канал доставки данных.
>пользователь отсоединился и пропустил сообщение
Да, об этой проблеме я уже осведомлён. Например, пользователь отсоединился на долгое время (несколько месяцев или год) и какой-то его собеседник или многие собеседники решили отредактировать или прочесть большое количество сообщений. И когда пользователь вновь откроет клиент, нужно обновить все эти сообщения. Очевидно, что нельзя подхватывать всё это большое количество сообщений сразу, и нужно подгружать их sequential, т.е. стримить.
Это касается только обновленных сообщений. Старые сообщения и новые сообщения в офлайне подхватываются с помощью API.
В чем ещё вебсокет может быть ненадёжен - я не знаю.
>> Когда отправляется первое сообщение пользователю, то открывается сначала форма для отправки сообщения, а потом делается редирект в только что созданную конференцию ...
>> Было бы лучше если бы приватная конференция имела такой же id как и пользователь. И пользователь сам по себе как бы представлялися как приватная конференция. Тогда можно будет открывать диалог между двумя пользователями без создания отдельной конференция для них.
>
>Этот вопрос можно решить без изменений в БД, на уровне интерфейса. Мы можем, например, сделать URL вида /talk/{userId} и при его открытии смотреть: есть приватный диалог с этим пользователем или нет. Если есть - подгружать данные (либо редиректить на URL с id конференции), если нет - показать пустую страницу. То есть тебе не надо создавать что-то в БД только потому, что пользователь нажал на имя контакта.
>
>Есть ли какие-то недостатки у такого подхода?
Да, это неудобно когда что-то редиректиться или выдается пустая страница. Гораздо привычней и соответственно удобней когда сразу открывается диалог и уже в него можно писать.
Я возможно неправильно изложил проблему, проблема была не составлении URL и не в получении записи конференции, а в том что при открытии диалога конференции впервые её просто не существует. И я решил сделать приватные конференции предопределенными, и в качестве их идентификатора/первичного ключа использовать дублированный uuid пользователя, так как предполагал, что переходя по URL с uuid пользователя будет удобно получить эту конференцию. Но после того как я прочитал ваш ответ, я понял одну критическую проблему. То что у двух пользователей будет две конференции с их uuid а сообщения должны находится в какой-то одной. Спасибо за это замечание.
Эту проблему действительно нужно решать на уровне интерфейса.
Нужно отдельный алгоритм для обработки приватных конференций и отдельный для публичных, а не пытаться всё привести к одному как пытался сделать я.
Всё очень просто:
Для приватной конференции мы используем URL 'u-{user_uuid}' и делаем метод API getConferenceByParticipant($user_uuid), и если её ещё не существует, то мы просто не запрашиваем сообщения, а после отправки сообщения и её создания нас уведомит об этом WAMP и мы её реактивно подхватим.
Для публичной конференции всё остаётся без изменений только открываем мы её по URL 'c-{conference_uuid}'.
По сути, это будет тоже самое что и ваша идея, т.к. conference_reference содержит идентификаторы и пользователя, и собеседника и самой конференции.
>>40221
>> И я сталкнулся с такой проблемой, что каждый из этих компонентов вынужден для себя добавлять путь к модулям, и этот путь добавляется снова и снова с каждым модулем.
>
>Есть возможность в конфиге указать дополнения к pythonpath: https://crossbar.io/docs/Native-Worker-Options/ . Не подойдет ли это тут?
Наверно подойдёт. Я не знал про это опцию.
>Единственное, я не советую привязываться к текущей директории, чтобы ничего не ломалось, когда текущая директория меняется. А привязываться к расположению файла. Либо использовать вспомогательный bash-скрипт, который задаст правильную текущую директорию перед запуском сервера.
Текущая директория это какая? Я бы указал относительный путь к ./wamp/src
>> Можно ли перекодировать исходный файл в Uint8Array а затем обратно в файл, для его сохранения на диск/хранилище?
>
>Я не очень понимаю, зачем ты хочешь это сделать
Чтобы зашифровать голосовое/видео сообщение и сохранить его на сервер. Или не только сам контент сообщения, но и приложения к нему, например фотографию. Любая информация должна быть зашифрована.
>Забивать хранилище огромными файлами, затрудняя поиск информации в нем, не очень логично, а зачем их хранить на диске в зашифрованном виде, вообще непонятно.
Зашифрованные файлы весят больше?
Я поискал в интернете и вроде не очень.
https://security.stackexchange.com/questions/76568/does-encrypting-a-file-make-it-larger
https://security.stackexchange.com/questions/8245/gpg-file-size-with-multiple-recipients
Давай почитаем определения от Мартина Фаулера (на сайте только выжимка, полные описания даны в его книге):
https://martinfowler.com/eaaCatalog/tableDataGateway.html
> A Table Data Gateway holds all the SQL for accessing a single table or view: selects, inserts, updates, and deletes. Other code calls its methods for all interaction with the database.
https://martinfowler.com/eaaCatalog/repository.html
> A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer.
Разница, на мой взгляд, в том, что TDG лишь содержит в себе набор методов со всеми SQL запросами к одной таблице. А Repository представляет таблицу как коллекцию с записями, в которой их можно искать, добавлять, удалять. При этом Repository используется как часть Data Mapper (который пытается представить набор записей в БД, как будто это граф связанных объектов, находящихся в памяти).
То есть TDG - просто набор методов и запросов для работы с таблицей, Repository - это как-бы коллекция объектов и используется обычно в рамках Data Mapper.
На практике, если мы возьмем ORM (Object-Relation Mapper) Доктрина, то вот класс встроенного в неё репозитория: https://github.com/doctrine/orm/blob/master/lib/Doctrine/ORM/EntityRepository.php
Видно, что он позволяет только искать данные, но не добавлять/удалять. И также очевидно, что он не является TDG, так как не содержит в себе все возможные SQL запросы для работы с определенной таблицей.
Тебе было бы полезно изучить все связанные с ORM паттерны (Data Mapper, Unit of Work, Identity Map), а также почитать документацию и может быть, код Доктрины. Это поможет тебе лучше понять как устроен ORM и какие паттерны в нем используются. Ну и знание Доктрины пригодится, если ты планируешь работать PHP разработчиком. Она сложная, но если изучать постепенно, то можно разобраться.
Задавай вопросы (в новом треде >>1446969 (OP) ), если что-то непонятно.
Давай почитаем определения от Мартина Фаулера (на сайте только выжимка, полные описания даны в его книге):
https://martinfowler.com/eaaCatalog/tableDataGateway.html
> A Table Data Gateway holds all the SQL for accessing a single table or view: selects, inserts, updates, and deletes. Other code calls its methods for all interaction with the database.
https://martinfowler.com/eaaCatalog/repository.html
> A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer.
Разница, на мой взгляд, в том, что TDG лишь содержит в себе набор методов со всеми SQL запросами к одной таблице. А Repository представляет таблицу как коллекцию с записями, в которой их можно искать, добавлять, удалять. При этом Repository используется как часть Data Mapper (который пытается представить набор записей в БД, как будто это граф связанных объектов, находящихся в памяти).
То есть TDG - просто набор методов и запросов для работы с таблицей, Repository - это как-бы коллекция объектов и используется обычно в рамках Data Mapper.
На практике, если мы возьмем ORM (Object-Relation Mapper) Доктрина, то вот класс встроенного в неё репозитория: https://github.com/doctrine/orm/blob/master/lib/Doctrine/ORM/EntityRepository.php
Видно, что он позволяет только искать данные, но не добавлять/удалять. И также очевидно, что он не является TDG, так как не содержит в себе все возможные SQL запросы для работы с определенной таблицей.
Тебе было бы полезно изучить все связанные с ORM паттерны (Data Mapper, Unit of Work, Identity Map), а также почитать документацию и может быть, код Доктрины. Это поможет тебе лучше понять как устроен ORM и какие паттерны в нем используются. Ну и знание Доктрины пригодится, если ты планируешь работать PHP разработчиком. Она сложная, но если изучать постепенно, то можно разобраться.
Задавай вопросы (в новом треде >>1446969 (OP) ), если что-то непонятно.
Это копия, сохраненная 5 августа 2019 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.