Это копия, сохраненная 28 июня 2016 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Это не чат! Пожалуйста не флудите, а старайтесь постить только вопросы, решения и ответы. Сколько лет вы не можете найти работу никому не интересно. Высказывайтесь одним большим постом а не цепочкой мелких
Это тред для начинающих. Не написал за свою жизнь ни одной программы? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Netbeans PHP или PhpStorm (с ним будет удобнее).
Предыдущий тред был тут: >>729430 (OP)
Что самое главное для программиста? Умение аккуратно оформлять код (читай второй пост прежде чем писать код).
Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко.
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП заходит редко, где-то раз в 2-3 дня, у него мало времени, не жди его, решай задачки дальше. ОП отвечает на все вопросы по его задачкам и учебнику, а вот насчет каких-то других вещей - только если останется время. Но в треде немало анонимных экспертов разного уровня, так что вряд ли вопрос останется без ответа.
У нас есть уроки по основам PHP, они собраны и выложены по адресу http://archive-ipq-co.narod.ru/ Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь, то надо начать с него. Он простой и понятный (по крайней мере в начале). Там есть задачи, их надо решать обязательно (чтобы стать программистом, надо писать код — иначе никак). Пости ссылки на решения в тред, мы их проверим, напишем замечания и дадим советы по улучшению.
Если не знаешь как решать, запости код, напиши в каком месте остановился и попроси подсказку.
Ты прошел весь учебник? Молодец, но это были лишь основы языка 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/Yii2: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование
- Если ты все решил, переходи к Symfony 2/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
Решения задач лучше показать мне, особенно на ООП,так как сам ты вряд ли увидишь все ошибки. Пости свой код на гитхаб и вкидывай ссылку в тред по мере решения. Я прокомментирую и укажу на ошибки.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания 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://gist.github.com/codedokode/10539213
Что почитать
- Мануал по 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
Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию.
Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета)
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет.
Подскажи сайты для поиска работы, я не умею гуглить? — hh.ru, geekjob.ru, moikrug.ru (склеен с brainstorage.me), fl.ru, upwork.com (бывший одеск). Имей в виду, что кроме фриланса есть еще постоянная удаленная работа (remote job) когда тебе не надо тратить время на поиск заказов и переговоры с неадекватными заказчиками.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
Нужно распарсить строку с ETag по RFC. Правильные ответы: "s"d, fsdf" "sdf, sd"f2" "dsf"sdf, sdf345".
И они правильно матчатся, но первая подмаска хранит только последнее значение. Как сохранить все значения?
http://w99953g4.bget.ru/evently/
Код: https://github.com/huyakhuyakivprodakshen/evently
Джипег макета в шакальем качестве: пик1. В нормальном: http://radikal.ru/lfp/s017.radikal.ru/i413/1605/9f/21d3cd8f22dd.jpg/htm . Исходных картинок в .psd не было.
+Верстка фулл респонсиве, от айфонных 320рх до фулл шд, при любом зуме, все тянется и перестраивается, ничего ниоткуда не торчит, по крайней мере у меня не получилось что-то сломать. В паре узких мест на границах брейкопинтов случается что-то впритык, это легко решается добавлением большего числа брейкпоинтов, скучно этим заниматься.
+Моднейшие свг-иконки и логосы, при любом зуме никакого мыла. Всем рекомендую, иконочные шрифты, base64 и png полное днище по сравнению С.
+На ретине картинки подменяются с двойной плотностью, в инспекторе все работает(пик 3), как на практике не знаю.
+Все модульно, реиспользуемо, любую менюшку, кнопку, инпут можно повторно вставить в любое место и они не сломаются.
+Как обычно повторяющиеся элементы - вопросы в фак, дни в расписание, посты в блог, чудаки в спикеры и пр. - добавляются изящно, например:
question('To be or not to be?','Answer'), post('subject','author','date','Lorem ipsum') вместо копипасты кусков html. Пример на пике 2 как выглядит исходник и скомпилированный результат.
+Все ссылки, кнопки нажимаются, листалки листаются, формы отправляются аяксом и даже валидируются, но правда очень примитивно, упор пока делаю на верстку. Но можно попробовать отправить пустую форму или инвалидное мыло или больше 3 сообщений подряд. Once again, пхп-скрипты там уровня hello world - лишь бы что-то отвечали.
+Все полностью самописное кроме плагинов для подмены стандартных скролбаров и детекта попадания элемента во вьюпорт.
+Черт, я даже нашел сервис с раскрашиванием стандартной гугл карты в цветовой гамме очень похожей на макет.
+Чтобы вам не было скучно я добавил в галерею ФОТО ОБНАЖЕННЫХ ДЕВИЦ И КОТИКОВ.
-В исходниках медиа запросы свалены в отдельные файлы. Сперва мне это показалось хорошей идеей и только когда закончил узнал что SASS умеет собирать несколько одинаковых запросов в один, а gulp их сортировать и добавлять в конец файла. Таким образом медиа-запросы можно инкапсулировать внутри компонентов что полностью соответствует идеологии бэм и вообще здорово. В следующей работе так и сделаю, тут уже лень переписывать.
-Кое-где кнопки плохо видны на фоне картинок - дизайн не мой, я просто разместил объяву. Также кнопка скролл даун тут явно ни к чему, но пусть уже.
-Кроссбраузерностью по-прежнему не занимаюсь. В последнем хроме, фф, эдже, опере у меня все ок. В старых ослах даже не хочу открывать и вообще не знаю как с этой темой совладать. Как можно запомнить какие из сотен разных правил/значений как себя ведут в десятках проблемных версий разных браузеров? Ладно еще прогнать автопрефиксером и то, на сколько последних версий ставить настройку? В общем пока с этим пробел.
-Скрипты слайдеров не модульные, не универсальные, привязаны к конкретной верстке и попросту нубские. Пока насилую верстку, когда возьмусь за фронтенд напишу пару каких-нибудь универсальных крутых плагинов для jquery. Для текущей цели считаю это оверкилом.
http://w99953g4.bget.ru/evently/
Код: https://github.com/huyakhuyakivprodakshen/evently
Джипег макета в шакальем качестве: пик1. В нормальном: http://radikal.ru/lfp/s017.radikal.ru/i413/1605/9f/21d3cd8f22dd.jpg/htm . Исходных картинок в .psd не было.
+Верстка фулл респонсиве, от айфонных 320рх до фулл шд, при любом зуме, все тянется и перестраивается, ничего ниоткуда не торчит, по крайней мере у меня не получилось что-то сломать. В паре узких мест на границах брейкопинтов случается что-то впритык, это легко решается добавлением большего числа брейкпоинтов, скучно этим заниматься.
+Моднейшие свг-иконки и логосы, при любом зуме никакого мыла. Всем рекомендую, иконочные шрифты, base64 и png полное днище по сравнению С.
+На ретине картинки подменяются с двойной плотностью, в инспекторе все работает(пик 3), как на практике не знаю.
+Все модульно, реиспользуемо, любую менюшку, кнопку, инпут можно повторно вставить в любое место и они не сломаются.
+Как обычно повторяющиеся элементы - вопросы в фак, дни в расписание, посты в блог, чудаки в спикеры и пр. - добавляются изящно, например:
question('To be or not to be?','Answer'), post('subject','author','date','Lorem ipsum') вместо копипасты кусков html. Пример на пике 2 как выглядит исходник и скомпилированный результат.
+Все ссылки, кнопки нажимаются, листалки листаются, формы отправляются аяксом и даже валидируются, но правда очень примитивно, упор пока делаю на верстку. Но можно попробовать отправить пустую форму или инвалидное мыло или больше 3 сообщений подряд. Once again, пхп-скрипты там уровня hello world - лишь бы что-то отвечали.
+Все полностью самописное кроме плагинов для подмены стандартных скролбаров и детекта попадания элемента во вьюпорт.
+Черт, я даже нашел сервис с раскрашиванием стандартной гугл карты в цветовой гамме очень похожей на макет.
+Чтобы вам не было скучно я добавил в галерею ФОТО ОБНАЖЕННЫХ ДЕВИЦ И КОТИКОВ.
-В исходниках медиа запросы свалены в отдельные файлы. Сперва мне это показалось хорошей идеей и только когда закончил узнал что SASS умеет собирать несколько одинаковых запросов в один, а gulp их сортировать и добавлять в конец файла. Таким образом медиа-запросы можно инкапсулировать внутри компонентов что полностью соответствует идеологии бэм и вообще здорово. В следующей работе так и сделаю, тут уже лень переписывать.
-Кое-где кнопки плохо видны на фоне картинок - дизайн не мой, я просто разместил объяву. Также кнопка скролл даун тут явно ни к чему, но пусть уже.
-Кроссбраузерностью по-прежнему не занимаюсь. В последнем хроме, фф, эдже, опере у меня все ок. В старых ослах даже не хочу открывать и вообще не знаю как с этой темой совладать. Как можно запомнить какие из сотен разных правил/значений как себя ведут в десятках проблемных версий разных браузеров? Ладно еще прогнать автопрефиксером и то, на сколько последних версий ставить настройку? В общем пока с этим пробел.
-Скрипты слайдеров не модульные, не универсальные, привязаны к конкретной верстке и попросту нубские. Пока насилую верстку, когда возьмусь за фронтенд напишу пару каких-нибудь универсальных крутых плагинов для jquery. Для текущей цели считаю это оверкилом.
Чего дропдаун не сверстал?
>В старых ослах даже не хочу открывать
Let me do this for you
IE 10, 11 нет иконок (теперь ты понимаешь, чому svg пока не в почете?)
С ИЕ9- сайт неюзабелен.
Третий пик - ИЕ 5.5 :3
>Если у тебя задача матчить списки вида a, b, c то одним применением регулярки сделать это не получится.
Спасибо, сейчас пришел к такому решению - http://ideone.com/RGFQmU
Сначала тестирую строку на валидность, и только потом паршу.
>Чего дропдаун не сверстал?
А че с ним не так? Вроде пашет. Или ты про то что опшены длиннее селекта? Ну да, там микрокостыль присутствует.
>IE 10, 11 нет иконок (теперь ты понимаешь, чому svg пока не в почете?)
Не понимаю. Старые ослы либо обучаются js-либой либо получают png через фолбек, это относительно несложно, но реально жаль времени, лучше освою побольше современных крутых штук :3
Хочу сделать костыль, который будет парсить теги к картинке и забивать их в базу (что-то на подобии http://gelbooru.iqdb.org/ ) Вот только как сделать запрос с поиском конкретной картинки я не знаю. Подскажете?
Приза нет, но есть windows 10, не нужна? https://geektimes.ru/post/276318/ тут пишут что крестик теперь значит "да".
>>753892
Охуенно, даунов наконец-то заставили обновиться.
Тем кто пользуется XP и старыми IE-офисами я бы вообще форматировал все жёсткие диски, пока они не догадаются обновиться.
Лучше поссу ещё разок тебе в ротеш :3
Как же майкрософт филигранно показал тебе твоё место у параши.
- не надо тратить деьги на поддержку и выпуск обновлений для старой версии
- если старая версия хорошая, то люди будут сидеть на ней, а не покупать новую. Если же большинство пересадить на новую, то под старую перестанут выпускать программы и ее владельцам тоже придется купить обновление
- новая версия содержит разные опции монетизации вроде облачных сервисов, шпионские фичи, интеграцию с фейсбуком и яндекс поиск.
Отдельно хочу сказать про интеграцию с фейсбуком. В один прекрасный день скайп зачем-то встроил в свою главную страницу фейсбук, причем отображался он как я понимаю через виджет от ИЕ. Зачем делать копию браузера в скайпе я плохо понимаю, но на всякий случай продолжил сидеть на старой версии скайпа, пока они не поменяли протокол. После обновления я все же как-то от фейсбука избавился, не помню уже как, по моему просто включил многооконный режим.
Ну дауны, у меня даже слов нет. зачем мне фейсбук которым я не пользуюсь, и не собираюсь потому что он кривой, неудобный и передает все данные в ЦРУ? Мне кажется, это какое-то издевательство, им просто обидно что люди пользуются скайпом бесплатно, так давайте этим людям подгадим, чтобы у них программа дольше запускалась и отвлекала их всякой ерундой.
Фейсбук тоже хорош, при регистрации требует пароль от почты и прячет ссылку для пропуска этого шага. А после того как ты зарегистрируешься, он начинает присылать каждый 2 дня спам вроде "вы наверно знаете этого негра из Зимбабве".
С другой стороны, аноны, делайте выводы. Если вы собираетесь выпускать какой-то свой продукт, возможно что без такой вот навязчивой дистрибуции никто ваш продукт и не купит. Вот, яндекс тоже свои программы хочет в телефоны предустанавливать, потому что так их никто не скачивает.
>передает все данные в ЦРУ?
Я бы еще мог понять эту претензию от гражданина США, но кому ты нахуй нужен?
Где гарантия что я через 10 лет нужен не стану? А тут уже досье накоплено. Чем меньше информации утекает тем лучше. Плюс, меня не устраивает, что информация со всего мира должна уходить в Вашингтон, а не в Москву или Пекин например. Или оставаться у владельца. Это несправедливо.
Подскажите хорошуюпо PHP конечно же книгу на русском, после прочтения которой можно легко разобраться с версткой тем для WordPress.
Смешно, но я ведь серьёзно, знания HTML/CSS есть, изучаю JS, но сейчас есть в моём городе неплохая вакансия WordPress Developer и хотелось бы туда устроиться. Мне бы что-то такое, после прочтения чего я бы уловил суть что и как в WordPress, ведь там явно не весь PHP задействован, а дальше я сам справлюсь.
http://scanlibs.com/cms/ - вот тут многое может пригодиться, если инглиш знаешь.
Ну или нагугли переводы, должны быть.
>WordPress Developer
Ты будешь пилить движок вместе с создателями цмс или клепать бложики для убогих?
Обычно Wordpress Developer это макака, задача которой натягивание шаблона на цмс.
Написав примитивную guestbook и испугавшись выкладывать её на Гитхаб, хочу приступить к изучению этого фреймворка.
Студенты и файлообменник отнимут слишком много времени, мне нужно до конца мая разобраться хотя бы в основах Yii2.
Может, посоветуете ещё каких-нибудь руководств или уроков?
Вот этот курс ещё хочу посмотреть: https://www.youtube.com/watch?v=R-yQux1S63w
Всё ли я правильно делаю?
>мне нужно до конца мая разобраться
Из дому выгоняют? Жить не на что?
Если тебе интересно мнение, то вот как я вижу твой дальнейший расклад: на Yii ты научишься создавать примитивнейшие CRUD'ы, но писать что-то более сложное не сможешь ввиду недостатка знаний в области ООП. Код если и будет работать, то на уродливейших костылях. В лучшем случае ты забьёшь пока на фреймворки и начнёшь делать студентов как я, в худшем - просто забьёшь.
>Студенты и файлообменник отнимут слишком много времени
Слишком много времени они отнимут лишь у тех, у кого действительно низкий уровень знаний. А ты за фреймворки браться собрался.
И тема видеоуроков уже подымалась в предыдущем треде.
>Из дому выгоняют? Жить не на что?
Да нет, в заднем проходе свербит собственный проект, который надо поднимать. Лето - самое время для этого, будет возможность.
Собираюсь заказывать модули необходимые у фрилансеров, самостоятельно интегрировать в систему.
>но писать что-то более сложное не сможешь ввиду недостатка знаний в области ООП
Я надеюсь хотя бы просто интегрировать научиться разные модули, виджеты - всё такое.
Просто забить я не могу - слишком далеко это всё уже зашло.
>Слишком много времени они отнимут лишь у тех, у кого действительно низкий уровень знаний. А ты за фреймворки браться собрался.
Знания разрознены у меня. Синтаксис хорошо разобрал и запомнил (благодаря ещё и куче просмотренных задач у братишек). Мне хочется просто понять принципы, на которых всё строится в Yii2, разобраться с разными важными штуковинами в нём, а дальше поднимать проект - хотя бы уже начать пытаться.
>И тема видеоуроков уже подымалась в предыдущем треде.
В одном из, да, я помню.
Ну а вдруг что более-менее хорошее посоветуют.
Я по книгам лучше учусь, правда, но видеокурсы помогают часто увидеть, как многое просто на самом деле.
>>745461 https://github.com/fidnex/students/
>>746699 https://github.com/greenTea242/Student_List/compare/master@{20 day}...master
>>747172 https://github.com/nsdvw/TestHub
>>747423 https://github.com/foobar1643/filehosting
>>747986 https://github.com/applejacky/tmp
>>748292 https://github.com/TheSidSpears/Students
Всем остальным я ответил в прошлом треде, зайдите и посмотрите: >>755108
>>755108много задач с https://github.com/timrene/php-link-list
>>755109 greenTea242/StudentList
>>755110testhub
>>755113 задачи по регуляркам
Если я кого-то пропустил - напомните о себе здесь, в новом треде.
Ну, надеюсь, он тебе поможет, братишка.
Вроде многое понятно там для новичков - сужу по себе.
Вот еще Полное руководство по Yii 2.0: https://github.com/yiisoft/yii2/tree/master/docs/guide-ru
Или тебе по Yii1.0 надо? Там многое отличается, как я понял. Но явно всегда обновлённой версии надо отдавать предпочтение.
Наверное все же второй. Видео да, там назначение папочек пояснили и уже как то уверенней себя чувствую, лол.
Можно без него?
Пытался когда-то. Просто он мне как-то чуть не скачал 300 Мб каких-то библиотек. И испортил path. И поставил wget.
>хипстерня
Веди себя прилично.
Скорее всего ты скопипастил его с какого-то сайта. Читай лучше оф.документацию по нему https://getcomposer.org/doc/00-intro.md
Спешу тебя разочаровать: composer ставит только то, что ты ему указываешь. Он берёт на себя возню с зависимостями, облегчая программисту жизнь. И как можно жить без wget'а на линуксе?
Я снова выхожу на связь . ОПушка прости меня пожалуйста, что без комментариев, я больше так не буду. поясни за клонирование объектов. Я перепробовал все комбинации из примеров, но все копии заменяют друг друга. Как делать не связанные с исходным объектом его клоны?
Напиши кратко код которым ты пытался клонировать и что не получилось.
> public function getCoffe()
>{
> return parent::getCoffe();
>}
В чем смысл? Методы и так наследуются сами по себе. Переопределять метод надо если ты хочешь что-то изменить в нем.
> public function getSalary()
>{
..
> $salary = $this->rate $factor $bossFactor;
В классе Employee нет свойства rate, значит ты не можешь к нему обращаться. Класс не знает ничего о своих наследниках (так как их могут написать уже после тебя - вот например в твою программу можно дописать пару новых классов для новых профессий).
> class Department
> public function __construct($employees, $departmentName)
Функция спроектирована плохо. Почему я должен в конструктор передавать какие-то данные в каком-то массиве в непонятном формате. У нас есть объект, представляющий работника - вот его и надо передавать.
Также, я думаю, лучше работников вообще убрать из конструктора. Удобнее создать пустой департамент и позже добавлять работников. А у тебя в принципе добавить их нельзя.
То есть вместо общей, универсальной функции "добавить работника в департамент" ты сделал заточенную под конкретную задачу функцию.
> echo "Неправильно указан ранг: ";
> die($employee[1]);
В ООП используют исключения.
> if($employee->rank == 1)
> {
> $number[1]+=1;
> }
> elseif($employee->rank == 2)
> {
> $number[2]+=1;
А нельзя это как-то обобщить, а не писать по ветке на каждый ранг?
> foreach($number as $key => $value)
key и value ничего не значат. Надо выбрать более осмысленные названия.
> if($value == 0)
> {
> unset($number[$key]);
Это же неудобно. Ты теперь прежде чем взять из массива число еще дополнительно должен проверять есть ли там такой элемент.
> public function fireEmployees($position, $boss, $quantity, $rank)
опять же, у тебя функция заточенная под одну задачу. А если завтра надо будет увольнять по другим критериям?
Я думаю, в департаменте должна быть универсальная функция удаления любого работника, а вот этот код, заточенный под антикризичные меры, должен быть в классе антикризисных мер.
Каждый класс должен заниматься своим делом, а ты смешиваешь в одном классе несколько функций. Задача Департамента - вести учет сотрудников. А задача "уволить X% инженеров" это задача антикризисного комитета.
> return implode("", (array_merge(preg_split('//u', $q, 0, PREG_SPLIT_NO_EMPTY), array_fill(0, $w-mb_strlen($q), " "))));
Слишком длинно и слишком много скобок, это невозможно прочесть. Более того, ты тут перемудрил. Для заполнения строки пробелами есть str_repeat.
> foreach($company as $departments)
объясни как по твоему это работает
Напиши кратко код которым ты пытался клонировать и что не получилось.
> public function getCoffe()
>{
> return parent::getCoffe();
>}
В чем смысл? Методы и так наследуются сами по себе. Переопределять метод надо если ты хочешь что-то изменить в нем.
> public function getSalary()
>{
..
> $salary = $this->rate $factor $bossFactor;
В классе Employee нет свойства rate, значит ты не можешь к нему обращаться. Класс не знает ничего о своих наследниках (так как их могут написать уже после тебя - вот например в твою программу можно дописать пару новых классов для новых профессий).
> class Department
> public function __construct($employees, $departmentName)
Функция спроектирована плохо. Почему я должен в конструктор передавать какие-то данные в каком-то массиве в непонятном формате. У нас есть объект, представляющий работника - вот его и надо передавать.
Также, я думаю, лучше работников вообще убрать из конструктора. Удобнее создать пустой департамент и позже добавлять работников. А у тебя в принципе добавить их нельзя.
То есть вместо общей, универсальной функции "добавить работника в департамент" ты сделал заточенную под конкретную задачу функцию.
> echo "Неправильно указан ранг: ";
> die($employee[1]);
В ООП используют исключения.
> if($employee->rank == 1)
> {
> $number[1]+=1;
> }
> elseif($employee->rank == 2)
> {
> $number[2]+=1;
А нельзя это как-то обобщить, а не писать по ветке на каждый ранг?
> foreach($number as $key => $value)
key и value ничего не значат. Надо выбрать более осмысленные названия.
> if($value == 0)
> {
> unset($number[$key]);
Это же неудобно. Ты теперь прежде чем взять из массива число еще дополнительно должен проверять есть ли там такой элемент.
> public function fireEmployees($position, $boss, $quantity, $rank)
опять же, у тебя функция заточенная под одну задачу. А если завтра надо будет увольнять по другим критериям?
Я думаю, в департаменте должна быть универсальная функция удаления любого работника, а вот этот код, заточенный под антикризичные меры, должен быть в классе антикризисных мер.
Каждый класс должен заниматься своим делом, а ты смешиваешь в одном классе несколько функций. Задача Департамента - вести учет сотрудников. А задача "уволить X% инженеров" это задача антикризисного комитета.
> return implode("", (array_merge(preg_split('//u', $q, 0, PREG_SPLIT_NO_EMPTY), array_fill(0, $w-mb_strlen($q), " "))));
Слишком длинно и слишком много скобок, это невозможно прочесть. Более того, ты тут перемудрил. Для заполнения строки пробелами есть str_repeat.
> foreach($company as $departments)
объясни как по твоему это работает
То ради для твоего варианта надо отказаться от обычных форм и ссылок на всем сайте?
Также, что насчет кроссдоменного аякса? Отправки запросов через флеш?
>кроссдоменного аякса
Same-origin policy
>Отправки запросов через флеш
Вот именно это - причина, почему нельзя так делать. Меня уже тыкнули в оригинальный вопрос на СО и слили репу.
>>Напиши кратко код которым ты пытался клонировать и что не получилось.
Уже разобрался, не заметил как переназначалась исходная переменная.
>>В классе Employee нет свойства rate, значит ты не можешь к нему обращаться. Класс не знает ничего о своих наследниках (так как их могут написать уже после тебя - вот например в твою программу можно дописать пару новых классов для новых профессий).
Нужно объявить поле $rate и оставить его пустым?
>>В ООП используют исключения.
Разве не нужно с исключениями использовать throw/catch?
>>объясни как по твоему это работает
Ну там компания состоит из департаментов и каждый цикл представляет каждый департамент.
а прикинь, он в центоси новой из коробки не стоит, охуеть вообще
а в дебиане и убунте мейка нет из каробки, прихоидтся билд-эсеншиалз ставить. Я не понимаю, зачем это сделали. В серверной, блджад, версии!
> Нужно объявить поле $rate и оставить его пустым?
да, как вариант, но тут есть другая проблема:
1) как человек который хочет уначледовать твой класс догадается что он обязан задать это свойство?
2) как проверить что он его не забыл задать?
Обе этих проблемы можно решить абстрактными методами.
> Разве не нужно с исключениями использовать throw/catch?
Ловить их ты не обязан. Этого разве нет в моем уроке по исключениям? Ты его читал? https://gist.github.com/codedokode/65d43ca5ac95c762bc1a
> >>объясни как по твоему это работает
> Ну там компания состоит из департаментов и каждый цикл представляет каждый департамент.
Если компания состоит из департаментов то почему у тебя 2 цикла, а не 1?
В данном случае ты явно пишешь код наугад, не понимая как он работает.
Попробуй сделать цикл по объекту Engineer и сдампь (var_dump) из чего он состоит.
Я, наверное, глупый вопрос задам, и срачегонный в перспекктиве.
Но может кто-то назвать, услонво, кол-во строк, после которого становится целесообразно юзать ООП?
Я не программер, но сейчас по работе много кодю, актически веду целый проеккт по продаже говна через интернет, с полноценным КРУДом-хуюдом, и ООП я юзаю только, так сказать, когда это предусмотрено разрабом модуля пеар. Или в mysqli например.
Я ей-богу не понимаю, что бы изменилось, если бы я преписал этот сервис в объектной парадигме?
Ну и что почитать по ооп в похапе.
ОП, ты мне нужен. Скажи, почему у PHP куча фреймворков, у JS куча фреймворков, а у питона только джанго и фласк? Объем изучения для змеебов меньше, чем для остальных? И там все интегрированнее?
>Тут мы изучаем язык PHP (а также JS/CSS/HTML/SQL)
А если я буду решать твои задачки на пистоне, ты бы смог подсказывать и давать советы какие-нибудь? Или ты в нем не шаришь? Ну хоть, может, по общим моментам каким.
Я понимаю Питон, но возможно что твои задачки будут проверяться во вторую очередь после задачек на php.
Почему надо спросить у Питонщиков, я откуда знаю?
>>755546
А что другие люди говорят о твоем коде? Легко ли в нем разбираться и поддерживать?
Почитать - книги в ОП посте и главу по ООП из учебника.
И просто интересно, а что ты имеешь против ООП? Классы это часть языка и не использовать классы по моему это то же что принципиально не использовать цикл for или анонимные функции, записывая вместо них более сложный код. Возникает вопрос, а зачем?
>>Если компания состоит из департаментов то почему у тебя 2 цикла, а не 1?
Если ты о двойном цикле в функции вывода таблицы, то там я действительно недосмотрел и должен был использовать метод вместо второго цикла.
По-твоему ООП это использование классов?
>зачем?
Затем что спроектировать грамотную архитектуру, при которой код будет поддерживаемым и расширяемым, чуть-чуть сложнее, чем написать стену процедурных инструкций в одном файле (или куче бессвязных).
мимопроходил
да упаси говинда, ничего не имею, просто я ведь _ПОКА_ всё это могу процедунрно реализовать, пониаешь? И я не понимаю, какова причина использования ООП в данном контексте, то есть не ясно примерно какого масштаба должен быть проект, чтобы для того, чтобы проще его мэйнтейнить, надо было юзать ООП.
>Почему надо спросить у Питонщиков, я откуда знаю?
Хуй знает, ты вроде во всем шаришь.
> но возможно что твои задачки будут проверяться во вторую очередь после задачек на php.
Спасибо, приду после сессии.
https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L50
В задаче написано что множить сущности не желательно, это касается класса валидации для формы редактирования? Старый класс валидации всегда будет выдавать ошибку о том что такая почта уже занята, если её не поменять. Так же это касается паролей, даже если их заполнить теми же самыми данным, то всё равно сгенерируется новый хэш и токен, и авторизация сломается, потому что в куках находиться не актуальный токен. Как обойти обновление полей которые мы не хотим обновлять?
https://github.com/someApprentice/Students/blob/master/app/Controller/SearchAction.php#L15
Нужна ли валидация и список ошибок для формы поиска?
Как избавиться от $_GET['input'] в URL?
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L52
Решил отказаться от залогинивания с помощью имени и фамилии. Слишком уж усложняется задача если использовать не уникальные данные. Главное что я понял что для каждого способа залогинивания должен быть свой метод.
https://github.com/codedokode/pasta/blob/master/student-list.md#Работа-с-формами
>После успешной регистрации или обновления данных об абитуриенте нам надо показать сообщение об этом. Проще всего для этого при редиректе (после успешной обработки формы мы делаем редирект, не забыл?) приписать дополнительный параметр в URL, то есть редиректить на адрес вида index.php?notify=registered, а уже в index.php проверять значение параметра notify.
А разве это не обязанность контроллера работать с $_GET данными?
https://github.com/codedokode/pasta/blob/master/student-list.md#Поиск-в-базе-данных
>Для поиска по всем колонкам можно применить оператор LIKE к соединенным через пробел значениям столбцов.
А можно простой пример такого запроса? У меня не получилось составить...
>name LIKE '%hello%'
А почему в примере без плейсхолдера написано? Это только в рамках простого примера?
https://github.com/codedokode/pasta/blob/master/student-list.md#Составление-url
Не совсем понимаю как этим пользоваться: Вот с ссылками для сортировки таблицы с результатами всё понятно, а как быть с формами? Допустим есть форма поиска с методом get и, насколько я знаю, её нельзя заставить отправлять этот запрос с помощью urlencode().
И еще не понятно какой класс должен заниматься этим. Свой собственный?
https://github.com/codedokode/pasta/blob/master/student-list.md#Постраничная-навигация
>Некоторые пытаются возложить на объект Pager лишние функции, например чтение параметров поиска из $_GET или подсчет числа записей в базе. Я не советую так делать, так как это явно должно быть в другом месте (например, работа с базой — в маппере).
Это касается всех $_GET запросов? Я бы на оборот сделал Pager контроллером который содержит метод, который на вход принимает результаты из БД, и затем из $_GET получал бы данные о номере страницы и сортировке, затем на их основе составлял таблицу с ссылками на страницы и сортировку. Он бы даже мог заниматься составлением ссылок для сортировки.
https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L50
В задаче написано что множить сущности не желательно, это касается класса валидации для формы редактирования? Старый класс валидации всегда будет выдавать ошибку о том что такая почта уже занята, если её не поменять. Так же это касается паролей, даже если их заполнить теми же самыми данным, то всё равно сгенерируется новый хэш и токен, и авторизация сломается, потому что в куках находиться не актуальный токен. Как обойти обновление полей которые мы не хотим обновлять?
https://github.com/someApprentice/Students/blob/master/app/Controller/SearchAction.php#L15
Нужна ли валидация и список ошибок для формы поиска?
Как избавиться от $_GET['input'] в URL?
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L52
Решил отказаться от залогинивания с помощью имени и фамилии. Слишком уж усложняется задача если использовать не уникальные данные. Главное что я понял что для каждого способа залогинивания должен быть свой метод.
https://github.com/codedokode/pasta/blob/master/student-list.md#Работа-с-формами
>После успешной регистрации или обновления данных об абитуриенте нам надо показать сообщение об этом. Проще всего для этого при редиректе (после успешной обработки формы мы делаем редирект, не забыл?) приписать дополнительный параметр в URL, то есть редиректить на адрес вида index.php?notify=registered, а уже в index.php проверять значение параметра notify.
А разве это не обязанность контроллера работать с $_GET данными?
https://github.com/codedokode/pasta/blob/master/student-list.md#Поиск-в-базе-данных
>Для поиска по всем колонкам можно применить оператор LIKE к соединенным через пробел значениям столбцов.
А можно простой пример такого запроса? У меня не получилось составить...
>name LIKE '%hello%'
А почему в примере без плейсхолдера написано? Это только в рамках простого примера?
https://github.com/codedokode/pasta/blob/master/student-list.md#Составление-url
Не совсем понимаю как этим пользоваться: Вот с ссылками для сортировки таблицы с результатами всё понятно, а как быть с формами? Допустим есть форма поиска с методом get и, насколько я знаю, её нельзя заставить отправлять этот запрос с помощью urlencode().
И еще не понятно какой класс должен заниматься этим. Свой собственный?
https://github.com/codedokode/pasta/blob/master/student-list.md#Постраничная-навигация
>Некоторые пытаются возложить на объект Pager лишние функции, например чтение параметров поиска из $_GET или подсчет числа записей в базе. Я не советую так делать, так как это явно должно быть в другом месте (например, работа с базой — в маппере).
Это касается всех $_GET запросов? Я бы на оборот сделал Pager контроллером который содержит метод, который на вход принимает результаты из БД, и затем из $_GET получал бы данные о номере страницы и сортировке, затем на их основе составлял таблицу с ссылками на страницы и сортировку. Он бы даже мог заниматься составлением ссылок для сортировки.
Бро, на кой хуй ты это делаешь, возишься тут с нами, ответы какие-то даёшь, копаешься в чужом говнокоде? Какие тебе с этого профиты? Поддерживаешь знания в актуальном состоянии, практикуешься так или всегда мечтал быть преподом?
https://github.com/applejacky/tmp
Пока проверять не надо.
К завтрашнему вечеру запушу пару коммитов, очень многое поменяется.
>хуй
>Поддерживаешь знания в актуальном состоянии, практикуешься так или всегда мечтал быть преподом?
Перечисление таких вещей больше говорит о тебе.
Люди совершенно не слышали о таких вещах как альтруизм, и я боюсь даже сказать о других высоких вещах. Надеюсь твоё невежество связанно лишь с юным возрастом, иначе тебе следует задуматься о ценностях в твоей жизни.
Хотя бы научись вести себя прилично и не материться. Ты не с быдлом в падике сидишь.
Т.е. часто есть конфиги вида:
USER="SOMENAME"
DATABASE_NAME="NAME"
Неужели нет готовой функции внутри языка для считывания подобных файлов и записи переменных в массив?
$res = yobafunction('/etc/filename');
echo $res['USER']; // SOMENAME
Так вот, существует ли yobafunction?
Прости за мат. Я как раз хотел спросить насчет альтруизма, не он ли это, но постеснялся выражаться. Ты же не ОП, а я хотел бы его ответа.
> Перечисление таких вещей больше говорит о тебе.
И психоаналитики не нужно.
> Люди совершенно не слышали о таких вещах как альтруизм, и я боюсь даже сказать о других высоких вещах
Реализация желания безвозмездно делать другим что-то хорошее приносит удовлетворение делающему. Значит, альтруизм - такое же действие ради СОБСТВЕННОЙ выгоды, пусть и нематериальной, как и любые другие. Своего рода хобби.
Опять этот выблядок из одноклассников вылез.
Хомяки уже на дваче учат жизни питурдов.
Я уже говорил, что твоя мать шлюха?
Что такое unix config? Чем тебе ini файл не годится? Если ты про dotEnv то наверно есть библиотека, хотя я не сторонник этой штуки, считаю она неудачно спроектирована и неудобна, хоть и упоминается в 12 factor app.
>Реализация желания безвозмездно делать другим что-то хорошее приносит удовлетворение делающему. Значит, альтруизм - такое же действие ради СОБСТВЕННОЙ выгоды, пусть и нематериальной, как и любые другие. Своего рода хобби.
Лежать в канаве под героином не имею ничего против героина тоже приносит удовольствие. Есть много вещей которые приносят удовольствие, разница в том какие из этих вещей приносят пользу другим или себе.
Никогда больше, никогда не отвечай на вопрос, смысл которого тебе не понятен.
Вобщем есть модуль Node Hierarchy который настроен так чтобы новая продукция имела вид /категория/наименование. Не знаю может это устаревший способ но в данный момент я не могу использовать другой так как всё уже настроено.
Нужно как то профильтровать продукцию по url (берётся url категории, фильтруется, из него вырезается часть с названием категории, затем эта часть транслируется в представление (views) и как то нужно сделать чтобы фильтровались только те типы нод которые имеют в url этот "маркер" т.е. наименование категории). В итоге получается следующая картина: модуль Views фильтрует вначале все ноды по типу материала (продукция), затем берёт url страницы на которой представление выводится как блок, затем из этого url вырезается маркер и производится (контекстная?) фильтрация по наличию этого "маркера" в url продукта. Как результат я должен получить вывод в блоке на странице подкатегории списка дочерних нод. Как это реализовать? Куда хотя бы копать? Переписать иерархию и способ вывода материалов уже нереально, т.к. уже много проделано.
p.s.
Как вариант, может быть как то реализовать фильтрацию url от слеша до слеша а потом сравнение с аналогичной фильтрацией url страницы на которой выводится блок views? Как это написать?
>>755326
штука в том, что я пользуюсь windows и у меня уже есть wget. Composer сохраняет библиотеки именно в текущую папку, или куда-то в "домашнюю папку"?
>Программист-стажер
>испытательный срок 3 месяца (возможно сокращение испытательного срока в связи с профессиональными успехами)
>Вознаграждение на испытательный срок:
>8 000 рублей/месяц
Я в отчаянии. Пойти туда, или ждать пока нормальная вакансия подвернется? Может скосят с 3ех месяцев до одного в связи с тем, что я не полный нуб?
Тебе сложно что-то посоветовать. Чем они занимаются? Будут ли тебя бросать один на один с проектом или будут радушно протягивать руку помощи на любую просьбу? Каков твой уровень компетенции? Уверен, что сможешь приносить компании даже эти 8 тысяч в месяц? Почему не на фрилансе тогда?
>Уверен, что сможешь приносить компании даже эти 8 тысяч в месяц? Почему не на фрилансе тогда?
Посмотрел тут вчера на апворке, может что приглянется. Все задачки связаны с вордпрессом, но самое охуенное - для подачи заявки на задание нужны некие коннекты, которые покупаются за деньги.
> для подачи заявки на задание нужны некие коннекты, которые покупаются за деньги.
Всё правильно сделали, нехуй бездумно спамить свое говно.
Их в месяц 60 штук дают бесплатно. На заявку нужно 2 коннекта, итого 30 заявок в месяц. Когда я в прошлом году искал фуллтайм-контракт, я их за 3 недели израсходовать не успел.
мимо проходил с руби-треда
В прошлых тредах вбрасывали, вродь годно: https://www.youtube.com/watch?v=Aw28-krO7ZM&list=PL7A20112CF84B2229
>Это не запрет отправлять запросы на другие домены.
Ничего кроме GET не сможешь отправить
>Он же без кук хочет отправлять запросы
Нихуя
>Cross-Site Request Forgery (CSRF) is a type of attack that occurs when a malicious web site, email, blog, instant message, or program causes a user's web browser to perform an unwanted action on a trusted site for which the user is currently authenticated.
Нужно чтобы ты был залогинен, и таким образом "POST bank.com/send-dengi-to-kulhatsker" с другого сайта сработало.
> Так как поле состоит из клеточек, у тебя может появиться желание сделать двухмерный массив для хранения животных. Не советую так делать, ведь этот массив надо будет поддерживать в актуальном состоянии. Проще хранить обычный одномерный массив животных, находящихся на карте.
ОП, объясни пожалуйста, что мне хранить в этом массиве то?
Ты вообще нулевой, я смотрю. Смысл лезишь к большим дядям?
Бери Sublime Text. Быстрый, из коробки умеет в автокомплит для переменных в текущем файле, множественные курсоры, уйма плагинов и так далее.
>- Далее простая, но полезная задача сделать список студентов, в ней много полезных советов: https://github.com/codedokode/pasta/blob/master/student-list.md
Ананасы в вт сдача практики подобного задание. Есть ли у кого-нить исходники данного проекта?
Например, я переписал кусок приложения, наверняка остались старые методы, которые нигде не используются, их надо бы удалить.
Вижу такую фишку, как find usages (alt + f7). Но это не надежно, например при использовании контейнера
$this->get('my_service')->doSmth()
метод doSmth не будет найден, потому что шторм не понимает, объект какого класса вернет get('алиас').
Поиск строкой только? Тоже не очень удобно, если имя неоригинальное типа save или create.
>>756810
Расширения mysqli ты хотел сказать? MySQL это система управления базами данных.
Слов "лучше/хуже, нормально/ненормально, правильно/неправильно" в программировании лучше избегать. У каждого решения есть свои преимущества и недостатки, нужно оценить их в зависимости от ситуации.
Преимущества pdo по сравнению с mysqli навскидку:
1. Автоматическая обработка ошибок (выбрасываются исключения), не нужно их обрабатывать вручную корявыми if (лажа) { выбросить исключение, ну или die('тупое сообщение'), как любят делать быдлокодеры }
2. Удобная работа с плейсхолдерами, экранирование без всяких real escape string. Впрочем в mysqli кажется тоже есть методы для работы с плейсхолдерами.
3. Внезапно mysqli поддерживает только субд MySQL.
Недостатков не вижу. Теоретически, может быть нативные функции для mysql быстрее, надо тестить.
https://habrahabr.ru/post/137664/
Полагаю, проблема решается только костылями плагинами под каждый контейнер/фреймворк. Вот под Symfony: https://plugins.jetbrains.com/plugin/7219?pr=idea
>Расширения mysqli ты хотел сказать?
Да, точно.
>2. Удобная работа с плейсхолдерами, экранирование без всяких real escape string. Впрочем в mysqli кажется тоже есть методы для работы с плейсхолдерами.
Это ты про SQL - иньекции? Или как их там.
3. Внезапно mysqli поддерживает только субд MySQL.
Стоит ли ньюфагу пробовал другие субд? Или без разницы это все? Вообще, поясните чем они все друг от друга отличаются, если вкратце.
>Стоит ли ньюфагу пробовал другие субд
Попробовать может быть и стоит, но запомни: всегда используй самые распространенные и обкатанные решения. Иначе сам будешь писать велосипеды.
create database Sudents;
Выдает следующее:
ERROR 1044 (42000): Access denied for user ' '@'localhost' to database 'students'
Как я понимаю допуска какого то нету, или что то вроде того. Кто что подскажет? Вроде бы смотрел гайды, но решения проблемы там не нашел.
Ответ не совсем по сути от новичка, разобравшего недавно MySQL в общих чертах.
Сначала надо подключиться к MySQL - дать имя пользователя и пароль.
Я с самого начала послушался ОПа, поэтому удалил WAMPP и установил отдельно Apache, MySQL и PHP7.
Я запускаю MySQL в командной строке вот так:
mysql.exe -uroot -ppassword
Потом уже можно создавать или активировать использование баз данных.
Вообще в топку все эти сборки - совет тебе от новичка. Установить всё по отдельности - минут 10-15, зато не будет у тебя всякой ерунды там, самый новый РНР будет и так далее, сам себе хозяин.
Дак доступ-то к базе настрой:
GRANT ALL ON students.* TO 'yoba@localhost' IDENTIFIED BY 'etoti';
И подключайся потом юзером yoba и паролем etoti.
Первоначально ты заходишь под админом - имя root, пароль либо пустой либо задается при услановке mysql (как в сборках сделано, я не знаю). От рута можно создавать новых пользователей (рекомендую потренироваться), при желании у себя локально можно и под рутом работать, но лучше наверно для каждого проекта - своего пользователя с доустпом только к своей базе.
>>745461
>>755118
https://github.com/fidnex/students/blob/master/app/Controller/Form.php#L45
Здесь код нелогичный: ты сначала залогиниваешь студента, а только потом вставляешь в БД. Это как-то нелогично, и есть шанс что например вставка не удастся. Надо делать в обратном порядке.
> $student->token = $authHelper->getToken();
> $tdg->update($student);
Непонятно зачем нужно обновлять токен.
В валидаторе мне не очень нравится, что у тебя смешаны в одном классе функционал валидации и результат валидации. Если уж ты так делаешь, то у тебя получился одноразовый валидатор (так как ошибки накапливаются в нем) и это тогда не сервис. Надо тогда переделать Pimple чтобы он кажды раз вовзвращал новый объект валидатора.
> class ValidateStudent
Имена классов это существительные. Не Validate, а Validator
https://github.com/fidnex/students/blob/master/app/Helper/FormHelper.php
Тут нет никаких белых списков. Пользователь может обновлять любые поля в объекте. В другом приложении (где у объектов есть какие-то поля которые должны быть недоступны пользователю) это будет уязвимость.
Также, тут ошибка в том, что обновляются вообще все поля объекта. В том числе например токен, который меняться не должен. И если кто-то завтра добавит новое поле, его тоже обнулит. Вообще, этот код неправильный. Все эти рассуждения неправильные:
- неправильно думать, что все поля в студенте должны соответствовать БД. могут быть и просто поля, не связанные с БД
- неправильно думать, что пользователь может обновлять любые поля в студенте. Это может повлечь уязвимость
- неправильно думать, что в будущем разработчик не добавит какие-то дополнительные поля
Должен быть белый список обновляемых полей. Список соответствует набору полей в форме.
https://github.com/fidnex/students/blob/master/app/Helper/AuthHelper.php#L21
Кука удаляется установкой пустого значения + даты в прошлом. Также, ты не указал path для куки. В итоге получится что ты просто создашь вторую куку с таким же именем.
https://github.com/fidnex/students/blob/master/app/Helper/AuthHelper.php#L28
Непонятно зачем метод checkExistEmail() в класссе, отвечающем за авторизацию
https://github.com/fidnex/students/blob/master/app/Helper/PaginationHelper.php#L5
Поля принято объявлять каждое отдельной строкой.
https://github.com/fidnex/students/blob/master/app/Lib/Controller.php
Здесь не объявлено поле view
https://github.com/fidnex/students/blob/master/app/Lib/Validation.php#L7
abstract public function check(Model $model);
Не очень понятно почему валидация должна ограничиваться моделями. тут лучше было разрешить любой объект
> $error = $this->{$method[0]}(...array_merge(
> array($var),
> array_slice($method, 1)
Жутковатое какое-то выражение. Лучше было вынести часть значений в переменные.
https://github.com/fidnex/students/blob/master/app/View/main/index.php#L8
Я думаю, уведомлению не нужна кнопка закрытия. И странно, что кнопка сделана тегов ссылки, где здесь логика?
> <?php if($this->pagination->getCurPage() == $i) echo 'class="active"';?
Тут лучше было применить <?= ... ? ... : ... ?>. Не исопльзуй echo в шаблоне
https://github.com/fidnex/students/blob/master/app/View/form/index.php#L6
> required="true"
Тут надо писать просто required
https://github.com/fidnex/students/blob/master/app/View/form/index.php#L22
Пол не берется из модели при выводе формы
Так, в общем, хорошо сделано.
>>745461
>>755118
https://github.com/fidnex/students/blob/master/app/Controller/Form.php#L45
Здесь код нелогичный: ты сначала залогиниваешь студента, а только потом вставляешь в БД. Это как-то нелогично, и есть шанс что например вставка не удастся. Надо делать в обратном порядке.
> $student->token = $authHelper->getToken();
> $tdg->update($student);
Непонятно зачем нужно обновлять токен.
В валидаторе мне не очень нравится, что у тебя смешаны в одном классе функционал валидации и результат валидации. Если уж ты так делаешь, то у тебя получился одноразовый валидатор (так как ошибки накапливаются в нем) и это тогда не сервис. Надо тогда переделать Pimple чтобы он кажды раз вовзвращал новый объект валидатора.
> class ValidateStudent
Имена классов это существительные. Не Validate, а Validator
https://github.com/fidnex/students/blob/master/app/Helper/FormHelper.php
Тут нет никаких белых списков. Пользователь может обновлять любые поля в объекте. В другом приложении (где у объектов есть какие-то поля которые должны быть недоступны пользователю) это будет уязвимость.
Также, тут ошибка в том, что обновляются вообще все поля объекта. В том числе например токен, который меняться не должен. И если кто-то завтра добавит новое поле, его тоже обнулит. Вообще, этот код неправильный. Все эти рассуждения неправильные:
- неправильно думать, что все поля в студенте должны соответствовать БД. могут быть и просто поля, не связанные с БД
- неправильно думать, что пользователь может обновлять любые поля в студенте. Это может повлечь уязвимость
- неправильно думать, что в будущем разработчик не добавит какие-то дополнительные поля
Должен быть белый список обновляемых полей. Список соответствует набору полей в форме.
https://github.com/fidnex/students/blob/master/app/Helper/AuthHelper.php#L21
Кука удаляется установкой пустого значения + даты в прошлом. Также, ты не указал path для куки. В итоге получится что ты просто создашь вторую куку с таким же именем.
https://github.com/fidnex/students/blob/master/app/Helper/AuthHelper.php#L28
Непонятно зачем метод checkExistEmail() в класссе, отвечающем за авторизацию
https://github.com/fidnex/students/blob/master/app/Helper/PaginationHelper.php#L5
Поля принято объявлять каждое отдельной строкой.
https://github.com/fidnex/students/blob/master/app/Lib/Controller.php
Здесь не объявлено поле view
https://github.com/fidnex/students/blob/master/app/Lib/Validation.php#L7
abstract public function check(Model $model);
Не очень понятно почему валидация должна ограничиваться моделями. тут лучше было разрешить любой объект
> $error = $this->{$method[0]}(...array_merge(
> array($var),
> array_slice($method, 1)
Жутковатое какое-то выражение. Лучше было вынести часть значений в переменные.
https://github.com/fidnex/students/blob/master/app/View/main/index.php#L8
Я думаю, уведомлению не нужна кнопка закрытия. И странно, что кнопка сделана тегов ссылки, где здесь логика?
> <?php if($this->pagination->getCurPage() == $i) echo 'class="active"';?
Тут лучше было применить <?= ... ? ... : ... ?>. Не исопльзуй echo в шаблоне
https://github.com/fidnex/students/blob/master/app/View/form/index.php#L6
> required="true"
Тут надо писать просто required
https://github.com/fidnex/students/blob/master/app/View/form/index.php#L22
Пол не берется из модели при выводе формы
Так, в общем, хорошо сделано.
Человек говорит, что в cloud9 можно посмотреть страницу в разных браузерах и разных устройствах.
Нимагу найти, как это делается.
Еще не разобрался как настроить виртуальный хост, чтобы он считал корнем папку web/.
У себя на убунте пишу хосты в /etc/apache2/sites-available. С облаком непонятно как это сделать, наверное через .htaccess, только не знаю что в него писать.
> При разработке сайтов в данной IDE, их результат можно посмотреть в отдельном блоке интерфейса. Этот «preview» обладает одной необычной возможностью, которая заключается в том, что сайт можно виртуально проверить в разных браузерах и на разных устройствах. Данная технология предоставлена компанией «Sauce labs».
Судя по https://community.c9.io/t/saucelabs-previews-not-working/4391/5 там какие-то проблемы
тут http://sauceio.com/index.php/2014/07/announcing-cloud9-preview-instantly-preview-your-cloud9-project-in-any-browser-powered-by-sauce-labs/ есть скриншот как должно быть
Гугл: https://www.google.ru/search?q=cloud9+preview+saucelabs&newwindow=1&gbv=1&sei=8lRMV5D4EIqKsgH3yZ24DQ
>>746699
>>755118
на случай, если ты не видел ответ в старом треде, вот он:
----------
> А зачем ты вынес bootstrap из public? Ведь там идея что корень сервера в public и все, что за ним, недоступно через веб-сервер. Я имел в виду что бутстрап должен быть в исходном, неисправленном виде в своей папке внутри public.
>> А если пользователь при редактировании захочет очистить какое-то поле, эта проверка ведь его не пропустит?
> Можно удалять поля полностью и добавлять их в бд? Я про это не знал. Мои регулярки не пропускают пустые значения. Менять? Пользователь что, вообще может не заполнять ничего?
Я имел в виду, что если там есть необязательное поле (не помню есть ли оно в условиях), то тогда разумеется пользователь может очистить это поле чтобы сохранить пустое значение.
> Я не нарушаю логику отображения перенося ее из шаблона в методы из вспомогательных классов как ты советовал? В борьбе с копипастом создал их(методов) несколько.
Немножечко нарушаешь, так что лучше особо большие куски кода так не делать. Вообще, в нормальных шаблонизаторах вроде twig есть макросы, то есть "подшаблоны", и в них можно выносить куски кода и позже их вызывать. У нас их нет, так что надо либо куски выносить в отдельные файлы либо делать класс-помощник.
Но для борьбы с копипастом, я думаю, это приемлемо. Просто изолируй все такие функции в отдельном классе.
> [Конфигурация нашей базы данных]
В ini файлах так обозначают не комментарий, а название секции. Их лучше делать латинницей и короткими (или вообще не делать если файл маленький). Комментарий это строка, начинающаяся с точки с запятой.
> static function isInputChecked($genderOfAbiturient, $valueFromInput)
вообще тут можно было просто написать <?= $student->getGender() == Student::GENDER_MALE ? ' checked' : '' ?>. Но можно и функцию.
Остальное позже в новом треде напишу.
> Это нормально что я несколько раз отклонился от общего для кода стиля(камелкейс) для имени переменной и имени метода и использовал вариант с нижним подчеркиванием ("CSRF_token")? Мне показалось, что не очень красиво если будет "CSRFToken". Стоило ли тогда тоже самое делать с authToken для, не знаю как сказать, симметрии чтоли.
Нет, нехорошо. Лучше CSRFToken или csrfToken.
> Ну он у меня в бд попадал через модель. Сейчас после манипуляций с двумя токенами я его добавляю отдельно https://github.com/greenTea242/Student_List/blob/master/public/register.php#L31 . Ок?
Я думаю, не стоит ради токена делать лишний аргумент в setProperties. Его можно задать отдельной командой вроде
$student->setToken(...);
-----------
https://github.com/greenTea242/Student_List/blob/master/public/index.php#L39
> require_once __DIR__ . "/../templates/index.html";
тут явно должно быть не require_once, а просто require
https://github.com/greenTea242/Student_List/blob/master/src/Authorization.php#L12
очень странно. что раюотас CSRF в классе авторизации, и CSRF токен очищается при разлогинивании
> check_CSRF_token
не в том стиле название, должно быть checkCsrfToken
https://github.com/greenTea242/Student_List/blob/master/src/ini.php#L18
Имя БД лучше сделать меняемым через конфиг
https://github.com/greenTea242/Student_List/blob/master/src/ini.php#L31
> $authToken = isset($_COOKIE["authToken"]) ? strval($_COOKIE["authToken"]) : "";
лучше всю работу с кукой авторизации убрать в класс авторицации
https://github.com/greenTea242/Student_List/tree/master/src
тут 2 конфига осталось
https://github.com/greenTea242/Student_List/blob/master/templates/index.html#L4
> <p>Поздравляем с успешной регистрацией!</p>
В бутстрапе есть специальный класс alert для вывода сообщений. Изучи возможности бутстрапа раз уж ты его используешь.
https://github.com/greenTea242/Student_List/blob/master/templates/index.html#L26
Тут странно что в заголвке колонки сделано 2 отдельных ссылки - наверно лучше было одну.
Так, в общем, неплохо сделано.
>>746699
>>755118
на случай, если ты не видел ответ в старом треде, вот он:
----------
> А зачем ты вынес bootstrap из public? Ведь там идея что корень сервера в public и все, что за ним, недоступно через веб-сервер. Я имел в виду что бутстрап должен быть в исходном, неисправленном виде в своей папке внутри public.
>> А если пользователь при редактировании захочет очистить какое-то поле, эта проверка ведь его не пропустит?
> Можно удалять поля полностью и добавлять их в бд? Я про это не знал. Мои регулярки не пропускают пустые значения. Менять? Пользователь что, вообще может не заполнять ничего?
Я имел в виду, что если там есть необязательное поле (не помню есть ли оно в условиях), то тогда разумеется пользователь может очистить это поле чтобы сохранить пустое значение.
> Я не нарушаю логику отображения перенося ее из шаблона в методы из вспомогательных классов как ты советовал? В борьбе с копипастом создал их(методов) несколько.
Немножечко нарушаешь, так что лучше особо большие куски кода так не делать. Вообще, в нормальных шаблонизаторах вроде twig есть макросы, то есть "подшаблоны", и в них можно выносить куски кода и позже их вызывать. У нас их нет, так что надо либо куски выносить в отдельные файлы либо делать класс-помощник.
Но для борьбы с копипастом, я думаю, это приемлемо. Просто изолируй все такие функции в отдельном классе.
> [Конфигурация нашей базы данных]
В ini файлах так обозначают не комментарий, а название секции. Их лучше делать латинницей и короткими (или вообще не делать если файл маленький). Комментарий это строка, начинающаяся с точки с запятой.
> static function isInputChecked($genderOfAbiturient, $valueFromInput)
вообще тут можно было просто написать <?= $student->getGender() == Student::GENDER_MALE ? ' checked' : '' ?>. Но можно и функцию.
Остальное позже в новом треде напишу.
> Это нормально что я несколько раз отклонился от общего для кода стиля(камелкейс) для имени переменной и имени метода и использовал вариант с нижним подчеркиванием ("CSRF_token")? Мне показалось, что не очень красиво если будет "CSRFToken". Стоило ли тогда тоже самое делать с authToken для, не знаю как сказать, симметрии чтоли.
Нет, нехорошо. Лучше CSRFToken или csrfToken.
> Ну он у меня в бд попадал через модель. Сейчас после манипуляций с двумя токенами я его добавляю отдельно https://github.com/greenTea242/Student_List/blob/master/public/register.php#L31 . Ок?
Я думаю, не стоит ради токена делать лишний аргумент в setProperties. Его можно задать отдельной командой вроде
$student->setToken(...);
-----------
https://github.com/greenTea242/Student_List/blob/master/public/index.php#L39
> require_once __DIR__ . "/../templates/index.html";
тут явно должно быть не require_once, а просто require
https://github.com/greenTea242/Student_List/blob/master/src/Authorization.php#L12
очень странно. что раюотас CSRF в классе авторизации, и CSRF токен очищается при разлогинивании
> check_CSRF_token
не в том стиле название, должно быть checkCsrfToken
https://github.com/greenTea242/Student_List/blob/master/src/ini.php#L18
Имя БД лучше сделать меняемым через конфиг
https://github.com/greenTea242/Student_List/blob/master/src/ini.php#L31
> $authToken = isset($_COOKIE["authToken"]) ? strval($_COOKIE["authToken"]) : "";
лучше всю работу с кукой авторизации убрать в класс авторицации
https://github.com/greenTea242/Student_List/tree/master/src
тут 2 конфига осталось
https://github.com/greenTea242/Student_List/blob/master/templates/index.html#L4
> <p>Поздравляем с успешной регистрацией!</p>
В бутстрапе есть специальный класс alert для вывода сообщений. Изучи возможности бутстрапа раз уж ты его используешь.
https://github.com/greenTea242/Student_List/blob/master/templates/index.html#L26
Тут странно что в заголвке колонки сделано 2 отдельных ссылки - наверно лучше было одну.
Так, в общем, неплохо сделано.
На данный момент нельзя выбрать браузер, оно конечно отображается в каком-то безымянном, но непонятно в каком.
Ладно, короче когда починят будет круто.
Выглядит намного лучше, но все равно мне кажется есть что улучшать.
https://github.com/nsdvw/TestHub
Прошлый тред смыло, ты мне там писал почитать про REST.
Почитал, мало что понял.
REST это всего лишь соглашение о семантичном url и уместном использовании http методов, правильно?
Допустим,
GET /book/123/page/456 - запрос на чтение 456 страницы 123 книги
POST /book/123/page/456 - запрос на добавление/изменение, возможно удаление соотв.ресурса
Вообще как-то размыто. Давным-давно все используют урлы типа /blog/post/123, это тоже REST?
RESTful api как я понимаю api с семантичными урлами?
Много мутной писанины, а по-сути можно выразить одним предложением.
> REST это всего лишь соглашение о семантичном url и уместном использовании http методов, правильно?
Это плебейский REST. Это то, что понимают под словом "REST" безграмотные индусы. Настоящему REST'у совершенно похуй на URL'ы. Настоящий REST - это https://en.wikipedia.org/wiki/HATEOAS
Вопрос "а нахуя в реальной жизни сдался этот настоящий REST?" остаётся открытым.
Или нужно обязательно ставить "не ниже 5.4"?
Последняя stable версия фреймворка по большей части с седьмой версией работает нормально, так что можешь ставить.
Спасибо!
Подскажи ещё такую вещь: у меня хорошие знания синтаксиса, средние знания ООП, вполне нормальные знания MySQL.
Открыта вкладка с пространствами имён.
Открыта вкладка с MVC.
Есть ли шанс разобрать этот фреймворк с такой вот скромной базой?
Чувствую себя не вполне уверенно, обложился курсами и всякой ерундой, боюсь устанавливать сам фреймворк...
И второй вопрос: "Можно ли назвать цмс высокоуровневым фреймворком? Ведь мы также добавляем модули и настаиваем их для перекачки данных туда-сюда, дописываем функционал.
Фреймворк это готовый для повторного использования код. Большие фреймворки кроме собственно кода содержат инструменты разработчика: консольные команды, инструменты отладки, генерации кода и т.д.
Это не готовое приложение, это фрагменты исходного кода приложения. Фреймворки принято использовать ради стандарта (если в команду приходит новый человек, он уже знает как устроено приложение, если знает фреймворк) и ради избежания рутинной работы.
Нужно сначала понимать, как работают веб-приложения, написать несколько уродливых велосипедов типа тех что предлагают в оп-посте для тренировки.
А затем уже учить фреймворки.
>Транспорт данных во фреймворке регламентирован.
Родился на улице Герцена. В гастрономе № 22.
>>757823
cms это не фреймворк, а примитивный crud для контент-манагеров.
Костыли вы дописываете, а не функционал.
У меня есть база данных. В одной таблице есть куча данных.
Есть еще одна таблица, с 3 полями. Одно поле это айди, PK. Остальные поля - поля, аналогичные которым есть в первой таблице.
Я с помощью INNER JOIN ищу совпадения и вывожу одно из полей первой таблицы по обьединению общих полей со второй.
Для того, чтобы вывести все эти значения на страничке, нужен всего-лишь
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo "grup: " . $row["grup"]. "<br>";
}
Как сделать, чтобы $row["grup"] забивалось в html-овский insert ?
Такое чувство что ты фреймворки и цмски в глаза не видел и не работал с ними.
>если в команду приходит новый человек, он уже знает как устроено приложение
По твоим словам выходит что фреймворк - это приложение.
>Большие фреймворки кроме собственно кода содержат инструменты разработчика: консольные команды, инструменты отладки, генерации кода и т.д.
Самопис тоже без этого не обходится.
>Фреймворк это готовый для повторного использования код.
Как о функции или либе сказал. Мысли шире. Фреймворк - это готовое приложение уже.
>Родился на улице Герцена. В гастрономе № 22.
Небыдло, да?
>Нужно сначала понимать, как работают веб-приложения, написать несколько уродливых велосипедов типа тех что предлагают в оп-посте для тренировки.
Мантра твоя?
Еще вопрос назрел. Можно ли сравнить программиста с рельсоукладчиком, который прокладывает пути для сигналов, строит депо?
Оп, исправил все, о чем ты написал. Опять возникла проблема с клонированием: клоны перезаписывают оригинал с каждым использованием. И почему-то количество работников в норме, а вот зарплата, кофе, страницы меняются.
http://ideone.com/rOfeNX
>выходит что фреймворк - это приложени
Это каркас приложения, даун. Он всегда одинаковый в общем, но разный в деталях. А еще фрамевок - это билиотека готовых ходовых решений, но всегда можно запилить что-то свое в рамках идеалогии каркаса.
>software providing generic functionality
Ты олигофрен или идиот, дурак или имбецил, кретин или балбес, балда или болван, дуралей или тупица?
Да тыж обосрался, долбоеб, немогущий в ингишь.
> an abstraction in which software providing generic functionality can be selectively changed
> an abstraction
Ёбаннный ты олень, тебе расшифровка абстракции дается.
Рискую увязнуть в них на всё лето.
Не могу себе позволить такую роскошь, просто не могу.
Я ведь обычно довожу до конца то, за что берусь.
Мне просто надо разобраться в фреймворке на уровне подключений, интеграции модулей и виджетов - и так далее.
Я тут как то спрашивал о том, как идти на повышение зарплаты и как оценить свою стоимость.
Мне подкинули какую то книжку о том, как говорить о зарплате. Я всё проебал и не могу найти в архиваче, может кто вбросить еще раз?
Никто не юзает этот клёвый шаблонизатор?
Шаблонизатор как шаблонизатор, откуда такой утиный восторг?
К тому же сильно устарел, хотя может в третьей версии подтянули, не знаю.
Пользуюсь твигом из-за множественного наследования, автоэскейпа и расширяемости (удобно добавлять свои фильтры и функции).
Я вот тут от нечего делать пишу древовидную структуру (в CMS встречается повсеместно - например категории и в них лежат товары).
Меня вот интересует, неужели оно везде сделано через такую же жопу?
[code]select group_concat(n.name order by n.id separator '/') as path from closure_table d left outer join closure_table a on (a.cid = d.cid) left outer join nodes n on (n.id = a.pid) where d.pid = 2 group by d.cid order by group_concat(lpad(n.id,11,'0') order by n.id separator ',');
+----------------------------+
| path |
+----------------------------+
| /var |
| /var/www |
| /var/run |
| /var/run/mysql |
| /var/run/mysql |
| /var/run/mysql/mysqld.sock |
| /var/run/mysql/mysqld.pid |
| /var/lib |
| /var/lib/mysql |
+----------------------------+
9 rows in set (0.00 sec)[/code]
Когда-то писал свой первый и последний промышленный скрипт - использовал в качестве шаблонизатора XSLT. Конечно, он адово перегружен по сравнению с тем же твигом, но внезапно довольно удобен.
Правда сама разметка заметно сложнее - всякие там xsl-foreach и ебля с xpath.
урок по теме https://github.com/codedokode/pasta/blob/master/db/trees.md
с closure_table по идее должен быть только один джойн
Мне просто неохота туда пихать дополнительные поля.
Смысл в том, что без двух джойнов сортировка идёт чисто по ID, и если допустим /var/lib/mysql/mysqld.pid был добавлен сильно позже /var/lib/mysq/mysqld.sock, то он и в списке будет идти отдельно. А мне надо бы всё отсортировать к красивому виду.
Смотря кому и в каких ситуациях.
В целом конечно можно обойтись и без шаблонизатора. Но это приятный синтаксический сахар в первую очередь.
{{ user.name }}
вместо
<?php echo $user->getName(); ?>
{{ var|e }} или даже {{ var }} (экранирование включено по умолчанию)
вместо
<?php echo htmlspecialchars($var, ENT_QUOTES, 'UTF-8') ?>
которые будешь забывать в 20% случаев
Множественное наследование - даже не знаю как от него отказаться, будет по-любому цирк с инклюдами, в конце концов получится велосипед или даже мопед если повезет.
Куча встроенных фильтров и функций тоже облегчает.
> {{ user.name }}
аналогом будет <?= htmlspecialchars($user->name, ET_QUOTES) ?> скорее и это special chars довольно надоедает писать руками. Можно конечно сделать функцию вроде html () или h() но все равно не то.
Ты еще макросы не упомянул.
уже решил
Только если ты наследуешь класс А от класса Б или имеешь зависимость класса А от класса Б. Ты узнаешь позже об этих вещах.
C:\PHP\php.exe
The php.ini used by your command-line PHP is: C:\PHP\php.ini
A setting in your php.ini could be causing the problem: Either the 'extension_dir' value is incorrect or the dll does not exist.
Program Output:
Warning: PHP Startup: Unable to load dynamic library 'C:\PHP\ext\php_interbase.dll' - %1 не является приложением Win32.
in Unknown on line 0
Что делать теперь?
Обновил PHP7.0.5 до PHP7.0.7 - сразу вот это всё прошло - >>759012
Но там у меня куча всего была раскомментирована до этого, просто скопировать код из php.ini страшновато.
Подскажите, что там раскрыть надо, я немного еду.
Сейчас стал сидеть до 2-3 ночи, но днём сплю час после обеда - неплохо, базарю, только начинаю тупить под конец
>После установки Composer устанавливать Yii можно запустив следующую команду в папке доступной через веб:
>composer global require "fxp/composer-asset-plugin:~1.1.1"
>composer create-project --prefer-dist yiisoft/yii2-app-basic basic
>Первая команда устанавливает composer asset plugin, который позволяет управлять зависимостями пакетов bower и npm через Composer. Эту команду достаточно выполнить один раз. Вторая команда устанавливает Yii в директорию basic. Если хотите, можете выбрать другое имя директории.
А у меня пикрелейтед происходит...
Подскажите, пожалуйста, как всё установить.
> Unable to load dynamic library
Это значит не хватает каких-то библиотек чтобы загрузить расширение. Ты можешь скачать программу dependency walker или аналогичную и увидеть какие dll нужны для php_interbase.dll (скорее всего что-то из комплекта interbase). Надо либо их установить либо отключить это расширение. Не надо было включать все подряд, сам виноват.
>>759016
Написано же: не хватает прав чтобы создать папку. Ты зачем в папке Windows пытаешься создать проект?
Давай я угадаю: ты не прочитал мой гайд в ОП посте по командной строке (или любой другой гайд), а просто пытаешься следовать инструкцим с какого-то сайта, не понимая, что они делают. Надо вернуться и прочитать гайд и научиться переходить в нужную папку.
>>759017
У тебя git либо не установлен либо его нет в PATH. Прочитай гайд по командной строке. Также, если бы ты использовал инсталлятор композера, а не отдельный phar файл, то он бы установил тебе все, что нужно.
Для этого тебе надо иметь объект класса Б (или наследоваться от него). Методы вызываются не сами по себе, а на каком-то объекте, если нет объекта - нельзя вызвать.
В вордпрессе есть все это готовое?
В свойствах ярлыка смени --open-shell на --open-shell=C:\path\to\folder
Где \path\to\folder очевидно папка которую ты хочешь сделать по умолчанию.
Исправить можно не укзаывая тип или указав интерфейс Throwable. Погугли.
И сразу скажу что ты очень невнимательно читал про исключения и видимо не изучал HTTP. Там написано что при ошибке принято отдавать код 5xx а ты отдаешь 3xx.
Спасибо большое, попробую всё.
Прошу прощения, действительно забыл ознакомиться с мануалом о командной строке - у меня всё получилось с MySQL, я переключился на другое. Я помню, ты советовал его мне.
Буду разбираться теперь, спасибо, ОП!
>если бы ты использовал инсталлятор композера, а не отдельный phar файл, то он бы установил тебе все, что нужно.
Хм, я использовал вот отсюда именно инсталлятор: https://getcomposer.org/download/
Сначала проводник ругался, что нет FbClient.dll, я его вроде бы установил, обновил версию РНР - всё установилось. Ну а далее уже вот это всё пошло.
Буду разбираться.
Может кто на примере объяснить как работать с singletone шаблоном?
Перед изучением этого паттерна тебе стоит прочесть хотя бы этот урок (а возможно и что-то еще): https://github.com/codedokode/pasta/blob/master/arch/di.md
На выходе пик.
Сама случайная строка генерируется вот так:
for ($i = 0; $i < $randomStringLength; $i++) {
$randomString .= chr(mt_rand(33, 126));
}
Или стоит прописать набор символов, из которых генерировать случайную строку, исключив special chars?
Извините, оказывается у ОП-а в пасте всё есть. Вопрос закрыт.
>>759020
Условно говоря, что надо?
Есть некий товар с кучей параметров, храниться в БД.
И есть простыня процедур, для разных действие с этим товаром - КРУД, теги товара и бог знает что ещё.
Что я хочу? Переписать весь этот джехад в объектном стиле.
Допустим, я сделаю класс Product. В нём будут поля, соовтестующие полям товара в бд и методы, скажем, для их вывода и изменения. Но получается таким образом, что мне придётся в методах класса описывать функцию выборки из бд, к примеру. И что мы в этоге получим? Да ту же простыню, не?
Или, возможно, я не до конца понимаю суть ООП.
Читал пасту ОПа по паттернам для работы с БД?
https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
>возможно, я не до конца понимаю суть ООП.
Ты и с MVC, по-видимому, дела не имел.
Если я на MVC начну это переписывать, то я сейчас ёбнусь.
Так не так чтоб очень большой проект
Спасибо за наводку, полезная статья, втыкаю.
я ещё похоже и суть MVC не понимаю до конца.
В данном случае, применительно для, условно говоря, сайтов по продаже говна через интернет,
представление (view) - это шаблоны html на каком-то шаблонизаторе
модель (model) - это вся логика, например, связанная с БД,
а контроллер (controller) - это, видимо, "роутер" разбирающий запросы?
Спасибо, поправил: https://github.com/fidnex/students/commit/f07543a8552746b94e13d2520dd7a32ba26170f0
>Непонятно зачем нужно обновлять токен.
Он не обновляет, а достает существующий из кук. Токен у меня роль айдишника в маппере выполняет.
>а контроллер (controller) - это, видимо, "роутер" разбирающий запросы?
Обычно роутер разбирает запрос и вызывает нужный контроллер. Но вообще да, что-то такое.
Прописывается исключительно для удобства, чтобы не печатать полный путь к исполняемому файлу. Если тебе не лень каждый раз вбивать полный путь к нему, то прописывать папку с ним в PATH незачем.
Если composer работает как-то неправильно (я не понял что ты подразумеваешь под "нормальной работой"), то добавление папки с ним в PATH ничего не изменит.
Зачем ты опять её запостил, у меня опять шишка дымиться начинает.
>Если composer работает как-то неправильно (я не понял что ты подразумеваешь под "нормальной работой")
У меня вот такой путь в РАТН:
>C:\Users\User\AppData\Roaming\Composer\vendor\bin
Но в самой этой папке нет папки bin - ничего не работает.
Сейчас пробую установить Git - тоже нет его пока.
Я вообще в растерянности - ни черта ничего не работает, я думал, гораздо проще и лучше будет это всё. Я когда программирование учить начинааааал...
У меня всего 4-5 картинок, извини, не буду
https://foobar1643.github.io/minesweeper/
Так же, благодаря подходу с моделью-представлением-контроллером получилось сделать забавную вещь, пять одинаковых представлений которые отображают одну модель.
https://foobar1643.github.io/minesweeper/multiple.html
А где у тебя исполняемый файл композера? Я не знаю, где в винде должен лежать композер, в линуксе бинарники, как правило, лежат в /usr/bin/
Вот нет его почему-то.
Только вот это
<-----
А в папке vendor есть две пустые папки - composer и fxp.
Я вообще пока бреду в темноте и раздвигаю руками какие-то неприятные заросли. Пока ничего не нараздвигал.
Когда остаётся последний квадрат - не получается поставить флажок - не реагирует на нажатие.
А дальше что-то непонятное.
Но я впервые играю, не понял, что тут такое вообще.
У меня вообще сегодня всё через задницу
Нет, это я и в простой игре туплю, простите.
Нет, просто не мог поставить лишний флажок - не реагировало. А потом нажал и БАБАХНУЛ.
Firefox самый последний какой-то.
Хорошо было бы, если бы при закончившихся флажках появлялось предупреждение, что они закончились.
Ок, сделаю, спасибо за фидбэк.
Вот сейчас установил Git и столкнулся с блокировкой запросов к API:
Created project in basic
Loading composer repositories with package information
Updating dependencies (including require-dev)
Reading bower.json of bower-asset/jquery.inputmask (3.1.55)
Could not fetch https://api.github.com/repos/RobinHerbots/jquery.inputmask/conte
nts/bower.json?ref, please create a Git
Hub OAuth token to go over the API rate limit
Head to https://github.com/settings/tokens/new?scopes=repo&description=Composer+
on++2016-06-01+1713
to retrieve a token. It will be stored in "C:/Users/User/AppData/Roaming/Compose
r/auth.json" for future use by Composer.
Token (hidden):
Как сделать этот токен?
Ну вот тут можно сгенерировать токен: https://github.com/settings/tokens/new?scopes=repo&description=Composer
А что там должно быть, что нужно указать, какие чекбоксы проставить?
У тебя можно с первого раза наткнуться на бомбу (пик). Одно из решений: поле должно заполняться бомбами только после того, как откроется первая клетка.
>>759519
Не знаю, братишка, я буквально через пару месяцев после того, как обзавёлся компом, пересел на линукс, чего и тебе советую. Там этот composer ставится и прописывается в PATH одной строчкой в консоли.
Как вариант попробуй зайти на официальный сайт composer'а, чтобы скачать оттуда архив с исполняемым файлом и уже им пробуй пользоваться. В винде есть опция вроде "открыть терминал в текущей директории"?
Спасибо
>>759115
> при ошибке принято отдавать код 5xx
На скриншоте переадресация на 503
> а ты отдаешь 3xx
Где? Ты осмотрелся ведь?
>поле должно заполняться бомбами только после того, как откроется первая клетка.
Лучше делит бомбы если попал при первом нажатии.
Все равно непонятно, если ты берешь студента из базы и в нем уже прописан токен, зачем его менять? Даже если ты перезаписываешь его тем же самым значением, все равно непонятно, зачем это.
>>759386
роутер это роутер.
>>759489
Можно прописать, да, только не composer.phar (смысл?), а файл с названием composer, который обычно является скриптом, запускющим composer.phar
Обчно если ты ставишь его инсталлятором, он сам куда надо прописывается, если вручную то конечно нет. Я просто скачиваю его в любую папку и запускаю как php d:/tmp/cmposer.phar ....
>>759507
Чтобы команда composer работала, там должен быть файл composer.exe или composer.bat
>>759524
А это правило ты сам придумал или оно в других версиях сапера тоже есть? Первый раз слышу.
>>759539
Никакие права и доступы давать не надо, то есть галочки ставить не надо. Токен нужен тольо для обхода ограничения на частоту запросов с одного IP
>если ты берешь студента из базы и в нем уже прописан токен, зачем его менять?
Действительно, да. Убрал.
Это костыль.
Во-первых, бомб становится меньше. Во-вторых, нужно уменьшать цифры вокруг удалённой бомбы на единицу. То есть, мы сначала расставили бомбы, посчитали их, расставили цифры вокруг. А после удаления бомбы, опять посчитали. А я предлагаю расставлять бомбы после первого хода и считать один раз.
Алсо, >>755604-это сообщение уже неактуально, допилил пагинацию и поиск жуткими костылями. Репозиторий переехал сюда: https://github.com/applejacky/students
Завтра добавлю дамп БД и инструкции по установке на локалхост.
А интересно, по какой логике классы распределены по файлам? В одном файле один класс, в другом - несколько. И сами определения классов как-то перемешаны.
Далее: http://pastebin.ru/8hYTOhA9
Чому не гуглишь? Контроллер, в общем и очень упрощенном случае, обращается к модели/мапперу, обрабатывает GET/POST параметры, обращается к валидатору, передаёт результат во вью.
>>Контроллер, в общем и очень упрощенном случае, обращается к модели/мапперу, обрабатывает GET/POST параметры, обращается к валидатору
Нуууу я по роутером ЭТО тоже имел ввиду, просто несколько его расширил.
Понятно, снимаю вопрсо.
- https://github.com/search?l=PHP&q=student+list&ref=searchresults&type=Repositories&utf8=✓
- https://github.com/search?l=PHP&p=1&q=student+&ref=searchresults&type=Repositories&utf8=✓
про файлообменник:
- https://github.com/search?l=PHP&q=file+sharing&ref=searchresults&type=Repositories&utf8=✓
Полезно бывает посмотреть другие варианты решения той же задачи. Однако, я советую тебе сначала решить ее самому, а только потом сравнивать - иначе ты вместо написания и обдумывания своего кода можешь начать подсознательно копировать ранее увиденное.
Чтобы попасть в эти списки, заполни описание своего репозитория и добавь туда нужные ключевые слова.
>>759656
Ты представляешь, как делается редирект? Редирект это отдача в качестве ответа кода 3xx и заголовка Location. Редиректить при ошибке, как и при 404 или 403 - неправильно. Вдобавок, в адресной строке браузера теряется УРЛ и обновить страницу нельзя.
>>759657
Роутер делает только одну функциию: анализирует УРЛ и решает какой контроллер и какое действие надо вызвать.
Роутер и контроллер это очень разные вещи.
Также, есть паттерн Front Controller, когда все запросы поступают первоначально на основной контроллер, а он уже вызывает какой-то конкретный.
Особую сложность представляет тестовое задание для HR: https://gist.github.com/noff/8705086
Наведи на мысль, как правильно написать многоэтажную форму по созданию теста в testhub.
Потому что у меня пока получается лютый говнокод, стыдно даже коммитить, но как правильно сделать не представляю.
Проблемы на всех этапах: хранение шаблонов для составляющих частей этой убер-формы, подстановка значений в эти шаблоны (не хочется подключать шаблонизатор типа handlebars чтобы заменить 3 атрибута), валидация на клиенте, разбор отправленной формы на сервере.
Я через пару часов запушу, но там по-любому нужно полностью переписывать.
Форма в Симфони строится поверх какой-то модели. Модель может быть 2 видов:
- массив или вложенная структура из массивов (не одобряю для данного случая)
- сущность или дерево (вложенная структура) из сущностей
Если ты внимательно изучал формы симфони, то наверно знаешь что понятия "форма" и "элемент формы" там описываются одним классом (Form). Просто в первом случае он привязан к сущности, а во втором - к ее полю. Form могут произвольно вкладываться друг в друга, то есть мы можем сделать поле для:
- вложенной сущности (при связях многие-к-1 и 1-к-1)
- коллекции вложенных сущностей (CollectionType)
В твоем случае многоэтажная форма должна строиться поверх дерева объектов. Очевидно что корневой объект тут Тест, он содержит коллекцию Вопросов, они могут содержать Варианты Ответов.
Соответственно, нам понадобится несколько вложенных друг в друга форм.
Так как вопросы динамически добавляются, и у них переключается тип, без JS вряд ли обойдется (даже если обойдется, это будет не очень удобно использовать).
Какие тут есть сложности? Ты их не сформулировал, придется формулировать мне:
- вопросы бывают разных типов (назовем это "полиморфность вопросов"). Форма на сервере должна корректно воссоздавать их при обработке данных и корректно выводить на странице. Очевидно, что это обозначает наследование в таблице и в зависимости от выбранного способа наследования (STI, ClTI, CoTI) будут разные реализации форм. Возможно что симфониевские формы из коробки не поддерживают некоторые варианты. Тогда тебе придется их расширить - например написав новый виджет формы "полиморфная коллекция", расширив существующий CollectionType или добавив "адаптер" между CollectionType и дочерними формами
Вот например я нагуглил такое:
https://gist.github.com/merk/3058342
https://github.com/infinite-networks/InfiniteFormBundle#polycollection
Там сделан кастомный виджет для коллекции из разнотипных сущностей. Но у меня есть и другая идея: может можно сделать "адаптер" который ставится между стандартным CollectionType и разнотипными вопросами. Может быть, это позволит избежать дублирования кода из collectionType, а также позволит использовать адаптер и для других случаев (полиморфная дочерняя сущность не в коллекции).
Я не советую спешить его копировать. Ты как минимум должен понимать каждую строчку в нем. Для этого, разумеется, потребуется понимание архитектуры symfony forms и без изучения исходников тут вряд ли получится обойтись.
- безопасное редактирование. Мы можем сделать так: удалять все вопросы, которые есть в базе, но которых нет в пришедших данных. А можем - удалять только те вопросы, для которых пришел флаг "delete". Очевидно второй подход чуть безопаснее и защищает от программистских ошибок. Также, если его реализовать в виде чекбокса (который можно стилизовать как кнопку например), как бонус, мы можем реализовать отмену удаления до отправки формы.
- еще проблема - как интегрировать яваскрипт-код добавления вопроса с кодом вывода форм симфони. Тут все относительно просто: во-первых, мы можем выводить форму вручную, по полям, во-вторых, мы можем сделать вопрос-пустышку и отрендерить форму с ним в JS-шаблон. И при добавлении нового вопроса брать шаблон и вставлять в код. Погугли symfony collection type dynamically add
- как интегрировать JS для переключения типа вопроса без потери введенных данных. Вот это интересный вопрос. Если бы мы использовали для вопроса STI то могли бы сделать форму содержащие все поля для всех типов, и просто скрывать их при переключении. Если же мы используем CoTI и несколько форм .... Может, можно сделать 1 форму, работающую с любым типом вопроса и так же скрывать поля? Вариант со скрытием лишних полей конечно лучше всего, так как данные не теряются. При пересоздании формы, даже если ты будещь руками копировать данные, есть много нюансов: теряются обработчики событий, теряется позиция прокрутки в текстареа и состояние сложных виджетов (wysiwyg редактора, виджетов с кастомным яваскриптом). Если ты хочешь качественно работающую форму, мне кажется, от пересоздания надо держаться как от огня
Также, еще есть вариант отказаться от серверного рендеринга в пользу клиентского. Сервер дает только данные о форме, а сама форма создается и динамически модифицируется с помощью клиенткского фреймворка вроде angular, knockout или react. Но это получается очень сложно, ведь фактически ты должен написать клиентское приложение и поддерживать его параллельно с серверной частью. Лучше наверно обойтись тут простыми средствами.
- как что-то менять в рендеринге форм. Ну тут ответ стандартный - symfony form theming. Там можно переопределить любой из шаблонов.
В общем, хотелось бы услышать конкретные вопросы, что именно сложно, а то приходится угадывать и длинные телеги писать.
Форма в Симфони строится поверх какой-то модели. Модель может быть 2 видов:
- массив или вложенная структура из массивов (не одобряю для данного случая)
- сущность или дерево (вложенная структура) из сущностей
Если ты внимательно изучал формы симфони, то наверно знаешь что понятия "форма" и "элемент формы" там описываются одним классом (Form). Просто в первом случае он привязан к сущности, а во втором - к ее полю. Form могут произвольно вкладываться друг в друга, то есть мы можем сделать поле для:
- вложенной сущности (при связях многие-к-1 и 1-к-1)
- коллекции вложенных сущностей (CollectionType)
В твоем случае многоэтажная форма должна строиться поверх дерева объектов. Очевидно что корневой объект тут Тест, он содержит коллекцию Вопросов, они могут содержать Варианты Ответов.
Соответственно, нам понадобится несколько вложенных друг в друга форм.
Так как вопросы динамически добавляются, и у них переключается тип, без JS вряд ли обойдется (даже если обойдется, это будет не очень удобно использовать).
Какие тут есть сложности? Ты их не сформулировал, придется формулировать мне:
- вопросы бывают разных типов (назовем это "полиморфность вопросов"). Форма на сервере должна корректно воссоздавать их при обработке данных и корректно выводить на странице. Очевидно, что это обозначает наследование в таблице и в зависимости от выбранного способа наследования (STI, ClTI, CoTI) будут разные реализации форм. Возможно что симфониевские формы из коробки не поддерживают некоторые варианты. Тогда тебе придется их расширить - например написав новый виджет формы "полиморфная коллекция", расширив существующий CollectionType или добавив "адаптер" между CollectionType и дочерними формами
Вот например я нагуглил такое:
https://gist.github.com/merk/3058342
https://github.com/infinite-networks/InfiniteFormBundle#polycollection
Там сделан кастомный виджет для коллекции из разнотипных сущностей. Но у меня есть и другая идея: может можно сделать "адаптер" который ставится между стандартным CollectionType и разнотипными вопросами. Может быть, это позволит избежать дублирования кода из collectionType, а также позволит использовать адаптер и для других случаев (полиморфная дочерняя сущность не в коллекции).
Я не советую спешить его копировать. Ты как минимум должен понимать каждую строчку в нем. Для этого, разумеется, потребуется понимание архитектуры symfony forms и без изучения исходников тут вряд ли получится обойтись.
- безопасное редактирование. Мы можем сделать так: удалять все вопросы, которые есть в базе, но которых нет в пришедших данных. А можем - удалять только те вопросы, для которых пришел флаг "delete". Очевидно второй подход чуть безопаснее и защищает от программистских ошибок. Также, если его реализовать в виде чекбокса (который можно стилизовать как кнопку например), как бонус, мы можем реализовать отмену удаления до отправки формы.
- еще проблема - как интегрировать яваскрипт-код добавления вопроса с кодом вывода форм симфони. Тут все относительно просто: во-первых, мы можем выводить форму вручную, по полям, во-вторых, мы можем сделать вопрос-пустышку и отрендерить форму с ним в JS-шаблон. И при добавлении нового вопроса брать шаблон и вставлять в код. Погугли symfony collection type dynamically add
- как интегрировать JS для переключения типа вопроса без потери введенных данных. Вот это интересный вопрос. Если бы мы использовали для вопроса STI то могли бы сделать форму содержащие все поля для всех типов, и просто скрывать их при переключении. Если же мы используем CoTI и несколько форм .... Может, можно сделать 1 форму, работающую с любым типом вопроса и так же скрывать поля? Вариант со скрытием лишних полей конечно лучше всего, так как данные не теряются. При пересоздании формы, даже если ты будещь руками копировать данные, есть много нюансов: теряются обработчики событий, теряется позиция прокрутки в текстареа и состояние сложных виджетов (wysiwyg редактора, виджетов с кастомным яваскриптом). Если ты хочешь качественно работающую форму, мне кажется, от пересоздания надо держаться как от огня
Также, еще есть вариант отказаться от серверного рендеринга в пользу клиентского. Сервер дает только данные о форме, а сама форма создается и динамически модифицируется с помощью клиенткского фреймворка вроде angular, knockout или react. Но это получается очень сложно, ведь фактически ты должен написать клиентское приложение и поддерживать его параллельно с серверной частью. Лучше наверно обойтись тут простыми средствами.
- как что-то менять в рендеринге форм. Ну тут ответ стандартный - symfony form theming. Там можно переопределить любой из шаблонов.
В общем, хотелось бы услышать конкретные вопросы, что именно сложно, а то приходится угадывать и длинные телеги писать.
> Проблемы на всех этапах: хранение шаблонов для составляющих частей этой убер-формы, подстановка значений в эти шаблоны (не хочется подключать шаблонизатор типа handlebars чтобы заменить 3 атрибута), валидация на клиенте, разбор отправленной формы на сервере.
там же динамически добавляются только вопросы и варианты ответов. Нужны шаблоны только для них. Шаблон можно получить, сделав например модель-пустышку и отрендерив.
Зачем ты это постишь? Издеваешься?
Тут никто не может продвинуться дальше задачи про блог студентов, а ты с собеседованиями от яндекса.
Анон выше тестхаб пилит и в симфони ковыряется, другой анон, я помню, reactphp изучал. Все возможно, если приложить достаточно усилий.
>При выполнении задания покажите всё, на что вы способны, как разработчик. Мы будем оценивать все аспекты решения
>Мы будем использовать все аспекты решения
Но у меня их нет!
Что я сделал не так?
У меня вот второй скрин.
Откуда вы лезете? Зачем вам Yii, если не осиливаете прочитать первую страницу документации?
Пикрил.
>>759762
Чистый PHP. И обзаводиться фундаментальными для программиста умениями: искать в документации и гугле, а не бежать отписываться в тред на каждый чих.
Из основных принципов Arch Way:
>"Read The Fucking (or Fine) Manual". This simple message is replied to a lot of new Linux/Arch users who ask about the functionality of a program when it is clearly defined in the program's manual.
>Whereas many GNU/Linux distributions attempt to be more user-friendly, Arch Linux has always been, and shall always remain user-centric. The distribution is intended to fill the needs of those contributing to it, rather than trying to appeal to as many users as possible. It is targeted at the proficient GNU/Linux user, or anyone with a do-it-yourself attitude who is willing to read the documentation, and solve their own problems.
Проблема в том, что легкая помощь становится своего рода наркотиком. Человек, получивший помощь, начинает полагать, что проще задать вопрос, чем поискать самому. И в случае возникновения проблемы в следующий раз, он опять обратиться за помощью, даже если ответ на его вопрос лежит на первых страницах гугла/документации. Только вот человек не задаётся вопросами "Почему они знают то, что знают? Тоже спрашивают кого-то на форумах и смотрят видеоуроки? Почему я этого не знаю и что стоит изменить в моём подходе получения знаний?".
https://gist.github.com/wannabearockstar/9bc790d3b5be4635b1c4
Вот это просто пушка, два дня делать такой круд с всякими дополнениями типа json-апи, только чтобы тебе на собеседовании сказали ВЫ НАМ НЕ ПОДХОДИТЕ. Причем задание это на уровне джуниора, просто какой-то странно большой объем работы для обычного тестового задания.
>два дня делать такой круд с всякими дополнениями типа json-апи, только чтобы тебе на собеседовании сказали ВЫ НАМ НЕ ПОДХОДИТЕ.
А с чего ты взял, что на джуниора? И не знаю, как на Symfony, но Laravel вполне реально подобный проект за пару дней сделать, даже зная только основы фреймворка.
1) Try/catch, как ловить, как написать свою ошибку, как идёт вверх по стеку, если не поймана в одном месте;
2) Приведение типов при сравнении, сложении, конкатенации, отличие ==/===;
3) Отличие or/and от || / $$ в таких местах: if(func1() or func2()) { ... }
4) Магические методы;
5) Традиционно много вопросов по БД и немного по паттернам.
Добавляйте.
В уроке по регулярным выражениям есть задача «Grammar Nazi». Там есть пункт:
>В случае обнаружения ошибки скрипт должен писать сообщение об этом и выводить кусок текста с ошибкой (чтобы было понятно, что не так).
В каких границах выводить "кусок текста с ошибкой"?
https://ideone.com/KmCYnU
На данный момент я реализовал это выводом 10 символов, следующих за шаблоном.
Хочется сделать вывод не обрывая слова, несколько слов перед и после. Это сложно реализовать?
>Хочется сделать вывод не обрывая слова, несколько слов перед и после. Это сложно реализовать?
Нет, погугли "word boundary regex".
>>759920
Блин, а на кого такие требования? Это совсем entry-level. Им, по-видимому, нужен человек для ковыряния CMS, потому что для тех, кто будет писать на фреймворках требования по знаниям в разы выше. Проходил через подобное, потом неделю разгребал джумлу и ушёл, не выдержав.
Самое простое - просили перечислить все типы данных в PHP, рассказать про типизацию, и подобное.
Так же спрашивали в чем разница между $this и self, что такое конструктор и деструктор, иногда просили привести пример, преинкремент и постинкремент - в чем между ними разница, разница между двойными и одинарными кавычками, и еще много вопросов по основам, я уже не вспомню их все.
Так же давали немного коротких задачек, самая знаменитая и легкая - задача на переворот строки без использования функции strrev. Иногда задачу усложняли - просили перевернуть строку без создания новой строки. Еще один раз мне дали задачу на проверку номера телефона, она была похожа на ту, что в учебнике ОПа.
> Им, по-видимому, нужен человек для ковыряния CMS
В итоге так и оказывалось. Внеси свои пять копеек, что будут спрашивать на мидла?
> Иногда задачу усложняли - просили перевернуть строку без создания новой строки.
Это же хардкор если массивы нельзя использовать. В utf-8 разные символы могут кодироваться разным числом байт.
>просили перевернуть строку без создания новой строки.
>массивы нельзя использовать.
Так не массивы а строку же. Решается так же, как и задача из учебника ОПа на палиндром.
Спросили что такое mvc, какие sql команды я знаю и попросили написать на листочке цикл, который выводит числа делящиеся на 3 и 5. Потом допытывали что делает использованный мной оператор % и из какого он языка, лол.
Битрикс-макак
>Битрикс-макак
Норм получаешь? У меня порой бывают мысли забить хуй на все новомодные фреймворки и вообще хабрамирок, устроится на битрикс и интегрировать его в рогах и копытах с 1с-складом.
Социоблядям чисто эстетически приятнее спросить у живого человека, чем читать нудную херню на 1000 страниц, надрыстанную каким-то поехавшим задротом.
Ну и лень не нужно забывать: если мне нужно прописать одну строчечку, то я лучше спрошу на stackoverflow или еще где, чем буду перечитывать гигантский мануал.
Ты утрируешь или я не понял шутки. Вопросы уровня "Чому composer не робит" свидетельствуют лишь о том, что человек слепо и без капли понимания копипастит команды или повторяет за тем, что пишут в видеоуроках, не утруждая себя пониманием того, как хотя бы примерно там всё работает. Нет элементарной базы вроде абсолютных/относительных путей в ФС и так далее. Или пишу на фреймворке, но не открывал ни единой страницы документации, это же вообще пушка.
Это квест на мидла.
И совсем не обязательно делать его полностью, просто тебе предложат меньшую зарплату если что-то конкретное не получится.
Я бы наверное справился (скорее всего наполовину как обычно, никогда не довожу до конца).
После тестхаба уже ничего не страшно.
Я только модуль авторизации пока не знаю, кажется там нужно освоить FOS user bundle, никак не дойду.
>Для книги и жанра реализовать механизм ЧПУ - URL показа соответствующей сущности не должен содержать в себе числовой идентификатор сущности, вместо этого - уникальное транслитирированное имя
Это не знаю как сделать, но наверняка есть какой-нибудь бандл под эти цели.
В общем, вполне адекватное задание на миддла с з.п. около 70 тысяч. Если сделать не до конца, то скорее всего возьмут, но на джуна с 1,5-2 раза меньшей зарплатой, вот и все.
Хотя конечно если в вакансии написано "мы йоба-важная компания, даем вам, ничтожествам, поучаствовать в конкурсе, ответ получат только избранные после выполнения тестового", тогда лучше не тратить время.
Такие на всю жизнь остаются джунами в дно-конторах, смысл о них говорить?
И ты слишком узко мыслишь, ситуации бывают разные.
Тот вопрос по установке yii, после которого ты завелся, задал вообще не программист.
Этот чувак как он о себе рассказывал работает сеошником и редактором в проекте книжного издательства.
Пытается во фреймворк и школьные задачи по php, чтобы не так часто наебывали фрилансеры, у которых иногда заказывает для себя.
И просто пообщаться с "братишками" в треде.
Еще английский и другие иностранные языки многим даются с трудом или вообще не даются.
Если говорить объективно, во всем виновата наша система образования.
На западе чуть ли не с детского сада детям помогают определиться с профессией, что им больше всего нравится, внушают важность учебы.
А что у нас? "Отмотай срок в вузике, тебе потом дадут диплом и сразу с ним работу и хорошую зарплату". А вот нихуя, сейчас ценятся только профессионалы
и преданные своему делу люди, по-крайней мере в айти. Это спрос.
Предложение: вованы, которые на бе услышали влажные мечты студентиков-первокурсников о космических зарплатах программистов, и решившие "вкатиться".
Думаю, стране нужна программа по айти образованию, например повысить еще плату за обучение (хотя она вроде и так не маленькая), и выделить 10% бюджетных мест для олимпиадников-гиков, повернутых на этом деле. Пусть богатые мажоры оплачивают себе корочку, которой потом подотрутся, а талантливые люди получат качественное образование.
Хотя встает вопрос, где брать хороших преподавателей?
>Только вот человек не задаётся вопросами "Почему они знают то, что знают?
Потому что задроты без личной жизни.
Задам-ка я вопрос на форуме, и пойду играть дотку, завтра вернусь, проскроллю кукареканье о гугле и многотомных манах, и наверняка найду АДЕКВАТНЫЙ ответ на мой вопрос, выделю мышкой ответ, нажму контроле цэ, потом открою свой божественный нотепад плюс плюс, контрол вэ, и у меня все получиТЬСЯ)))) Посмеиваясь над лохами, годами пердолящими маны, получаю свои 30 тыщ, нихуя не делая.
Примерно так это работает.
Взвоешь через два месяца.
>>Еще английский и другие иностранные языки многим даются с трудом
Если человеку даётся с трудом английский, то любой язык программирования ему не дастся вообще. Без английского в индустрии делать нечего, сейчас дела чуть лучше, чем было лет 5 назад, но тем не менее.
>>и пойду играть дотку
сказал не-задрот с личной жизнью.
Вы ударились в какую-то философию. Если у человака стоит задача "сделать вчера", у него в данный конкретный момент нет времени разрывать 1000 страничный мануал. Он спросит на стековерфлоу или в ирк канале, получит ответ (не всегда, но часто), и сделает работу.
>только чтобы тебе на собеседовании сказали ВЫ НАМ НЕ ПОДХОДИТЕ
>НО НАМ ПОДХОДИТ ВАШЕ ВЫПОЛНЕННОЕ ЗАДАНИЕ
Я приблизительно это и имел ввиду.
Тащемта дотка насколько я слышал очень социальна, все с микрофонами, можно знакомиться с телачками))
>>760079
Ты не можешь отличить рабочий проект от демонстрационного хелловорлда?
Хотя да, бывает и такое, что под видом тестового дно-конторы подсовывают свои текущие таски.
Но это же легко задетектить, хотя оказывается не для всех.
Спасибо!
Я так и подумал, что в этом разница в папках.
>не бежать отписываться в тред на каждый чих.
У нас же тут своя атмосфера, братишка, подобные вопросы я сам на будущее всегда читаю (хотя я всё полностью читаю в тредах, даже длинные посты ОПа, адресованные другим). Что-то лучше закрепляется в памяти.
Что-то, кстати, уже пару месяцев не было молодой крови - не появлялись кучи постов с просьбами подсказать решение задачи про Айфон и Айпад.
http://phpfaq.ru/pdo
по моему там написано то же самое что я всегда говорю. У меня есть урок по исключениям: https://github.com/codedokode/pasta/blob/master/php/exceptions.md
catch + die пиушт просто для примера чтобы показать что исключение можно поймать, а люди бездумно копируют этот код в реальные приложения.
Не работает, потому что вместо ORDER BY name DESC вставляет ORDER BY 'name' 'DESC'
Что делать? Может query builder использовать? И если да, то какой?
>Что делать?
Использовать prepared statements только там, где нужно. Попробуй поменять ORDER BY :sort на ORDER BY {$sort}, и из параметров execute убери $sort.
С помощью плейсхолдеров можно подставлять только значения (числа и строки) но нельзя подставлять идентификаторы (названия) полей и таблиц. Названия полей придется вставлять напрямую в запрос, при этом проверяя их по белому списку разрешенных полей.
https://github.com/timrene/php-link-list/blob/master/решение задач.md
>1) Try/catch, как ловить, как написать свою ошибку, как идёт вверх по стеку, если не поймана в одном месте;
>2) Приведение типов при сравнении, сложении, конкатенации, отличие ==/===;
>3) Отличие or/and от || / $$ в таких местах: if(func1() or func2()) { ... }
>4) Магические методы;
Я читал про все это 2 раза, но все забыл. Когда использовал - смотрел в документацию. Это нормально? Сколько раз вы повторяете для запоминания? Сколько километров кода надо написать чтобы запомнить такие простые вещи?
я глубоко убеждён, что надо не запоминать всё подряд, а запоминать где посмотреть всё, что нужно в данный момент.
Иначе можно просто поехать.
>>760597
другое дело, что паста, которую ты отцитировал, должна быть у уме постольку, поскольку это как бе совсем алфавит практически.
Ну я только только пытаюсь все осмыслить чтобы в терминах не путаться. Веб огромный. PHP тоже не маленький. Такое ощущение что я книгу в голове пишу. Я сейчас стал понимать авторов книг. У них столько знаний что они без запинаний могут главы книг писать. В каждом предложении по несколько посылов. Все посылы связаны между собой. Вот сижу сейчас читаю официальную документацию PHP. Там 26 разделов в справочнике функций, 19 разделов в справочнике языка. В каждом разделе по несколько подразделов. Никто мне не говорит что вот эта штука в 100 раз нужнее той штуки, приходится равномерно все читать. Причем задача горит, делать надо быстрее, нет времени вчитываться в слова и обдумывать их. А ведь если вчитаться в слова, то мы попадем в вики, математику, информатику, историю и т.д. И это только PHP, а ведь еще веб надо учить. Тысячи их.
И еще добавлю. Вот то что ты знал, анон, еще 5-10 лет назад, я только вчера прочитал например. А если мысль архиважная? Ты прожил 5 лет и у тебя знания поверх этой мысли накладывались как надо, а у меня бардак из мыслей только вчера начал утихать. Я от мыслей так устал, что отгоняю их, потому как считаю бесполезными. Все равно рассуждение стройное не получается построить хотя бы из нескольких связанных предложений. Куча обрывков, вспышки памяти, стресс и угнетение от мыслей что я неосилятор и быть мне хелловордщиком всю жизнь и не дойти до уровня макаки. А я ведь самоучка, учился урывками в свободное время между работой на стройке и получением вышки. Вот сейчас повезло, устроился сайты на цмсках клепать, время появилось свободное чтобы за пёкой сидеть. Жалею что раньше не мог набраться смелости, лет 5-6 упустил. Ребята молодые рядом сидят, разбираются больше чем я в некоторых вопросах, но профессию не уважают - злоба берет от них.
> Вот сижу сейчас читаю официальную документацию PHP. Там 26 разделов в справочнике функций, 19 разделов в справочнике языка. В каждом разделе по несколько подразделов. Никто мне не говорит что вот эта штука в 100 раз нужнее той штуки, приходится равномерно все читать.
Поздравляю, ты познал всю говенность библиотеки PHP.
Уволился с работы прошлой, есть 1-2 месяца, чтоб выучить php, чтобы устроится на 35к в ДС.
Не буду спрашивать, реально ли это, т.к. надеюсь на лучшее. Так вот, к сути. Учил кто-либо из ва успешно по 8-12 часов в день? ПОнимаю ,что все субъективно, но просто интересно.
Есть еще всякие ноотропы, но планирую ограничиться 3-4 кружками кофе.
Я считаю что документация PHP очень понятна и может выступать как образец документации по ЯП. Говеность библиотеки? Почему?
>успешно по 8-12 часов в день
Максимум 6, в идеале вообще 4. Остальное время подтягивай английский, листай документацию, курсы на ютубчике смотри и хорошо высыпайся. Вообще держи себя в тонусе. Не надо себя истязать, ты выгоришь через неделю и получится только хуже.
А в какой срок можно, по-твоему уложиться, чтобы начать что-то зарабатывать? Немного платина, да.
>Максимум 6, в идеале вообще 4.
Лел, 99% вакансий пхп-макаки имеет график с 9 до 18, то есть 8 часов, учитывая обед.
До обеда очень продуктивно работаю, потом в пол силы ковыряю, совсем сложное оставляя на завтра. Может я дефектный, хз.
>>760751
Я около месяца просиживал на learn.javascript.ru, дошел до прототипов и устроился стажером за бесплатно в вебстудию. Там в течении полугода вкачивался до среднестатистической макаки.
Хм, вот я надеялся на что-то такое: за месяц-два доучиться до уровня макаки ебаной.
Ты зря так к макакам относишься, малыш. Макака - это вполне сносный программист, которого держать можно. Просто ему может не хватать опыта, базы и IQ.
Хелловордщик - это копипастер обычный.
За 1-2 месяца ты даже до азов хелловорда не дойдешь.
>>760770
>течении полугода вкачивался до среднестатистической макаки.
Очень сомневаюсь. До хелловордщиков вкачался наверное?
>За 1-2 месяца ты даже до азов хелловорда не дойдешь.
Да вы что блять стебетесь? На уровне жуниора сложности только с ООП у более-менее умного анона должны возникать. Вы таким образом хотите отпугнуть потенциальных конкурентов?
130IQ, учу пых 7 месяцев. Уровень начинающего хелловордщика. До ООП как до луны пешком. Задавай свои ответы.
>>За 1-2 месяца ты даже до азов хелловорда не дойдешь.
я не оч понимаю, что ты вкладываешь в поянтие "азы хеллоуворлда".
Проблема вся в том, что лично я, к примеру, не в состоянии запомнить что-либо, что не подтверждено практикой. И прав ОП красавчег, заставляя нубюов решать задачи. Без этого не имеет вообще никакого смысла читать спецификации и маны.
А абстрактную задачу мало кто в состоянии себе приудмать сам. Поэому ОПу респект.
>что ты вкладываешь в поянтие "азы хеллоуворлда"
Неуверенное владение синтаксисом, парадигмой, основными алгоритмами. Стадия изучения основ инструментария. До решения практических задач без дедлайнов знаний не хватает.
>заставляя нубюов решать задачи
Да тут не такие уж и нубы сидят так то. Я, например, привык к компилируемой статике. Для меня модель исполнения и динамика в новизну были. ООП не проходил по-человечески, хотя могу функциональное GUI приложение с ООП на С++ и Delphi написать. Ассемблер дрочил, алгоритмы, ОСы, сети, архитектуру, CS. Веб в новинку, в вебе все не так, в вебе все динамично, быстро, разбросанно.
>А абстрактную задачу мало кто в состоянии себе приудмать сам.
Да легко. По 10 штук за день. Только техзадание надо уметь писать и в разумное время укладывать как ОП. У ОПа опыт владения русским языком и стеком, ОП примерно знает что сколько времени займет.
Table Gateway.
Пишем класс - соотв. таблице БД. Свойства - поля таблицы, методы - допустим, КРУД. Данные подаются через аргументы методов.
Чем это вот всё будет отличаться от подклбчаемого реквайром файла mytable.php со списком функций КРУДа? Без ООП?
Я, не подумай, не выступаю против ООП. Я просто хочу понять, ЧЕМ ооп лучше процедурного стиля.
чо ты гонишь, если ты на плюсах писал, ты ПХП не осилишь чтоль? Бро, ну ты ваще.
Осиливаю со скрипом. Знаешь как блевать и дропнуть хочется? Питоню и скалаёбю для отдыха от пыха. Небо и земля.
>Очень сомневаюсь. До хелловордщиков вкачался наверное?
Функциональное программирование, знание кмски и командная разработка в общем - миграции, контроль версий. Примерно тогда прошла паника при новых тикетах и мог браться за практически любую поступающую в отдел задачу. Для справки, на пике вакансии на ту самую среднестатистическую макаку, чтоб ты понимал о чем сейчас идет речь.
>знание кмски и командная разработка в общем - миграции, контроль версий.
Пффф
>Функциональное программирование
Колбеки - это малая часть функциональщины.
>Для справки, на пике вакансии на ту самую среднестатистическую макаку, чтоб ты понимал о чем сейчас идет речь.
Для макаки список требований. Ты каким образом в эту сферу проник? Полгода на полный стек (да даже на 25% от JS)? Не верю.
Тут нужно особое десантное подразделение.
https://www.livecoding.tv/livestreams/php/
Иди лучше кинца наверни, потом приходи.
Наткнулся на этот курс. Что скажете о нем?
Ни в коем случае!
Я там как Александров отписывался пару месяцев назад.
Сказать, что это плохо - ничего не сказать.
Это просто отвратительно.
Человек путает всё на свете, копипастит код, при этом путает сами файлы - был один код в этом файл - хоп, он вставляет другой.
Не наглядно, бестолково, не для новичков.
Выше есть слитый курс про Yii2 - он платный был. Такое же всё поверхностное, непонятное.
Капитальные стандарты преподавания не соблюдаются.
Не рекомендую Гикбрейнс вообще, короче.
Ну я скептично отношусь к платному, но тут увидел, что беплатно и решил, что возможно годно.
Я понял тебя.
Буду читать шапку.
К сожалению, нет.
От себя рекомендую учебник ОПа и книгу Робина Никсона "Создаем динамические веб-сайты с помощью PHP, MySQL и JavaScript" (первая ссылка в Гугле на PDF в ВКонтакте).
Там самая выжимка, поэтому учебник ОПа должен быть к этому времени полностью проработан, все основные понятия должны быть уяснены.
В конце там проект без шаблона, но дающий базовые представления о всех основных технологиях веба. К тому времени ты и сам будешь понимать, что там может быть улучшено.
>>760891
Согласен с обоими пунктами.
Хехе. Я тебя понимаю. Я вообще обожаю перл и невроятно клёвый фреймврк Mojolicious, а ПХП занимаюсь по работе главным образом, да и то, думаю, плюну и перепишу перле на Mojo.
Но в наше время ПХП надо знать. Просто хотя бы для того чтобы разбираться в чужом коде.
без питона-то, кстати, можно обойтись
Как человек, который читал Никсона до того, как наткнулся на учебник ОПа - не рекомендую. По крайней мере не в том порядке, в котором ты советуешь. После учебника ОПа книжка никсона мало в чем может тебя просветить. Да, у ОПа нет ничего по основам БД или там по серверной части, вроде пост запросов и форм и тому подобного, но к моменту как ты его пройдешь, искать такие вещи проще в интернете, чем читать устаревшую книгу.
Там для закрепления основных понятий - отлично.
Для перехода к базам данных, для создания динамики.
Я советую его только для закрепления того, что есть у ОПа.
>без питона-то, кстати, можно обойтись
Питон и Скала как разминка для глаз идет. Скала выносит мозг хорошо, питон и руби олицетворяют скрипты.
Пакистанец стримит. Обкуренный походу, поет. Пьет воду так что кажется что это последняя вода в его жизни и он работает фрилансером только чтобы хотя бы заработать на эту воду.
Нет, не всё. На ООП можно применить множество решений, паттернов - процедурно уже никто не программирует. Поэтому я и привел первым аргументом именно читабельность, потому что понятный код, это наше альфа и омега.
К тому же ООП лучше подходит в плане проектирования.
MVC
Почему не MCV?
Ведь надо идти последовательно: модель -> контроллер -> вид.
Вы хотите сказать, FCMCV?
Иначе NULL использует вас!
http://ahrameev.ru/article/nikogda-ne-ispolzujjte-null.html
>>747423
>>755118
ты используешь константу-массив, которая пока доступна только в новом php. Пришлось исправлять, чтобы запустить.
В странице ошибки нет тега meta charset и вместо русских букв выводится абракадабра.
В sphinx.conf неправильно указаны пути к файлам с индексами - ну кто кладет индексы в /etc? Он для конфигов, а для таких вещей есть /var/lib/sphinx например. Но я бы тебе советовал сделать еще гибче и сделать, чтобы префикс для всех путей сфинкса задавался бы через переменную окружения, например SPHINX_ROOT. То есть мы задаем SPHINX_ROOT=/home/ivan/sphinx и все индексы, логи пиушутся туда.А если она не задана - то например в общесистемные папки. Это позволяет не использовать общесистемный сфинкс, а завести для проекта отдельный демон.
Чтобы это реалилизовать, тебе надо использовать фичу сфинкса которая позволяет делать конфиг на скриптовом языке, например php: http://sphinxsearch.com/blog/2013/11/05/sphinx-configuration-features-and-tricks/
Сам поиск у меня падает с сообщением 'exception 'ErrorException' with message 'PDO::__construct(): Server sent charset (0) unknown to the client. Please, report to the developers'
Для того, чтобы при большом числе файлов эффективно работал поиск последних/популярных, нужны индексы по соотв. колонкам. Нужны индексы для эффективной выборки комментариев. Прочитай для начала ознакомительный урок http://ruhighload.com/post/Работа+с+индексами+в+MySQL
На таблицы надо проставить больше ограничений. Например, у комментариев к одному файлу parent_path не может повторяться - это надо зафиксировать. В постгресе есть уникальные ключи, а также правило CHECK которое тебе стоит изучить, хотя я не вижу где его можно тут применить.
В комментариях при отпраке аяксом содержится XSS. Попробуй добавить например комментарий Hello <s>world</div></div></div>
Переводы строк теряются в комментариях.
Формат дат в комментарии - американский и чуждый русскоязычному интерфейсу сайта.
"1 скачиваний" - что это за насмешка над правилами русского языка? Ты же делал задачу про числа прописью? Кроме варианта писать функцию склонения руками, есть еще вариант раскурить расширение Intl: http://php.net/manual/ru/book.intl.php
Оно скопировано с Явы и там есть функция, которая позволяет делать в строку подстановки с учетом правил языка. Например: http://php.net/manual/ru/messageformatter.formatmessage.php
> echo MessageFormatter::formatMessage("en_US", "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree\n", array(4560, 123, 4560/123));
> $fmt = new MessageFormatter("en_US", "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree");
> echo $fmt->format(array(4560, 123, 4560/123));
Предлагаю тебе раскурить документацию, если знаешь английский, и перевести все сообщения на использование этого замечательного объекта. Дополнительно, если есть желание и время, можешь сделать 2- (или более)-язычную версию сайта. Никаких велосипедов - использовать надо Intl и gettext. Для gettext есть готовые редакторы переводов, например, poedit. Уверен, знание основ локализации будет хорошо смотреться в твоем резюме. Страницы с разным языком должны отличаться в URL. Пользователь должен иметь возмодность переключить язык. Если пользователь зашел на сайт впервые, мы определяем его язык (по IP страны или заголовкам от браузера) и если он не совпадает с текущим, выводим вежливое скромное напоминание, что сайт доступен на родном языке. Если он игнорирует его, перестаем спамить на какое-то время.
> 'displayErrorDetails' => true,
Я думаю это можно брать из значения display_errors
> set_error_handler(function ($errno, $errstr, $errfile, $errline)
> {
> if (!(error_reporting() & $errno)) {
Разве Слим этого не делает?
Создание контейнера наверно тоже стоило вынести в app/init.php. Идея бутстрап файла в том, что мы подключаем один файл и получаем рабочее окружение.
CsrfGuard на мой взгляд мутный, хоть он и сделан по рекомендациям OWASP. Требует сессий. На каждый запрос генерирует токены, даже если они потом не используются.
Особо коварный момент: сессии умирают за 30 минут неиспользоания. Если пользователь откроет форму, оставит на 30 минут и попробует отправить, то получит страницу ошибки, и потеряет введенные данные.
Есть и плюсы: например благодаря одноразовым токенам не получится повторно отправить запрос с теми же данными. Хотя это может быть и недостатком: у пользователя плохая связь, не получил ответа на форму, отправил форму повторно и потерял данные.
Не хочешь сделать по мотивам этого более простой гвард на куках, с фичами:
- произвольный срок жизни куки
https://github.com/foobar1643/filehosting/blob/master/templates/file.twig#L22
> {% if file.getAuthToken == authToken %}
Вот тут ты в шаблон вынес логику проверки прав доступа. Должен быть где-то метод, вроде $someHelper->canManageFile($user, $file) или $someHelper->canManageFile($file).
https://github.com/foobar1643/filehosting/blob/master/app/Helper/IdHelper.php
Этот объект реализован неправильно на мой взгляд. Ты его сделал сервисом, то есть он существует в одном экземпляре. Но внутри он хранит информацию о каком-то конкретном файле. Это значит, что в некоторые моменты времени в нем нет этой информации и ни в какой момент времени в нем нелзя хранить информацию о нескольких файлах. То есть в одном месте кода мы засовываем туда файл, и в другом месте кода что-то ломается.
Это не может быть сервис. У сервиса не должно быть такого непредсказуемого состояния. Это должен быть класс-"сущность", который создается в нужном числе экземпляров, по одному на каждый файл.
Также, у этого класса есть недостаток. В промежуток между созданием и вызовом analyzeFile его состояние пусто. Из-за этого ты городишь везде нелепые isset. Лучше отказаться от пустого состояния и передавать файл в конструктор. Код упростится (если конечно у тебя нет цели поддерживать сценарий, когда файл отстутсвует).
https://github.com/foobar1643/filehosting/blob/master/app/Controller/FileController.php#L25
> if($file == false) {
> throw new \Slim\Exception\NotFoundException($request, $response);
> } else {
> много строк
> }
В такой ситуации блок else только ухудшает читаьельность кода лишним отступом, он не нужен. Везде где в if в итоге стоит выход из функции (return/die/throw) - else не нужен.
https://github.com/foobar1643/filehosting/blob/master/app/Controller/FileController.php#L39
> 'commentErrors' => isset($args['commentErrors']) ? $args['commentErrors'] : NULL
Вот тут немного мутный момент. Ты не рассмтривал вариант сделать обработку постинга комментария в том же контроллере что выводит страницу файла, по стандартному алгоритму обработки форм? При этом ты точно так же мог бы вынести код в CommentController (или просто в отдельный метод), только тут FileController будет вызывать CommentController, а не наоборот. не лучше ли такой вариант?
>>747423
>>755118
ты используешь константу-массив, которая пока доступна только в новом php. Пришлось исправлять, чтобы запустить.
В странице ошибки нет тега meta charset и вместо русских букв выводится абракадабра.
В sphinx.conf неправильно указаны пути к файлам с индексами - ну кто кладет индексы в /etc? Он для конфигов, а для таких вещей есть /var/lib/sphinx например. Но я бы тебе советовал сделать еще гибче и сделать, чтобы префикс для всех путей сфинкса задавался бы через переменную окружения, например SPHINX_ROOT. То есть мы задаем SPHINX_ROOT=/home/ivan/sphinx и все индексы, логи пиушутся туда.А если она не задана - то например в общесистемные папки. Это позволяет не использовать общесистемный сфинкс, а завести для проекта отдельный демон.
Чтобы это реалилизовать, тебе надо использовать фичу сфинкса которая позволяет делать конфиг на скриптовом языке, например php: http://sphinxsearch.com/blog/2013/11/05/sphinx-configuration-features-and-tricks/
Сам поиск у меня падает с сообщением 'exception 'ErrorException' with message 'PDO::__construct(): Server sent charset (0) unknown to the client. Please, report to the developers'
Для того, чтобы при большом числе файлов эффективно работал поиск последних/популярных, нужны индексы по соотв. колонкам. Нужны индексы для эффективной выборки комментариев. Прочитай для начала ознакомительный урок http://ruhighload.com/post/Работа+с+индексами+в+MySQL
На таблицы надо проставить больше ограничений. Например, у комментариев к одному файлу parent_path не может повторяться - это надо зафиксировать. В постгресе есть уникальные ключи, а также правило CHECK которое тебе стоит изучить, хотя я не вижу где его можно тут применить.
В комментариях при отпраке аяксом содержится XSS. Попробуй добавить например комментарий Hello <s>world</div></div></div>
Переводы строк теряются в комментариях.
Формат дат в комментарии - американский и чуждый русскоязычному интерфейсу сайта.
"1 скачиваний" - что это за насмешка над правилами русского языка? Ты же делал задачу про числа прописью? Кроме варианта писать функцию склонения руками, есть еще вариант раскурить расширение Intl: http://php.net/manual/ru/book.intl.php
Оно скопировано с Явы и там есть функция, которая позволяет делать в строку подстановки с учетом правил языка. Например: http://php.net/manual/ru/messageformatter.formatmessage.php
> echo MessageFormatter::formatMessage("en_US", "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree\n", array(4560, 123, 4560/123));
> $fmt = new MessageFormatter("en_US", "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree");
> echo $fmt->format(array(4560, 123, 4560/123));
Предлагаю тебе раскурить документацию, если знаешь английский, и перевести все сообщения на использование этого замечательного объекта. Дополнительно, если есть желание и время, можешь сделать 2- (или более)-язычную версию сайта. Никаких велосипедов - использовать надо Intl и gettext. Для gettext есть готовые редакторы переводов, например, poedit. Уверен, знание основ локализации будет хорошо смотреться в твоем резюме. Страницы с разным языком должны отличаться в URL. Пользователь должен иметь возмодность переключить язык. Если пользователь зашел на сайт впервые, мы определяем его язык (по IP страны или заголовкам от браузера) и если он не совпадает с текущим, выводим вежливое скромное напоминание, что сайт доступен на родном языке. Если он игнорирует его, перестаем спамить на какое-то время.
> 'displayErrorDetails' => true,
Я думаю это можно брать из значения display_errors
> set_error_handler(function ($errno, $errstr, $errfile, $errline)
> {
> if (!(error_reporting() & $errno)) {
Разве Слим этого не делает?
Создание контейнера наверно тоже стоило вынести в app/init.php. Идея бутстрап файла в том, что мы подключаем один файл и получаем рабочее окружение.
CsrfGuard на мой взгляд мутный, хоть он и сделан по рекомендациям OWASP. Требует сессий. На каждый запрос генерирует токены, даже если они потом не используются.
Особо коварный момент: сессии умирают за 30 минут неиспользоания. Если пользователь откроет форму, оставит на 30 минут и попробует отправить, то получит страницу ошибки, и потеряет введенные данные.
Есть и плюсы: например благодаря одноразовым токенам не получится повторно отправить запрос с теми же данными. Хотя это может быть и недостатком: у пользователя плохая связь, не получил ответа на форму, отправил форму повторно и потерял данные.
Не хочешь сделать по мотивам этого более простой гвард на куках, с фичами:
- произвольный срок жизни куки
https://github.com/foobar1643/filehosting/blob/master/templates/file.twig#L22
> {% if file.getAuthToken == authToken %}
Вот тут ты в шаблон вынес логику проверки прав доступа. Должен быть где-то метод, вроде $someHelper->canManageFile($user, $file) или $someHelper->canManageFile($file).
https://github.com/foobar1643/filehosting/blob/master/app/Helper/IdHelper.php
Этот объект реализован неправильно на мой взгляд. Ты его сделал сервисом, то есть он существует в одном экземпляре. Но внутри он хранит информацию о каком-то конкретном файле. Это значит, что в некоторые моменты времени в нем нет этой информации и ни в какой момент времени в нем нелзя хранить информацию о нескольких файлах. То есть в одном месте кода мы засовываем туда файл, и в другом месте кода что-то ломается.
Это не может быть сервис. У сервиса не должно быть такого непредсказуемого состояния. Это должен быть класс-"сущность", который создается в нужном числе экземпляров, по одному на каждый файл.
Также, у этого класса есть недостаток. В промежуток между созданием и вызовом analyzeFile его состояние пусто. Из-за этого ты городишь везде нелепые isset. Лучше отказаться от пустого состояния и передавать файл в конструктор. Код упростится (если конечно у тебя нет цели поддерживать сценарий, когда файл отстутсвует).
https://github.com/foobar1643/filehosting/blob/master/app/Controller/FileController.php#L25
> if($file == false) {
> throw new \Slim\Exception\NotFoundException($request, $response);
> } else {
> много строк
> }
В такой ситуации блок else только ухудшает читаьельность кода лишним отступом, он не нужен. Везде где в if в итоге стоит выход из функции (return/die/throw) - else не нужен.
https://github.com/foobar1643/filehosting/blob/master/app/Controller/FileController.php#L39
> 'commentErrors' => isset($args['commentErrors']) ? $args['commentErrors'] : NULL
Вот тут немного мутный момент. Ты не рассмтривал вариант сделать обработку постинга комментария в том же контроллере что выводит страницу файла, по стандартному алгоритму обработки форм? При этом ты точно так же мог бы вынести код в CommentController (или просто в отдельный метод), только тут FileController будет вызывать CommentController, а не наоборот. не лучше ли такой вариант?
>>747423
>>755118
В валидаторе ты валидируешь массив: https://github.com/foobar1643/filehosting/blob/master/app/Validation/Validation.php#L34 А ведь можно валидировать модель комментария.
Это имеет такие преимущества:
- валидируем объект с полями и методами вместо бесформенного нетbпизированного массива. Не надо ставить кучу isset.
- мы можем провалидировать любой объект комменатрия, не обязательно пришедший из формы. код более универсальный и менее спутанный
Конечно форма и модель не всегда совпадают. Например при регистрации пользователя в форме есть 2 поля пароля, а в модели - поле хеша. Потому в фреймворках часто у формы есть своя внутренняя модель. Но тут я думаю это не проблема.
А как ты считаешь, что лучше?
https://github.com/foobar1643/filehosting/blob/master/app/Controller/CommentController.php#L35
> if(isset($getVars['ajax']) && $getVars['ajax'] == "true") {
Опять массиво-ориентированное программирование. Там у request есть что-то вроде $r->getQuery()->get('ajax');
> print(json_encode($jsonResponse));
по моему это неправильно. Ты должен вписать это в тело ответа, а не выводить. То есть $response->withBody или как-то так.
Также, там по моему есть класс JsonResponse (или нет, только в сифмони?) ну или ты можешь сделать метод $this->sendJson чтобы не копипастить код.
> return $response->withHeader('Location', "/file/" . $args['id']);
тут еще можно прибавлять якорь с id комментария и с помощью CSS анимации и :target подсветить новый комментарий.
Далее, тут https://github.com/foobar1643/filehosting/blob/master/app/Controller/CommentController.php#L19 одна функция и в ней стена текста, плохо читаемая. Надо разбивать на части, например:
- функция отдачи json
- функция воссоздающая модель из POST данных
- функция, вызывающая вывод страницы файла
У тебя стена кода получается из-за нежелания разбить задачу на выоскоуровневые части, ты просто пишешь низкоуровневый код, который получается большим и тяжелочитаемым. Если проводить аналогию с рецептами, то вместо
- взять стакан муки
- добавить в него 2 яйца
ты пишешь:
- подойти к шкафу
- открыть дверцу
- осмотреть верхнюю полку в поисках цилиндрического предмета с открытым верхом определенных габаритов
- переместить ладонь к предмету
....
Пройдись сам по другим контроллерами и посмотри, не надо ли разбить код там.
Вот как еще один пример тут:
https://github.com/foobar1643/filehosting/blob/master/app/Controller/DownloadController.php#L39
> if($config->getValue('app', 'enableXsendfile') == 1) {
Вместо низкоровневого кода должен быть вызов if (canUseSendfile()), более того, сам код отдачи файла стоит вынести в хелпер так как он может быть повторно использован где-то еще, и вообще выглядит как отдельная от данного контроллера вещь, а каждый класс, как мы помним, должен заниматься своим делом.
> readfile($filePath);
Там я думаю, есть метод стриминга ответа из файла - изучи-ка PSR, как можно задавать тело HTTP-ответа из файла.
То, что ты принтишь данные вместо того чтобы вставить их в ответ, это не очень соответствует идее фреймворка (что контроллер должен заполнить response), да и тестирование усложнит.
> $fileMapper = $this->container->get('FileMapper');
> $pathingHelper = $this->container->get('PathingHelper');
> $config = $this->container->get('config');
Можно чуть облагородить код если эту ерунду перенести в конструктор контроллера и сохранить сервисы в приватных полях. Хотя, код станет чуть длинее.
Попробуй не сохранять контейнер, а именно в конструкторе извлекать, что тебе надо далее. Может будет чуть лучше.
Вот в SearchController я вижу не очень хороший момент:
https://github.com/foobar1643/filehosting/blob/master/app/Controller/SearchController.php#L40
> $searchIds = $searchGateway->search($searchHelper->escapeString($query), self::RESULTS_PER_PAGE, $offset);
> $searchMeta = $searchGateway->showMeta(); // results count
Во-первых, тут в контроллер из поискового сервиса вытекает знание о формате ответа сфинкса (что он возвращает id и метаданные). Во-вторых, код поиска нельзя использовать повторно где-то еще.
Я думаю, нам нужен примерно такой метод:
search(query, offset, limit) -> ([File], totalCount)
То есть нам нужны сущности, а не их id, и число, а не какие-то метаданные.
https://github.com/foobar1643/filehosting/blob/master/templates/file.twig#L70
> <div class="media" id="comment-dummy" style="display: none;">
для шаблонов есть более интересные решения:
- https://learn.javascript.ru/templates
- https://learn.javascript.ru/template-tag
Сейчас у тебя очень много рутинного кода, который сломается при первой же правке шаблона комментария, вот тут:
> replyForm.children(".form-group").removeClass("has-error");
> replyForm.children(".form-group").children(".real-label").remove();
> replyForm.children(".form-group").attr('id', "reply-form-group")
(хуже того, тут еще и копипаста выражения replyForm.children(".form-group"))
Лучше сделать например так:
<div class="comment-author">%author%</div>
Ты можешь сделать свою велосипедную систему или поискать готовый шаблонизатор тут: https://garann.github.io/template-chooser/
https://github.com/foobar1643/filehosting/blob/master/app/Database/CommentMapper.php#L21
> :file_id_bind",
Я думаю что можно было просто писать :file_id, bind добавлять не надо было. Ну добавил, ладно, пусть будет.
https://github.com/foobar1643/filehosting/blob/master/app/Helper/CommentHelper.php#L16
> public function addComment(Comment $comment)
Тут алгоритм какой-то мутный. Нельзя минимизировать число обращений к БД? Также, многократные операции записи в БД надо оборачивать в транзакцию.
Насчет выборк комментов - ты строишь дерево. А не проще ли делать плоскую структуру? Просто выбирать комменты ORDER BY path?
> $this->normalizePath("{$parentComment->getParentPath()}.{$comment->getId()}"))
Вот это лучше было бы вынести в метод appendToPath(oldPath, newId)
https://github.com/foobar1643/filehosting/blob/master/app/Helper/FileHelper.php#L20
> public function createFile(File $file, $adminFlag)
Надо выносить часть кода в функции. Например, генерация пути к файлу должна быть в конкретном методе, а не размазана по коду.
> $searchGateway->insertRtValue($file->getId(), $file->getName());
тут лучше высокоуровневый метод
indexNewFile($file)
>>747423
>>755118
В валидаторе ты валидируешь массив: https://github.com/foobar1643/filehosting/blob/master/app/Validation/Validation.php#L34 А ведь можно валидировать модель комментария.
Это имеет такие преимущества:
- валидируем объект с полями и методами вместо бесформенного нетbпизированного массива. Не надо ставить кучу isset.
- мы можем провалидировать любой объект комменатрия, не обязательно пришедший из формы. код более универсальный и менее спутанный
Конечно форма и модель не всегда совпадают. Например при регистрации пользователя в форме есть 2 поля пароля, а в модели - поле хеша. Потому в фреймворках часто у формы есть своя внутренняя модель. Но тут я думаю это не проблема.
А как ты считаешь, что лучше?
https://github.com/foobar1643/filehosting/blob/master/app/Controller/CommentController.php#L35
> if(isset($getVars['ajax']) && $getVars['ajax'] == "true") {
Опять массиво-ориентированное программирование. Там у request есть что-то вроде $r->getQuery()->get('ajax');
> print(json_encode($jsonResponse));
по моему это неправильно. Ты должен вписать это в тело ответа, а не выводить. То есть $response->withBody или как-то так.
Также, там по моему есть класс JsonResponse (или нет, только в сифмони?) ну или ты можешь сделать метод $this->sendJson чтобы не копипастить код.
> return $response->withHeader('Location', "/file/" . $args['id']);
тут еще можно прибавлять якорь с id комментария и с помощью CSS анимации и :target подсветить новый комментарий.
Далее, тут https://github.com/foobar1643/filehosting/blob/master/app/Controller/CommentController.php#L19 одна функция и в ней стена текста, плохо читаемая. Надо разбивать на части, например:
- функция отдачи json
- функция воссоздающая модель из POST данных
- функция, вызывающая вывод страницы файла
У тебя стена кода получается из-за нежелания разбить задачу на выоскоуровневые части, ты просто пишешь низкоуровневый код, который получается большим и тяжелочитаемым. Если проводить аналогию с рецептами, то вместо
- взять стакан муки
- добавить в него 2 яйца
ты пишешь:
- подойти к шкафу
- открыть дверцу
- осмотреть верхнюю полку в поисках цилиндрического предмета с открытым верхом определенных габаритов
- переместить ладонь к предмету
....
Пройдись сам по другим контроллерами и посмотри, не надо ли разбить код там.
Вот как еще один пример тут:
https://github.com/foobar1643/filehosting/blob/master/app/Controller/DownloadController.php#L39
> if($config->getValue('app', 'enableXsendfile') == 1) {
Вместо низкоровневого кода должен быть вызов if (canUseSendfile()), более того, сам код отдачи файла стоит вынести в хелпер так как он может быть повторно использован где-то еще, и вообще выглядит как отдельная от данного контроллера вещь, а каждый класс, как мы помним, должен заниматься своим делом.
> readfile($filePath);
Там я думаю, есть метод стриминга ответа из файла - изучи-ка PSR, как можно задавать тело HTTP-ответа из файла.
То, что ты принтишь данные вместо того чтобы вставить их в ответ, это не очень соответствует идее фреймворка (что контроллер должен заполнить response), да и тестирование усложнит.
> $fileMapper = $this->container->get('FileMapper');
> $pathingHelper = $this->container->get('PathingHelper');
> $config = $this->container->get('config');
Можно чуть облагородить код если эту ерунду перенести в конструктор контроллера и сохранить сервисы в приватных полях. Хотя, код станет чуть длинее.
Попробуй не сохранять контейнер, а именно в конструкторе извлекать, что тебе надо далее. Может будет чуть лучше.
Вот в SearchController я вижу не очень хороший момент:
https://github.com/foobar1643/filehosting/blob/master/app/Controller/SearchController.php#L40
> $searchIds = $searchGateway->search($searchHelper->escapeString($query), self::RESULTS_PER_PAGE, $offset);
> $searchMeta = $searchGateway->showMeta(); // results count
Во-первых, тут в контроллер из поискового сервиса вытекает знание о формате ответа сфинкса (что он возвращает id и метаданные). Во-вторых, код поиска нельзя использовать повторно где-то еще.
Я думаю, нам нужен примерно такой метод:
search(query, offset, limit) -> ([File], totalCount)
То есть нам нужны сущности, а не их id, и число, а не какие-то метаданные.
https://github.com/foobar1643/filehosting/blob/master/templates/file.twig#L70
> <div class="media" id="comment-dummy" style="display: none;">
для шаблонов есть более интересные решения:
- https://learn.javascript.ru/templates
- https://learn.javascript.ru/template-tag
Сейчас у тебя очень много рутинного кода, который сломается при первой же правке шаблона комментария, вот тут:
> replyForm.children(".form-group").removeClass("has-error");
> replyForm.children(".form-group").children(".real-label").remove();
> replyForm.children(".form-group").attr('id', "reply-form-group")
(хуже того, тут еще и копипаста выражения replyForm.children(".form-group"))
Лучше сделать например так:
<div class="comment-author">%author%</div>
Ты можешь сделать свою велосипедную систему или поискать готовый шаблонизатор тут: https://garann.github.io/template-chooser/
https://github.com/foobar1643/filehosting/blob/master/app/Database/CommentMapper.php#L21
> :file_id_bind",
Я думаю что можно было просто писать :file_id, bind добавлять не надо было. Ну добавил, ладно, пусть будет.
https://github.com/foobar1643/filehosting/blob/master/app/Helper/CommentHelper.php#L16
> public function addComment(Comment $comment)
Тут алгоритм какой-то мутный. Нельзя минимизировать число обращений к БД? Также, многократные операции записи в БД надо оборачивать в транзакцию.
Насчет выборк комментов - ты строишь дерево. А не проще ли делать плоскую структуру? Просто выбирать комменты ORDER BY path?
> $this->normalizePath("{$parentComment->getParentPath()}.{$comment->getId()}"))
Вот это лучше было бы вынести в метод appendToPath(oldPath, newId)
https://github.com/foobar1643/filehosting/blob/master/app/Helper/FileHelper.php#L20
> public function createFile(File $file, $adminFlag)
Надо выносить часть кода в функции. Например, генерация пути к файлу должна быть в конкретном методе, а не размазана по коду.
> $searchGateway->insertRtValue($file->getId(), $file->getName());
тут лучше высокоуровневый метод
indexNewFile($file)
>>747423
>>755118
> if(!mkdir("{$pathingHelper->getPathToStorage()}/{$file->getFolder()}")) {
> $fileMapper->rollBack();
> throw new FileUploadException(UPLOAD_ERR_CANT_WRITE);
> }
Чтобы не писать кучу роллбеков руками, есть более простой способ:
beginTransaction()
try {
executeSteps()
} catch () {
rollback()
throw
}
commit()
Более того, уже есть готовые функции для этого: http://doctrine-orm.readthedocs.io/projects/doctrine-dbal/en/latest/reference/transactions.html - можно сделать аналогичную.
https://github.com/foobar1643/filehosting/blob/master/app/Helper/FileHelper.php#L56
> public function deleteFile(File $file)
> $commentMapper->deleteComments($file->getId());
> $fileMapper->deleteFile($file);
> $searchGateway->deleteRtValue($file->getId());
Мне кажется там в сервисе должен быть высокоуровневый метод "удалить файл со всем что есть в рамках транзакции". А не в контроллере.
Более того, кли версия - не удаляет комментарии: https://github.com/foobar1643/filehosting/blob/master/cli-tools/admin-tools.php#L66
> class PathingHelper
Ты не пробовал поискать готовые решения? Чем-то не подошли? например
- https://github.com/eloquent/pathogen
- https://packagist.org/search/?search_query[query]=path
- http://phptrends.com/dig_in/path (поиск что-то тормозит)
https://github.com/foobar1643/filehosting/blob/master/public/media/js/comments.js#L19
Плоховато сделана работа с аяксом, почитай мой урок: https://github.com/codedokode/pasta/blob/master/js/ajax.md
-------
В общем, я все пока проверить не успел, увы, времени и так много ушло. Разберись пока с этим. Если есть какие-то вопросы - задавай, я постараюсь не тянуть время и сразу ответить. Напомню еще раз:
- не пиши стены кода, разбивай на подзадачи
- в сервисах определяй красивые и удобные методы, которыми удобно пользоваться, и которые не требуют лишней обвязки чтобы получить результат (антипример: поиск в сфинксе)
- ищи готовые библиотеки прежде чем писать велосипед
В общем, неплохая работа, но наверно стоит еще доработать.
И стандартный вопрос: автоматизированные тесты сделать не хочешь?
>>747423
>>755118
> if(!mkdir("{$pathingHelper->getPathToStorage()}/{$file->getFolder()}")) {
> $fileMapper->rollBack();
> throw new FileUploadException(UPLOAD_ERR_CANT_WRITE);
> }
Чтобы не писать кучу роллбеков руками, есть более простой способ:
beginTransaction()
try {
executeSteps()
} catch () {
rollback()
throw
}
commit()
Более того, уже есть готовые функции для этого: http://doctrine-orm.readthedocs.io/projects/doctrine-dbal/en/latest/reference/transactions.html - можно сделать аналогичную.
https://github.com/foobar1643/filehosting/blob/master/app/Helper/FileHelper.php#L56
> public function deleteFile(File $file)
> $commentMapper->deleteComments($file->getId());
> $fileMapper->deleteFile($file);
> $searchGateway->deleteRtValue($file->getId());
Мне кажется там в сервисе должен быть высокоуровневый метод "удалить файл со всем что есть в рамках транзакции". А не в контроллере.
Более того, кли версия - не удаляет комментарии: https://github.com/foobar1643/filehosting/blob/master/cli-tools/admin-tools.php#L66
> class PathingHelper
Ты не пробовал поискать готовые решения? Чем-то не подошли? например
- https://github.com/eloquent/pathogen
- https://packagist.org/search/?search_query[query]=path
- http://phptrends.com/dig_in/path (поиск что-то тормозит)
https://github.com/foobar1643/filehosting/blob/master/public/media/js/comments.js#L19
Плоховато сделана работа с аяксом, почитай мой урок: https://github.com/codedokode/pasta/blob/master/js/ajax.md
-------
В общем, я все пока проверить не успел, увы, времени и так много ушло. Разберись пока с этим. Если есть какие-то вопросы - задавай, я постараюсь не тянуть время и сразу ответить. Напомню еще раз:
- не пиши стены кода, разбивай на подзадачи
- в сервисах определяй красивые и удобные методы, которыми удобно пользоваться, и которые не требуют лишней обвязки чтобы получить результат (антипример: поиск в сфинксе)
- ищи готовые библиотеки прежде чем писать велосипед
В общем, неплохая работа, но наверно стоит еще доработать.
И стандартный вопрос: автоматизированные тесты сделать не хочешь?
У тебя ошибка в том что ты в 16 строке обращаешься не к свойству а к $this->array(...), проще говоря ты поставил переменную $get в место свойства
Опять этот чертов доллар! Спасибо, в упор не видел
Ему нужно по готовому примеру студентов сделать. Видимо, очередной YouTube'овский саморазвиванец прикатился. Не удобно, когда не ведут за ручку.
>
Уже 2 часа с этим кодом мучаюсь. Это задание про тупого шкАльника из книжки, который покупает себе новый айпад. Я уже понял, что ошибка у меня в цикле, но какая? Подскажите, люди добрые
http://ideone.com/Gt7wk2
Ты два часа не можешь понять, что твой цикл не делает ни одной итерации, так как изначально не проходит условие?
Ох, спасибо. Понял. Похоже, задача про меня
В плане иное? Вообще "уйти из авиации" в смысле из айти? Не, не реально мне. Вспоминаю как в особенностях охоты чувак говорил менту, потерявшему пистолет: "Работать пойдешь... делать-то умеешь что-нибудь?" на что мент грустно мотал головой, вот тут как-то так же.
Да и не охота, от офисов и менеджеров по говноебанию тянет блевать.
У тебя считает неверно.
1. Процент надо высчитывать каждый раз от оставшейся суммы задолженности.
2. У тебя сумма ежемесячного платежа прибавляется в последней итерации цикла, так как стоит в шапке цикла.
Я имею ввиду в js, java и прочее.
И еще в последнем месяце проценты и оплата ведение счета не будут начисляться. Это не правильно.
Для пыхи есть 2 сферы.
Энтерпрайз (обычно внутренний продукт), и Уэб - сайтики/стартапы
И зп зависит по большей мере от уровня спеца
У тебя есть задачка про кошек и мышей.
В ней рекомендуется хранить животных в ОДНОМЕРНОМ массиве.
А что там конкретно хранить? объекты Animal?
Делать специальный метод для отрисовки животных на 2d поле?
А как представить себе перемещение по этому полю? вычитать/прибавлять к значению координаты . Ты говорил что так проще будет.
PHP-гуру, подскажите поожалуйста как оформить такой код:
Внести данные в таблицу TEST где id = 1;
Если ( такой запрос к базе был сделан в теченнии последних 15 секунд ) {
Не делать запрос
} else if ( запрос не был сделан в течении последних 15 секунд ) {
Обновить старый запрос - новым.
}
такой запрос к базе был сделан в теченнии последних 15 секунд
Многопоточность, таймер с который через 15 сек обратно ставит переменную в false
по другому никак
блять, разметку проебал
Ну типу дело в том, что предумсмотрена отправка запроса из разных компьютеров.
Как дслеать вот такой код правильно
(SELECT * FROM test_table WHERE id=1);
if ( test_table['time'] > "сейчас минус 15 секунд" ) {
Не делать ничего.
} else if ( test_table['time'] < "сейчас минус 15 секунд" ) {
UPDATE test_table SET test_row = test_text WHERE id=1;
Что нужно вставить в "сейчас минус 15 секунд", чтобы оно меня поняло ?
}
С головой всё в порядке? А если у меня пека зависнет, и не тикнет. Фиг ты че обновишь на другой пеке
Так оно аяксом тикается вне зависимости от твоего компьютера. Сервер вызывает скрипт раз в 15 секунд.
>Внести данные в таблицу TEST где id = 1
Если под "внести данные" подразумевается insert или update (вставка или обновление записи), то наверное в таблице и хранить время последней модификации, затем сравнивать его с текущим.
Напиши лучше в какой ситуации это понадобилось, возможно есть более рациональное решение.
Генерируется вопрос, заносится в таблицу. 15 секунд этот вопрос отображается у всех пользователей. Если обновить страницу, то этот вопрос всё ещё останется. Через 15 секунд, вопрос поменяется. И так каждые 15 секунд собстна.
Я бы на вебсокетах сделал, аякс для такого реалтайма неудачное решение. На PHP есть библиотека Rachet для этого.
Так в чем сложность?
В таблице добавить поле с временной меткой, когда был создан "вопрос" (что еще за "вопрос"?).
Сверхсложный алгоритм:
Выбрать из бд последний (по дате создания) "вопрос".
Если (дата создания вопроса + 15 секунд < текущего времени) { сгенерировать новый вопрос и сохранить в базу, отдать клиенту новый вопрос }
иначе { отдать последний найденный вопрос }
Если подробно опишешь проблему, получишь соответствующий ответ.
Пока непонятно, что у тебя за "вопросы", какой смысл их "генерировать" каждые 15 секунд, вместо того чтобы заранее занести в базу определенное количество и отдавать рандомный.
Есть ли какой-нибудь мануал, типа Thinking with ActiveRecord, или Core principles of ActiveRecord?
В вузе учили SQL, а что такое ActiveRecord и зачем он нужен - не понятно.
В yii2 есть отличные средства для отправки sql-запросов, пилил бы как в вузе учили. Но сейчас говорят, что нельзя использовать чистый sql.
А как по мне, то он лучший.
Ну так если просто отдавать рандомный, то у каждого пользователя будет отдельный рандом, а соответственно разные вопросы. А мне вопрос нужен одинаковый на всех страницах чтобы выводился.
Опиши. Почему неудачное ? Есть отдельный файлик с запросом и выводом. Я его просто Аяксом обновляю каждые 15 секунд и всё. Зачем вебсокет целый тащить для этого ?
float?
>>761840
Можно отдать список с запланированным временем показа.
>>761830
Есть такое https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
> Но сейчас говорят, что нельзя использовать чистый sql.
> А как по мне, то он лучший.
Придется писать много рутинного однотипного кода.
>>761665
> объекты Animal?
да
> как представить себе перемещение по этому полю?
Изменение координаты животного, которая хранится в самом животном. А представь, если у тебя двухмерный массив, как ты гарантируешь что он всегда будет содержать актуальные данные и соответствовать координатам животных? Тут тогда надо будет координаты выносить из объекта Animal в карту, так как иначе гарантии дать трудно.
А какие у тебя есть идеи?
>>761327
Если ты пишешь задачки на 100 строк то разницы особой нет. Разница есть при работе с проектами, где много файлов. Ну и плюс тебе надо изучить разные горячие клавиши облегчающие работу с ИДЕ - например, переход к функции по имени и тд.
> а что если я захочу дебажить отдельный файл, который подключается через контроллер, причем с разными аргументами?
Точно так же, ставишь брейкпойнт, запускаешь в браузере с нужными параметрами и отлаживаешь. Тебе надо просто получше разобраться, ты явно пока не пониамешь все возможности отладчика.
>>761317
А где платят больше, просто интересно? В магазине? В макдональдсе? В ЖКХ? На госпредприятии? Сейчас кризис на дворе вообще-то и на рынке труда не лучшая ситуация.
>>761170
Готового примера нет, но в задаче на студентво есть много комментариев. Также, ты можешь задавать конкретные вопросы вроде "как делается X" и тебе либо подскажут либо отправят в гугл.
>>761222
Не понял, в чем вопрос.
float?
>>761840
Можно отдать список с запланированным временем показа.
>>761830
Есть такое https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
> Но сейчас говорят, что нельзя использовать чистый sql.
> А как по мне, то он лучший.
Придется писать много рутинного однотипного кода.
>>761665
> объекты Animal?
да
> как представить себе перемещение по этому полю?
Изменение координаты животного, которая хранится в самом животном. А представь, если у тебя двухмерный массив, как ты гарантируешь что он всегда будет содержать актуальные данные и соответствовать координатам животных? Тут тогда надо будет координаты выносить из объекта Animal в карту, так как иначе гарантии дать трудно.
А какие у тебя есть идеи?
>>761327
Если ты пишешь задачки на 100 строк то разницы особой нет. Разница есть при работе с проектами, где много файлов. Ну и плюс тебе надо изучить разные горячие клавиши облегчающие работу с ИДЕ - например, переход к функции по имени и тд.
> а что если я захочу дебажить отдельный файл, который подключается через контроллер, причем с разными аргументами?
Точно так же, ставишь брейкпойнт, запускаешь в браузере с нужными параметрами и отлаживаешь. Тебе надо просто получше разобраться, ты явно пока не пониамешь все возможности отладчика.
>>761317
А где платят больше, просто интересно? В магазине? В макдональдсе? В ЖКХ? На госпредприятии? Сейчас кризис на дворе вообще-то и на рынке труда не лучшая ситуация.
>>761170
Готового примера нет, но в задаче на студентво есть много комментариев. Также, ты можешь задавать конкретные вопросы вроде "как делается X" и тебе либо подскажут либо отправят в гугл.
>>761222
Не понял, в чем вопрос.
По моему тем что ?? проверяет на !empty и не дает ошибки если переменная слева отстутствует
>>760999
Да, ну, отдает фанатизмом (в плане того что везде надо делать NullObject). В случае с логгером можно всего сделать метод log() который будет проверять наличие логгера. В других случаях - учитывать возможность наличия NULL.
> акой подход всё ещё часто используется в состояниях ошибки вместо того, чтобы бросать исключения.
ну так не надо использовать null вместо исключения
>>760864
Ты правильно делаешь, что задаешь вопросы. Инженер принимает решения не потому что "уважаемый человек так сказал" или "я прочел в твиттере что на этой неделе яваскриптщики пишут на этом фреймворке" или "фейсбук это использует". Нет, он сравнивает преимущества и недостатки разных подходов и выбирает оптимальный для решения задачи.
> Пишем класс - соотв. таблице БД. Свойства - поля таблицы, методы - допустим, КРУД.
> Данные подаются через аргументы методов.
Это ближе к ActiveRecord и точно не TDG. невнимательно читал ты мой урок.
> Я просто хочу понять, ЧЕМ ооп лучше процедурного стиля.
Тем, что на процедурном стиле хорошо пишутся только маленькие программы из 10 функций. Как только программа становится сложнее, код становится запутанным и не читаемым и нам нужен другой подход к организации кода - например, ООП.
Ну вот у тебя задача: ты пишешь соцсеть и надо сделать форму регистрации. Надо написать функцию, проверяющую правильно ли заполнена форма (указаны ли все нужные поля, правильный ли возраст и тд). Что ты подаешь на вход функции? Что на выход? Затем, появляются доработки. Надо также уметь проверять не только данные в форме, но и в базе данных. Чтобы проверить, соответствуют ли старые пользователи нашим новым правилам. Далее, мы вводим многоязычность и надо возвращать описания ошибок на нужном языке. После этого, разработчики просят добавить отключаемый логгер, чтобы у себя в тестовой версии сайта они могли включить запись информации в логи. И в конце, надо написать автоматические тесты, тестирующие твою функцию (например проверяющие что если мы не заполнили в форме имя, то выводится ошибка что имя не заполнено). Как ты это все реализуешь без ООП?
Или другой пример: у тебя есть файл A, работающий с базой данных. Надо логгировать все запросы, которые делает этот файл. Код файла менять нельзя, так как это сторонняя библиотека.
Или третий пример: у тебя есть файл, в нем функции, которые берут информацию из базы данных. Например, по id найти данные о пользователе. затем, туда вводят работу с кешем, чтобы функция сначала искала данные в кеше, а только потом в базе. А теперь мы хотим чтобы параметры соединения с БД и с кешем не были жестко заданы, а могли бы меняться. То есть мы можем сказать: поищи пользователя вот в этой базе, используя такой кеш. Или не используя никакого кеша например.
Или четвертый пример: ты делаешь приложение TestHub для создания и прохождения тестов и проверки знаний ученика. Тест состоит из нескольких вопросов, вопросы бывают разных типов (с вводом слова, числа в пределах погрешности, выбором готового варианта). Тест может быть помечен тегами (полное описание задачи: https://gist.github.com/codedokode/8733007 ). как в своей программе ты представишь сущность "тест"? Ну то есть тест с описанием, со всеми вопросами, тегами итд. Вот тебе надо его передать например в функцию, которая считает максимально возможное число баллов за него. Или которая проверяет правильно ли введены ответы.
Или пятый пример. Допустим, ты пишешь соцсеть и хранишь информацию о постах в массиве. Тебя просят под постом выводить кроме имени автора, еще и его рейтинг. Ты смотришь в шаблон поста и видишь там:
<div><?= html($post['text']) ?></div>
<div>Автор: <?= html($post['author_name']) ?></div>
Как понять, есть ли в массиве рейтинг пользователя? В каком поле он хранится? Откуда берется массив $post, понять трудно так как этот шаблон подключается из другого шаблона, а тот вызвается из нескольких разных мест кода.
По моему тем что ?? проверяет на !empty и не дает ошибки если переменная слева отстутствует
>>760999
Да, ну, отдает фанатизмом (в плане того что везде надо делать NullObject). В случае с логгером можно всего сделать метод log() который будет проверять наличие логгера. В других случаях - учитывать возможность наличия NULL.
> акой подход всё ещё часто используется в состояниях ошибки вместо того, чтобы бросать исключения.
ну так не надо использовать null вместо исключения
>>760864
Ты правильно делаешь, что задаешь вопросы. Инженер принимает решения не потому что "уважаемый человек так сказал" или "я прочел в твиттере что на этой неделе яваскриптщики пишут на этом фреймворке" или "фейсбук это использует". Нет, он сравнивает преимущества и недостатки разных подходов и выбирает оптимальный для решения задачи.
> Пишем класс - соотв. таблице БД. Свойства - поля таблицы, методы - допустим, КРУД.
> Данные подаются через аргументы методов.
Это ближе к ActiveRecord и точно не TDG. невнимательно читал ты мой урок.
> Я просто хочу понять, ЧЕМ ооп лучше процедурного стиля.
Тем, что на процедурном стиле хорошо пишутся только маленькие программы из 10 функций. Как только программа становится сложнее, код становится запутанным и не читаемым и нам нужен другой подход к организации кода - например, ООП.
Ну вот у тебя задача: ты пишешь соцсеть и надо сделать форму регистрации. Надо написать функцию, проверяющую правильно ли заполнена форма (указаны ли все нужные поля, правильный ли возраст и тд). Что ты подаешь на вход функции? Что на выход? Затем, появляются доработки. Надо также уметь проверять не только данные в форме, но и в базе данных. Чтобы проверить, соответствуют ли старые пользователи нашим новым правилам. Далее, мы вводим многоязычность и надо возвращать описания ошибок на нужном языке. После этого, разработчики просят добавить отключаемый логгер, чтобы у себя в тестовой версии сайта они могли включить запись информации в логи. И в конце, надо написать автоматические тесты, тестирующие твою функцию (например проверяющие что если мы не заполнили в форме имя, то выводится ошибка что имя не заполнено). Как ты это все реализуешь без ООП?
Или другой пример: у тебя есть файл A, работающий с базой данных. Надо логгировать все запросы, которые делает этот файл. Код файла менять нельзя, так как это сторонняя библиотека.
Или третий пример: у тебя есть файл, в нем функции, которые берут информацию из базы данных. Например, по id найти данные о пользователе. затем, туда вводят работу с кешем, чтобы функция сначала искала данные в кеше, а только потом в базе. А теперь мы хотим чтобы параметры соединения с БД и с кешем не были жестко заданы, а могли бы меняться. То есть мы можем сказать: поищи пользователя вот в этой базе, используя такой кеш. Или не используя никакого кеша например.
Или четвертый пример: ты делаешь приложение TestHub для создания и прохождения тестов и проверки знаний ученика. Тест состоит из нескольких вопросов, вопросы бывают разных типов (с вводом слова, числа в пределах погрешности, выбором готового варианта). Тест может быть помечен тегами (полное описание задачи: https://gist.github.com/codedokode/8733007 ). как в своей программе ты представишь сущность "тест"? Ну то есть тест с описанием, со всеми вопросами, тегами итд. Вот тебе надо его передать например в функцию, которая считает максимально возможное число баллов за него. Или которая проверяет правильно ли введены ответы.
Или пятый пример. Допустим, ты пишешь соцсеть и хранишь информацию о постах в массиве. Тебя просят под постом выводить кроме имени автора, еще и его рейтинг. Ты смотришь в шаблон поста и видишь там:
<div><?= html($post['text']) ?></div>
<div>Автор: <?= html($post['author_name']) ?></div>
Как понять, есть ли в массиве рейтинг пользователя? В каком поле он хранится? Откуда берется массив $post, понять трудно так как этот шаблон подключается из другого шаблона, а тот вызвается из нескольких разных мест кода.
И еще кое-что. Вот у меня есть задача про ООО Вектор и Кошки-мышки в моем учебнике: http://archive-ipq-co.narod.ru/l1/pasta.html
Попробуй решить любую из них без ООП.
ООП позволяет нам вместо неразборчивой кучи функций и массивов разбить код на отдельные слабосвязанные модули-классы, каждый из которых отвечает за свою часть задачи и может использоваться независимо. Модули можно конфигурировать и связывать друг с другом произвольно. Да и просто хранить информацию о чем-то удобнее в объектах. В отличие от массивов, у них есть жестко определенный список полей, методы, опции видимости (private/public). Как ты все это без ООП реализуешь?
ООП позволяет ставить ограничения, например: это поле может менять только этот класс, в эту функцию можно передать только объект такого-то класса. Это защищает от ошибок. Как ты это сделаешь без ООП?
В ООП ты можешь создать несколько однотипных объектов, например, подлючений к базе данных, с разными настройками. Можно создать временный объект, сделать что-то с ним и выкинуть, не влияя на остальной код. Можно вообще классу вместо модуля работы с базой данных подсунуть заглушку.
Можно строить сложные структуры из объектов. Например, пост, в него включить комментарии, в комментарии их авторов, в авторов - список их друзей, список лайков. Потом одной строчкой сконвертировать всю эту структуру в JSON, причем часть полей не будет туда экспортирована.
Как ты это сделаешь без ООП? На массивах? Ты запутаешься в этой куче данных.
> ООП не проходил по-человечески, хотя могу функциональное GUI приложение с ООП на С++ и Delphi написать.
ООП это важно. Я не представляю как писать поддерживаемый, аккуратный и тестируемый код без ООП будь то PHP, JS или C++.
> Я, например, привык к компилируемой статике. Для меня модель исполнения и динамика в новизну были.
В фреймворках как раз к типизации стремятся, все в объектах, у функций прописаны тайп-хинты.
>>760840
Не спеша как-то ты учишь. В нашем учебнике до ООП можно за 7 месяцев дойти, хоть он и в последней главе.
>>760836
Не должно быть у джуниора непонимания ООП. Как он код писать собрался? Если это конечно не джумла или вордпресс.
>>760770
Ой, какие низкие требования у вашей студии.
>>760742
Насчет библиотеки - претензии отчасти справедливы. Смотри, что плохо:
- все сделано в виде функций, никак не разбитых на модули. То есть есть 1000 глобальных функций и все. А лучше бы было так:
string.find(...) вместо strpos
regexp.match вместо preg_match
- нет единого принципа именования. например strpos без подчеркивания, а str_replace - с ним.
- нет единого порядка аргументов. Одна функция принимает аргументы в одном порядке, другая -в другом. Запомнить тяжело, приходится смотреть в справочник
- строковые функции безнадежно плохи и не работают - например substr не умеет вырезать подстроку в utf-8 так как она рассчитана на однобайтные кодировки, а в utf-8 буква может кодироваться несколькими байтами
- нет никакой логики в обработке ошибок. Одна функция при ошибке вернет false, другая выведет Warning и вернет false, PDO может либо вернуть false либо выбрасывать исключение в зависимости от режима работы. Вообще, обработка ошибок в PHP сделана неправильно на 100%, куда не глянь. Нужно было с самого начала: 1) использовать исключения 2) останавливаться при ошибке а не продолжать 3) при фатальной ошибке не выводить белую страницу, а страницу ошибки 4) не делать опций в php.ini для игнорирования ошибок
> ООП не проходил по-человечески, хотя могу функциональное GUI приложение с ООП на С++ и Delphi написать.
ООП это важно. Я не представляю как писать поддерживаемый, аккуратный и тестируемый код без ООП будь то PHP, JS или C++.
> Я, например, привык к компилируемой статике. Для меня модель исполнения и динамика в новизну были.
В фреймворках как раз к типизации стремятся, все в объектах, у функций прописаны тайп-хинты.
>>760840
Не спеша как-то ты учишь. В нашем учебнике до ООП можно за 7 месяцев дойти, хоть он и в последней главе.
>>760836
Не должно быть у джуниора непонимания ООП. Как он код писать собрался? Если это конечно не джумла или вордпресс.
>>760770
Ой, какие низкие требования у вашей студии.
>>760742
Насчет библиотеки - претензии отчасти справедливы. Смотри, что плохо:
- все сделано в виде функций, никак не разбитых на модули. То есть есть 1000 глобальных функций и все. А лучше бы было так:
string.find(...) вместо strpos
regexp.match вместо preg_match
- нет единого принципа именования. например strpos без подчеркивания, а str_replace - с ним.
- нет единого порядка аргументов. Одна функция принимает аргументы в одном порядке, другая -в другом. Запомнить тяжело, приходится смотреть в справочник
- строковые функции безнадежно плохи и не работают - например substr не умеет вырезать подстроку в utf-8 так как она рассчитана на однобайтные кодировки, а в utf-8 буква может кодироваться несколькими байтами
- нет никакой логики в обработке ошибок. Одна функция при ошибке вернет false, другая выведет Warning и вернет false, PDO может либо вернуть false либо выбрасывать исключение в зависимости от режима работы. Вообще, обработка ошибок в PHP сделана неправильно на 100%, куда не глянь. Нужно было с самого начала: 1) использовать исключения 2) останавливаться при ошибке а не продолжать 3) при фатальной ошибке не выводить белую страницу, а страницу ошибки 4) не делать опций в php.ini для игнорирования ошибок
А, еще пример идиотизма: PDO умеет создавать объекты и заполнять данными из базы но он сначала создает объект, затем проставляет значения полей и только потом вызывает конструктор. Кто мог до такого додуматься? только человек, не понимающий ООП в принципе.
Еще проблема: отстутствие объектов запроса/ответа. Вот у тебя есть код делающий setcookie() и попробуй-ка перехвати этот вызов. И сверхглобальные переменные GET/POST, доступные везде, хотя мы бы хотели чтобы к ним имел доступ например только контроллер. И опять же, мы не можем их подменить. Попробуй представить что мы хотим параллельно обрабатывать несколько запросов, переключаясь между обработчиками. Мы же замучаемся перезаписывать GET/POST так как в каждом запросе свой набор параметров.А был бы у нас объект запроса -мы бы просто создали несколько объектов и каждому обработчику дали бы свой.
>>760675
Учить все разделы функций не надо. Читать надо справочник языка + поплярные функции работы со строками, массивами, датами, базой данных.
>>759975
У меня есть подозрение что авторизация в Симфони может быть переусложнена Разберись сам в этом вопросе, если переусложнена - делай что-то свое.
ЧПУ делается элеметарно, просто делаешь в тблице поле slug и прописываешь его при создании сущности, а потом по нему ищешь. Можешь в тестхабе для тестов сделать слуги например, хотя тут вопрос, не получатся ли УРЛ слишком большие?
Те, кто осилил тестхаб на симфони, сделал автоматизированные тесты для него и исправил все мои замечания, наверно могут считать себя годными кандидатами в джуниора.
>>759955
Ты пробовал быть тем, кто отвечает на вопросы? устанешь отвечать. Если бы у меня кто-то спросил , как пользоваться композером - я бы назвал несколько основных команд и отправил гуглить документацию. А вот например на вопрос "как в нашем проекте работаем с БД", "как реализована эта штука", "зачем эта таблица" - пояснил бы и сказал какие файлы с кодом смотреть или как найти.
>>759940
> и из какого он языка, лол.
Из пхп? Из Си? Странный вопрос.
>>759937
Ну, тут надо вовремя остановиться и знать многобайтовые функции. Хотя да, мне пришла мысль именно разбить на массив букв и его переворачивать.
>>759920
> Отличие or/and от || / $$ в таких местах: if(func1() or func2()) { ... }
я кстати это не помню, помню что вроде есть разница в приоритете. Это вообще неправильно что есть такие тонкие различия.
> Приведение типов при сравнении, сложении,
Тоже есть сложности. Кстати, ты знаешь что с PHP7.1 попытка сложить строки не хранящие число ("3 cakes" + 3 или "hello" + "world") будет приводить к ошибке? Столько лет этого ждал.
>>759878
Не знаю, если роутер там как отдельный компонент то можно его вызвать, но это лишь проверить правильность роута, а не выдаст ли он ошибку.
>>759725
Бывают и платные тестовые задания. Где я работал, там новому разработчику давали реальную оплачиваемую задачу и смотрели как справится. Но это не везде работает:
- разработчик должен успеть настроить и разобраться в проекте. Если проект сложный, это займет много времени
- надо давать разработчику доступ к коду
- нужна специальная версия базы
А, еще пример идиотизма: PDO умеет создавать объекты и заполнять данными из базы но он сначала создает объект, затем проставляет значения полей и только потом вызывает конструктор. Кто мог до такого додуматься? только человек, не понимающий ООП в принципе.
Еще проблема: отстутствие объектов запроса/ответа. Вот у тебя есть код делающий setcookie() и попробуй-ка перехвати этот вызов. И сверхглобальные переменные GET/POST, доступные везде, хотя мы бы хотели чтобы к ним имел доступ например только контроллер. И опять же, мы не можем их подменить. Попробуй представить что мы хотим параллельно обрабатывать несколько запросов, переключаясь между обработчиками. Мы же замучаемся перезаписывать GET/POST так как в каждом запросе свой набор параметров.А был бы у нас объект запроса -мы бы просто создали несколько объектов и каждому обработчику дали бы свой.
>>760675
Учить все разделы функций не надо. Читать надо справочник языка + поплярные функции работы со строками, массивами, датами, базой данных.
>>759975
У меня есть подозрение что авторизация в Симфони может быть переусложнена Разберись сам в этом вопросе, если переусложнена - делай что-то свое.
ЧПУ делается элеметарно, просто делаешь в тблице поле slug и прописываешь его при создании сущности, а потом по нему ищешь. Можешь в тестхабе для тестов сделать слуги например, хотя тут вопрос, не получатся ли УРЛ слишком большие?
Те, кто осилил тестхаб на симфони, сделал автоматизированные тесты для него и исправил все мои замечания, наверно могут считать себя годными кандидатами в джуниора.
>>759955
Ты пробовал быть тем, кто отвечает на вопросы? устанешь отвечать. Если бы у меня кто-то спросил , как пользоваться композером - я бы назвал несколько основных команд и отправил гуглить документацию. А вот например на вопрос "как в нашем проекте работаем с БД", "как реализована эта штука", "зачем эта таблица" - пояснил бы и сказал какие файлы с кодом смотреть или как найти.
>>759940
> и из какого он языка, лол.
Из пхп? Из Си? Странный вопрос.
>>759937
Ну, тут надо вовремя остановиться и знать многобайтовые функции. Хотя да, мне пришла мысль именно разбить на массив букв и его переворачивать.
>>759920
> Отличие or/and от || / $$ в таких местах: if(func1() or func2()) { ... }
я кстати это не помню, помню что вроде есть разница в приоритете. Это вообще неправильно что есть такие тонкие различия.
> Приведение типов при сравнении, сложении,
Тоже есть сложности. Кстати, ты знаешь что с PHP7.1 попытка сложить строки не хранящие число ("3 cakes" + 3 или "hello" + "world") будет приводить к ошибке? Столько лет этого ждал.
>>759878
Не знаю, если роутер там как отдельный компонент то можно его вызвать, но это лишь проверить правильность роута, а не выдаст ли он ошибку.
>>759725
Бывают и платные тестовые задания. Где я работал, там новому разработчику давали реальную оплачиваемую задачу и смотрели как справится. Но это не везде работает:
- разработчик должен успеть настроить и разобраться в проекте. Если проект сложный, это займет много времени
- надо давать разработчику доступ к коду
- нужна специальная версия базы
Неделю назад заполнил первую анкетку, и вот прислали приглашение пройти у них тест. В котором вроде как будет матан, алгоритмы и ооп. Очень сильно боюсь обосраться. Как бы мне сейчас освежить хотя бы основы в ооп и прочих современных штуках по нашей теме? В веб-студии я не уверен что освоил хорошее ооп, просто MVC, в котором модели и контроллеры описаны как классы, ну и которые наследуются от игнайтеровских моделей и контроллеров. Ну и конструкторы использовались там, ничего более.
Ща думаю вернуться к продвинутым задачам от ОПа, с ооп, рекурсией и прочими анонимными функциями, в которых я в своё время хуево разобрался, а так же хотелось бы параллельно книжку почитать годную. Что есть для среднего джунивора, который уже видел в глаза как написана приложуха на mvc и работал пол года с несложными задачами?
Что скажите про вот эту статью?
https://habrahabr.ru/post/260201/
В общем надеюсь на вас, анчоусы. Если что, с радостью отвечу на вопросы стремящихся, которые еще только хотят устроиться на свою первую работку, но неуверенны в себе.
яркие рандомные картинки, для привлечения внимания, как вашего, так и моего в дальнейшем
По ООП - я переделал урок про ООП в учебнике и немного его дополнил. Также, отдельные уроки:
- https://github.com/codedokode/pasta/blob/master/arch/di.md
- https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
- https://github.com/codedokode/pasta/blob/master/php/autoload.md
- https://github.com/codedokode/pasta/blob/master/php/exceptions.md
Также, советую погуглить и почитать про SOLID, DRY, YAGNI. Почитать статью на хабре "как 2 пекаря хлею пекли". Про паттерны - сайт http://designpatternsphp.readthedocs.io/ru/latest/README.html (примеры правда там довольно дурацкие и бессмысленные и не объясняют зачем нужен паттерн)
> Прочитать книжки (по неделе на книжку, читая вечерами и на выходных):
> Совершенный код
Эта неплохая, хотя не про PHP
> Symfony 2
Полезно почитать
> PHPUnit — основы, перевод 8 глав документации
перед этим читай мой урок про автоматизированное тестирование
> Vagrant — документация, в PhpStorm
Сомневаюсь что нужен. Докер вроде сейчас чаще используют, да и на него не стоит тратить время, это скорее задача администратора.
> Идеи проектов:
> блог
> интернет-магазин
ерунда полная, бери лучше наши задачи, они подробнее с большим числом комментариев
> Научиться набирать вслепую
> Выучить на память TOP100-300 ф
не трать время
> Изучить еще один язык программирования на уровне написания простеньких приложений. Желательно совсем другой парадигмы:
Я рекомендую тогда книгу по SICP (например http://newstar.rinet.ru/~goga/sicp/sicp.pdf ) - там изучается язык scheme.
Боюсь только у тебя на все это времени не хватит.
По ООП - я переделал урок про ООП в учебнике и немного его дополнил. Также, отдельные уроки:
- https://github.com/codedokode/pasta/blob/master/arch/di.md
- https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
- https://github.com/codedokode/pasta/blob/master/php/autoload.md
- https://github.com/codedokode/pasta/blob/master/php/exceptions.md
Также, советую погуглить и почитать про SOLID, DRY, YAGNI. Почитать статью на хабре "как 2 пекаря хлею пекли". Про паттерны - сайт http://designpatternsphp.readthedocs.io/ru/latest/README.html (примеры правда там довольно дурацкие и бессмысленные и не объясняют зачем нужен паттерн)
> Прочитать книжки (по неделе на книжку, читая вечерами и на выходных):
> Совершенный код
Эта неплохая, хотя не про PHP
> Symfony 2
Полезно почитать
> PHPUnit — основы, перевод 8 глав документации
перед этим читай мой урок про автоматизированное тестирование
> Vagrant — документация, в PhpStorm
Сомневаюсь что нужен. Докер вроде сейчас чаще используют, да и на него не стоит тратить время, это скорее задача администратора.
> Идеи проектов:
> блог
> интернет-магазин
ерунда полная, бери лучше наши задачи, они подробнее с большим числом комментариев
> Научиться набирать вслепую
> Выучить на память TOP100-300 ф
не трать время
> Изучить еще один язык программирования на уровне написания простеньких приложений. Желательно совсем другой парадигмы:
Я рекомендую тогда книгу по SICP (например http://newstar.rinet.ru/~goga/sicp/sicp.pdf ) - там изучается язык scheme.
Боюсь только у тебя на все это времени не хватит.
О, привет опчик. Подкинь мне сразу еще задач на рекурсию и анонимные функции и коллбеки, я помню они есть у тебя. Я тогда как раз на всем этом сидел + кофе считал и антикризисные меры, ага. Ща продолжу с перечитыванием мануалов.
http://alvarotrigo.com/fullPage/ то есть легонько проворачиваешь скролл а у тебя листаются блоки размером 100% на 100%. Шняга для промо-сайтов короче.
Ну и чтоб чуть отличалось сделал чтоб текущий слайд не уезжал, а на месте оставался.
http://codepen.io/anon/pen/RRPyGL
У меня вопрос. Почему у этого чела подобный функционал занимает 3к строк на гитхабе? И почему я в нем нихрена не могу понять он для меня выглядит как китайские иероглифы. Мой код похож на говно, но он занимает 30 строк ну и просто работает (в последнем хроме, хехе). Да, понятно, что нужны фолбеки для определения события для разных браузеров, поддержка скролла с клавы и на мобилах, заворачивание в плагин и т. д. но все равно я не пойму как писать такой профессионально выглядящий нихера непонятный код вместо
боди.онскролл = {
страница[текущая].крутаниВверх()
}
>Не спеша как-то ты учишь. В нашем учебнике до ООП можно за 7 месяцев дойти, хоть он и в последней главе.
Дизайн, верстка, ЦМСки, инструментарий мелкий (тысячи их), фреймворки, JS, CEO, алгоритмы. В оставшееся время PHP.
Когда дорастешь до этих людей, тогда сам будешь знать, как и зачем.
Мне нечего пока показать. Весь мой дизайн пока сводится к тому что я пытаюсь отрисовать в фотошопе по памяти отдельные функциональные блоки, которые вышли у авторов очень удачно, дрочу на идеальный пэддинг, оптимальную высоту шрифта, ширину колонок текста. Микровопросы пока идут в общем.
Попробуй еще векторные редакторы (inkscape например) - многие вещи там удобнее делать чем в ФШ.
Illustrator использую.
Желательно еще чтобы он умел проверять, если ли запрос на подтверждение телефона.
Именно на работе кто-то перекатился и зачинает новые проекты на нем?
не в моей локации наверное.
https://github.com/foobar1643/filehosting
>ты используешь константу-массив, которая пока доступна только в новом php. Пришлось исправлять, чтобы запустить.
5.6 же последняя пятая, да и вышла она уже давно. Я вообще думал к следующей задаче (тестхаб) делать перекат на седьмую версию, вроде бы Yii 2 с ней нормально работает. То что в ридми указано 5.5 это потом исправлю, когда буду его на английский переводить.
>Чтобы это реалилизовать, тебе надо использовать фичу сфинкса которая позволяет делать конфиг на скриптовом языке
Я сделал, но я не очень понимаю откуда брать эту переменную окружения. Точнее, каким образом администратор узнает что её можно задавать. Можно написать в ридми что есть возможность задать переменную окружения SPHINX_ROOT, для хранения индексов в другой директории. Этого хватит?
>Сам поиск у меня падает с сообщением 'exception 'ErrorException' with message 'PDO::__construct(): Server sent charset (0) unknown to the client. Please, report to the developers'
Я погуглил, пишут что это пофиксили в новых версиях (правда запись в багтрекере еще за 2012 год).
http://sphinxsearch.com/bugs/view.php?id=1249
>В комментариях при отпраке аяксом содержится XSS.
Тут я не совсем понял как бороться с XSS с помощью JS. Насколько я знаю там нет функции которая по функционалу была бы как htmlspecialchars, единственное что я смог придумать это encodeURIComponent(comment.text), это работает, но совсем не так как нужно, и вообще назначение у функции другое. Я еще думал регулярками убирать теги, но этот вариант мне не очень нравится, и насколько я помню там нужна хорошая регулярка, потому что существует много способов её обойти.
>CsrfGuard на мой взгляд мутный, хоть он и сделан по рекомендациям OWASP. Требует сессий. На каждый запрос генерирует токены, даже если они потом не используются.
CsrfGuard я вообще убрал, при отправке комментариев он не давал аяксом отправить два комментария без перезагрузки страницы. Напишу свой который использует куки.
>В валидаторе ты валидируешь массив А ведь можно валидировать модель комментария.
>А как ты считаешь, что лучше?
Когда я делал тот валидатор, вроде бы логика была в том, чтобы отправить туда данные из формы, и если они неправильные то не заполнять модель зря, а показать пользователю ошибку. Я не подумал о том что мне так же нужно будет проверять комментарий который был получен другим способом (CLI скриптом например, я там сделал добавление комментариев). В таком случае, конечно лучше всегда проверять модель.
У меня еще вопрос был по поводу валидации. Это нормально что я совместил валидацию формы загрузки файла и комментариев в одном классе? Я сначала думал делать по классу-валидатору на сущность, но потом понял что тут у меня всего две сущности и легче будет сделать класс в котором два метода. А если сущностей в приложении 5 и более, тут будет разумным делать по классу-валидатору на каждую (при условии что их вообще нужно проверять, конечно)?
>> public function addComment(Comment $comment)
>Тут алгоритм какой-то мутный. Нельзя минимизировать число обращений к БД?
Если не добавлять id комментария в его mpath, тогда сломается сортировка при выборке из БД. Хотя мне должно быть все равно, я же дерево строю. Я пока оставил, потом потестирую и уберу, если получится.
>Насчет выборк комментов - ты строишь дерево. А не проще ли делать плоскую структуру? Просто выбирать комменты ORDER BY path?
В начале я так и планировал и даже вроде бы сделал рабочий вариант, но с такой структурой мне было неудобно работать, особенно со стороны js. Решил переделать потому что до этого деревья нигде не использовал и было интересно посмотреть на результат.
> https://github.com/foobar1643/filehosting/blob/master/app/Helper/FileHelper.php#L56
>Мне кажется там в сервисе должен быть высокоуровневый метод "удалить файл со всем что есть в рамках транзакции". А не в контроллере.
Так это и есть сервис, в контроллере такой метод как раз и используется.
>> class PathingHelper
>Ты не пробовал поискать готовые решения? Чем-то не подошли?
Я смотрел готовые решения, но они мне показались слишком перегруженными, и больше половины функций оттуда я бы в этой работе все равно не использовал бы. А PathingHelper это простой класс, который строит пути из корня, который ему передали. Он выполняет свою функцию и я не вижу смысла его менять.
>Плоховато сделана работа с аяксом, почитай мой урок
Я читал, но все равно не понял что я упустил у себя. Индикатор прогресса я думаю тут не нужен, это комментарии и их постинг очень быстрый. А все остальное (сообщения об ошибках, возможность повтора) я сделал.
>И стандартный вопрос: автоматизированные тесты сделать не хочешь?
Хочу, только я слабо понимаю как они работают. Я читал урок, понимаю зачем они нужны, но раньше никогда тестов не писал и не очень понимаю что именно нужно будет тестировать в этой задаче. Операции с файлами и комментариями (добавление, удаление)? Хотя сам PHPUnit я толком и не изучал, почитаю как доделаю локализацию и csrf.
https://github.com/foobar1643/filehosting
>ты используешь константу-массив, которая пока доступна только в новом php. Пришлось исправлять, чтобы запустить.
5.6 же последняя пятая, да и вышла она уже давно. Я вообще думал к следующей задаче (тестхаб) делать перекат на седьмую версию, вроде бы Yii 2 с ней нормально работает. То что в ридми указано 5.5 это потом исправлю, когда буду его на английский переводить.
>Чтобы это реалилизовать, тебе надо использовать фичу сфинкса которая позволяет делать конфиг на скриптовом языке
Я сделал, но я не очень понимаю откуда брать эту переменную окружения. Точнее, каким образом администратор узнает что её можно задавать. Можно написать в ридми что есть возможность задать переменную окружения SPHINX_ROOT, для хранения индексов в другой директории. Этого хватит?
>Сам поиск у меня падает с сообщением 'exception 'ErrorException' with message 'PDO::__construct(): Server sent charset (0) unknown to the client. Please, report to the developers'
Я погуглил, пишут что это пофиксили в новых версиях (правда запись в багтрекере еще за 2012 год).
http://sphinxsearch.com/bugs/view.php?id=1249
>В комментариях при отпраке аяксом содержится XSS.
Тут я не совсем понял как бороться с XSS с помощью JS. Насколько я знаю там нет функции которая по функционалу была бы как htmlspecialchars, единственное что я смог придумать это encodeURIComponent(comment.text), это работает, но совсем не так как нужно, и вообще назначение у функции другое. Я еще думал регулярками убирать теги, но этот вариант мне не очень нравится, и насколько я помню там нужна хорошая регулярка, потому что существует много способов её обойти.
>CsrfGuard на мой взгляд мутный, хоть он и сделан по рекомендациям OWASP. Требует сессий. На каждый запрос генерирует токены, даже если они потом не используются.
CsrfGuard я вообще убрал, при отправке комментариев он не давал аяксом отправить два комментария без перезагрузки страницы. Напишу свой который использует куки.
>В валидаторе ты валидируешь массив А ведь можно валидировать модель комментария.
>А как ты считаешь, что лучше?
Когда я делал тот валидатор, вроде бы логика была в том, чтобы отправить туда данные из формы, и если они неправильные то не заполнять модель зря, а показать пользователю ошибку. Я не подумал о том что мне так же нужно будет проверять комментарий который был получен другим способом (CLI скриптом например, я там сделал добавление комментариев). В таком случае, конечно лучше всегда проверять модель.
У меня еще вопрос был по поводу валидации. Это нормально что я совместил валидацию формы загрузки файла и комментариев в одном классе? Я сначала думал делать по классу-валидатору на сущность, но потом понял что тут у меня всего две сущности и легче будет сделать класс в котором два метода. А если сущностей в приложении 5 и более, тут будет разумным делать по классу-валидатору на каждую (при условии что их вообще нужно проверять, конечно)?
>> public function addComment(Comment $comment)
>Тут алгоритм какой-то мутный. Нельзя минимизировать число обращений к БД?
Если не добавлять id комментария в его mpath, тогда сломается сортировка при выборке из БД. Хотя мне должно быть все равно, я же дерево строю. Я пока оставил, потом потестирую и уберу, если получится.
>Насчет выборк комментов - ты строишь дерево. А не проще ли делать плоскую структуру? Просто выбирать комменты ORDER BY path?
В начале я так и планировал и даже вроде бы сделал рабочий вариант, но с такой структурой мне было неудобно работать, особенно со стороны js. Решил переделать потому что до этого деревья нигде не использовал и было интересно посмотреть на результат.
> https://github.com/foobar1643/filehosting/blob/master/app/Helper/FileHelper.php#L56
>Мне кажется там в сервисе должен быть высокоуровневый метод "удалить файл со всем что есть в рамках транзакции". А не в контроллере.
Так это и есть сервис, в контроллере такой метод как раз и используется.
>> class PathingHelper
>Ты не пробовал поискать готовые решения? Чем-то не подошли?
Я смотрел готовые решения, но они мне показались слишком перегруженными, и больше половины функций оттуда я бы в этой работе все равно не использовал бы. А PathingHelper это простой класс, который строит пути из корня, который ему передали. Он выполняет свою функцию и я не вижу смысла его менять.
>Плоховато сделана работа с аяксом, почитай мой урок
Я читал, но все равно не понял что я упустил у себя. Индикатор прогресса я думаю тут не нужен, это комментарии и их постинг очень быстрый. А все остальное (сообщения об ошибках, возможность повтора) я сделал.
>И стандартный вопрос: автоматизированные тесты сделать не хочешь?
Хочу, только я слабо понимаю как они работают. Я читал урок, понимаю зачем они нужны, но раньше никогда тестов не писал и не очень понимаю что именно нужно будет тестировать в этой задаче. Операции с файлами и комментариями (добавление, удаление)? Хотя сам PHPUnit я толком и не изучал, почитаю как доделаю локализацию и csrf.
32564376345%1000 = -23
23542423432%1000 = 952
Должно же быть 345 и 432?
анончики, я ещё совсем ньюфаг, дрочу preg_split, разбиение текста на предложения. Внезапно вылетает bool(false), читал в мануале, они мне что то втирают про не соответсвие regexp со строкой, но в моем коде, вроде все соответствует
Надо экранировать некоторые символы, если именно они тебе нужны.
Посмотри у ОПа, какие.
Ещё надо читать оповещения: PHP Warning: preg_split(): Compilation failed: missing terminating ] for character class at offset 4 in /home/Bo1kal/prog.php on line 4
Понятно, что ты хотел в квадратных скобках перечислить знаки конца предложения, но всё же.
бля, я косоглазый. Спасибо, анончик.
Иногда замечаю, что когда не можешь найти ошибку, надо развеяться на некоторое время и вернуться к коду, и сразу внезапно видешь совершенно глупую ошибку.
Спасибо!
Меня взял. Пилим сейчас стартап в Аргентине еще с двумя анонами. Ищем 4-го. Оставляйте свои резюме в треде.
Выслал тебе в личку, ответь в течении дня подалуйста, а то меня в яндекс зовут, но я дропну их, если с вами срастется.
Какой у тебя уровень юмора, TARS?
Оказалось, что слово Parent нельзя использовать в качестве имени класса, так как зарезервировано. Хотя не вижу его в списке
http://php.net/manual/ru/reserved.keywords.php
Просто странно, почему не обычная 500 ошибка?
EMPTY_RESPONSE скорее всего значит что процесс Апача с PHP внутри упал. В логе будет запись вроде child terminated unexpectedly. Это баг который ты мог бы зарепортить, если можешь собрать все подробности и минималистичный пример кода для повторения.
> почему не обычная 500 ошибка?
php не отадет код 5xx, при фатальной ошибке он отдает белую страницу с кодом 200.
Ну классика, делаешь сайт, тебе надо туда какое-то подобие цмс прикрутить, то есть условно кнопка "добавить статью", это текстовое поле и кнопка "добавить". Допустим нахрен весь джаваскрипт, по первой кнопке проваливаемся на новую пхп-страничку добавления статьи, которая с полем и кнопкой. Заполняем поле, жмакаем, это я так понимаю пост-запрос, возвращается изначальная страница, но уже с условно добавленным дивом.
Как это дело сохранять? Только базы данных?
Да и вообще, логику я правильно описал или как оно?
>Как это дело сохранять? Только базы данных?
Конечно бд, как же еще? В текстовый файлик сохранять? Все верно ты описал.
>Заполняем поле, жмакаем, это я так понимаю пост-запрос, возвращается изначальная страница, но уже с условно добавленным дивоманицы то джаваскрипт.
Когда жмагаем на кнопку добавить то твоя статья попадает в базу, никаких дивов никуда не добавляется. После чего редирект на изначальную страницу которой пофиг что ты куда добавлял, она достает из базы последние статьи или их превью и показывает как обычно. Если нужно без обновления страницы тогда только аякс
Не откажусь от ссылочки на какую-нибудь годную библиотеку на пдо/mysqli, где это уже предусмотрено.
>Какой самый простой и эффективный способ защиты от них?
Использовать только PDO и его Prepared statements.
http://php.net/manual/ru/pdo.prepared-statements.php
И еще не подставлять данные в SQL запросы из переменных вообще никогда.
Хм, то есть, если я, например, сюда:
$stmt = $dbh->prepare ("INSERT INTO user (firstname, surname) VALUES (:fname, :sname)");
$stmt -> bindParam(':fname', $_GET['fname']);
$stmt -> bindParam(':sname', $_GET['sname']);
$stmt -> execute();
Передам какой-нибудь ') DROP TABLE users, это не сработает?
В чем магия-то?
А, прошу прощения, пропустил этот пункт. Спасибо.
Не сработает. Просто в таблицу будет вставлено переданное значение, оно не будет воспринято как команда.
Возникла проблема с реализацией многоязычного варианта. Весь функционал перевода я сделал, кроме одной строки
>"1 скачиваний" - что это за насмешка над правилами русского языка?
Как реализовать перевод этой строки на несколько языков и при этом не сломать множественное число?
Обычные склонения в русском языке у меня получилось сделать через MessageFormatter вот так
> new MessageFormatter("ru_RU", "У файла {n, plural, =0{нет загрузок} =1{1 загрузка} one{# загрузка} few{# загрузки} many{# загрузок} other{# загрузок}}");
Но проблема в том что в шаблонах Twig это можно реализовать только следующим способом (тег {% plural %})
http://twig.sensiolabs.org/doc/extensions/i18n.html#complex-translations-within-an-expression-or-tag
Там нет такого функционала как у MessageFormatter, максимум что можно сделать это склонения в английском языке вроде 1 download > 2 downloads.
Единственный вариант который приходит на ум - передавать в шаблон уже отформатированную MessageFormatter строку, а в MessageFormatter локаль получать через Locale::getDefault(), текст получать через gettext. Это уже велосипедом каким-то выглядит, передавать одну строку в шаблон.
> Но проблема в том что в шаблонах Twig это можно реализовать только следующим способом (тег {% plural %})
Это плохой и неудачно реализованный тег. Например он не позволит склонять несколько слов независимо в одном предложении.
> передавать в шаблон уже отформатированную MessageFormatter строку,
А нельзя передавать объект MesageFromatter с уже настроенной локалью? Если нет то ты можешь просто запилить свою функцию для вызова MessageFormatter в твиг.
Также, ты можешь сделать свой класс для переводов. И передавать его объект в твиг.
> а в MessageFormatter локаль получать через Locale::getDefault(),
Глобальная переменная, какой кошмар
Среда для работы скриптов на сервере. Костяк веб-сайта. Взаимосвязанные кирпичики, из которых можно собрать что угодно.
Фреймворк это каркас приложения. Вот представь что ты делаешь сайты и у тебя вдруг появлилось несколько похожих заказов: нужны сайты со статьями, новостями и обратной связью. Они почти одинаковы в плане функционала, только дизайн немного разный. Ты можешь писать каждый раз сайт с нуля, а можешь сделать один общий движок (каркас) с нужным функционалом и на его основе уже делать эти сайты, добавляя разные шаблоны и настраивая число полей в форме обратной связи.
Фреймворк это примерно то же самое, только намного универсальнее. То есть в нем не заложен модуль новостей или обратной связи, а только основа, на которой можно сделать любой нужный модуль. Что-то общее, что пригодится на любом сайте. Например, функции для работы с базой данных, функции для создания и вывода форм, и тд.
>А нельзя передавать объект MesageFromatter с уже настроенной локалью
Спасибо, пока что сделал так, но мне не очень нравится. Потом еще подумаю как получше это реализовать.
>Также, ты можешь сделать свой класс для переводов. И передавать его объект в твиг.
В этом не очень много смысла, потому что дополнение http://twig.sensiolabs.org/doc/extensions/i18n.html предоставляет тег {% trans %} для переводов в шаблонах.
>Глобальная переменная, какой кошмар
Это класс из Locale из Intl http://php.net/manual/en/class.locale.php , я его использовал чтобы получать локаль для gettext из заголовка HTTP_ACCEPT_LANGUAGE.
Не знаю, мне было норм. Я очень легко в него вкатился и на его основе вроде как даже понял немного ооп и MVC. Тогда я просто тоже писал в тредике, спрашивал советов и всё такое, а мне ОП говорил что фреймворк старый, сыпет ошибками и прочее. Но я наверное в силу того, что писал на нем относительно простую логику ничего такого не замечал. Видимо всё познается в сравнении с другими фремворками и то, как пишут на них. Хотя опять же именно год назад выкатили его обновление до 3.0, и я первым делом собственно на него перекатил проект, который мне дали. То есть с 2.х версиями я вообще не работал.
Как можно описать в regex игнорирование символов " -()", чтобы была возможность указать кол-во цифр в телефоне равное 10?
Используй квадратные скобки, Люк!
Просто напиши 10 раз подряд идет одна цифра, за ней любое чсло скобок, пробелов, минусов.
>>763302
> Это класс из Locale из Intl http://php.net/manual/en/class.locale.php , я его использовал чтобы получать локаль для gettext из заголовка HTTP_ACCEPT_LANGUAGE.
Очевидно дефолтный намертво прописанный алгоритм определения языка не подходит. У нас будет отдельный код для его определения и отдельная переменная для него.
>Очевидно дефолтный намертво прописанный алгоритм определения языка не подходит. У нас будет отдельный код для его определения и отдельная переменная для него.
Я для разных языков сделал разные ссылки. И из заголовка я получаю родной язык пользователя, и если приложение доступно на родном языке - показываю уведомление. Так же сделаю возможность сменить язык в любой момент через настройки.
Опять вопрос возник - как получать список доступных языков в приложении? Вот есть папка locales где есть папки вида ru_RU, en_US - можно получать список папок через scandir и пропускать результаты через регулярку, чтобы ничего лишнего туда не попало. Или можно сделать таблицу languages в базе данных, но как туда добавлять новые языки? Тут я придумал только два варианта - добавлять языки в дамп, таким образом при установке приложение сразу будет иметь список доступных в нем языков, или сделать панель для администратора с возможностью добавления языка. Админ панель только ради этого не очень хочется делать, но с другой стороны, в случае с дампом, каждый раз открывать консоль и подключатся к базе чтобы добавить новый язык - это не очень удобно.
Решаю задачу "Опечаточники" из раздела "Регулярные выражения" учебника ОПа.
http://ideone.com/jicK9b
Не получается реализовать вывод слова с несколькими ошибками в нем. Проблема в том что внутри группы подгруппы выделяются только один раз, в параметре $replacement функции preg_replace() нельзя вывести несколько подгрупп с одинаковыми номерами и совпавшими с разными участками текста.
Можно попробовать разложить $match на составляющие с помощью цикла for, посчитав перед этим количество элементов в нём.
for ($i = 0; $i < count($match) - 1; $i++) {
$wrongText = $match[$i];
}
Извини за столь прозрачную подсказку, но тут не всё так однозначно - сам поймёшь, когда попробуешь.
Админы хайлоад-проектов в треде, я спокоен.
Госпади, за что мне такое наказание.
Читаю, я значит, умные книжки. Делаю задачки, все оки идет.
Время своего творчества, пора накатывать сервак, все дела.
Запускаю тест, как в инструкции, через командную строку-ошибка.
Пошел скачивать редистирбутивы с от мелкомягких. Процесс установки...другая ошибка. Переустановите, обновите систему. Сделал. Снова ошибка. 1 из многих раз, вроде бы, ошибки не было. Но пыха все равно не запускается, не хватает библиотечного файла, который ставится после обновления дистрибутивов. Как же я заебался, чес слово. Хоть по клаве головой катайся, ну что за херня. Ошибка на ошибке, шиндоус, что ты делаешь.
Ну нахуй это говно.
Попробуй xampp. Я им пользуюсь только потому что когда то давно установщик denwer какую-то ошибку показал и обламал меня
вспомнил свой бугурт когда лет 7 назад php не поставился на macbook, а там не было никаких альтернатив в виде денверов и пр. Приходилось кодить в стол
>xampp
Но ведь это просто и удобно, поставил и все.
А в гайдах написано, что я должен вручную все ставить, чтобы разбираться.
Как это вообще, влияет на работу, на фриланс? Или не сильно?
Ясен пень, если дело пойдет, то в будущем себе накатаю по нескольку систем, чтобы мозги не парить.
>Как это вообще, влияет на работу
Устроился вот ты в офис, приходишь в первый день, тебе дают компьютер на котором стоит чистый линукс без интерфейса, и говорят чтобы через час ты уже код писал. Как ты думаешь, это сильно будет влиять на работу?
Я скажу что я спермовор и не собираюсь отказываться от своего рациона.
Я не думал пока о работе в офисе.
Плюс я и с линуксом не знаком, но знаю, что там тот еще мозготрах. Но с другой стороны, так и в чем-то проще. Короче, это отдельная тема. Пока у меня шиндоус и никакого продвижения в работе.
Позже отпишусь, пока что спасибо за наводку.
Я же говорю-ошибки у меня.
https://www.smartftp.com/support/kb/the-program-cant-start-because-api-ms-win-crt-runtime-l1-1-0dll-is-missing-f2702.html
http://superuser.com/questions/979546/vc-redist-x86-exe-setup-failed-0x80240017-unspecified-error
Вот это вот. Ничего не помогает. Еще и ошибки с дисками. Я бы переустановил винду, накатил бы линукс-но мой комп так по-божески засран неотсортированной инфой, поэтому я ничего радикального делать не хочу.
> тебе дают компьютер на котором стоит чистый линукс без интерфейса, и говорят чтобы через час ты уже код писал
Это правда? Дайте линк на клуб изучающих гей-проституцию пока не поздно.
Как отключить порт? Я пытался удалить все масклы в программах, но эта штука все равно работает.
Пробовал писать все команды из гугла-да нихера не работает, одни линуксовые инструментарии.
Поменял в ини дефолтный порт-не переключает.
Аггрх, как же меня всегда вставляли проблемы с софтом. Щелки не нюхал, а уже поебался, называется.
А точнее, я знаю как он работает и что он создает на серваке файл и копирует в него контент, вопрос в том чому не работает как должен
Пилю плагин для вордпресса для специфических нужд, нужно загрузить несколько файлов. Пока что эта хрень только один файл грузит. По идее upload_bits должен просто загрузить все что прикреплено, но чет не делает. Думал циклами пошарится и по одному попробовать, но не робит
$_FILES имеет следующий вид
`Array`
`(`
`[file1] => Array`
`(`
`[name] => MyFile.txt`
`[type] => text/plain`
`[tmp_name] => /tmp/php/php1h4j1o`
`[error] => UPLOAD_ERR_OK (= 0)`
`[size] => 123 (the size in bytes)`
`)`
`[file2] => Array`
`(`
`[name] => MyFile.jpg`
`[type] => image/jpeg`
`[tmp_name] => /tmp/php/php6hst32`
`[error] => UPLOAD_ERR_OK`
`[size] => 98174`
`)`
`)`
но `wp_upload_bits($_FILES['files']['name'], null, file_get_contents($_FILES['files']['tmp_name']));` не хочет делать то что мне надо.
Я бы уже сделал аякс калбэком или на чистом пхп вручную, но мучает вопрос именно по этой функции.
Может, дело в хтмл5 в метабоксе?
`<input type="file" id="files" name="files" size="25" multiple="multiple"/>`
Раньше делал вебкитом чтоб папки загружать, но так вообще не пахало, таким же способом удалось создать нужные файлы на сервере (но содержимое туда так и не скопировалось)
А точнее, я знаю как он работает и что он создает на серваке файл и копирует в него контент, вопрос в том чому не работает как должен
Пилю плагин для вордпресса для специфических нужд, нужно загрузить несколько файлов. Пока что эта хрень только один файл грузит. По идее upload_bits должен просто загрузить все что прикреплено, но чет не делает. Думал циклами пошарится и по одному попробовать, но не робит
$_FILES имеет следующий вид
`Array`
`(`
`[file1] => Array`
`(`
`[name] => MyFile.txt`
`[type] => text/plain`
`[tmp_name] => /tmp/php/php1h4j1o`
`[error] => UPLOAD_ERR_OK (= 0)`
`[size] => 123 (the size in bytes)`
`)`
`[file2] => Array`
`(`
`[name] => MyFile.jpg`
`[type] => image/jpeg`
`[tmp_name] => /tmp/php/php6hst32`
`[error] => UPLOAD_ERR_OK`
`[size] => 98174`
`)`
`)`
но `wp_upload_bits($_FILES['files']['name'], null, file_get_contents($_FILES['files']['tmp_name']));` не хочет делать то что мне надо.
Я бы уже сделал аякс калбэком или на чистом пхп вручную, но мучает вопрос именно по этой функции.
Может, дело в хтмл5 в метабоксе?
`<input type="file" id="files" name="files" size="25" multiple="multiple"/>`
Раньше делал вебкитом чтоб папки загружать, но так вообще не пахало, таким же способом удалось создать нужные файлы на сервере (но содержимое туда так и не скопировалось)
Я двачевал этот вопрос задолго до того как он стал мейнстримом. Годных бесплатных макетов не существует. Можно своровать виндовс, фотошоп, игру престолов, 3 ведьмака - все что угодно, кроме путевых макетов. Максимум что ты найдешь - однотипное вырвиглазное дерьмище из 2007-го года. Даже не трать время.
Окей, тогда с чего вообще начать ? самому рисовать в фотошопе потом делать верстку, хотелось бы как то упростить этот вопрос, учитывая бюджет проекта в 5 тыр.
Бери первую попавшуюся парашу и верстай. На досуге дрочи скилл передирания дизайна с джипегов. Шрифты можно вырезать и распознавать, цвета пикать, картинки искать через гугол, пропорции считать на калькуляторе. Может у тебя дар проснется и ты без этой ебатни сможешь рисовать красоту. Годные джипеги проектов ищи на дрибле и бехансе правда тем дебильный поиск. Также можно спамить личку авторам с просьбой поделиться макетом если он не коммерческий. Лучше прикидываясь телкой.
Покажи мне куда тыкнуть в бутстрапе чтоб получить ну что-нибудь уровня этого https://www.behance.net/gallery/37276397/FC-Barcelona
Да я хуй знает, поищи темку какую-нибудь, авось чего похожего подыщешь.
Не найдется среди бутстраповских поищи в вордпрессовских, там их заебись много и годные очень хорошо настраиваются. Навешай плагинов разных по вкусу и вуаля - твори что хочешь. Добавишь еще каких-нибудь модных ui js фреймворков и пиздец.
Я просто предпологаю что ты врядли на серьёзных типов работаешь, которые понимают как и что за сколько делается, им может и красивой чуть кастомизированной тут и там темки хватить.
Но то что ты кинул это как-то слишком.
Тебе типа такого сделать сказали? Если да, то ты наверное очень состоятельный паря.
>>764037
Я не тот чел которому что-то заказали. Он спросил совет, где взять макет визитки. Я ему ответил что годный и бесплатный нигде, так как сам закумарился их искать. А тысячи однотипного индусского шлака для вп - где угодно по первой ссылке free template.
Список можно захардкодить в конфиге или коде определения языка.
>>763594
Это O(1)
>>763622
никак. Либо не передавать столько либо поменять настрйоки.
>>763806
Альтернатива всегда была в виду виртуалки с линксом например или ручной компиляции.
>>763808
там разве пхп не устаревший стоит?
>>763827
Написал бы уже конкретно что ты делаешь и какая ошибка выходит - а то я тебе даже помочь не могу так как нет подробностей.
>>763858
остановить сервис (службу) либо поменять номер порта в конфиге
>>763861
читал доки и код?
>>763923
сказать заказчику что ты не дизайнер и нарисуешь сам как получится. Вообще, брать защищенный авторским правом контент плохая идея. Заказчик хитро перекладвает на тебя необходимость запачкать руки.
а так, можно взять один или несколько сайтов и с каждого взять по несколько идей и реализвтаь своими силами, без копирования. С одного структуру старницы, с дургого стиль заголовков и тд.
Картинки кстати с гугла тоже брать нельзя если они не под свободной лицензией.
Если совсем плохо - можешь скопировать дизайн с учебника ОПа, так уж и быть, только цвета поменяй. Неплохой дизайн, я считаю.
>>764062
Хороший дизайн может нарисовать только дизайнер. Когда я еще делал сайты, у нас часто были свои макеты от дизайнера, и кстати, довольно хорошие.
Но конечно в 5 тр это никак не уложится.
Список можно захардкодить в конфиге или коде определения языка.
>>763594
Это O(1)
>>763622
никак. Либо не передавать столько либо поменять настрйоки.
>>763806
Альтернатива всегда была в виду виртуалки с линксом например или ручной компиляции.
>>763808
там разве пхп не устаревший стоит?
>>763827
Написал бы уже конкретно что ты делаешь и какая ошибка выходит - а то я тебе даже помочь не могу так как нет подробностей.
>>763858
остановить сервис (службу) либо поменять номер порта в конфиге
>>763861
читал доки и код?
>>763923
сказать заказчику что ты не дизайнер и нарисуешь сам как получится. Вообще, брать защищенный авторским правом контент плохая идея. Заказчик хитро перекладвает на тебя необходимость запачкать руки.
а так, можно взять один или несколько сайтов и с каждого взять по несколько идей и реализвтаь своими силами, без копирования. С одного структуру старницы, с дургого стиль заголовков и тд.
Картинки кстати с гугла тоже брать нельзя если они не под свободной лицензией.
Если совсем плохо - можешь скопировать дизайн с учебника ОПа, так уж и быть, только цвета поменяй. Неплохой дизайн, я считаю.
>>764062
Хороший дизайн может нарисовать только дизайнер. Когда я еще делал сайты, у нас часто были свои макеты от дизайнера, и кстати, довольно хорошие.
Но конечно в 5 тр это никак не уложится.
Читал. Судя по всему передают туда $_FILES['file_field']['name'] что есть тоже массив имен прикрепленных файлов. Если просто использовать вкупе с html5 input = file multiple на сервере оказывается только последний файл, а не оба.
НО
Может быть я тупой и в функции привязанной к хуку save_post надо сделать как-то в цикле вызов другой функции в зависимости от колличества прикрепленных файлов, ибо я сохраняю мету в бд вордпресса и что-то мне подсказывает, что там должна быть мета одного файла и $id одного только post.
Сейчас там одна функция и один $id я как-то надеялся что все что прикреплено по умолчанию сразу грузится и записывается в мету, но нет.
>чистый линукс без интерфейса
Такое реально бывает? Ладно у нас ОП адепт философии "ИНТЕРФЕЙС НИНУЖОН", но я не думал что такое извращение может встретиться в конторах.
> но я не думал что такое извращение может встретиться в конторах
Какое извращение? Это твое рабочее место, ты можешь настроить как тебе удобно. Из командной строки легко ставится x11, туда накатывается гном или кде, остальные пакеты по желанию. Потом для тестирования устанавливаешь апач и последнюю версию пхп с базой. Всё это можно сделать и настроить за минут 30 - 40 при быстром интернете.
"scp Local_File username@host:/директория" Проблема в локал файле. Как блядь его правильно указать, D:\papka\test.txt не работает. !Could not resolve hostname D: Name or service not known lost connection! Окей, пишу без D, пишет, что не может найти файл. Как блядь правильно указать путь? Что считается корнем? Папка где PuTTy или Аллах. Файл который нужно залить: D:\programms\test.txt
scp не нужен. Используй Filezilla для этого, у него есть sftp (over ssh) соединение. Только через него всё скачиваю/закачиваю на удалённые сервера
Страницу полностью я хранить не могу, потому что в зависимости от пользователя там разный интерфейс (хотя это можно обойти, отдавая stub, который через JS на client side'е сам поправит все формы и меню).
Теперь вопрос: как ускорять? Я не могу миллиард страниц хранить в ОЗУ. Завозить нормальный key-value с вытеснением?
Знаю, но хотелось бы научиться работать с консолью, я не думаю, что и через консоль сложно залить, значить бы что указать
Тут https://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter5.html написано что команда в составе putty назвается pscp, а не scp (scp это та что в линуксе). И написано что надо писать pscp d:\file ...
Но если у тебя работает scp то это наверно команда не из состава путти, а из чего-то другого. Попробуй d:/files или /d/files или просто перейти в папку с файлом и писать не полный путь к нему, а только имя: scp file.txt
> Что считается корнем? Папка где PuTTy или Аллах.
Э? Не понял. Ты наверно спрашивал про то, что считается текущей папкой? В винде она написана в командной строке в подсказке и меняется командой chdir /d d:\files
>>764491
> Я не могу миллиард страниц хранить в ОЗУ
Тогда ты и отдавтать их быстро не можешь. Не очень понимаю, зачем ты там сделал самодельный кеш на shm когда есть редис умеющий сохранять данные (правда он и память ест). Все эти самодельные штуки могут тормозить в самых неожиданных местах.
Вообще, в твоем случае я думаю надо оптимизировать запросы, ставить индексы. Если это все правильно сделано и все равно скорости не хватает, значит надо либо больше памяти либо больше серверов. либо SSD диски если вдруг ты их не используешь.
Вот ты написал "все пыхтит" но я не вижу даже предварительно анализа. Что тормозит? база медленно отдает ответы? или на строне пхп что-то? Что загружено. а что нет? На сколько процентов загружены ядра, сколько памяти занято, сколько свопа (должно быть немного), какой дисковый трафик?
Посмотрите пожалуйста. Выводит верно, но можно ли обойтись без вторых регулярок?
С хтмл, цсс, джаваскриптом проблем нет. Спс.
Задача про студентов из ОП-поста.
SSH я нахожусь на хосте. Cd у меня будет по хосту, то бишь cd domen.com/www/. А вот путь на локалке я так и не нашел :)
> Что считается корнем? Папка где PuTTy или Аллах.
>Э? Не понял. Ты наверно спрашивал про то, что считается текущей папкой? В винде она написана в командной строке в подсказке и меняется командой chdir /d d:\files.
Если я через terminal(ssh) нахожусь уже на сервере(ввел юзернейм, пасс), то там я понимаю где "корень". А вот как с Локальной машины(на моем компе), взять файл и залить туда. Я не знаю как найти эту картинку у себя на пк(путь правильный прописать)
Ты с сервера файл с винды хочешь скопирвать, чтоль? Чувак, так не получится.
Ты понимаешь, что на венде не запущен ssh сервер? Его там попросту нет и неизвестно, когда сделают (пилят реализацию). Я уж не говорю о том, что на машине с виндой должен быть либо белы айпи, либо как-то проброшен порт.
Поставь на винду git-sch или как он там называется, в него входит mingw и тогда сможешь делать scp на удалённый сервак
Юзай путти только для комманд на сервере, для файлов юзай файлзилу или любой другой фтп. Не еби себе мозги
Какая разница в какой среде кодить, если код в результате один и тот же?
Где тебе удобно, там и кодь.
Я лично ненавижу линукс на десктопе, например.
Поэтому пишу код под виндой.
С другой стороны, веб-кодеру до лампочки среда, пхп интерпретиеруемый язык и исполняется все равно на сервере.
Столкнулся тут с тем, что часто годные мануалы-руководства рассчитаны на Linux.
Вот взять это руководство, например:
<-----
В итоге буду ставить Linux на виртуальном диске.
А ещё буду и nginx изучать - всё равно свой реальный проект на нём буду поднимать, а не на Apache.
Мне просто блевать хочется с windows 10 и его тормозами, хотя у меня не самый слабый ноутбук. Но на мак пока денег нет.
О каких советах идет речь? Перебор for'ом когда есть key в foreach? Как видишь http://ideone.com/mHSVPc тут брать из массива нечего.
>>764645
Верно выводит. Символы, которые может различить человек, не берутся во внимание.
>>763842
Вы утрируете сложность освоения линукса.
Всё, что вам понадобится по работе, изучается за пару дней. Если хотите по-настоящему хорошо разобраться, то в виртуалке пробуете ставить Arch/Gentoo (без шуток). У первого дистрибутива шикарнейшая вики, у второго есть handbook, там тоже всё с азов. Уйдёт максимум пару недель.
Мимо-с-арча
>Символы, которые может различить человек, не берутся во внимание.
Там в "Удмуртской" латиница в каждом похожем символе, не только в заглавной Y.
Но не работает именно из-за заглавной Y. Верно все. Таких франкенштейнов никто не будет писать.
Я не понимаю твоей проблемы. При чем тут руководства? У тебя твои php скрипты должны лежать на сервере под управлением какого-нибудь апача, какая разница, в чем ты набирать код будешь?
Поставь вон virtual box
А проявить ТВОРЧЕСКИЙ ПОДХОД?
Ну вот что там например такого Linux-специфического, чего нету вообще под Windows?
А про два стула не спрашивают? Что за идиотия.
Там всё через консоль Linuxовскую, все команды.
Это просто как пример.
>>764680
Да ничего там специфического. У меня был ноутбук на Убунту - я плевал практически от всего, что там было.
Просто часто красноглазики пишут хорошие книги.
Так-то думаю просто аналогичные команды подыскивать, пока не дошёл ещё до этого руководства, читаю основное руководство по Yii2 на Гитхабе.
>>Там всё через консоль Linuxовскую, все команды.
Это просто как пример.
Да ты траллируешь чтоль? Зашёл по ssh на сервак и вбивай команды, етить колотить.
Дефолтная убунта - не линукс, а раздутая донельзя свинья с неудобным окружением рабочего стола. Соберите debian netinstall c легковесным DE/WM, а потом уже делайте выводы.
На сервак к себе в локалхост Apache24, азаза?
>>764690
Как раз буду ставить Дебиан - тоже был опыт её использования, был всем доволен.
Выводы я сделал только такие:
1. Замороченные программисты пишут хорошие книги с использованием консоли Linux.
2. Параллельно с cmd.exe надо и консоль Linux изучать.
Почему теперь латинскую "O" в "Области" не видит?
Она же есть в регулярке?
http://ideone.com/8LpdaC
Та папка, в которой ты оказываешься после логина - это не корень, а просто текущий каталог. Путь к нему можно получить через pwd и потом этот путь вписать в команду scp на винде.
> Я не знаю как найти эту картинку у себя на пк(путь правильный прописать)
А откуда у тебя взялась команда scp на винде? Из какого она пакета программ? В любом случае, я написал, перейди в папку с файлом и тогда тебе не надо указывать полный путь к нему, а достаточно указать только имя файла.
Если что мануал по командной строке есть в ОП посте, стоит почитать.
>>764688
Тут главное понимать что нам достаточно просто искать слова, содержащие буквы разных алфавитов, сами буквы перечислять не обязательно.
> \b(([А-ЯЁа-яё]+)([AaBCcEeHKMOoPpTy]+))+([А-ЯЁа-яё]*
Правило слишком сложное. Можно же написать так:
- начало слова, русские буквы, за ними латинская, за ними любые буквы
Для подсветки букв ты по моему сделал какие-то переусложненные выражения. Можно же проще: если у нас русское слово, заменяем в нем любую латинскую букву на такую же в скобках.
Для замены стоит использоваь preg_replace а не preg_match_all, она для поиска.
В общем, попробуй упростить код - там явно есть куда двигаться.
>>На сервак к себе в локалхост Apache24, азаза?
Вот же ты долбоеб-то, а
Тебе сказали - поставь виртуальную машину, на неё накати любую ос какая тебе нравится, а на ней запускай апач. Да, азазаза, на локалхосте.
У тебя не получится меня затраллить, лалка.
Ведь именно это я и собираюсь сделать - написал именно об этом в самом первом сообщении.
>Для подсветки букв ты по моему сделал какие-то переусложненные выражения. Можно же проще: если у нас русское слово, заменяем в нем любую латинскую букву на такую же в скобках.
Не работает если несколь
>Для замены стоит использоваь preg_replace а не preg_match_all, она для поиска.
Там не замена, а сборка идет.
>В общем, попробуй упростить код - там явно есть куда двигаться.
Ну ОП. Ну некуда же упрощать.
>>764700
Это баг был. Исправил. http://ideone.com/BPGuYs
Согласен, но у них более менее нормальные курсы, лучше чем у "гуру" PHP типа Михаила Русакова, Попова, Geekbrains
Не проще ли посмотреть в мануале чем ждать ответа с сосача ?
http://php.net/manual/ru/function.gettype.php
> Не работает если несколь
Должно работать, посмотри выражение внимательно - оно не запрещает слова с несколькими замененными буквами.
>>Для замены стоит использоваь preg_replace а не preg_match_all, она для поиска.
> Там не замена, а сборка идет.
Там цикл который заменяется одной строчкой с preg_replace
>>764751
gettype, is_integer, is_string, тайп хинты
>>764655
Разобраться полезно. Тем более что в командной строке и скрипты можно писать.
>>764653
По моему ты не понял в чем вопрос. Он на винде запускает клиент scp, а не сервер. Единственное что он не признается откуда он его взял. Ну и не хочет следовать моему совету и перейти в папку с нужным файлом.
> Не работает если несколь
Должно работать, посмотри выражение внимательно - оно не запрещает слова с несколькими замененными буквами.
>>Для замены стоит использоваь preg_replace а не preg_match_all, она для поиска.
> Там не замена, а сборка идет.
Там цикл который заменяется одной строчкой с preg_replace
>>764751
gettype, is_integer, is_string, тайп хинты
>>764655
Разобраться полезно. Тем более что в командной строке и скрипты можно писать.
>>764653
По моему ты не понял в чем вопрос. Он на винде запускает клиент scp, а не сервер. Единственное что он не признается откуда он его взял. Ну и не хочет следовать моему совету и перейти в папку с нужным файлом.
>Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '0' for key 'PRIMARY'' in C:\OpenServer\domains\test.local\conf\Mapper.php:19 Stack trace: #0 C:\OpenServer\domains\test.local\conf\Mapper.php(19): PDOStatement->execute() #1 C:\OpenServer\domains\test.local\index.php(15): Mapper->addName(Object(Name)) #2 {main} thrown in C:\OpenServer\domains\test.local\conf\Mapper.php on line 19
Хм, смотрю на свою же ошибку и думаю может сделать столбец таблицы автоинкрементным?
>Должно работать
Но не работает. Пройдено. http://ideone.com/jicK9b
>Там цикл который заменяется одной строчкой с preg_replace
Но не работает. Пройдено. http://ideone.com/jicK9b
Да, очистил табличку, сделал столбец id автоинкрементным и все заработало. Правильно сделал или это костыль?
> Не очень понимаю, зачем ты там сделал самодельный кеш на shm когда есть редис умеющий сохранять данные (правда он и память ест)
Понимаешь, редиску надо было учить. А самодельный key value storage через file_get_contents / file_put_contents и файловые мьютексы через flock было написать достаточно быстро. Вот когда я вдруг задумался о написании своего kv, который будет висеть в памяти, я понял, что какой-то ад творю и реально надо лучше редиску покурить.
> Вообще, в твоем случае я думаю надо оптимизировать запросы, ставить индексы.
Оптимизировано
> либо SSD диски если вдруг ты их не используешь
Нет, не использую. Был выбор между не помню чем и SSD. Выбрал не помню что
> база медленно отдает ответы?
Медленно их обрабатывает (потому что их много параллельных), но всё в индексах, проверял через explain. Точнее, так было раньше, потом я переехал на 5.7, и сейчас в процессах пусто (кроме реплики-слейва).
> или на строне пхп что-то?
Пых мне говорит (через профайлинг), что обработка (вплоть до единственного реального echo) идёт 280-500 мс. Хром уверяет, что прошло ~900 мс. Я знаю, что часть latency уходит на прокси (запрос идёт к одному серверу (с кучей IP), а он через proxy_pass на реальный сервер) и с этим ничего не сделаешь.
> На сколько процентов загружены ядра
> сколько памяти занято
прикрепил
> сколько свопа (должно быть немного)
все 4 гб это как раз kv в /dev/shm
> какой дисковый трафик?
Прикрепил iostat 5 вторым файлом
пасибки, что ответил
>>747986
> Попытался сделать DI-контейнер похожим на Pimple, до сих пор не знаю, хорошая ли это идея;
Гм, а почему бы тогда сразу pimple и не взять?
> Сессия используется только для одноразовых сообщений (пытался возложить это на куки, чтобы полностью от сессий избавиться, но не осилил, куки становятся доступными только после перезагрузки страницы).
Вообще мне не очень нравятся сессии. например, они общие для нескольких вкладок браузера. В твоем случае "одноразовые сообщения" могут сгенерироваться в одной влкадке и отобразиться в другой при неудачном стечении обстоятельств, а также потеряться при перезагрузке станицы. На мой взгляд, удобнее их делать через GET-параметр в URL, хотя можно наверно и сессии оставить.
> Как обойтись без isset в шаблонах - не знаю
Сделать так что переменные всегда передаются. Например, как описано в моем алгоритме работы с формами. Как можно писать надежный код если у тебя переменная может остутствовать? ты каждую строчку в if isset собираешься заключать?
> В валидаторе студента 2 метода: validate и checkEmailUnique. Их пришлось разделить так как в случае с обновлением данных проверять email на уникальность не нужно.
Вообще-то при обновлении тоже надо проверять. Иначе человек заменит email на уже существующий у другого студента.
> В модели студента есть поля password и passwordHash. В БД есть только второе, первое добавлено в модель, чтобы удобнее было валидировать всего студента, а не передавать переменную password отдельно.
На мой взгляд, это неудачное решение. У тебя в части случаев поле заполнено, а в части нет. Получается если какая-то функция его использует то в части случаев она не будет работать. Надо стремиться к тому чтобы код был надежным, а не закладывать мины замедленного действия.
Также, ты можешь сделать объект "форма редактирования" и в него положить студента + дополнительные поля. Или просто передавать их отдельно.
https://github.com/applejacky/students/blob/master/dump.sql#L8
> `group_name` text NOT NULL,
> `salt` text NOT NULL,
Почему TEXT? Имя группы может содержать до 65535 символов? Зато email ограничен 40 символами.
https://github.com/applejacky/students/blob/master/app/Lib/ExceptionHandler.php#L13
> public function handleError ($errno, $errstr) {
> echo "<b>Error:</b> [$errno] $errstr<br>"
Ты делаешь странные вещи. Во-первых, php из коробки умеет выводить тексты ошибок. Зачем повторять этот функционал, только теряя при этом название файла и номер строки? Более того, php по умолчанию логгирует ошибки в лог - твой код нет, php позволяет управлять выводом ошибок на экран через параметр в php.ini - твой код нет. И вдобавок выводишь пользователю непонятную надпись на английском языке.
Если ты хочешь писать обработчик ошибок, ты должен сначала разобраться в том как эта система сделана в php.
Но даже если взять тот обработчик ошибок, что встроен в php, он абсолютно неправильный. Кто в здравом уме будет при ошибке продолжать выполнение программы? Ошибка это ошибка, ее надо исправлять, а не притворяться что все нормально. Разработчики php (как и bash который поступает так же) которые когда-то эту систему сделали, сами толком не понимали как обрабатвать ошибки.
Единственный правльный вариант обработки ошибок - это выбрасывать исключение. Так сделано в Яве, Питоне и других языках. Это соответствует принципу fail fast.
Обработчик исключений тоже реализован некорреткно. Они не пишутся в лог и в итоге ты о них не узнаешь.
> <?php namespace App\Lib;
namespace надо писать на новой строке
https://github.com/applejacky/students/blob/master/public/index.php#L3
> ini_set('display_errors', 1);
Это надо писать в php.ini и разумеется отключать на боевом сервере
> require_once dirname(__DIR__) . '/vendor/autoload.php';
> use App\Lib\Request;
use должны идти раньше чем любой код кроме namespace
Тут https://github.com/applejacky/students/blob/master/public/index.php#L52 и тут https://github.com/applejacky/students/blob/master/util/StudentTableSeeder.php#L16 у тебя повторяется код создания объекта PDO. Нехорошо. У тебя должен быть бустрап-файл и его идея в том, что ты можешь подключить его и получить полностью настроенное окружение, со всеми нужными сервисами, а не копипастить код инициализации по нескольким файлам.
https://github.com/applejacky/students/blob/master/app/Lib/Request.php#L15
> switch ($method)
> case 'GET':
> $queryArray = $_GET;
> case 'POST':
Существуют другие методы например HEAD. Массивы $_POST и $_GET существуют независимо и могут быть заполнены оба в некоторых случаях.
https://github.com/applejacky/students/blob/master/app/Routing/Route.php#L39
> $placeholderInArray = key($placeholderRegexArray); // Получить первый ключ массива
> $regexRuleInArray = reset($placeholderRegexArray); // Получить первое значение массива
Как-то странно что передается массив и исопльзуется только первый элемент.
https://github.com/applejacky/students/blob/master/app/Routing/RouteRequestMatcher.php#L19
> $placeholder = key($route->placeholderRegex); // Первый ключ массива
> $ruleRegex = reset($route->placeholderRegex); // Первое значение массива
Опять же, вот это странно как-то выглядит, если ты используешь 1 элемент массива, почему не сделать 2 отдельных поля вместо массива?
И кстати, почему ты не сделал просто метод match(Request) в классе Route?
Насчет классов Request, Route - они подозрительно напоминают классы из компонентов Симфони. Может лучше было оттуда их и взять?
https://github.com/applejacky/students/blob/master/app/Routing/RouteRequestMatcher.php#L13
> if () {
> много кода
> }
Не стоит делать такие гигантские ифы. Лучше перевернуть условие и сделать
> if () {
> continue;
>}
>много кода
https://github.com/applejacky/students/blob/master/app/Routing/RouteRequestMatcher.php
Этот класс вообще странно спроектирвоан. Почему match не возвращает результат роутинга сразу, а возвращает просто true/false? Также, класс сработает некорректно если match вызвать несколько раз подряд:
$matcher->match('/');
$matcher->match('/sdadasdadad');
$matcher->getMatchedRoute() -> вернет результат для /, а не для последнего вызова
Роутер почему-то не позволяет использовать больше 1 параметра в URL. Ну может в этой задаче это и не требуется, конечно.
>>747986
> Попытался сделать DI-контейнер похожим на Pimple, до сих пор не знаю, хорошая ли это идея;
Гм, а почему бы тогда сразу pimple и не взять?
> Сессия используется только для одноразовых сообщений (пытался возложить это на куки, чтобы полностью от сессий избавиться, но не осилил, куки становятся доступными только после перезагрузки страницы).
Вообще мне не очень нравятся сессии. например, они общие для нескольких вкладок браузера. В твоем случае "одноразовые сообщения" могут сгенерироваться в одной влкадке и отобразиться в другой при неудачном стечении обстоятельств, а также потеряться при перезагрузке станицы. На мой взгляд, удобнее их делать через GET-параметр в URL, хотя можно наверно и сессии оставить.
> Как обойтись без isset в шаблонах - не знаю
Сделать так что переменные всегда передаются. Например, как описано в моем алгоритме работы с формами. Как можно писать надежный код если у тебя переменная может остутствовать? ты каждую строчку в if isset собираешься заключать?
> В валидаторе студента 2 метода: validate и checkEmailUnique. Их пришлось разделить так как в случае с обновлением данных проверять email на уникальность не нужно.
Вообще-то при обновлении тоже надо проверять. Иначе человек заменит email на уже существующий у другого студента.
> В модели студента есть поля password и passwordHash. В БД есть только второе, первое добавлено в модель, чтобы удобнее было валидировать всего студента, а не передавать переменную password отдельно.
На мой взгляд, это неудачное решение. У тебя в части случаев поле заполнено, а в части нет. Получается если какая-то функция его использует то в части случаев она не будет работать. Надо стремиться к тому чтобы код был надежным, а не закладывать мины замедленного действия.
Также, ты можешь сделать объект "форма редактирования" и в него положить студента + дополнительные поля. Или просто передавать их отдельно.
https://github.com/applejacky/students/blob/master/dump.sql#L8
> `group_name` text NOT NULL,
> `salt` text NOT NULL,
Почему TEXT? Имя группы может содержать до 65535 символов? Зато email ограничен 40 символами.
https://github.com/applejacky/students/blob/master/app/Lib/ExceptionHandler.php#L13
> public function handleError ($errno, $errstr) {
> echo "<b>Error:</b> [$errno] $errstr<br>"
Ты делаешь странные вещи. Во-первых, php из коробки умеет выводить тексты ошибок. Зачем повторять этот функционал, только теряя при этом название файла и номер строки? Более того, php по умолчанию логгирует ошибки в лог - твой код нет, php позволяет управлять выводом ошибок на экран через параметр в php.ini - твой код нет. И вдобавок выводишь пользователю непонятную надпись на английском языке.
Если ты хочешь писать обработчик ошибок, ты должен сначала разобраться в том как эта система сделана в php.
Но даже если взять тот обработчик ошибок, что встроен в php, он абсолютно неправильный. Кто в здравом уме будет при ошибке продолжать выполнение программы? Ошибка это ошибка, ее надо исправлять, а не притворяться что все нормально. Разработчики php (как и bash который поступает так же) которые когда-то эту систему сделали, сами толком не понимали как обрабатвать ошибки.
Единственный правльный вариант обработки ошибок - это выбрасывать исключение. Так сделано в Яве, Питоне и других языках. Это соответствует принципу fail fast.
Обработчик исключений тоже реализован некорреткно. Они не пишутся в лог и в итоге ты о них не узнаешь.
> <?php namespace App\Lib;
namespace надо писать на новой строке
https://github.com/applejacky/students/blob/master/public/index.php#L3
> ini_set('display_errors', 1);
Это надо писать в php.ini и разумеется отключать на боевом сервере
> require_once dirname(__DIR__) . '/vendor/autoload.php';
> use App\Lib\Request;
use должны идти раньше чем любой код кроме namespace
Тут https://github.com/applejacky/students/blob/master/public/index.php#L52 и тут https://github.com/applejacky/students/blob/master/util/StudentTableSeeder.php#L16 у тебя повторяется код создания объекта PDO. Нехорошо. У тебя должен быть бустрап-файл и его идея в том, что ты можешь подключить его и получить полностью настроенное окружение, со всеми нужными сервисами, а не копипастить код инициализации по нескольким файлам.
https://github.com/applejacky/students/blob/master/app/Lib/Request.php#L15
> switch ($method)
> case 'GET':
> $queryArray = $_GET;
> case 'POST':
Существуют другие методы например HEAD. Массивы $_POST и $_GET существуют независимо и могут быть заполнены оба в некоторых случаях.
https://github.com/applejacky/students/blob/master/app/Routing/Route.php#L39
> $placeholderInArray = key($placeholderRegexArray); // Получить первый ключ массива
> $regexRuleInArray = reset($placeholderRegexArray); // Получить первое значение массива
Как-то странно что передается массив и исопльзуется только первый элемент.
https://github.com/applejacky/students/blob/master/app/Routing/RouteRequestMatcher.php#L19
> $placeholder = key($route->placeholderRegex); // Первый ключ массива
> $ruleRegex = reset($route->placeholderRegex); // Первое значение массива
Опять же, вот это странно как-то выглядит, если ты используешь 1 элемент массива, почему не сделать 2 отдельных поля вместо массива?
И кстати, почему ты не сделал просто метод match(Request) в классе Route?
Насчет классов Request, Route - они подозрительно напоминают классы из компонентов Симфони. Может лучше было оттуда их и взять?
https://github.com/applejacky/students/blob/master/app/Routing/RouteRequestMatcher.php#L13
> if () {
> много кода
> }
Не стоит делать такие гигантские ифы. Лучше перевернуть условие и сделать
> if () {
> continue;
>}
>много кода
https://github.com/applejacky/students/blob/master/app/Routing/RouteRequestMatcher.php
Этот класс вообще странно спроектирвоан. Почему match не возвращает результат роутинга сразу, а возвращает просто true/false? Также, класс сработает некорректно если match вызвать несколько раз подряд:
$matcher->match('/');
$matcher->match('/sdadasdadad');
$matcher->getMatchedRoute() -> вернет результат для /, а не для последнего вызова
Роутер почему-то не позволяет использовать больше 1 параметра в URL. Ну может в этой задаче это и не требуется, конечно.
>>747986
> BadMethodCallException(sprintf('В классе "%s" не существует действия "%s".
Исключения обычно пишутся на английском, они же не для пользователей предназначены.
https://github.com/applejacky/students/blob/master/app/Lib/DiContainer.php#L7
> public function offsetExists($offset)
Для контейнеров есть PSR с описанием интерфейса и там описан метод get(), а тебя его нет.
Контейнер реализован неправильно. При повторном обращении надо возвращать ранее созданный экземпляр сервиса, а не создавать новый.
https://github.com/applejacky/students/blob/master/app/Lib/DiContainer.php#L23
> if (is_callable($value)) {
Тут надо использовать тайп хинт callable
https://github.com/applejacky/students/blob/master/app/Lib/Response.php#L69
> public function sendHeaders()
Вот интересно, а в какой ситуации может понадобиться этот метод? Я что-то не понимаю. Какой смысл отправлять заголовки и не отправлять например остальное?
https://github.com/applejacky/students/blob/master/app/Controller/Controller.php#L19
> require_once dirname(__DIR__) . '/functions.php';
У нас же ООП, используй класс со статическими методами
https://github.com/applejacky/students/blob/master/app/Controller/Controller.php#L23
тут не сделана обработка исключений в процессе вывода шаблона. У тебя все, что успело вывестись, вывалится на экран.
https://github.com/applejacky/students/blob/master/app/Controller/StudentController.php#L19
> if ($recordsPerPage > $recordsCount) {
> $recordsPerPage = 5;
> }
Логика немного непонятна. тут нет ошибки?
> $students = $this->studentMapper->findAllWith($search, $sortBy, $order, $page, $recordsPerPage);
Универсальнее передавать offset, а не номер страницы. А так ты возлагаешь функцию пагинации на маппер.
https://github.com/applejacky/students/blob/master/app/Controller/StudentController.php#L38
> if (is_null($this->studentMapper->findById($id))) {
> return $response->withRedirect('/', "Студент с id {$id} не найден");
> };
для случаев когда страница не найдена есть специальный HTTP код 404. Редирект обычно исплоьзуется когда страница переехала по другому адресу или после ПОСТ запроса. Не стоит его ставить всегда.
https://github.com/applejacky/students/blob/master/app/Controller/FormController.php
лучше сделать как описано в моему алгоритме работы с формами. Вместо 4 однотипных методов, для форм регистрации и редактирования достаточно одного общего метода.
> if (is_null($student)) {
> return $response->withRedirect('/');
> }
нехорошо, что ты молча без объяснений зачем-то редиректишь человека на главную.
https://github.com/applejacky/students/blob/master/app/Controller/FormController.php#L119
> $errors = $this->studentValidator
> ->validate($student)
> ->getAllErrors();
тут лучше вернуть ошибки сразу. Почему у тебя одна функция для проверки, а другая для возврата результата? Это вообще не очень удачная идея, так как надо знать в каком порядке вызвать функции, если его перепутать, можно получить не те данные.
https://github.com/applejacky/students/blob/master/app/Database/AbstractMapper.php#L69
> return static::mapObject($result);
mapObject - не статическая функция, а ты ее вызываешь как статическую.
https://github.com/applejacky/students/blob/master/app/Database/AbstractMapper.php#L97
> public function __destruct()
> {
> $this->pdo = null;
Этот метод не требуется.
$table лучше сделать не статическим полем, а абстрактным методом.
https://github.com/applejacky/students/blob/master/app/Database/StudentMapper.php#L72
> LIMIT {$start}, {$recordsPerPage}
Тут SQL инъекция по моему
https://github.com/applejacky/students/blob/master/app/Exception/ClassNotFoundException.php#L7
Непонятно что тут делает ucfirst
https://github.com/applejacky/students/blob/master/app/Helper/Auth.php#L72
Непонятно почему при разлогинивании стирается CSRF токен и почему с ним работает класс авторизации, а не класс отвечающий за CSRF. Явно нарушение инкапсуляции.
https://github.com/applejacky/students/blob/master/app/Helper/Csrf.php#L21
Тут токену не продлевается время жизни если он уже есть
https://github.com/applejacky/students/blob/master/app/Helper/StringGenerator.php#L18
Не очень понятно почему этот метод тут, а не в классе авторизации.
https://github.com/applejacky/students/blob/master/app/Helper/Auth.php#L39
Метод регистрации на мой взгляд сделан не универсально. Что если мы хотим зарегистрировать студента не ставя кук или из консоли?
Тут https://github.com/applejacky/students/blob/master/app/View/form/index.phtml надо добавить HTML5 валидацию. Объект Student удобнее передавать всегда, а не делать кучу isset.
https://github.com/applejacky/students/blob/master/app/View/student/index.phtml#L6
> ", удовлетворяющие запросу {$search}, "
Тут xss по моему
>>747986
> BadMethodCallException(sprintf('В классе "%s" не существует действия "%s".
Исключения обычно пишутся на английском, они же не для пользователей предназначены.
https://github.com/applejacky/students/blob/master/app/Lib/DiContainer.php#L7
> public function offsetExists($offset)
Для контейнеров есть PSR с описанием интерфейса и там описан метод get(), а тебя его нет.
Контейнер реализован неправильно. При повторном обращении надо возвращать ранее созданный экземпляр сервиса, а не создавать новый.
https://github.com/applejacky/students/blob/master/app/Lib/DiContainer.php#L23
> if (is_callable($value)) {
Тут надо использовать тайп хинт callable
https://github.com/applejacky/students/blob/master/app/Lib/Response.php#L69
> public function sendHeaders()
Вот интересно, а в какой ситуации может понадобиться этот метод? Я что-то не понимаю. Какой смысл отправлять заголовки и не отправлять например остальное?
https://github.com/applejacky/students/blob/master/app/Controller/Controller.php#L19
> require_once dirname(__DIR__) . '/functions.php';
У нас же ООП, используй класс со статическими методами
https://github.com/applejacky/students/blob/master/app/Controller/Controller.php#L23
тут не сделана обработка исключений в процессе вывода шаблона. У тебя все, что успело вывестись, вывалится на экран.
https://github.com/applejacky/students/blob/master/app/Controller/StudentController.php#L19
> if ($recordsPerPage > $recordsCount) {
> $recordsPerPage = 5;
> }
Логика немного непонятна. тут нет ошибки?
> $students = $this->studentMapper->findAllWith($search, $sortBy, $order, $page, $recordsPerPage);
Универсальнее передавать offset, а не номер страницы. А так ты возлагаешь функцию пагинации на маппер.
https://github.com/applejacky/students/blob/master/app/Controller/StudentController.php#L38
> if (is_null($this->studentMapper->findById($id))) {
> return $response->withRedirect('/', "Студент с id {$id} не найден");
> };
для случаев когда страница не найдена есть специальный HTTP код 404. Редирект обычно исплоьзуется когда страница переехала по другому адресу или после ПОСТ запроса. Не стоит его ставить всегда.
https://github.com/applejacky/students/blob/master/app/Controller/FormController.php
лучше сделать как описано в моему алгоритме работы с формами. Вместо 4 однотипных методов, для форм регистрации и редактирования достаточно одного общего метода.
> if (is_null($student)) {
> return $response->withRedirect('/');
> }
нехорошо, что ты молча без объяснений зачем-то редиректишь человека на главную.
https://github.com/applejacky/students/blob/master/app/Controller/FormController.php#L119
> $errors = $this->studentValidator
> ->validate($student)
> ->getAllErrors();
тут лучше вернуть ошибки сразу. Почему у тебя одна функция для проверки, а другая для возврата результата? Это вообще не очень удачная идея, так как надо знать в каком порядке вызвать функции, если его перепутать, можно получить не те данные.
https://github.com/applejacky/students/blob/master/app/Database/AbstractMapper.php#L69
> return static::mapObject($result);
mapObject - не статическая функция, а ты ее вызываешь как статическую.
https://github.com/applejacky/students/blob/master/app/Database/AbstractMapper.php#L97
> public function __destruct()
> {
> $this->pdo = null;
Этот метод не требуется.
$table лучше сделать не статическим полем, а абстрактным методом.
https://github.com/applejacky/students/blob/master/app/Database/StudentMapper.php#L72
> LIMIT {$start}, {$recordsPerPage}
Тут SQL инъекция по моему
https://github.com/applejacky/students/blob/master/app/Exception/ClassNotFoundException.php#L7
Непонятно что тут делает ucfirst
https://github.com/applejacky/students/blob/master/app/Helper/Auth.php#L72
Непонятно почему при разлогинивании стирается CSRF токен и почему с ним работает класс авторизации, а не класс отвечающий за CSRF. Явно нарушение инкапсуляции.
https://github.com/applejacky/students/blob/master/app/Helper/Csrf.php#L21
Тут токену не продлевается время жизни если он уже есть
https://github.com/applejacky/students/blob/master/app/Helper/StringGenerator.php#L18
Не очень понятно почему этот метод тут, а не в классе авторизации.
https://github.com/applejacky/students/blob/master/app/Helper/Auth.php#L39
Метод регистрации на мой взгляд сделан не универсально. Что если мы хотим зарегистрировать студента не ставя кук или из консоли?
Тут https://github.com/applejacky/students/blob/master/app/View/form/index.phtml надо добавить HTML5 валидацию. Объект Student удобнее передавать всегда, а не делать кучу isset.
https://github.com/applejacky/students/blob/master/app/View/student/index.phtml#L6
> ", удовлетворяющие запросу {$search}, "
Тут xss по моему
> Но не работает. Пройдено.
Ты ведь сделал не так, как я написал. по прежнему регулярка переусложнена и ее можно упростить.
Для замены придется конечно написать отдельное выражение, а не использовать то же что и для поиска. Заменять достаточно просто все буквы противоположного алфавита, а не писать сложных выражений.
>>764851
> . А самодельный key value storage через file_get_contents / file_put_contents и файловые мьютексы через flock было написать достаточно быстро.
Он может быть в итоге бутылочным горшылком. Блокировки например не позволяют параллельно его использовать.
Судя по скриншоту у тебя большинство ядер не нагружено. Там и нагрузки-то серьезной не видно. На диск тоже нет нагрузки никакой.
> Пых мне говорит (через профайлинг), что обработка (вплоть до единственного реального echo) идёт 280-500 мс.
Посмотри тогда еще профайлером какие именно функции сколько выполняются. На что уходят эти 500 мс. Или можешь натыкать echo с выводом прошедшего времени. opcache кстати настроен?
> Блокировки например не позволяют параллельно его использовать
При чтении там flock set, file_get_contents, flock release. Если туда никто не писал, это должно быть очень быстрым. И, да, я замерял чисто свой kv, и на запись и на чтение параллельно во много процессов. Дело не в нём, он вполне быстрый (только fopen делает, а это само по себе долго, даже если tmpfs)
> Посмотри тогда еще профайлером какие именно функции сколько выполняются
Уже смотрел. Много времени уходит на ob. То есть на
[CODE]
<?php
$ts=[microtime(true)];
ob_start();
$ts[]=microtime(true);
for ($i=0;$i<1000;$i++){
echo mt_rand(0,10000)."\n";
}
$ts[]=microtime(true);
$buf=ob_get_contents(); // Здесь ~300 килобайт в живом коде
$ts[]=microtime(true);
ob_end_clean();
[/CODE]
Вот очень много (~150-170) уходит на ob_get_contents. Уж не знаю с чем это связано. С иммутабельными строками или что там под капотом.
Вообще я забыл сказать, что часть своих профилирований я делал ещё на php 5.6, а после того как переехал на php 7, стало не хватать времени
> opcache кстати настроен?
Я не знаю почему, но он не настроен был из коробки. Я неделю назад его поставил руками и стало ещё шустрее. Вот сейчас у меня как раз 300-500 мс на выполнение кода, а раньше (на php 5.6) там могла быть целая секунда. Но не факт, что дело в PHP. У меня в MySQL 5.5 постоянно висели insert delayed, которые спаунились самописным писателем логов запросов к веб-страницам в memory таблицу mysql (откуда потом отдельный скрипт всё перекладывал в нормальную таблицу. insert delayed доступен только для memory), после переезда на 5.7 они все пропали, видимо код в mysql переписали.
[CODE]
public function getDatetime($ch)
{
$STH = $this->DBH->prepare("SELECT datetime FROM mainTable WHERE ch = :ch");
$STH->bindValue(":ch", $ch);
$STH->execute();
}
[/CODE]
Мне нужно взять значение переменной $ch из инпута. КАК? Я просто уже ничего не понимаю.
$inputCh = $_POST["ch"];
getDatetime($inputCh);
"ch" - имя элемента в HTML форме, задавать через аттрибут name. http://www.w3schools.com/tags/att_input_name.asp
Так же, почитай урок ОПа про обработку форм https://github.com/codedokode/pasta/blob/master/forms.md
Вообще я придумал такую систему:
1. nginx кеширует по URL'у
2. PHP отдаёт stub без управляющих элементов, которые появляются, если пользователь залогинился
3. Страница после загрузки обращается через json к PHP, который отдаёт данные. JS меняет элементы, перерисовывает всякую херню и так далее. Таким образом всем отдаётся один контент
4. Если к странице обращается админ, то PHP выдаёт конкретное поле в хидерах, увидев которое, nginx не кеширует эту страницу (там управляющие элементы только для админа сайта, например, удаление камента). Также nginx не кеширует, если обращение сделал бот Гугла/Яндекса (по IP или по User-Agent'у)
5. Страница хранится в памяти nginx в течение N времени
Вы не подскажете, можно ли nginx (без lua-скриптов) настроить так, чтобы он, увидев, что к какому-то урлу обращаются часто (более N раз за L времени), страницу хранил дольше других? Например, просто страницу хранить 90 минут. А к этой странице за час обратились уже 5 раз, её мы сохраним на день.
И можно ли как-то девалидировать запиши в кеше nginx'а (на такой-то странице, например, появился новый комментарий, а значит контент должен уходить другой)?
>Ты ведь сделал не так, как я написал.
Исправил все http://ideone.com/BPGuYs
По регуляркам куча материала осталась. Про то как их составлять. Изучить это или можно двигаться дальше?
Это index.php: http://ideone.com/kCtER4
Это mapper.php: http://ideone.com/dFlDK6
Почему строка <p><?php count($timers); ?></p> ничего не возвращает, хотя таймеров в таблице четыре штуки?
Уточню, что var_dump($timers) возвращает NULL
<?php
$val = $myClass->getValue();
?>
<script>
myPlugin.start($val);
</script>
Заранее благодарю.
Конечно, я же говорю - переменная печатается вне джаваскрипта, а внутри вот что консолька говорит
<div id='lol' data-val=<?php echo $val; ?>>
<script>
var val = $('#lol').attr('data-val')
</script>
Ебанутый? У меня переменная меняется, например. Первый скрин смотри.
>>765076
Што? Я скопировал так, как мне здесь подсказали >>765057
Ну значит я неправильно подсказал, заключай в кавычки и не задавай лишних вопросов.
>Но что-то мне подсказывает, что это просто пиздец какой костыль.
Так и есть. По хорошему надо было аяксом делать запрос на отдельную страницу и получать оттуда то что тебе нужно.
Да как оно могло не заработать? Это ответ с первой ссылки на стаковерфлоу. Я сам почти так же делал, только через шаблонизатор твиг.
да я пошутил, конечно это костыль. то решение должно заработать, или как альтернатива
<?php
echo "<script> var val=".$val."</script>";
?>
Во, это уже лучше. Хоть нигде не палится.
а вообще ты что-то неправильно делаешь. для js есть один кошерный способ получить что-то от сервера - ajax. что это за данные? если они отображаются на странице то лучше парсить html, а не такой ерундой страдать.
Я так и понял, изначально думал, что мне как раз с аяксом помогут.
Сама страничка - набор таймеров для разных событий.
$.ajax({
type: 'POST',
url: 'script.php',
data: 2,
success: function(response) { console.log(response)} //выведет 4
});
--------------
<?php
var input = $_POST;
echo 2*input;
?>
могу обосраться с преобразованием типов обычно передают не примитивы а json-объект, но идею ты понел
>data: 2,
А это по идее запишет в пост по ключу "2" пустое значение, но не уверен, никогда такое говно не писал
ну мне влом было мастерить идеоне и вспоминать, хрен ли ты докопался?
>>765106
>>765102
>>765100
>>765097
Зачем тащить быдлокод из интернета сюда? Если ты не знаешь php или яваскрипт на достаточном уровне то надо их подучить а не копипастить бездумно то, что написал какой-то быдлокодер. Переменая в скрипт подставляется очень просто. Во-первых, не надо смешивать в кучу JS код и вставки PHP, вынеси их отдельно:
var a = ...;
var b = ...;
doSomething(a, b);
Во-вторых, какой аякс? вы поехавшие? Аякс это "асинхронный запрос на сервер" и он используется когда тебе надо сделать что-то уже после загрузки страницы, например, отправить комментарий по нажатию кнопки или проголосовать за пост. Если тебе надо просто передать переменную то надо просто вставить ее в страницу а не усложнять код и создавать лишние запросы на сервер. Один дебил написал в интернете про аякс, а потом у кого-то сайт тормозит из-за кучи запросов.
В-третьих, данные надо экранировать. В данном случае так, чтобы какие бы значения не были в переменной, они не сломали JS код. Тут есть такие варианты:
- intval для чисел
- json_encode для строк, null, булевых знаечний или массивов
>>765068
Исходник страницы посмотреть и увидеть где ошибка ты не можешь?
>var a = ...;
>var b = ...;
>doSomething(a, b);
Это зачем приплел?
>>765250
>Во-вторых, какой аякс? вы поехавшие?
Сам ты поехавший. Аякс может быть вторым (после загрузки кода) этапом инициализации приложения - загрузкой данных.
Вообще есть 3 варианта передавать данные для инициализации:
1. Подставлять данные напрямую в JS
2. Подгружать данные через аякс.
3. Подставлять данные в разметку.
>>765250
>В-третьих, данные надо экранировать.
Ничего не надо "экранировать".
Какие преимущества дает использование аякса для загрузки данных вместо других подходов вроде вставки их в страницу?
> Ничего не надо "экранировать".
Надо иначе можем получить разновидность XSS.
>Зачем тащить быдлокод из интернета сюда?
Никакой не быдлокод, а вполне приемлемый метод.
>var a = ...;
>var b = ...;
>doSomething(a, b);
И дальше что? Как ты тут передал данные из php в js?
>создавать лишние запросы на сервер
Что значит лишние? Чувак описал юзкейс - у него на странице таймеры на js, по их срабатыванию необходимо пообщаться с сервером. Это не лишний, а необходимый запрос.
>Какие преимущества дает использование аякса для загрузки данных вместо других подходов вроде вставки их в страницу?
Кэширование кода.
Единый стиль получения данных.
Единый стиль получения данных - это на мой взгляд не аругмент так как без аякса данные получить проще (не нужен код отправки запроса, не нужна обработка ошибок в случае невозможности получить данные). То есть добавление предзагруженных данных вряд ли усложнит код больше чем на несколько строчек.
Кеширование - более интересный аргумент, действительно, можно использовать кеш браузера. Но это оправданно только в части случаев: если данные занимают большой объем, если пользователь заходит на страницу многократно, то есть это вариант не для сайтов, а скорее для приложений. И то не для всех.
Стоит также помнить что сейчас все сайты забивают кеш своими данными и шансы уже спустя сутки найти те же данные в кеше невелики.
Недостатки этого подхода: бессмысленное усложнение кода, увеличение нагрузки на сервер, сильное замедление загрузки страницы. То есть для случаев "блог" например это не годится, а годится только для каких-то приложений, которыми пользователь пользуется часто и где он готов терпеть более медленную загрузку данных. В общем, мне кажется это редко когда оправданно.
В его примере был echo, то есть вставка данных в страницу его устраивала. про таймеры по моему ты уже придумывать от себя начал.
Ты явно путаешь причину. Вот скрипт:
<?php
$ts=[microtime(true)];
ob_start();
$ts[]=microtime(true);
for ($i = 0; $i < 1000; $i++) {
echo str_repeat(" ", 300);
}
$ts[]=microtime(true);
$buf=ob_get_contents(); // Здесь ~300 килобайт в живом коде
$ts[]=microtime(true);
ob_end_clean();
$start = array_shift($ts);
var_dump(strlen($buf));
foreach ($ts as $value) {
var_dump($value - $start);
}
Я запускал его под Windows, на относительно старом железе и он работает меньше 15 мс (на винде точнее не померять через microtime). Выводит ровно 300 000 байт данных. дело не в ob_get_contents. Однако, возможно в твоем приложении устанавливается функция-фильтр которая работает с буферизованными данными. В таком случае, разумеется, все зависит от скорости ее работы. Почитать про нее можно в мануале http://php.net/manual/ru/function.ob-start.php
Насчет логов в mysql: почитай в мануале про bulk insert, там есть особые способы ускорить вставку. Например группировать данные и вставлять большими транзакциями.
Само собой я знаю про ob-фильтрацию. Я забыл сказать, что этот код тоже внутри своего ob стоит (моя цмска глушит все echo). Но дело именно в строках. Я замерял без echo внутри for i, падало координально. (А может это пыхомагия, которая видела, что return из функции никуда не идёт и просто не делала return строки)
>>765330
Я знаю, что есть куча причин почему надо вставлять много за раз (как минимум пересборка индексов). Но тут концепция такая: один заход на страницу = один insert. Но заходы вещь некритичная, поэтому insert delayed и memory table. Я хотел это переписать (через очередь сообщений, чтобы побыстрее проходило), но теперь insert delayed не висят и причины переписывать пока тоже нет
Вообще надо перезамерить весь код. После переезда на PHP7 там явно до черта изменилось в профайлинге моего кода
Судя по иерархии, там только часть кода.
Выполни мой скрипт который я привел выше. Он выводит 300 кб пробелов и на моем core2duo совсем не тормозит. Если он и на сервере у тебя не тормозит то дело не в ob.
>>764965
Там поиск слов одним выражением через preg_match_all и в цикле вывод неправильных слов с подсветкой букв через preg_replace если память мне не изменяет.
> Выполни мой скрипт который я привел выше
/delmenow.php:14:int 300000
/delmenow.php:16:float 3.0994415283203E-6
/delmenow.php:16:float 0.00055408477783203
/delmenow.php:16:float 0.00057101249694824
Но это новый пых, на нём я ещё не замерял где же там тормозит
Кэширование на уровне сервера идет.
Аякс не усложняет код. Уменьшает нагрузку на сервер за счет кэширования. Ускоряет загрузку страниц.
Вот я зашел из гугла на твой сайт. Как мне кеширование ускорит загрузку страниц?
Ты говоришь что код не усложняет, но это не совсем так. Как минимум тебе надо:
- добавить jquery
- добавить код отправки запроса
- написать код обработки ошибок
Давай бороться, я бэрилдан.
Функции и новый айпад :
http://ideone.com/Mw3lr3
Скажите, пишу как монгол ? Как можно было по-другому написать ?
Братишка посоветовал сначала запилить Студентов.
Ну а я по официальному руководству запилил, по сути, Студентов на самом Yii2 с помощью Gii.
Это обычный CRUD внутри фреймворка, ничего сверхъестественного.
Все скрипты я разобрал, которые генерируются, там всё более-менее понятно даже мне. До этого простое выведение данных из БД делал внутри фреймворка - тоже всё вроде бы понятно.
Так вот вспоминаются слова братишки о том, что всё, что у меня получится без глубокого изучения, это примитивный CRUD и всё не по ООП.
А что не CRUD и что имеется в виду "не по ООП"? Там нет процедурщины, Gii же генерирует, там ООП в основе.
Короче, немного озадачен теми словами и прошу пояснить этот момент, наверняка братишка тут снова и опять.
Просто мне кажется, вы усложняете многое правильным и последовательным подходом, хотя он и правилен и последователен. Я следовал ему как раз до Студентов.
Зачем k++ перед брейк?
И почему and, а не &&?
>>765627
Тебе будет сложно запилить свой большой модуль или действовать в обход фреймворка.
Так же зная основы проще перекатываться по технологиям и хуесосить всех за то что пишут криво.
Ну у тебя по моему далеко от решения задачи. Например, нет поля поиска, не переведены англоязычные надписи, в меню лишние ссылки, бесполезные хлебные крошки, в таблице лишние кнопки.
В студентах нет ничего сверхестественного, это задача для изучения основ, работы с таблицами, формами, и тд. Суть задачи не в том чтобы получить таблицу студентов, а чтобы узнать паттерны работы с БД, обработки данных форм итд.
> А что не CRUD и что имеется в виду "не по ООП"? Там нет процедурщины, Gii же генерирует, там ООП в основе.
мы код не видели, но насколько я знаю. gii любит генерировать копипасту, если так, то ее надо убрать. Если ты используешь фреймворк, то ООП в фрейморке - модели, формы и тд.
Если ты хочешь делать что-то на фреймворке, тебе бы стоило взять задачу посложнее, например, TestHub: https://gist.github.com/codedokode/8733007
Или например можно придумать какую-то задачу, которая потребует не просто использовать стандартные компоненты Юи, а как-то всерьез переопределить их работу. Всякие деревья, сложные валидации, составные формы, нестандартные виджеты и тд.
Оформлено плохо, я думал - гораздо лучше будет это всё.
>$k
Ну что это такое?
>>765631
>Зачем k++ перед брейк?
Иначе у него не посчитает правильно месяцы. Там во втором банке 13 месяцев должно быть.
>Тебе будет сложно запилить свой большой модуль или действовать в обход фреймворка.
Возможно, пока не пробовал ведь.
>>765633
Да, там у меня не всё, что нужно, но просто - в общих чертах.
>Суть задачи не в том чтобы получить таблицу студентов, а чтобы узнать паттерны работы с БД, обработки данных форм итд.
Это я понимаю. Ну, ознакомился сейчас с этим на примере генерируемого Gii.
>Если ты используешь фреймворк, то ООП в фрейморке - модели, формы и тд.
Да, там всё так, по ООП.
>>765638
Спасибо, интересная задача, видел пару попыток её решения.
>Время выполнения: все зависит от тебя, но я бы смотрел на 4-6 недель
Вот тут уже не будет жалко времени, спасибо.
Попробую.
Есть специальный SQL-оператор для конкатенации. Погугли.
>>765627
>Так вот вспоминаются слова братишки о том, что всё, что у меня получится без глубокого изучения, это примитивный CRUD и всё не по ООП.
Именно так я не писал. Имел в виду, что дальше CRUD'ов не зайдёшь. Задачки, которые по уровню сложности "дальше CRUD'ов", тебе уже вбросили выше.
запятых нет. Майскул же пишет перед каким местом ошибка. Алсо почитай
http://phpclub.ru/mysql/doc/string-syntax.html
http://phpclub.ru/mysql/doc/legal-names.html
> SELECT FROM `students` WHERE `name`,`sname`,`group_num`,`points`,`gender`,`email`,`b_year`,`is_resident` LIKE `%Хи%` ORDER BY `points` asc LIMIT 0 OFFSET 10
Так то же самое
> Алсо почитай
Заменить ` на ' , ты к этому
>>765666
SELECT FROM `students` WHERE CONCAT(`name`,`sname`,`group_num`,`points`,`gender`,`email`,`b_year`,`is_resident`) LIKE `%Hi%` ORDER BY `points` asc LIMIT 0 OFFSET 10
> Unknown column '%Hi%' in 'where clause'
Походу он смотрит названия колонок, а не их содердимое
>LIKE `%Hi%`
> Unknown column '%Hi%' in 'where clause'
Так `%Hi%` это не строка. Попробуй здесь поставить ' вместо символов `
Да, точно. Только вот чё он выводит 0 строк, когда их должно быть 4?
что-то у меня сегодня сложно
OFFSET убери, у ОПа в пасте написано "LIMIT 0, 10 можно читать как LIMIT 0 OFFSET 10".
И ещё, тебе нужно столбцы перемежать строками с пробелом. Вот так: name, ' ', sname
Представим, что у тебя регистронезависимый поиск и ты ищешь по всем колонкам строку "голова члена". Так как у тебя итоговый CONCAT будет выглядеть как "ГоловачЛенаИТБ25100ffoomail@mail.ru", эта срока выберется как подходящая.
Не "голова члена", а "член". Извиняюсь, я спать.
$sql = "SELECT FROM Orders LIMIT 10 OFFSET 15";
You could also use a shorter syntax to achieve the same result:
$sql = "SELECT FROM Orders LIMIT 15, 10";
Странно, а у меня такая же ошибка, как и у анона выше, если использую LIMIT 0 OFFSET 10 вместо LIMIT 0, 10
> Ты представляешь, как делается редирект? Редирект это отдача в качестве ответа кода 3xx и заголовка Location. Редиректить при ошибке, как и при 404 или 403 - неправильно. Вдобавок, в адресной строке браузера теряется УРЛ и обновить страницу нельзя.
А как нужно? include'ом получается коряво. Средствами Apache? Копипаста с инета не работает
зачем убирать? с offset же понятнее
>>765710
не факт. Я бы вообще в поисковой строке заменял пробел на % чтобы лучше искалось
>>765725
Нужно отдавать нужный код и выводить нужную страницу. как ты это будешь делать, стандарт HTTP не волнует.
Посмотри что с адресной строкой бразера при редирректе происходит.
Ну вот смотри что будет при include() в set_exception_handler
Я не знаю как это можно исправить
echo там в целях дебага, в реальном кейсе его там конечно же нет. И у меня страница и правда с таймерами, я выше уточнял.
>В-третьих, данные надо экранировать.
Не думаю, поскольку переменные передаются в джаваскрипт из пхп и там остаются навсегда, а не наоборот.
А вообще я всегда открыт для чего-то нового, и если у тебя есть еще какой-то способ передачи переменных, то говори.
>>Как это вообще, влияет на работу
>Устроился вот ты в офис, приходишь в первый день, тебе дают компьютер на котором стоит чистый линукс без интерфейса
Насмотрелся на личностей, гордо именующих себя линуксоидами, так вот, ни один полноценно по профилю не работал в течение двух-трех первых дней, несмотря даже что линукс с интерфейсом. Как с момента устройства на работу, так и с момента апгрейда железа с переустановкой любимой системы. То что-то не заводится, то права на папки блядь, то понос то золотуха, сидит ковыряется под капотом, когда любой виндоёб просто скачивает XAMPP/Денвер и не ебет мозги ни себе, ни окружающим.
В офисах может быть проблема с лицензированием Windows и и любого другого платного софта (пакета офис и прочего). Многие из-за этого в офисы ставят только линукс, ну и от кандидатов на работу требуют умения установить и настроить апач с php, поднять базу и прочего.
Это что патерн такой? Я 1.5 часа в коде рылся что бы найти, я чего то не понимаю или просто фреймворк хуйня?
Что посоветуете, отцы?
https://secure.php.net/manual/ru/class.errorexception.php
Здесь код обработчиков: http://pastebin.com/PGitKBtx
Неймспейсы не используются.
А include 'несуществующий_путь' обработчик ловит. Насколько я понял, это связано с уровнем ошибки, которую выбрасывает require, а именно E_COMPILE_ERROR. Из-за этой ошибки скрипт умирает прежде, чем обработчик ошибок перехватывает ошибку.
Гугл советует использовать register_shutdown_function(), но все мои попытки вызвать throw ErrorException внутри неё безуспешны. Может забить на это? Переделываю студентов.
На мое усмотрение там такая куча материала что прифитнее выучить базовые принципы а дальше по надобности открывать шпаргалку в случае заданий.
https://github.com/TheSidSpears/Students/blob/master/app/controllers/edit.php
https://github.com/TheSidSpears/Students/blob/master/app/controllers/register.php
Я вот думал, например это
$token= (isset($_COOKIE['token'])) ? $_COOKIE['token'] : Util::randHash(20);
setcookie('token',$token,time()+3600,'/',null,false,true);
засунуть в ф-ию Util::token(). Но тогда ф-ия будет работать с куками и устанавливать глобальную переменную, что, наверное, не очень правильно
Дайте ссылку на ОПа или на нормальный мануал по паттернам, пожалуйста!
>>748292
>>755118
https://github.com/TheSidSpears/Students/blob/master/students.sql#L31
> `hash` text NOT NULL,
Неудачный тип поля на мой взгляд - почему TEXT? Он для строк длиной до 65535 символов, и вряд ли хеш будет такой длины, плюс я не уверен можно ли по нему сделать индекс в дальнейшем.
https://github.com/TheSidSpears/Students/blob/master/errors.log
Этот файл надо убрать из репозитория, добавив в .gitignore и сделав git rm с нужными флагами
https://github.com/TheSidSpears/Students/blob/master/config.json
Не стоит делать конфиг слишком большим. В конфиг мы выносим то, что будет менять конечный пользователь. Название папки с контроллерами вряд ли имеет смысл менять.
https://github.com/TheSidSpears/Students/blob/master/public/503.php#L1
> header(' ', true, 503);
Что это за синтаксис? Что за пустой заголовок? По моему это не будет работать. Там надо отправлять заголоок вроде HTTP/1.1 503 xxxx, почитай хотя бы мануал по функции header().
> <a href='index.php'>На главную</a>
У тебя URL главной - это index.php или / ? Желательно иметь для одной страницы один УРЛ.
https://github.com/TheSidSpears/Students/blob/master/public/503.php#L11
> $array=file('errors.log');
ЧТо если файл огромный? Это будет медленно и займет много памяти. Ну и не очень понятно, зачем ты вообще выводишь лог для посетителей сайта.
> for ($i=$count-21; $i < $count; $i++) {
А что если в файле меньше 21 строки? Плюс, ты выводишь данные без экранирования и тут явно может быть XSS если HTML код от злоумышлеенника попадет в сообщение в логе. Ты путаешь язык HTML и простой текстовый файл. В HTML некоторые символы имеют специальное знаечние (например < обозначает начало тега) и нельзя просто так выводить произвольный текст.
Кстати, раз ты путаешься с этим, реши-ка задачку на экранирование отсюда (и заодно прочитай сам урок): https://github.com/codedokode/pasta/blob/master/soft/web-server.md#Экранирование
https://github.com/TheSidSpears/Students/blob/master/public/index.php
> chdir ('../')
Вместо того, чтобы полагаться на текущий каталог, лучше использовать абсолютные пути, например, содержащие _ _ DIR _ _ в начале. А то у тебя код получается зависит от того праивльный ли текущий каталог задан, что создает возможности сделать ошибку.
https://github.com/TheSidSpears/Students/blob/master/app/bootstrap.php#L12
А зачем заводить свой собственный лог? Не лучше ли писать в стандартный лог PHP? ты кстати, знаешь, где он находится?
https://github.com/TheSidSpears/Students/blob/master/app/bootstrap.php#L20
> spl_autoload_register(
Тут незачем делать 2 автозагрузчика, проще сделать один, который проверяет разные пути. А еще лучше, конечно, было бы использовать PSR-4 при выборе названий классов и файлов.
https://github.com/TheSidSpears/Students/tree/master/app/models
Тут в папку свалены разные классы, часть из которых точно не модели - например, FrontController никак моделью не является. Роутер явно не является частью модели. И вообще, MVC не значит что у тебя должно быть ровно 3 папки view, controller и model. Это деление приложения на 3 части, а не файлов на 3 папки.
https://github.com/TheSidSpears/Students/blob/master/app/models/FrontController.php
Тут единственная функция со стеной кода. Учись разбивать код на части и выносить в отдельные функции. Я тут явно вижу функции вроде определения контроллера или вроде вывода шаблона.
> if ($authorized){
> //Для вида
> $userName=$authorized['name'];
Неправильно что переменная модет существовать, а может и нет. Как в таком случае писать надежный код если ты даже не знаешь, есть ли такая переменная?
> //Подключаем контроллер
> if (!empty($controller)){
А если она пусто то что? Выведем белую страницу?
> if (!empty($view)){
Опять же, мне это не нравится, ты полагаешься на то, что код где-то в другом месте приложения выставит переменную. Это очень неочевидно и ненадежно, как мне кажется.
https://github.com/TheSidSpears/Students/blob/master/app/models/JSON.php
json_decode может вернуть null если в JSON ошибка. Тут нет такой проверки.
Блок кода после if должен быть в фигурных скобках.
https://github.com/TheSidSpears/Students/blob/master/app/models/JSON.php
Название класса мало что говорит о его функции. Надо назвать вроде ConfigLoader.
https://github.com/TheSidSpears/Students/blob/master/app/models/Router.php
Для "игнорирования" query string праивльне использовать функцию parse_url а не самодельный сомнительный код. Он еще и работает неправильно в случае /a/b/c?d=e/f
Далее, ты разбиваешь УРЛ на части и берешь последнюю, а что если УРЛ имеет вид /a/b/c/d/e/f - ты берешь только f, а остальные игнорируются?
> if( ($module=='index.php') or ($module=='')){
Непонятно зачем разрешать УРЛ содержащий index.php? У тебя же возможность задавать произвольные УРЛ есть.
https://github.com/TheSidSpears/Students/blob/master/app/controllers/main.php
Если ты используешь ООП, почему бы и контроллер не сделать классом?
> $db=new DataBase($config['db']);
Это раскидано в нескольких местах кода. Вообще-то идея была, чтобы в bootstrap создать нужные объекты один раз. Ты создаешь несколько соединений с базой данных например, несколько StudentDataGateway. Это не очень логично.
> if($currentPage<=0){$currentPage=1;}
Тебе надо лучше форматировать код. Иф пишется в 3 строки, а не в одну. Также, тут можно было обойтись функцией max.
https://github.com/TheSidSpears/Students/blob/master/app/models/ViewHelper.php
Тут оформление кода ужасное. Что за полотна из пустых строк? Почему скобка на одной строке с заголовком функции?
> $routes = explode('/', $_SERVER['REQUEST_URI']);
> $routes[count($routes)-1]=$url;
Это копипаста (причем неточная) кода из роутера. Почему у тебя разбор УРЛ сделан в 2 разных местах, причем еще и по-разному? Принцип "единой ответственности", когда за каждую задачу отвечает кто-то один, не соблюдается.
> static function html($string,$find=NULL){
По моему экранирование и подсветка совпадений - это две разные функции.
>>748292
>>755118
https://github.com/TheSidSpears/Students/blob/master/students.sql#L31
> `hash` text NOT NULL,
Неудачный тип поля на мой взгляд - почему TEXT? Он для строк длиной до 65535 символов, и вряд ли хеш будет такой длины, плюс я не уверен можно ли по нему сделать индекс в дальнейшем.
https://github.com/TheSidSpears/Students/blob/master/errors.log
Этот файл надо убрать из репозитория, добавив в .gitignore и сделав git rm с нужными флагами
https://github.com/TheSidSpears/Students/blob/master/config.json
Не стоит делать конфиг слишком большим. В конфиг мы выносим то, что будет менять конечный пользователь. Название папки с контроллерами вряд ли имеет смысл менять.
https://github.com/TheSidSpears/Students/blob/master/public/503.php#L1
> header(' ', true, 503);
Что это за синтаксис? Что за пустой заголовок? По моему это не будет работать. Там надо отправлять заголоок вроде HTTP/1.1 503 xxxx, почитай хотя бы мануал по функции header().
> <a href='index.php'>На главную</a>
У тебя URL главной - это index.php или / ? Желательно иметь для одной страницы один УРЛ.
https://github.com/TheSidSpears/Students/blob/master/public/503.php#L11
> $array=file('errors.log');
ЧТо если файл огромный? Это будет медленно и займет много памяти. Ну и не очень понятно, зачем ты вообще выводишь лог для посетителей сайта.
> for ($i=$count-21; $i < $count; $i++) {
А что если в файле меньше 21 строки? Плюс, ты выводишь данные без экранирования и тут явно может быть XSS если HTML код от злоумышлеенника попадет в сообщение в логе. Ты путаешь язык HTML и простой текстовый файл. В HTML некоторые символы имеют специальное знаечние (например < обозначает начало тега) и нельзя просто так выводить произвольный текст.
Кстати, раз ты путаешься с этим, реши-ка задачку на экранирование отсюда (и заодно прочитай сам урок): https://github.com/codedokode/pasta/blob/master/soft/web-server.md#Экранирование
https://github.com/TheSidSpears/Students/blob/master/public/index.php
> chdir ('../')
Вместо того, чтобы полагаться на текущий каталог, лучше использовать абсолютные пути, например, содержащие _ _ DIR _ _ в начале. А то у тебя код получается зависит от того праивльный ли текущий каталог задан, что создает возможности сделать ошибку.
https://github.com/TheSidSpears/Students/blob/master/app/bootstrap.php#L12
А зачем заводить свой собственный лог? Не лучше ли писать в стандартный лог PHP? ты кстати, знаешь, где он находится?
https://github.com/TheSidSpears/Students/blob/master/app/bootstrap.php#L20
> spl_autoload_register(
Тут незачем делать 2 автозагрузчика, проще сделать один, который проверяет разные пути. А еще лучше, конечно, было бы использовать PSR-4 при выборе названий классов и файлов.
https://github.com/TheSidSpears/Students/tree/master/app/models
Тут в папку свалены разные классы, часть из которых точно не модели - например, FrontController никак моделью не является. Роутер явно не является частью модели. И вообще, MVC не значит что у тебя должно быть ровно 3 папки view, controller и model. Это деление приложения на 3 части, а не файлов на 3 папки.
https://github.com/TheSidSpears/Students/blob/master/app/models/FrontController.php
Тут единственная функция со стеной кода. Учись разбивать код на части и выносить в отдельные функции. Я тут явно вижу функции вроде определения контроллера или вроде вывода шаблона.
> if ($authorized){
> //Для вида
> $userName=$authorized['name'];
Неправильно что переменная модет существовать, а может и нет. Как в таком случае писать надежный код если ты даже не знаешь, есть ли такая переменная?
> //Подключаем контроллер
> if (!empty($controller)){
А если она пусто то что? Выведем белую страницу?
> if (!empty($view)){
Опять же, мне это не нравится, ты полагаешься на то, что код где-то в другом месте приложения выставит переменную. Это очень неочевидно и ненадежно, как мне кажется.
https://github.com/TheSidSpears/Students/blob/master/app/models/JSON.php
json_decode может вернуть null если в JSON ошибка. Тут нет такой проверки.
Блок кода после if должен быть в фигурных скобках.
https://github.com/TheSidSpears/Students/blob/master/app/models/JSON.php
Название класса мало что говорит о его функции. Надо назвать вроде ConfigLoader.
https://github.com/TheSidSpears/Students/blob/master/app/models/Router.php
Для "игнорирования" query string праивльне использовать функцию parse_url а не самодельный сомнительный код. Он еще и работает неправильно в случае /a/b/c?d=e/f
Далее, ты разбиваешь УРЛ на части и берешь последнюю, а что если УРЛ имеет вид /a/b/c/d/e/f - ты берешь только f, а остальные игнорируются?
> if( ($module=='index.php') or ($module=='')){
Непонятно зачем разрешать УРЛ содержащий index.php? У тебя же возможность задавать произвольные УРЛ есть.
https://github.com/TheSidSpears/Students/blob/master/app/controllers/main.php
Если ты используешь ООП, почему бы и контроллер не сделать классом?
> $db=new DataBase($config['db']);
Это раскидано в нескольких местах кода. Вообще-то идея была, чтобы в bootstrap создать нужные объекты один раз. Ты создаешь несколько соединений с базой данных например, несколько StudentDataGateway. Это не очень логично.
> if($currentPage<=0){$currentPage=1;}
Тебе надо лучше форматировать код. Иф пишется в 3 строки, а не в одну. Также, тут можно было обойтись функцией max.
https://github.com/TheSidSpears/Students/blob/master/app/models/ViewHelper.php
Тут оформление кода ужасное. Что за полотна из пустых строк? Почему скобка на одной строке с заголовком функции?
> $routes = explode('/', $_SERVER['REQUEST_URI']);
> $routes[count($routes)-1]=$url;
Это копипаста (причем неточная) кода из роутера. Почему у тебя разбор УРЛ сделан в 2 разных местах, причем еще и по-разному? Принцип "единой ответственности", когда за каждую задачу отвечает кто-то один, не соблюдается.
> static function html($string,$find=NULL){
По моему экранирование и подсветка совпадений - это две разные функции.
>>748292
>>755118
> $reg="/$find/ui";
Ты подставляешь то, что ввел пользователь, в регулярку, но что если там есть специсмволы, например, плюс, звездочка, точка? надо либо использовать str_replace либо экранировать спецсимволы с помощью preg_quote.
https://github.com/TheSidSpears/Students/blob/master/app/models/ViewHelper.php#L51
> $router=new Router();
Опять же, почему-то у тебя создается несколько экземплятров роутера в приложении.
https://github.com/TheSidSpears/Students/blob/master/app/models/ViewHelper.php#L63
> return self::html($url);
Почему функция makeUrl вызывает self::html? А что если нам нужен исходный неискаженный УРЛ (например мы хотим редиректить на него)?
> https://github.com/TheSidSpears/Students/blob/master/app/models/ViewHelper.php#L57
> foreach ($blockedParams as $key => $value) {
> $url.=$key."=".$value."&";
Что если в value содержится символ &, #, ? или какой-то еще, имеющий специальное значение в УРЛ?
https://github.com/TheSidSpears/Students/blob/master/app/models/Util.php#L12
> $result .= $array[mt_rand(0, 35)];
Число 35 надо не вписывать в код, а считать из размера массива.
https://github.com/TheSidSpears/Students/blob/master/app/controllers/edit.php
Этот класс на 90% копипаста класса register.php. Ты не должен копипастить код, надо остановиться и подумать, а как можно избежать дублирования кода? Вообще, регистрация и редактирование это практически одно и то же действие.
Те кто копипастят, не думают что будет с кодом дальше, ведь дальше им же самим придется править или добавлять что-то в несколько копий кода.
> if (isset($_COOKIE['hash'])) { //нет кука с хешем => не выполнять скрипт
> ОП, эту куку нужно как-то проверять?
Надо проверять что она соответствует реальному студенту в БД
> $token= (isset($_COOKIE['token'])) ? $_COOKIE['token'] : Util::randHash(20);
> setcookie('token',$token,time()+3600,'/',null,false,true);
Не лучше ли работу с CSRF кукой вынести в отдельный класс? Как ты повторно исплоьзуешь этот код в другом месте? Надо сделать универсальный класс, позволяющий бороться с CSRF в любом контроллере.
> foreach($editStudent as $fieldName=>&$fieldValue){
Это неправильно. В студенте могут быть поля, которые не должны быть доступны для изменения. более того, их могут добавить уже после написания этого кода.
Более того, ты не уничтожил ссылку после цикла. Перечитай мануал про foreach.
Более того, ты еще и ниже второй раз этот код скопипастил. Не копипасть.
Само редактирвоание на мой взгляд, сделано неправильно. Логичнее взять студента из БД, изменить у него часть полей и сохранить обратно. Ты же предполагаешь что все данные о студенте будет в форме. Но это не обязательно так. Что если например позже добавят какие-то скрытые поля которые есть в студенте но не редактируются через форму? Твой код будет их обнулять.
> $table->editStudent($editStudent);
> if( empty($table->userErrors) ){
Вот у тебя есть функция, которая может вернуть ошибки. Почему ты использешь лишнее поле вместо return? Вообще, это плохое поле так как например до вызова функции editStudent оно ничего не содержит. А если вызвать функцию несколько раз то ошибки накапливаются в ней и перестают соответствовать действительности. То есть это поле большую часть времени содержит недействительные данные.
> header(' ', true, 400); //Так, вроде правильней
Мало того, что это в общем неправильный синтаксис, так ты еще и дальше продолжаешь выполнять код как ни в чем не бывало.
https://github.com/TheSidSpears/Students/blob/master/app/models/DataBase.php
Что делает этот класс? Что он добавляет, чего нет в PDO?
> public function connection(){
Имена функций начинаются с глагола
https://github.com/TheSidSpears/Students/blob/master/app/models/Student.php#L11
> public $name; //string(200)
Этот комментарий может быстро устареть, если поменяют код в валидаторе.
https://github.com/TheSidSpears/Students/blob/master/app/models/StudentDataGateway.php#L38
> if (!$rows->execute()){
> throw new StudentDataGatewayException("Ошибка в ф-ии $func_name: ".__CLASS__);
Если ты используешь ERRMODE_EXCEPTION то PDO сам выкидывает искючения при ошибке. Этот иф не нужен.
https://github.com/TheSidSpears/Students/blob/master/app/models/StudentDataGateway.php#L63
> LIKE '%$search%'");
Это SQL инъекция. Не вставляй данные напрямую в запрос
> $count=$rows->fetchAll(PDO::FETCH_ASSOC);
> return $count[0]["COUNT(*)"];
В PDO есть функция чтобы вернуть первое значение из первой строки.
> foreach ($columns as &$column) {
> $column=$column["Field"];
Есть array_column для этого
> $students[]=new Student();
> $students[count($students)-1]->addInfo($studentRow);
Вместо count(...) лучше просто завести переменную для объекта
> $student=array();
> $student=$studentRow[0];
Есть функция чтобы взять толкьо первую строку результата
> $alredyRegistered=$this->checkEmail($student->email);
> if($alredyRegistered){
> $this->userErrors[]='Такой e-mail уже зарегистрирован';
Разве это не задача валидатора?
> $error_array = $this->db->errorInfo();
> if($this->db->errorCode() != 0000){
Это не надо проверять при ERRMODE_EXCEPTION
> //Исключение совпадения e-mail'ов разных юзеров
> $currentStudentData=$this->getStudentByHash($student->hash);
Это делается гораздо проще: надо просто искать по условию WHERE email = ? AND id <> ?
>>748292
>>755118
> $reg="/$find/ui";
Ты подставляешь то, что ввел пользователь, в регулярку, но что если там есть специсмволы, например, плюс, звездочка, точка? надо либо использовать str_replace либо экранировать спецсимволы с помощью preg_quote.
https://github.com/TheSidSpears/Students/blob/master/app/models/ViewHelper.php#L51
> $router=new Router();
Опять же, почему-то у тебя создается несколько экземплятров роутера в приложении.
https://github.com/TheSidSpears/Students/blob/master/app/models/ViewHelper.php#L63
> return self::html($url);
Почему функция makeUrl вызывает self::html? А что если нам нужен исходный неискаженный УРЛ (например мы хотим редиректить на него)?
> https://github.com/TheSidSpears/Students/blob/master/app/models/ViewHelper.php#L57
> foreach ($blockedParams as $key => $value) {
> $url.=$key."=".$value."&";
Что если в value содержится символ &, #, ? или какой-то еще, имеющий специальное значение в УРЛ?
https://github.com/TheSidSpears/Students/blob/master/app/models/Util.php#L12
> $result .= $array[mt_rand(0, 35)];
Число 35 надо не вписывать в код, а считать из размера массива.
https://github.com/TheSidSpears/Students/blob/master/app/controllers/edit.php
Этот класс на 90% копипаста класса register.php. Ты не должен копипастить код, надо остановиться и подумать, а как можно избежать дублирования кода? Вообще, регистрация и редактирование это практически одно и то же действие.
Те кто копипастят, не думают что будет с кодом дальше, ведь дальше им же самим придется править или добавлять что-то в несколько копий кода.
> if (isset($_COOKIE['hash'])) { //нет кука с хешем => не выполнять скрипт
> ОП, эту куку нужно как-то проверять?
Надо проверять что она соответствует реальному студенту в БД
> $token= (isset($_COOKIE['token'])) ? $_COOKIE['token'] : Util::randHash(20);
> setcookie('token',$token,time()+3600,'/',null,false,true);
Не лучше ли работу с CSRF кукой вынести в отдельный класс? Как ты повторно исплоьзуешь этот код в другом месте? Надо сделать универсальный класс, позволяющий бороться с CSRF в любом контроллере.
> foreach($editStudent as $fieldName=>&$fieldValue){
Это неправильно. В студенте могут быть поля, которые не должны быть доступны для изменения. более того, их могут добавить уже после написания этого кода.
Более того, ты не уничтожил ссылку после цикла. Перечитай мануал про foreach.
Более того, ты еще и ниже второй раз этот код скопипастил. Не копипасть.
Само редактирвоание на мой взгляд, сделано неправильно. Логичнее взять студента из БД, изменить у него часть полей и сохранить обратно. Ты же предполагаешь что все данные о студенте будет в форме. Но это не обязательно так. Что если например позже добавят какие-то скрытые поля которые есть в студенте но не редактируются через форму? Твой код будет их обнулять.
> $table->editStudent($editStudent);
> if( empty($table->userErrors) ){
Вот у тебя есть функция, которая может вернуть ошибки. Почему ты использешь лишнее поле вместо return? Вообще, это плохое поле так как например до вызова функции editStudent оно ничего не содержит. А если вызвать функцию несколько раз то ошибки накапливаются в ней и перестают соответствовать действительности. То есть это поле большую часть времени содержит недействительные данные.
> header(' ', true, 400); //Так, вроде правильней
Мало того, что это в общем неправильный синтаксис, так ты еще и дальше продолжаешь выполнять код как ни в чем не бывало.
https://github.com/TheSidSpears/Students/blob/master/app/models/DataBase.php
Что делает этот класс? Что он добавляет, чего нет в PDO?
> public function connection(){
Имена функций начинаются с глагола
https://github.com/TheSidSpears/Students/blob/master/app/models/Student.php#L11
> public $name; //string(200)
Этот комментарий может быстро устареть, если поменяют код в валидаторе.
https://github.com/TheSidSpears/Students/blob/master/app/models/StudentDataGateway.php#L38
> if (!$rows->execute()){
> throw new StudentDataGatewayException("Ошибка в ф-ии $func_name: ".__CLASS__);
Если ты используешь ERRMODE_EXCEPTION то PDO сам выкидывает искючения при ошибке. Этот иф не нужен.
https://github.com/TheSidSpears/Students/blob/master/app/models/StudentDataGateway.php#L63
> LIKE '%$search%'");
Это SQL инъекция. Не вставляй данные напрямую в запрос
> $count=$rows->fetchAll(PDO::FETCH_ASSOC);
> return $count[0]["COUNT(*)"];
В PDO есть функция чтобы вернуть первое значение из первой строки.
> foreach ($columns as &$column) {
> $column=$column["Field"];
Есть array_column для этого
> $students[]=new Student();
> $students[count($students)-1]->addInfo($studentRow);
Вместо count(...) лучше просто завести переменную для объекта
> $student=array();
> $student=$studentRow[0];
Есть функция чтобы взять толкьо первую строку результата
> $alredyRegistered=$this->checkEmail($student->email);
> if($alredyRegistered){
> $this->userErrors[]='Такой e-mail уже зарегистрирован';
Разве это не задача валидатора?
> $error_array = $this->db->errorInfo();
> if($this->db->errorCode() != 0000){
Это не надо проверять при ERRMODE_EXCEPTION
> //Исключение совпадения e-mail'ов разных юзеров
> $currentStudentData=$this->getStudentByHash($student->hash);
Это делается гораздо проще: надо просто искать по условию WHERE email = ? AND id <> ?
>>764855
Спасибо большое за ответы!
> Существуют другие методы например HEAD. Массивы $_POST и $_GET существуют независимо и могут быть заполнены оба в некоторых случаях.
Разделил на bodyParams и queryParams в классе Request: https://github.com/applejacky/students/blob/master/app/Lib/Request.php
Добавил PUT и DELETE, немножко поковырял curl, теперь можно делать так:
curl -X DELETE http://test.loc/student/12
Разумеется, это только для демонстрации работы метода DELETE. Алсо, получилось зарегаться через curl и изменить профиль: https://github.com/applejacky/students/blob/master/util/curl_rest_test.sh
>> Попытался сделать DI-контейнер похожим на Pimple, до сих пор не знаю, хорошая ли это идея;
> Гм, а почему бы тогда сразу pimple и не взять?
Читал твой пост о том, что в студентах будет много велосипедов. В файлообменнике я буду активно использовать сторонние решения.
>> В валидаторе студента 2 метода: validate и checkEmailUnique. Их пришлось разделить так как в случае с обновлением данных проверять email на уникальность не нужно.
> Вообще-то при обновлении тоже надо проверять. Иначе человек заменит email на уже существующий у другого студента.
У меня есть проверка в шаблоне, если пользователь обновляет профиль, то инпуты с паролем и email'ом не отображаются: https://github.com/applejacky/students/blob/master/app/View/form/index.phtml#L48
В контроллер приходит массив данных о студенте без пароля и email, этот массив мапится на существующего студента у которого есть email и password.
Как мне кажется, не очень хорошо, что email и password можно так просто поменять. Зависящие от них токены тоже менять придётся. Поэтому я лишил пользователя возможности менять email и пароль.
> И кстати, почему ты не сделал просто метод match(Request) в классе Route?
Ну у меня и так роут сам себя валидирует, а тут ещё и предоставить ему возможность проверять себя на соответствие Request'у. Это лучше, чем подход с RouteRequestMatcher'ом?
Алсо, сейчас в последнем только один метод: https://github.com/applejacky/students/blob/master/app/Routing/RouteRequestMatcher.php
> Насчет классов Request, Route - они подозрительно напоминают классы из компонентов Симфони.
Ты мне советовал посмотреть реализацию роутинга в компонентах Symfony, поэтому мои классы похожи.
> Может лучше было оттуда их и взять?
Я ньюфаг, какой Symfony? Я когда студентов начинал писать, то не понимал как работает многостраничное приложение, не знал как регистрировать и запоминать пользователя, не работал с неймспейсами, композером и так далее.
Мне очень нравится документация этого фреймворка и я обязательно до него доберусь, но студентов хочу сделать на велосипедах.
Алсо, вот хочу я использовать symfony/validation в файлообменнике, ищу большую-красную-кнопку вменяемый гайд, но ничего не нахожу.
https://symfony.com/doc/current/book/validation.html
https://github.com/symfony/validator
Где тут описано использование symfony/validation как standalone компонента?
>> if (is_callable($value)) {
> Тут надо использовать тайп хинт callable
Не получилось. Пик 1.
>> https://github.com/applejacky/students/blob/master/app/Controller/Controller.php#L23
> тут не сделана обработка исключений в процессе вывода шаблона. У тебя все, что успело вывестись, вывалится на экран.
Сделал обработчик ошибок по-человечески и теперь вроде всё норм. На пике 2 - результат обращения к несуществующей переменной из шаблона. Только в методе view пришлось заменить require на include, причины описаны здесь: >>766467
>> if ($recordsPerPage > $recordsCount) {
>>$recordsPerPage = 5;
>> }
> Логика немного непонятна. тут нет ошибки?
$recordsPerPage приходит из GET-параметров. Сейчас сделал по-другому: если пользователь запрашивает 20 студентов на страницу, а в базе всего 10, то просто отображаем всех студентов.
А если у меня в базе over9000 студентов, а пользователь просит over9000+1, то все они вывалятся в браузер. И тут браузер, наверное, упадёт. Поэтому и я выставлял 5 студентов на страницу, если пользователь просит слишком много.
> лучше сделать как описано в моему алгоритме работы с формами. Вместо 4 однотипных методов, для форм регистрации и редактирования достаточно одного общего метода.
Сделал. В результате избавился от однотипности, но получил сложность ввиду множественных ветвлений. Для наглядности залил в виде паст.
До: http://paste.ofcode.org/D5ZFbX5E5aaeqBkApKaF2b
После: http://paste.ofcode.org/rHP8sLyfNnqxsYBK7aNdzC
Алсо из-за объединения я теперь не могу в роутах сделать так:
POST /student FormController@postRegister
У меня теперь доступен только метод postRegisterOrUpdate, а ведь для обновления данных о ресурсе нужно использовать метод PUT. Можно делать вот так, но это выглядит как-то странно:
'PUT', '/student', 'Form@postRegisterOrUpdate';
'POST', '/student', 'Form@postRegisterOrUpdate';
Я даже название не смог подобрать для этого метод нормальное, потому что он делает 2 разных дела.
Могу сделать ['PUT', 'POST'], '/student', 'FormController@postRegisterOrUpdate', вроде лучше, но всё равно выглядит как-то странно.
https://github.com/applejacky/students/blob/master/app/Helper/StringGenerator.php#L18
> Не очень понятно почему этот метод тут, а не в классе авторизации.
Переименовал названия аргументов с password, login на string1, string2: https://github.com/applejacky/students/blob/master/app/Helper/StringGenerator.php#L20
В таком случае можно оставлять этот метод в классе StringGenerator?
Метод просто генерирует мешанину из 2-х строк.
Вопросы:
1. Чтобы реализовать разлогинивание по CSRF-токену нужно передавать этот CSRF-токен в каждый вид (не дёргать же из шаблона глобальные переменные?), чтобы иметь возможность в шаблоне генерировать ссылку вида /logout?token<?= $csrfToken ?>.
Есть 2 стула:
- В каждом методе, который выводит шаблон, а не редиректит куда-то, нужно передавать csrf_token. Если будут появляться ещё методы и контроллеры, то нужно опять копипастить.
- Возложить передачу токена на базовый контроллер. Плюс в том, что нет копипасты, минус в том, что непонятно почему базовый контроллер отвечает за CSRF.
2. В случае с curl'ом хотелось бы, чтобы action, к примеру, не редиректил после регистрации, выплёвывая стену html-кода, а отдавал Response::HTTP_CREATED. Это нужно как-то по Content-type реквеста определять, каким будет Response? Например, если Content-type: application/json, то возвращать $response->withStatus(Response::HTTP_CREATED), если text/html - заполнять тело ответа страницей или делать редирект.
3. Хотелось бы, чтобы display_errors настраивался сразу в сonfig.ini для конкретного проекта, чтобы не изменять глобально значение в php.ini
Директива display_erros доступна глобально по всему проекту, почему нельзя использовать ini_set('display_errors', 1) в начале фронтконтроллера?
>>764855
Спасибо большое за ответы!
> Существуют другие методы например HEAD. Массивы $_POST и $_GET существуют независимо и могут быть заполнены оба в некоторых случаях.
Разделил на bodyParams и queryParams в классе Request: https://github.com/applejacky/students/blob/master/app/Lib/Request.php
Добавил PUT и DELETE, немножко поковырял curl, теперь можно делать так:
curl -X DELETE http://test.loc/student/12
Разумеется, это только для демонстрации работы метода DELETE. Алсо, получилось зарегаться через curl и изменить профиль: https://github.com/applejacky/students/blob/master/util/curl_rest_test.sh
>> Попытался сделать DI-контейнер похожим на Pimple, до сих пор не знаю, хорошая ли это идея;
> Гм, а почему бы тогда сразу pimple и не взять?
Читал твой пост о том, что в студентах будет много велосипедов. В файлообменнике я буду активно использовать сторонние решения.
>> В валидаторе студента 2 метода: validate и checkEmailUnique. Их пришлось разделить так как в случае с обновлением данных проверять email на уникальность не нужно.
> Вообще-то при обновлении тоже надо проверять. Иначе человек заменит email на уже существующий у другого студента.
У меня есть проверка в шаблоне, если пользователь обновляет профиль, то инпуты с паролем и email'ом не отображаются: https://github.com/applejacky/students/blob/master/app/View/form/index.phtml#L48
В контроллер приходит массив данных о студенте без пароля и email, этот массив мапится на существующего студента у которого есть email и password.
Как мне кажется, не очень хорошо, что email и password можно так просто поменять. Зависящие от них токены тоже менять придётся. Поэтому я лишил пользователя возможности менять email и пароль.
> И кстати, почему ты не сделал просто метод match(Request) в классе Route?
Ну у меня и так роут сам себя валидирует, а тут ещё и предоставить ему возможность проверять себя на соответствие Request'у. Это лучше, чем подход с RouteRequestMatcher'ом?
Алсо, сейчас в последнем только один метод: https://github.com/applejacky/students/blob/master/app/Routing/RouteRequestMatcher.php
> Насчет классов Request, Route - они подозрительно напоминают классы из компонентов Симфони.
Ты мне советовал посмотреть реализацию роутинга в компонентах Symfony, поэтому мои классы похожи.
> Может лучше было оттуда их и взять?
Я ньюфаг, какой Symfony? Я когда студентов начинал писать, то не понимал как работает многостраничное приложение, не знал как регистрировать и запоминать пользователя, не работал с неймспейсами, композером и так далее.
Мне очень нравится документация этого фреймворка и я обязательно до него доберусь, но студентов хочу сделать на велосипедах.
Алсо, вот хочу я использовать symfony/validation в файлообменнике, ищу большую-красную-кнопку вменяемый гайд, но ничего не нахожу.
https://symfony.com/doc/current/book/validation.html
https://github.com/symfony/validator
Где тут описано использование symfony/validation как standalone компонента?
>> if (is_callable($value)) {
> Тут надо использовать тайп хинт callable
Не получилось. Пик 1.
>> https://github.com/applejacky/students/blob/master/app/Controller/Controller.php#L23
> тут не сделана обработка исключений в процессе вывода шаблона. У тебя все, что успело вывестись, вывалится на экран.
Сделал обработчик ошибок по-человечески и теперь вроде всё норм. На пике 2 - результат обращения к несуществующей переменной из шаблона. Только в методе view пришлось заменить require на include, причины описаны здесь: >>766467
>> if ($recordsPerPage > $recordsCount) {
>>$recordsPerPage = 5;
>> }
> Логика немного непонятна. тут нет ошибки?
$recordsPerPage приходит из GET-параметров. Сейчас сделал по-другому: если пользователь запрашивает 20 студентов на страницу, а в базе всего 10, то просто отображаем всех студентов.
А если у меня в базе over9000 студентов, а пользователь просит over9000+1, то все они вывалятся в браузер. И тут браузер, наверное, упадёт. Поэтому и я выставлял 5 студентов на страницу, если пользователь просит слишком много.
> лучше сделать как описано в моему алгоритме работы с формами. Вместо 4 однотипных методов, для форм регистрации и редактирования достаточно одного общего метода.
Сделал. В результате избавился от однотипности, но получил сложность ввиду множественных ветвлений. Для наглядности залил в виде паст.
До: http://paste.ofcode.org/D5ZFbX5E5aaeqBkApKaF2b
После: http://paste.ofcode.org/rHP8sLyfNnqxsYBK7aNdzC
Алсо из-за объединения я теперь не могу в роутах сделать так:
POST /student FormController@postRegister
У меня теперь доступен только метод postRegisterOrUpdate, а ведь для обновления данных о ресурсе нужно использовать метод PUT. Можно делать вот так, но это выглядит как-то странно:
'PUT', '/student', 'Form@postRegisterOrUpdate';
'POST', '/student', 'Form@postRegisterOrUpdate';
Я даже название не смог подобрать для этого метод нормальное, потому что он делает 2 разных дела.
Могу сделать ['PUT', 'POST'], '/student', 'FormController@postRegisterOrUpdate', вроде лучше, но всё равно выглядит как-то странно.
https://github.com/applejacky/students/blob/master/app/Helper/StringGenerator.php#L18
> Не очень понятно почему этот метод тут, а не в классе авторизации.
Переименовал названия аргументов с password, login на string1, string2: https://github.com/applejacky/students/blob/master/app/Helper/StringGenerator.php#L20
В таком случае можно оставлять этот метод в классе StringGenerator?
Метод просто генерирует мешанину из 2-х строк.
Вопросы:
1. Чтобы реализовать разлогинивание по CSRF-токену нужно передавать этот CSRF-токен в каждый вид (не дёргать же из шаблона глобальные переменные?), чтобы иметь возможность в шаблоне генерировать ссылку вида /logout?token<?= $csrfToken ?>.
Есть 2 стула:
- В каждом методе, который выводит шаблон, а не редиректит куда-то, нужно передавать csrf_token. Если будут появляться ещё методы и контроллеры, то нужно опять копипастить.
- Возложить передачу токена на базовый контроллер. Плюс в том, что нет копипасты, минус в том, что непонятно почему базовый контроллер отвечает за CSRF.
2. В случае с curl'ом хотелось бы, чтобы action, к примеру, не редиректил после регистрации, выплёвывая стену html-кода, а отдавал Response::HTTP_CREATED. Это нужно как-то по Content-type реквеста определять, каким будет Response? Например, если Content-type: application/json, то возвращать $response->withStatus(Response::HTTP_CREATED), если text/html - заполнять тело ответа страницей или делать редирект.
3. Хотелось бы, чтобы display_errors настраивался сразу в сonfig.ini для конкретного проекта, чтобы не изменять глобально значение в php.ini
Директива display_erros доступна глобально по всему проекту, почему нельзя использовать ini_set('display_errors', 1) в начале фронтконтроллера?
>>748292
>>755118
https://github.com/TheSidSpears/Students/blob/master/app/views/auth_form.php
br вернй признак того, что ты не знаешь CSS. Обрати внимание, в ОП посте есть задачи по CSS.
https://github.com/TheSidSpears/Students/blob/master/app/views/student_form.php
Тут стоит добавить html5 валидацию, хотя бы required например.
> <?php if($s->is_resident): ?>
> <input type="radio" name="is_resident" value="resident" checked> Местный
> <?php else: ?>
> <input type="radio" name="is_resident" value="resident"> Местный
Не требуется копипастить input, хватит <?= $resident ? ' checked ' : '' ?>
https://github.com/TheSidSpears/Students/tree/master/app/views
тут у тебя много файлов и их надо хотя бы по папкам организровать как-то
>>766820
Регистрация и редактирования это по сути одно и то же и должен быть 1 контроллер и 1 вью для них.
> засунуть в ф-ию Util::token(). Но тогда ф-ия будет работать с куками и устанавливать глобальную переменную, что, наверное, не очень правильно
надо вынести всю работу с CSRF в класс.
> Ну у меня и так роут сам себя валидирует, а тут ещё и предоставить ему возможность проверять себя на соответствие Request'у. Это лучше, чем подход с RouteRequestMatcher'ом?
Не знаю, можно и так и так сделать. И чтобы сам себя матчил или чтобы снаружи это делалось.
> Алсо, вот хочу я использовать symfony/validation в файлообменнике, ищу большую-красную-кнопку вменяемый гайд, но ничего не нахожу.
http://symfony.com/components/Validator но тут нет доков, так что придется читать тут: https://symfony.com/doc/current/book/validation.html
В принципе, разница только в том, что в Сифмони объект валидатора получается через $this->get('validator'), а ты будешь создавать его через new или через ValidationBuilder, придется немного порыться в коде и понять как его создать и как заставить прочитать аннотации или конфиг. Если будет сложно, напиши, я тоже гляну.
> > Тут надо использовать тайп хинт callable
> Не получилось. Пик 1.
Да, тут я ошибся, при наследовании можно только расширять класс или метод, но не сужать его, в соовтетсвии с принципом Лисков (класс-потомок всегда можно использовать вместо предка).
Вообще, в моем алгоритме POST и GET обрабатывает один метод. тобы не было стены кода, надо разбивать его на более мелкие функции.
Также, мне не нравится что там в один и тот же шаблон передаются разные наборы переменных, и часть переименных может отстутствоать. Это неудобно, легко где-то ошибиться.
> Алсо из-за объединения я теперь не могу в роутах сделать так:
> POST /student FormController@postRegister
Это проблема твоего роутера
> У меня теперь доступен только метод postRegisterOrUpdate, а ведь для обновления данных о ресурсе нужно использовать метод PUT.
Вообще, нет.
http://ru.stackoverflow.com/questions/193978/Разница-между-put-и-post
PUT используется для обновления ресурса. но идентификатор ресурса должен быть в URL в этом случае (/student/xxxx). POST же это общий метод для изменения чего-то на сервере.
В спецификации я вижу такое: https://tools.ietf.org/html/rfc7231#section-4.3.4
> The PUT method requests that the state of the target resource be
> created or replaced with the state defined by the representation
> enclosed in the request message payload. A successful PUT of a given
> representation would suggest that a subsequent GET on that same
> target resource will result in an equivalent representation being
> sent in a 200 (OK) response.
PUT создает или заменяет ресурс переданными данными.
> Переименовал названия аргументов с password, login на string1, string2:
Смысл? Получилось по моему менее читабельно. Ну и все равно знание о том как именно хешировать пароль, должно быть мне кажется в классе отвечающем за авторизацию, или может где-то еще, но в одном месте.
> 1. Чтобы реализовать разлогинивание по CSRF-токену нужно передавать этот CSRF-токен в каждый вид (не дёргать же из шаблона глобальные переменные?),
Можно организовать передачу "глобальных" для шаблона переменных, которые передаются всегда.
> - Возложить передачу токена на базовый контроллер. Плюс в том, что нет копипасты, минус в том, что непонятно почему базовый контроллер отвечает за CSRF.
Можно на базовый или фронт-контроллер. Там всегда есть такие переменные, например информация о залогиненном пользователе.
> 2. В случае с curl'ом хотелось бы, чтобы action, к примеру, не редиректил после регистрации, выплёвывая стену html-кода,
Можно добавить флаг курлу не следовать далее при редиректе.
> Это нужно как-то по Content-type реквеста определять, каким будет Response?
Это плохая идея так как затрудняет отладку. Такие вещи лучше передавать явно, например через GET параметр, но в данном случае проще передать флаг курлу.
> 3. Хотелось бы, чтобы display_errors настраивался сразу в сonfig.ini для конкретного проекта, чтобы не изменять глобально значение в php.ini
Флаг можно менять не только в php.ini, но и в конфиге апача для данного хоста, в htaccess, в php-fpm. Изобретать велосипед не требуется.
> почему нельзя использовать ini_set('display_errors', 1) в начале фронтконтроллера?
Как ты собрался отключать это на продакшене? Руками код каждый раз править?
> Ну у меня и так роут сам себя валидирует, а тут ещё и предоставить ему возможность проверять себя на соответствие Request'у. Это лучше, чем подход с RouteRequestMatcher'ом?
Не знаю, можно и так и так сделать. И чтобы сам себя матчил или чтобы снаружи это делалось.
> Алсо, вот хочу я использовать symfony/validation в файлообменнике, ищу большую-красную-кнопку вменяемый гайд, но ничего не нахожу.
http://symfony.com/components/Validator но тут нет доков, так что придется читать тут: https://symfony.com/doc/current/book/validation.html
В принципе, разница только в том, что в Сифмони объект валидатора получается через $this->get('validator'), а ты будешь создавать его через new или через ValidationBuilder, придется немного порыться в коде и понять как его создать и как заставить прочитать аннотации или конфиг. Если будет сложно, напиши, я тоже гляну.
> > Тут надо использовать тайп хинт callable
> Не получилось. Пик 1.
Да, тут я ошибся, при наследовании можно только расширять класс или метод, но не сужать его, в соовтетсвии с принципом Лисков (класс-потомок всегда можно использовать вместо предка).
Вообще, в моем алгоритме POST и GET обрабатывает один метод. тобы не было стены кода, надо разбивать его на более мелкие функции.
Также, мне не нравится что там в один и тот же шаблон передаются разные наборы переменных, и часть переименных может отстутствоать. Это неудобно, легко где-то ошибиться.
> Алсо из-за объединения я теперь не могу в роутах сделать так:
> POST /student FormController@postRegister
Это проблема твоего роутера
> У меня теперь доступен только метод postRegisterOrUpdate, а ведь для обновления данных о ресурсе нужно использовать метод PUT.
Вообще, нет.
http://ru.stackoverflow.com/questions/193978/Разница-между-put-и-post
PUT используется для обновления ресурса. но идентификатор ресурса должен быть в URL в этом случае (/student/xxxx). POST же это общий метод для изменения чего-то на сервере.
В спецификации я вижу такое: https://tools.ietf.org/html/rfc7231#section-4.3.4
> The PUT method requests that the state of the target resource be
> created or replaced with the state defined by the representation
> enclosed in the request message payload. A successful PUT of a given
> representation would suggest that a subsequent GET on that same
> target resource will result in an equivalent representation being
> sent in a 200 (OK) response.
PUT создает или заменяет ресурс переданными данными.
> Переименовал названия аргументов с password, login на string1, string2:
Смысл? Получилось по моему менее читабельно. Ну и все равно знание о том как именно хешировать пароль, должно быть мне кажется в классе отвечающем за авторизацию, или может где-то еще, но в одном месте.
> 1. Чтобы реализовать разлогинивание по CSRF-токену нужно передавать этот CSRF-токен в каждый вид (не дёргать же из шаблона глобальные переменные?),
Можно организовать передачу "глобальных" для шаблона переменных, которые передаются всегда.
> - Возложить передачу токена на базовый контроллер. Плюс в том, что нет копипасты, минус в том, что непонятно почему базовый контроллер отвечает за CSRF.
Можно на базовый или фронт-контроллер. Там всегда есть такие переменные, например информация о залогиненном пользователе.
> 2. В случае с curl'ом хотелось бы, чтобы action, к примеру, не редиректил после регистрации, выплёвывая стену html-кода,
Можно добавить флаг курлу не следовать далее при редиректе.
> Это нужно как-то по Content-type реквеста определять, каким будет Response?
Это плохая идея так как затрудняет отладку. Такие вещи лучше передавать явно, например через GET параметр, но в данном случае проще передать флаг курлу.
> 3. Хотелось бы, чтобы display_errors настраивался сразу в сonfig.ini для конкретного проекта, чтобы не изменять глобально значение в php.ini
Флаг можно менять не только в php.ini, но и в конфиге апача для данного хоста, в htaccess, в php-fpm. Изобретать велосипед не требуется.
> почему нельзя использовать ini_set('display_errors', 1) в начале фронтконтроллера?
Как ты собрался отключать это на продакшене? Руками код каждый раз править?
>>в 95% случаев геттеры/сеттеры генерятся IDE
А что так можно было? Пользуетесь? В каком редакторе?
Мой сниппет для саблайма, запускается нажатием gs + Tab: https://gist.github.com/codedokode/cd2f41c8dcf1237fde4b
http://ideone.com/G1IqpJ
Все правильно делаю анон? вроде все компилируется
>Вообще, в моем алгоритме POST и GET обрабатывает один метод.
Тогда надо ещё одно условие в action'е. Я только ради того, чтобы избавится от этого условия начал писать роутер. В общем, у меня пик.
>Также, мне не нравится что там в один и тот же шаблон передаются разные наборы переменных, и часть переименных может отстутствоать.
В конечном итоге передаются одни и те же переменные.
https://github.com/applejacky/students/blob/master/app/Controller/Controller.php#L18
Есть 2 переменные, которые опциональны для передачи из потомков контроллера, но в базовом контроллере они таки получают значение:
1) переменная, отвечающая за название страницы. Если переменная из контроллера-наследника не передана, то базовый контроллер ставит значение этой переменной в 'Default';
2) логическая переменная $isUserLoggedIn. По дефолту базовый контроллер ставит ей false. Её нужно передавать в каждый вид, так как от неё зависит смена надписи 'Вход/Обновить профиль' в навбаре.
Вот к ним-то я и добавлю CSRF-токен тогда.
>PUT используется для обновления ресурса. но идентификатор ресурса должен быть в URL
Да, я упустил это.
>> POST /student FormController@postRegister
> Это проблема твоего роутера
Но ведь во фреймворках в основном используется примерно такая схема:
GET /res ResController@getCreate
GET /res ResController@getUpdate
POST /res ResController@postCreate
PUT /res/X ResController@postUpdate(X)
DELETE /res/X ResController@delete(X)
Так можно было сделать до объединения методов регистрации/обновления. Я понимаю, что регистрация/обновление очень похожи и их бы нужно собрать, но в моём случае, из-за разделения на GET и POST, это приводит к нечитабельным ветвлениям. А если убрать разделение на GET и POST, то тогда и роутер можно выкинуть. Но тогда и не о каком REST не может быть и речи.
В общем, вот так можно оставлять?
GET /student FormController@getRegisterOrUpdate
POST /student FormController@postRegisterOrUpdate
PUT /res/X FormController@postUpdate(X)
Или вернуть как было?
>Как ты собрался отключать это на продакшене? Руками код каждый раз править?
Выставлять значение в config.ini
В slim'e же можно передать в конструктор класса App значение 'displayErrorDetails' => true.
>> Переименовал названия аргументов с password, login на string1, string2:
> Смысл? Получилось по моему менее читабельно. Ну и все равно знание о том как именно хешировать пароль, должно быть мне кажется в классе отвечающем за авторизацию
Вот теперь до меня дошло, это ведь из-за md5() всё. Перенесу метод в другой класс.
> Можно добавить флаг курлу не следовать далее при редиректе.
В любом случае нужно как-то оповестить клиент статусом 201, что создание ресурса произошло успешно. А у меня сейчас редирект там.
>> Это нужно как-то по Content-type реквеста определять, каким будет Response?
> Это плохая идея так как затрудняет отладку. Такие вещи лучше передавать явно, например через GET параметр, но в данном случае проще передать флаг курлу.
Имел в виду в action'е делать следующее:
Если request->getContenType == application/json, то вернуть response(statusCode)
Если request->getContentType == application/json, то вернуть response(htmlBody, statusCode)
>Вообще, в моем алгоритме POST и GET обрабатывает один метод.
Тогда надо ещё одно условие в action'е. Я только ради того, чтобы избавится от этого условия начал писать роутер. В общем, у меня пик.
>Также, мне не нравится что там в один и тот же шаблон передаются разные наборы переменных, и часть переименных может отстутствоать.
В конечном итоге передаются одни и те же переменные.
https://github.com/applejacky/students/blob/master/app/Controller/Controller.php#L18
Есть 2 переменные, которые опциональны для передачи из потомков контроллера, но в базовом контроллере они таки получают значение:
1) переменная, отвечающая за название страницы. Если переменная из контроллера-наследника не передана, то базовый контроллер ставит значение этой переменной в 'Default';
2) логическая переменная $isUserLoggedIn. По дефолту базовый контроллер ставит ей false. Её нужно передавать в каждый вид, так как от неё зависит смена надписи 'Вход/Обновить профиль' в навбаре.
Вот к ним-то я и добавлю CSRF-токен тогда.
>PUT используется для обновления ресурса. но идентификатор ресурса должен быть в URL
Да, я упустил это.
>> POST /student FormController@postRegister
> Это проблема твоего роутера
Но ведь во фреймворках в основном используется примерно такая схема:
GET /res ResController@getCreate
GET /res ResController@getUpdate
POST /res ResController@postCreate
PUT /res/X ResController@postUpdate(X)
DELETE /res/X ResController@delete(X)
Так можно было сделать до объединения методов регистрации/обновления. Я понимаю, что регистрация/обновление очень похожи и их бы нужно собрать, но в моём случае, из-за разделения на GET и POST, это приводит к нечитабельным ветвлениям. А если убрать разделение на GET и POST, то тогда и роутер можно выкинуть. Но тогда и не о каком REST не может быть и речи.
В общем, вот так можно оставлять?
GET /student FormController@getRegisterOrUpdate
POST /student FormController@postRegisterOrUpdate
PUT /res/X FormController@postUpdate(X)
Или вернуть как было?
>Как ты собрался отключать это на продакшене? Руками код каждый раз править?
Выставлять значение в config.ini
В slim'e же можно передать в конструктор класса App значение 'displayErrorDetails' => true.
>> Переименовал названия аргументов с password, login на string1, string2:
> Смысл? Получилось по моему менее читабельно. Ну и все равно знание о том как именно хешировать пароль, должно быть мне кажется в классе отвечающем за авторизацию
Вот теперь до меня дошло, это ведь из-за md5() всё. Перенесу метод в другой класс.
> Можно добавить флаг курлу не следовать далее при редиректе.
В любом случае нужно как-то оповестить клиент статусом 201, что создание ресурса произошло успешно. А у меня сейчас редирект там.
>> Это нужно как-то по Content-type реквеста определять, каким будет Response?
> Это плохая идея так как затрудняет отладку. Такие вещи лучше передавать явно, например через GET параметр, но в данном случае проще передать флаг курлу.
Имел в виду в action'е делать следующее:
Если request->getContenType == application/json, то вернуть response(statusCode)
Если request->getContentType == application/json, то вернуть response(htmlBody, statusCode)
>GET /res ResController@getUpdate
Здесь, наверное /res/update
>>766881
В шапке указана книга Зандстры по ООП в PHP + есть ссылка на сайт designpatterns.
>Если request->getContenType == application/json, то вернуть response(statusCode)
>Если request->getContentType == application/json, то вернуть response(htmlBody, statusCode)
Не так, я ошибся. Вот так:
Если request->getContenType == application/json, то вернуть response(statusCode)
Если request->getContentType == text/html, то вернуть response(htmlBody, statusCode)
Извиняюсь за то, что наплодил ответов. Посты перечитываю несколько раз, но, похоже, у меня диагноз.
Плохо по-разному реагировать на разный content-type, это сильно усложняет отладку так как разница неочевидна на первый взгляд. Лучше делать разный URL.
Например, добавляя префикс api в путь?
Тогда я понимаю это так:
PUT /api/res/X ResController@update(X)
POST /res/X ResController@update(X)
А update(X) уже возвращает нужный response в зависимости от наличия/отсутствия префикса. Однако в каждом action'е по-прежнему будут проверки. Было бы круто, если бы action возвращал какой-то более общий response, который во фронтконтроллере перед ->send() выбирал, что отправлять клиенту, исходя из наличия/отсутствия префикса. В студентах я тогда не буду это реализовывать, а в файлообменнике на Slim можно попробовать, там как раз есть middleware. Должно подойти.
Список языков я захардкодил в LanguageHelper, перевел полностью все приложение на английский, если родной язык пользователя (определяется по заголовку HTTP_ACCEPT_LANGUAGE) отличается от текущего, выводится сообщение о том что сайт доступен на родном языке пользователя (если он доступен, конечно же), так же для разных языков сделал разные ссылки (с этим было больше всего проблем). По переводу - для того чтобы вытащить строки из .twig файлов используется CLI скрипт https://github.com/foobar1643/filehosting/blob/master/cli-tools/template-extractor.php который создает из шаблонов кэш (который является .php файлами), этот кэш можно импортировать в Poedit и переводить.
CSRF guard я сделал таким же middleware, только теперь он использует куки с фиксированным именем и не генерирует новый токен после каждого запроса. Время жизни куки и и длинну токена можно задать при создании объекта.
Дальше буду переходить к изучению PHPUnit и автоматизированного тестирования. Только вот мне не очень понятно что тут можно тестировать кроме сервисов по добавлению\удалению файлов и комментариев.
> , этот кэш можно импортировать в Poedit и переводить.
Надо бы полностью автоматический экстрактор, то есть он ищет по коду все переводимые строки и создает .mo файл или какой там формат используется. Может есть какое готовое решение, не знаю.
>>767126
> Дальше буду переходить к изучению PHPUnit и автоматизированного тестирования. Только вот мне не очень понятно что тут можно тестировать кроме сервисов по добавлению\удалению файлов и комментариев.
Функциональные тесты тестируют функционал приложения, то есть возможность загрузить файл, скачать файл, добавить комментарий, и тд, все фичи, что у тебя есть. Также, надо сделать негативные сценарии - загрузка файла неправильного типа, отправка пустого комментария и тд.
В идеале функциональные тесты покрывают все страницы, ссылки, кнопки и формы на сайте.
Юнит-тесты тестируют отдельные функции или классы в изоляции от остального кода. Ими ты проверяешь что функция или класс выполняет свою работу. Тут да, не все получится протестировать, например, контроллеры юнит-тестами не протестируешь.
Если ты сомневаешься, можешь составить план тестирования (что именно тестируешь).
Ну вообще, обычно REST API делают отдельным набором контроллеров. И поведение там немного другое. Например, при ошибке мы можем отдавать JSON, а не HTML страницу. Другой способ авторизации, например без использования кук, заголовком Authorization.
То ест не факт что получится и будет удобно одним контроллером это обслужить.
Там еще обычно желательно иметь документацию и есть инструменты вроде swagger ( http://petstore.swagger.io/ демо ) для ее создания. В идеале хорошо бы нужные данные как-то генерировать автоматически. То есть где-то есть описание метода, где-то описание формата входных параметров (например JSON Schema или аннотации в модели), и из этого генерируется документация.
> Было бы круто, если бы action возвращал какой-то более общий response, который во фронтконтроллере перед ->send() выбирал, что отправлять клиенту,
Мне кажется, лучше развивать сервисный слой так, чтобы контроллер был как можно тоньше. А для АПИ можно вообще как-то это все обобщить, например сделать классы-обработчики запросов к АПИ, наследующиеся от базового.
Не надо делать 2 слоя контроллеров.
> 'messageFormatter' => new \MessageFormatter(\Locale::getDefault(), _("{0, plural, =0{No downloads} one{# download} other{# downloads}}")),
Ой, это не очень практично. если ты будешь каждое сообщение так в контроллере формировать. Я имел в виду сделать так: в контроллере мы создаем например объект
new Translator('ru_RU')
А в шаблоне используем его:
{{ tr.format('....', { number: 100 }) }}
Или же сделать кастомную функцию:
{{ translate('.....', { number: 100 }) }}
> 'csrf_token' => FigRequestCookies::get($request, "csrf_token")->getValue()]);
Это тоже сплошное нарушение инкапсуляции. За куки должен отвечать соотв. компонент, и у него и надо брать эти куки. Либо можно сделать договоренность что он их передает через атрибут запроса.
> const PATH_TO_LOCALES = "/var/www/filehosting/locale";
Путь лучше как-то относительно корня проекта вычислять
Тут https://github.com/foobar1643/filehosting/blob/master/app/Helper/LanguageHelper.php что-то много публичных методов. Трудно понять как работает класс. И Request нужен почти в каждом втором - может стоит его передавать через конструктор?
> $serverParams = $request->getServerParams();
> $httpLanguage = isset($serverParams['HTTP_ACCEPT_LANGUAGE']) ? $serverParams['HTTP_ACCEPT_LANGUAGE'] : NULL;
А там нет метода вроде getHeader() или getHeaders()->get() ?
С локалью на мой взгляд сделано не оень удачно: https://github.com/foobar1643/filehosting/blob/master/app/Middleware/LocaleMiddleware.php#L32
Во-первых, локаль общая на процесс по моему и нельзя многопоточно работать с разными локалями. Или например сделать асинхронную обработку нескольких параллельных запросов через ReactPHP. Также, ты ставишь переменную окружения, и она будет передаваться запускаемым из твоего приложения программам, что тоже может быть нежелательно.
И наконец это не позволяет нам извлекать фразы на разных языках без возни с переключением локалей.
Лучше использовать для переключения языка textdomain. Он лишен перечисленных недостатков.
Тут https://github.com/foobar1643/filehosting/blob/master/app/Middleware/CsrfMiddleware.php ты продлеваешь токен, если он уже есть? Иначе он может закончиться совсем неожиданно.
Насчет тестирования - тестировать тут можно много чего. Те же Middleware, хелперы, валидатор как минимум.
> 'messageFormatter' => new \MessageFormatter(\Locale::getDefault(), _("{0, plural, =0{No downloads} one{# download} other{# downloads}}")),
Ой, это не очень практично. если ты будешь каждое сообщение так в контроллере формировать. Я имел в виду сделать так: в контроллере мы создаем например объект
new Translator('ru_RU')
А в шаблоне используем его:
{{ tr.format('....', { number: 100 }) }}
Или же сделать кастомную функцию:
{{ translate('.....', { number: 100 }) }}
> 'csrf_token' => FigRequestCookies::get($request, "csrf_token")->getValue()]);
Это тоже сплошное нарушение инкапсуляции. За куки должен отвечать соотв. компонент, и у него и надо брать эти куки. Либо можно сделать договоренность что он их передает через атрибут запроса.
> const PATH_TO_LOCALES = "/var/www/filehosting/locale";
Путь лучше как-то относительно корня проекта вычислять
Тут https://github.com/foobar1643/filehosting/blob/master/app/Helper/LanguageHelper.php что-то много публичных методов. Трудно понять как работает класс. И Request нужен почти в каждом втором - может стоит его передавать через конструктор?
> $serverParams = $request->getServerParams();
> $httpLanguage = isset($serverParams['HTTP_ACCEPT_LANGUAGE']) ? $serverParams['HTTP_ACCEPT_LANGUAGE'] : NULL;
А там нет метода вроде getHeader() или getHeaders()->get() ?
С локалью на мой взгляд сделано не оень удачно: https://github.com/foobar1643/filehosting/blob/master/app/Middleware/LocaleMiddleware.php#L32
Во-первых, локаль общая на процесс по моему и нельзя многопоточно работать с разными локалями. Или например сделать асинхронную обработку нескольких параллельных запросов через ReactPHP. Также, ты ставишь переменную окружения, и она будет передаваться запускаемым из твоего приложения программам, что тоже может быть нежелательно.
И наконец это не позволяет нам извлекать фразы на разных языках без возни с переключением локалей.
Лучше использовать для переключения языка textdomain. Он лишен перечисленных недостатков.
Тут https://github.com/foobar1643/filehosting/blob/master/app/Middleware/CsrfMiddleware.php ты продлеваешь токен, если он уже есть? Иначе он может закончиться совсем неожиданно.
Насчет тестирования - тестировать тут можно много чего. Те же Middleware, хелперы, валидатор как минимум.
В cli-tools по моему синтаксис получился несколько избыточный:
> --add-file -f <option>
Почему нельзя убрать -f отсюда? getopt понимает синтаксис с двоеточием для длинных опций вроде бы.
> copy($filepath, self::TEMP_FILENAME);
> $uploadedFile = new Slim\Http\UploadedFile(self::TEMP_FILENAME, pathinfo($filepath, PATHINFO_BASENAME));
Имя временного файла надо генерировать через http://php.net/manual/ru/function.tempnam.php
А вообще конечно было былучше если бы временный файл не требовался.
Тут https://github.com/foobar1643/filehosting/blob/master/app/Database/FileMapper.php#L96 не стоило заморачиваться с генерацией кучи плейсхолдеров - проще было заэкранировать данные и подставить напрямую.
Вот здесь видна явная ошибка локализации:
https://github.com/foobar1643/filehosting/blob/master/templates/file.twig#L47
> {% trans %}To view this video please enable JavaScript, and consider upgrading to a web browser that{% endtrans %}
> <a href="http://videojs.com/html5-video-support/" target="_blank">{% trans %}supports HTML5 video{% endtrans %}</a>
Нельзя разрывать предложение на 2 части. Во-первых, они могут оказаться далеко и после перевода они не состыкуются (или они могут достаться разным переводчикам). Во-вторых, в других языках может быть другой порядок слов - например, в японском порядок слов будет не "web broser that supports HTML5 video", а "HTML5 video supporting browser".
Надо бы обходиться без разрыва предложения.
Вот еще скользкое место:
https://github.com/foobar1643/filehosting/blob/master/templates/file.twig#L89
> {% trans %}Send{% endtrans %}
Как это перевести, не зная контекста? Отправить? Послать? Послан? Для таких случаев в формате gettext предусмотрена возможность добавлять комментарии, например "Button to submit a comment". Было бы хорошо если бы они поддерживались у тебя.
Аналогично слова вроде Download - загрузить? загрузка? Загрузок? Save = сохранить? спасти? сэкономить?
Отдельно еще существует проблема перевода JS строк. В более сложном случае надо было бы писать свою функцию перевода для JS, свой коллектор строк и экспортер, который экспортирует JS-переводы в js-файл.
> If you want to change language, you can do so on the{% endtrans %}
> <a href="/{{ lang }}/settings/">{% trans %}settings page{% endtrans %}
Дружелюбнее было бы конечно сразу давать ссылку/кнопку для переключения....
> <a href="/{{ lang }}/file/{{ popularFile.id }}/">{{ popularFile.name }}</a>
Так тебе приходится руками всюду вписывать этот lang. Надо было использовать класс для генерации URL - тогда это можно было бы сделать централизованно.
В cli-tools по моему синтаксис получился несколько избыточный:
> --add-file -f <option>
Почему нельзя убрать -f отсюда? getopt понимает синтаксис с двоеточием для длинных опций вроде бы.
> copy($filepath, self::TEMP_FILENAME);
> $uploadedFile = new Slim\Http\UploadedFile(self::TEMP_FILENAME, pathinfo($filepath, PATHINFO_BASENAME));
Имя временного файла надо генерировать через http://php.net/manual/ru/function.tempnam.php
А вообще конечно было былучше если бы временный файл не требовался.
Тут https://github.com/foobar1643/filehosting/blob/master/app/Database/FileMapper.php#L96 не стоило заморачиваться с генерацией кучи плейсхолдеров - проще было заэкранировать данные и подставить напрямую.
Вот здесь видна явная ошибка локализации:
https://github.com/foobar1643/filehosting/blob/master/templates/file.twig#L47
> {% trans %}To view this video please enable JavaScript, and consider upgrading to a web browser that{% endtrans %}
> <a href="http://videojs.com/html5-video-support/" target="_blank">{% trans %}supports HTML5 video{% endtrans %}</a>
Нельзя разрывать предложение на 2 части. Во-первых, они могут оказаться далеко и после перевода они не состыкуются (или они могут достаться разным переводчикам). Во-вторых, в других языках может быть другой порядок слов - например, в японском порядок слов будет не "web broser that supports HTML5 video", а "HTML5 video supporting browser".
Надо бы обходиться без разрыва предложения.
Вот еще скользкое место:
https://github.com/foobar1643/filehosting/blob/master/templates/file.twig#L89
> {% trans %}Send{% endtrans %}
Как это перевести, не зная контекста? Отправить? Послать? Послан? Для таких случаев в формате gettext предусмотрена возможность добавлять комментарии, например "Button to submit a comment". Было бы хорошо если бы они поддерживались у тебя.
Аналогично слова вроде Download - загрузить? загрузка? Загрузок? Save = сохранить? спасти? сэкономить?
Отдельно еще существует проблема перевода JS строк. В более сложном случае надо было бы писать свою функцию перевода для JS, свой коллектор строк и экспортер, который экспортирует JS-переводы в js-файл.
> If you want to change language, you can do so on the{% endtrans %}
> <a href="/{{ lang }}/settings/">{% trans %}settings page{% endtrans %}
Дружелюбнее было бы конечно сразу давать ссылку/кнопку для переключения....
> <a href="/{{ lang }}/file/{{ popularFile.id }}/">{{ popularFile.name }}</a>
Так тебе приходится руками всюду вписывать этот lang. Надо было использовать класс для генерации URL - тогда это можно было бы сделать централизованно.
>Зандстры
Оо, точно, спасибо!
Начинал её читать, но потом столкнулся со сложнотой в ООП и перестал.
Точно, там же шаблоны и вот это всё.
Ну и тот сайт тоже посмотрю, спасибо.
ОПче, что за хуйня? Yii, Symphony, Doctrine, Slim, Лар(в)ав(р)ель, апи вордпресса, не дохуища ли? Это все взаимозаменяемые фреймворки об одном и том же или они для разных задач? Я не вижу, чтобы в вакансиях, кроме совсем ебанутых, требовались одновременно Симфони и Yii
Ты что-нибудь слышал про пхп девелоп, wxphp, phpgtk? Пиздануто пытаться писать на пхэпхэ десктопные софтины или норм?
> https://github.com/TheSidSpears/Students/blob/master/public/503.php#L1
> header(' ', true, 503);
> Что это за синтаксис? Что за пустой заголовок? По моему это не будет работать. Там надо отправлять заголоок вроде HTTP/1.1 503 xxxx, почитай хотя бы мануал по функции header().
Видел такой вариант на тостере, сейчас не найду, но чувак даже обосновывал, почему так правильней. Главное - 503 заголовок он передаёт, проверял
> chdir ('../')
> Вместо того, чтобы полагаться на текущий каталог, лучше использовать абсолютные пути, например, содержащие _ _ DIR _ _ в начале
Не очень понял. Мне в начале каждого адреса такую шнагу прописывать __DIR__.'/../ ?
>Не очень понял. Мне в начале каждого адреса такую шнагу прописывать __DIR__.'/../ ?
Это из разрядка хороших практик. Представь, что ты на директрорию выше/ниже перенёс файл, который include'ит другой файл по относительному пути. Тогда тебе ещё придётся править относительный путь в перенесённом файле. А если бы использовал абсолютный путь к файлу, то ничего менять не пришлось бы.
>Видел такой вариант на тостере, сейчас не найду, но чувак даже обосновывал, почему так правильней. Главное - 503 заголовок он передаёт, проверял
Звучит как "так правильно, сам не помню почему, обосновывать не буду".
>>767522
>>767581
> хуйня
> дохуища
> ебанутых
> Пиздануто
> пхэпхэ
Ты прям из падика сюда пришёл? Перечисленные тобой обёртки над тулкитами для GUI пригодны разве что для личных нужд, никакой серьёзный софт на них не пишется.
> Я не вижу, чтобы в вакансиях, кроме совсем ебанутых, требовались одновременно Симфони и Yii
Я видел часто. Одногруппник работает джуном за 30k, пишет почти на всём, что ты перечислил + заставляют писать ещё на куче других CMS и фреймворках. Бывает, просто говорят так: "Следующий проект будет на X, разберись в нём за недельку, где находить время на это время - думай сам". И это обыденно, к тому же при должном опыте в смежных технологиях это не так уж и сложно.
> Я имел в виду сделать так: в контроллере мы создаем например объект
> new Translator('ru_RU')
Я не очень понимаю как в таком случае переводить эти строки через gettext. Сейчас у меня вот эта строка
>_("{0, plural, =0{No downloads} one{# download} other{# downloads}}")
Получается через gettext и при разных языках она будет разная. А если делать такой объект, то функция форматирования будет выглядеть как-то так
>{{ tr.format('download', { number: 2}) }}
Тогда получается что остальные варианты через gettext не задать?
Единственный вариант который могу придумать это такой:
>{{ tr.format('{0, plural, =0{No downloads} one{# download} other{# downloads}}', { number: 2}) }}
Он подойдет?
>А вообще конечно было былучше если бы временный файл не требовался.
В слиме при использовании функции moveTo оригинальный файл удаляется. https://github.com/slimphp/Slim/blob/3.x/Slim/Http/UploadedFile.php#L202 Я создаю временный чтобы не городить велосипедов с копированием в хелперах.
Мне 23 и я вообще не соображаю в программировании и, в колледже, уже начал тупить когда в паскале начались слова типа "массив". Сейчас работаю эникеем_техподдержкой, немного понимаю в винде, линуксе, сетях.
Не хочу заниматься заправкой картриджей и прочим дерьмом.
Когда читаю резюме на 25-35к джун пхп там какие-то непонятные для меня слова типа ООП, МVC и что-то про паттерны и красоту кода (для которых, видимо, нужно читать отдельные книжки)
Сейчас прохожу хтмл_академи, что дальше? Реально ли вкатиться (не занимаясь дизайном сайтов) и за какой срок? Везде требуют опыт работы с коммерческими проектами, который я не могу получить ПОТОМУ ЧТО ВЕЗДЕ ТРЕБУЮТ ОПЫТ.
Но это пожалуй не важно - самое важное это сколько времени необходимо чтобы вкатиться (разумеется учитывая что не фултайм дроч книжек, так как работа, я же не мильйонер сидеть дома 24/7 читать книжки) и КАКИЕ книжки читать.
Спасибо.
> В задаче написано что множить сущности не желательно, это касается класса валидации для формы редактирования? Старый класс валидации всегда будет выдавать ошибку о том что такая почта уже занята, если её не поменять.
Значит надо исправить его чтобы он не сравнивал свою почту со своей, а только с другими.
> Так же это касается паролей, даже если их заполнить теми же самыми данным, то всё равно сгенерируется новый хэш и токен, и авторизация сломается, потому что в куках находиться не актуальный токен. Как обойти обновление полей которые мы не хотим обновлять?
Сделать параметр какой-то который предается валидатору. В случае с авторизацией при смене хеша надо выдавать новую куку.
> Нужна ли валидация и список ошибок для формы поиска?
Да вообще не особо. Если форма содержит не-пробелы - делаем поиск, если не содержит - не делаем.
Этот класс https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Search.php на мой взгляд не особо нужен.
> Как избавиться от $_GET['input'] в URL?
Не избавляться. А вообще, можно попробовать сделать button / input без названия и посмотреть что будет.
> А разве это не обязанность контроллера работать с $_GET данными?
Да, просто я имел в виду что index.php это и есть контроллер.
> >Для поиска по всем колонкам можно применить оператор LIKE к соединенным через пробел значениям столбцов.
> А можно простой пример такого запроса? У меня не получилось составить...
WHERE CONCAT(...) LIKE ....
> А почему в примере без плейсхолдера написано? Это только в рамках простого примера?
Там и значение жестко прописано, а не из переменно берется. Потому плейсхолдер не требуется. Это просто для примера.
> а как быть с формами? Допустим есть форма поиска с методом get и, насколько я знаю, её нельзя заставить отправлять этот запрос с помощью urlencode().
А зачем? Форму заполняет и отправляет пользователь ведь.
> В задаче написано что множить сущности не желательно, это касается класса валидации для формы редактирования? Старый класс валидации всегда будет выдавать ошибку о том что такая почта уже занята, если её не поменять.
Значит надо исправить его чтобы он не сравнивал свою почту со своей, а только с другими.
> Так же это касается паролей, даже если их заполнить теми же самыми данным, то всё равно сгенерируется новый хэш и токен, и авторизация сломается, потому что в куках находиться не актуальный токен. Как обойти обновление полей которые мы не хотим обновлять?
Сделать параметр какой-то который предается валидатору. В случае с авторизацией при смене хеша надо выдавать новую куку.
> Нужна ли валидация и список ошибок для формы поиска?
Да вообще не особо. Если форма содержит не-пробелы - делаем поиск, если не содержит - не делаем.
Этот класс https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Search.php на мой взгляд не особо нужен.
> Как избавиться от $_GET['input'] в URL?
Не избавляться. А вообще, можно попробовать сделать button / input без названия и посмотреть что будет.
> А разве это не обязанность контроллера работать с $_GET данными?
Да, просто я имел в виду что index.php это и есть контроллер.
> >Для поиска по всем колонкам можно применить оператор LIKE к соединенным через пробел значениям столбцов.
> А можно простой пример такого запроса? У меня не получилось составить...
WHERE CONCAT(...) LIKE ....
> А почему в примере без плейсхолдера написано? Это только в рамках простого примера?
Там и значение жестко прописано, а не из переменно берется. Потому плейсхолдер не требуется. Это просто для примера.
> а как быть с формами? Допустим есть форма поиска с методом get и, насколько я знаю, её нельзя заставить отправлять этот запрос с помощью urlencode().
А зачем? Форму заполняет и отправляет пользователь ведь.
Ох, у тебя опять какой-то визуально перегруженный и тяжелочитаемый макет (это конечно претензия к дизайнеру, а не к тебе). Что подтверждает анимированная надпись "scroll down". Дизайнер сделал макет который выглядит как одностраничный сайт и вынужден объяснять что там внизу что-то еще есть.
Твой сайт постоянно ест от 5 до 10% CPU когда страница неподвижна. Попробуй покрутить, попереключать страницу, а затем открыть в отладчике Хрома вкладку timeline и записать несколько секунд. У меня например там какое-то нереальное число таймеров (242 срабатывания за секунду). Часто такое бывает при неправильном написании или подключении плагинов у людей которые плохо знают JS. Осваивай timeline и профайлер.
Более того, кастомная прокрутка, которую ты используешь, вообще плохо работает. Я привык что если я резко провожу пальцем по тачпаду то прокрутка получается сильнее, но твой плагин ее замедляет. Это неудобно. Я вообще не понимаю, зачем это нужно. Ну хочется тебе поменять внешний вид полосы прокрутки, ну поменяй, а зачем делать ее медленнее? Это ты сайт для людей с замедленным сознанием пишешь?
И я не понимаю, какая выгода от более тонкой линейки прокрутки. Толстую линейку можно перетащить мышью, тонкую нельзя. В чем выгода? Да, такая линейка прокрутки есть на макбуках, но там взамен есть удобный тачпад с кучей функций. Более того, если говорить о блоке "15 October Business Conference" то там вообще от линейки прокрутки один вред, удобнее было бы вывести весь список как есть. Прокрутка внутри прокрутки это ад с точки зрения юзабилити. И на мобильных платформах далеко не все пользователи знают как прокручивать такие блоки.
Когда я прокручиваю страницу и курсор оказывается над картой, прокрутка перестант работать и вместо этого уменьшается масштаб на карте. При наведении на черную метку на карте мышь меняет форму, но клик по метке ничего не делает.
В разделе Sponsors стрелки прокручивают блок в противоположную сторону. И вообще, по моему задвигать спонсоров за блок прокрутки, где их никто не увидит (я никогда не жму на такие стрелки) неуважительно к ним.
Тут http://w99953g4.bget.ru/evently/js/ajax.js ты попытался сделать отправку форм аяксом, но не все учел. Прочитай этот урок: https://github.com/codedokode/pasta/blob/master/js/ajax.md
У тебя нет ни индикации прогресса, ни обработки ошибок, ни блокировки кнопки отправки.
Не уверен что хорошая идея выводить ошибки вверху страницы так как они оторваны от формы, если прокрутить страницу то вообще непонятно к чему относятся. Хотя.... кто знает.
Вижу, ты сделал адаптивную версию. Но слайдеры в мобильной версии получились неудачные. например, в разделе Speakers ты можешь прокрутить до фото и переклчюать их, но при этом не видно что меняется блок с описанием. Все эти блоки с прокруткой здорово мешают прокручивать страницу, когда она узкая.
Я еще тут бегло посмотрел и вот что я вижу:
- анимация при прокрутке. Анимация привлекает внимание, но какой смысл привлекать внимание к блокам, которые только что выехали снизу? Я и так вижу, что они выехали.
- если прокрутить страницу до раздела Speakers и попробовать выделить слово "vestibulum" во втором абзаце текста, то оно не выделяется. Почему? Потому что там див из слайдера с opacity = 0. Див может быть не виден, но он все равно там есть, он блокирует доступ к тексту, который лежит под ним. Если тебе интересно, то это див с классом "mCustomScrollBox mCS-light mCSB_vertical mCSB_inside"
> <h1 class="header__promo-header header-promo">london</h1>
> <h2 class="header__promo-header header-promo-caption">business <strong>conference</strong></h2>
По моему, заголовок страницы "London Business Conference", а не "London". Теги h1, h2 это не теги для задания размера текста, а теги для смысловой разметки содержимого.
Я вижу, ты везде используешь SVG. А изучал ли ты уровень поддержки SVG? Можешь ли назвать проблемы которые связаны с его использованием? Нельзя использовать такие сложные технологии без предварительного изучения, если конечно тебе не наплевать на результат.
Тем более что у тебя не просто SVG картинка, а используется инлайново вставленный код. Вот например какие проблемы я видел, когда рассматривал вариант использовать SVG в своем учебнике:
- стандарт SVG развивается и браузеры не поддерживают все фичи, которые поддерживают редакторы. Более того, иногда фичи, которые планировалось добавить в стандарт, отменяют. Например, форматирование текста которое есть в inkscape, в итоге убрали из стандарта и заверстанный в блок текст в браузере не отображается
- информации о том какие именно фичи поддерживаются какими бразерами, толком нигде нет. Каких-то автоматических скриптов-проверяльщиков для SVG картинки тоже нет. Непонятно как в таких условиях рисовать картинки, если неизвестно отобразится ли она и где.
- есть куча багов рендеринга, найти причину которым я не смог. Пример: в Хроме текст отображается нормально, на Андроиде все строчки наезжают друг на друга. Почему - непонятно, вроде никаких навороченных фич не используется.
- у тебя нет никакого фоллбека для браузеров, не поддерживающих SVG. Я вообще не понимаю, зачем так делают. Видимо хотят, чтобы как можно меньше людей могло прочитать их сайт, чтобы он отображался на как можно меньшем числе устройств. Что движет этими людьми, я не понимаю.
- непонятно, насколько их можно использовать в background-image
Я для себя сделал вывод, что сейчас SVG прямо на сайте использовать нельзя (и возможно, никогда не будет можно). Его можно использовать в подготовке картинок, но на сам сайт выгоднее ставить старые добрые PNG и джипеги (возможно, автоматически отрендеренные из SVG).
Интересно, почему такие исследования и такие выводы не делают авторы статей про свг? Наверно, знаний не хватает. Они некомпетентные, но это им не мешает учить других.
> <p class="p p--common-alt">
Что это за страное название? ЧТо за класс "p"?
Вот это файл тяжеловато читать: http://w99953g4.bget.ru/evently/js/sliders.js И код странный. Вот например, зачем так писать?
> zoomOut.attr('class','list-slider__item animated zoomOut');
А что будет, когда в верстке добавят новый класс? Твой код перезапишет его и он исчезнет. Почему не используешь add/removeClass?
И вообще, почему в слайдерах так много кода? Там нельзя разве обойтись средствами CSS? В мое время слайдер либо анимировал какой-нибудь scrollLeft либо просто переставлял классы на 2 элементах. Вот например, возьмем спонсоров. Там достаточно сделать див с overflow hidden и анимироват ему scrollLeft, единственная сложность - если нам надо закольцевать прокрутку.
Вообще, код читать тяжеловато. Вот как например понять, какой код отвечает за тот или иной слайдер? Я вижу слайдер в HTML коде, но какая функция его инициализиурет - непонятно. Как ее найти тоже непонятно. У тебя еще относительно маленький макет, а если бы это был большой сайт с кучей страниц и разделов?
Вообще, у тебя в яваскриптах какие-то стены кода без разбиения на части, например: http://w99953g4.bget.ru/evently/js/map.js
Тут вообще eval: http://w99953g4.bget.ru/evently/js/smooth-scroll.js
> setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
Ну что за ерунда? Во-первых, надо использовать тут анонимную фнукцию, во-вторых, у тебя же jquery есть. И вообще, setTimeout не дает никаких гарантий что он будет вызываться с нужным интервалом. И для анимации есть requestAnimationFrame.
В общем, я тебе советую исправить все эти проблемы.
Ох, у тебя опять какой-то визуально перегруженный и тяжелочитаемый макет (это конечно претензия к дизайнеру, а не к тебе). Что подтверждает анимированная надпись "scroll down". Дизайнер сделал макет который выглядит как одностраничный сайт и вынужден объяснять что там внизу что-то еще есть.
Твой сайт постоянно ест от 5 до 10% CPU когда страница неподвижна. Попробуй покрутить, попереключать страницу, а затем открыть в отладчике Хрома вкладку timeline и записать несколько секунд. У меня например там какое-то нереальное число таймеров (242 срабатывания за секунду). Часто такое бывает при неправильном написании или подключении плагинов у людей которые плохо знают JS. Осваивай timeline и профайлер.
Более того, кастомная прокрутка, которую ты используешь, вообще плохо работает. Я привык что если я резко провожу пальцем по тачпаду то прокрутка получается сильнее, но твой плагин ее замедляет. Это неудобно. Я вообще не понимаю, зачем это нужно. Ну хочется тебе поменять внешний вид полосы прокрутки, ну поменяй, а зачем делать ее медленнее? Это ты сайт для людей с замедленным сознанием пишешь?
И я не понимаю, какая выгода от более тонкой линейки прокрутки. Толстую линейку можно перетащить мышью, тонкую нельзя. В чем выгода? Да, такая линейка прокрутки есть на макбуках, но там взамен есть удобный тачпад с кучей функций. Более того, если говорить о блоке "15 October Business Conference" то там вообще от линейки прокрутки один вред, удобнее было бы вывести весь список как есть. Прокрутка внутри прокрутки это ад с точки зрения юзабилити. И на мобильных платформах далеко не все пользователи знают как прокручивать такие блоки.
Когда я прокручиваю страницу и курсор оказывается над картой, прокрутка перестант работать и вместо этого уменьшается масштаб на карте. При наведении на черную метку на карте мышь меняет форму, но клик по метке ничего не делает.
В разделе Sponsors стрелки прокручивают блок в противоположную сторону. И вообще, по моему задвигать спонсоров за блок прокрутки, где их никто не увидит (я никогда не жму на такие стрелки) неуважительно к ним.
Тут http://w99953g4.bget.ru/evently/js/ajax.js ты попытался сделать отправку форм аяксом, но не все учел. Прочитай этот урок: https://github.com/codedokode/pasta/blob/master/js/ajax.md
У тебя нет ни индикации прогресса, ни обработки ошибок, ни блокировки кнопки отправки.
Не уверен что хорошая идея выводить ошибки вверху страницы так как они оторваны от формы, если прокрутить страницу то вообще непонятно к чему относятся. Хотя.... кто знает.
Вижу, ты сделал адаптивную версию. Но слайдеры в мобильной версии получились неудачные. например, в разделе Speakers ты можешь прокрутить до фото и переклчюать их, но при этом не видно что меняется блок с описанием. Все эти блоки с прокруткой здорово мешают прокручивать страницу, когда она узкая.
Я еще тут бегло посмотрел и вот что я вижу:
- анимация при прокрутке. Анимация привлекает внимание, но какой смысл привлекать внимание к блокам, которые только что выехали снизу? Я и так вижу, что они выехали.
- если прокрутить страницу до раздела Speakers и попробовать выделить слово "vestibulum" во втором абзаце текста, то оно не выделяется. Почему? Потому что там див из слайдера с opacity = 0. Див может быть не виден, но он все равно там есть, он блокирует доступ к тексту, который лежит под ним. Если тебе интересно, то это див с классом "mCustomScrollBox mCS-light mCSB_vertical mCSB_inside"
> <h1 class="header__promo-header header-promo">london</h1>
> <h2 class="header__promo-header header-promo-caption">business <strong>conference</strong></h2>
По моему, заголовок страницы "London Business Conference", а не "London". Теги h1, h2 это не теги для задания размера текста, а теги для смысловой разметки содержимого.
Я вижу, ты везде используешь SVG. А изучал ли ты уровень поддержки SVG? Можешь ли назвать проблемы которые связаны с его использованием? Нельзя использовать такие сложные технологии без предварительного изучения, если конечно тебе не наплевать на результат.
Тем более что у тебя не просто SVG картинка, а используется инлайново вставленный код. Вот например какие проблемы я видел, когда рассматривал вариант использовать SVG в своем учебнике:
- стандарт SVG развивается и браузеры не поддерживают все фичи, которые поддерживают редакторы. Более того, иногда фичи, которые планировалось добавить в стандарт, отменяют. Например, форматирование текста которое есть в inkscape, в итоге убрали из стандарта и заверстанный в блок текст в браузере не отображается
- информации о том какие именно фичи поддерживаются какими бразерами, толком нигде нет. Каких-то автоматических скриптов-проверяльщиков для SVG картинки тоже нет. Непонятно как в таких условиях рисовать картинки, если неизвестно отобразится ли она и где.
- есть куча багов рендеринга, найти причину которым я не смог. Пример: в Хроме текст отображается нормально, на Андроиде все строчки наезжают друг на друга. Почему - непонятно, вроде никаких навороченных фич не используется.
- у тебя нет никакого фоллбека для браузеров, не поддерживающих SVG. Я вообще не понимаю, зачем так делают. Видимо хотят, чтобы как можно меньше людей могло прочитать их сайт, чтобы он отображался на как можно меньшем числе устройств. Что движет этими людьми, я не понимаю.
- непонятно, насколько их можно использовать в background-image
Я для себя сделал вывод, что сейчас SVG прямо на сайте использовать нельзя (и возможно, никогда не будет можно). Его можно использовать в подготовке картинок, но на сам сайт выгоднее ставить старые добрые PNG и джипеги (возможно, автоматически отрендеренные из SVG).
Интересно, почему такие исследования и такие выводы не делают авторы статей про свг? Наверно, знаний не хватает. Они некомпетентные, но это им не мешает учить других.
> <p class="p p--common-alt">
Что это за страное название? ЧТо за класс "p"?
Вот это файл тяжеловато читать: http://w99953g4.bget.ru/evently/js/sliders.js И код странный. Вот например, зачем так писать?
> zoomOut.attr('class','list-slider__item animated zoomOut');
А что будет, когда в верстке добавят новый класс? Твой код перезапишет его и он исчезнет. Почему не используешь add/removeClass?
И вообще, почему в слайдерах так много кода? Там нельзя разве обойтись средствами CSS? В мое время слайдер либо анимировал какой-нибудь scrollLeft либо просто переставлял классы на 2 элементах. Вот например, возьмем спонсоров. Там достаточно сделать див с overflow hidden и анимироват ему scrollLeft, единственная сложность - если нам надо закольцевать прокрутку.
Вообще, код читать тяжеловато. Вот как например понять, какой код отвечает за тот или иной слайдер? Я вижу слайдер в HTML коде, но какая функция его инициализиурет - непонятно. Как ее найти тоже непонятно. У тебя еще относительно маленький макет, а если бы это был большой сайт с кучей страниц и разделов?
Вообще, у тебя в яваскриптах какие-то стены кода без разбиения на части, например: http://w99953g4.bget.ru/evently/js/map.js
Тут вообще eval: http://w99953g4.bget.ru/evently/js/smooth-scroll.js
> setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
Ну что за ерунда? Во-первых, надо использовать тут анонимную фнукцию, во-вторых, у тебя же jquery есть. И вообще, setTimeout не дает никаких гарантий что он будет вызываться с нужным интервалом. И для анимации есть requestAnimationFrame.
В общем, я тебе советую исправить все эти проблемы.
> Кроссбраузерностью по-прежнему не занимаюсь.
Верстка это и есть кроссбраузерность за исключением случаев когда модель и версия браузера или движка заранее известны.
> В старых ослах даже не хочу открывать и вообще не знаю как с этой темой совладать. Как можно запомнить какие из сотен разных правил/значений как себя ведут в десятках проблемных версий разных браузеров?
По ИЕ у меня есть гайд для начинающих: https://gist.github.com/codedokode/855e3970124687b26a1c
Есть стандарты. Если брать например стандарт CSS2.1 то худо-бедно он поддерживается даже в ИЕ8.
По поводу других браузеров - есть сайт http://caniuse.com/. Дождись там 97-98% поддержки и используй. Это значит что технологии которые появлились в последние лет 5 можно использовать только с возмодностью фоллбека. Также, всегда старайся предусмотреть фоллбек. Например:
- ИЕ8 не умеет скруглять уголки - пусть будут квадратные, не надо ничего усложнять
- выводим SVG картинку через object с фоллбеком на PNG внутри либо через srcset (не уверен что это работает, я тестироал и по моему там что-то было не так)
- ставим такие цвета чтобы даже если картинка не загрузилась, текст был контрастным и читался
- пишем по несколько css правил подряд - старые браузеры прочтут первое, новые - оба и второе перекроет первое.
Полезно использовать метод "прогресивного улучшения", то есть старый браузер показвает просто читабельную страницу, а новый - более красивую.
Помни что есть куча разных устройств, которые никогда не обновятся. Старый компьютер, где не идут новые браузеры. Андроид, который не обновляется в принципе. "умный" телевизор с браузером. И так далее.
https://gist.github.com/codedokode/3f6063edf0a2227eb313
Чем марджины на флоатах принципиально отличаются от марджинов на не флоатах?
Хороший вопрос. Я тут задумался, а где есть разница, и кое-что пришло мне в голову. Сравним поведение флоата и блочного элемента с width = auto.
Правый маргин на флоате влияет на расталкивание текста и инлайн-блоков в элементе, поверх которого он находится. Но на блочном элементе правый маргин будет растягивать/сужать сам элемент.
А вот левые маргины ведут себя одинаково: в обоих случаях они сдвигают левую границу элемента.
Привет, спасибо за отзыв.
>Я вообще не понимаю, зачем это нужно. Ну хочется тебе поменять внешний вид полосы прокрутки, ну поменяй, а зачем делать ее медленнее?
Это готовый плагин так работает, всмысле помимо изменения внешнего вида тормозит скрол.
>Когда я прокручиваю страницу и курсор оказывается над картой, прокрутка перестант работать и вместо этого уменьшается масштаб на карте
Это вроде обычное поведение для карты. Можно его заблокировать, но тогда расстроится ползователь который наоборот ожидает что скролл над картой это зум.
>В разделе Sponsors стрелки прокручивают блок в противоположную сторону
Левая стрелка - влево, правая - вправо, так и задумано/
>У тебя нет ни индикации прогресса, ни обработки ошибок, ни блокировки кнопки отправки.
Лень было, да и там всего по три поля.
>Но слайдеры в мобильной версии получились неудачные. например, в разделе Speakers ты можешь прокрутить до фото и переклчюать их, но при этом не видно что меняется блок с описанием
Да, это так. Но а как их еще сделаешь? И блок с текстом и картинку рядом на 320рх не уместишь, только друг под другом. Можно уменьшить высоту картинки чтобы с бОльшей вероятностью оба блока оказались перед глазами при листании, но во-первых все равно не факт что пользователь не пролистнет до момента когда блок с текстом окажется вне вьюпорта, а во-вторых фотки будут мелкие.
>Анимация привлекает внимание, но какой смысл привлекать внимание к блокам, которые только что выехали снизу? Я и так вижу, что они выехали.
Просто нескучный эффект. Часто вижу на промо-сайтах
>попробовать выделить слово
Когда-то натыкался на это, забыл исправить.
>По моему, заголовок страницы "London Business Conference", а не "London"
Может быть.
>ты везде используешь SVG. А изучал ли ты уровень поддержки SVG?
Читал попутно. Я намерено использовал самый крутой и моднейший способ вставки без оглядки на совместимость. Он позволяет рулить svg из css, например изменять цвет заливки фигуры. Вот у меня там несколько одинаковых по форме, но разного цвета иконок социальных в разных местах встречаются. В png-спрайт надо добавлять три разных иконки разных цветов и потом их крутить в background-position. При вставке через <svg><use ...sprite.svg#icon-facebook></svg> в самом svg-спрайте такая иконка-фигура всего одна и ее можно раскрашивать в css.
Зачем так? Показать на собеседовании какой я крутой. Вот смотрите, даже на ютабе, в гугле и вк иконокнет и мыло за 2300, а я вот так умею. Разумеется с оговоркой что если в ТЗ будут старые браузеры я подсуну им png, просто тут лень было так как проект учебный.
>ЧТо за класс "p"?
Тоже самое что и тег р, paragraph, наиболее общие правила для абзацов. Что-то типа классов .link, .btn и т. д.
>Вообще, у тебя в яваскриптах какие-то стены кода без разбиения на части, например: http://w99953g4.bget.ru/evently/js/map.js
Эту дичь генерирует гугл и сервис который раскрашивает стандартную карту. Я там только добавил пару строк высичляющих координаты пина чтобы он на любом разрешении оказывался в центре
>Тут вообще eval: http://w99953g4.bget.ru/evently/js/smooth-scroll.js
Это сворованный скрипт, не читал его, только скорость подобрал.
>Твой код перезапишет его и он исчезнет. Почему не используешь add/removeClass?
Знаю про этом. Уже не помню какая именно но была сложность с add/remove.
>В мое время слайдер либо анимировал какой-нибудь scrollLeft либо просто переставлял классы на 2 элементах
Я и так и так делаю. Спикеры например листаются перестановкой классов, а споносоры смещением дивов. Вот слайдер споносоров на 2 пике, по-моему тут абсолютный минимум кода.
>>768318
Не, ну я не такой уж нуб) Нагуглить какое свойство где поддерживается могу.
Просто, блин, так мурыжить каждую кнопочку и блочок постоянно заглядывать в справочник и дописывать сопли для ослов 6-7 и отказываться от современных решений когда фолбек невозможен это отстой. Из средств позиционирования там вообще походу кроме флоатов а то и таблиц ничего не останется. Если на работе скажут - тогда да, а пока учусь ну его в пень. Ну и поглядывая на вакансии я все чаще вижу что пишут что-то типа 'мучать ие 8- не будем'. Да и в конце концов, я вообще не в версталы собираюсь, прост творческий кризис в php, решил отвлечься на фронт.
Привет, спасибо за отзыв.
>Я вообще не понимаю, зачем это нужно. Ну хочется тебе поменять внешний вид полосы прокрутки, ну поменяй, а зачем делать ее медленнее?
Это готовый плагин так работает, всмысле помимо изменения внешнего вида тормозит скрол.
>Когда я прокручиваю страницу и курсор оказывается над картой, прокрутка перестант работать и вместо этого уменьшается масштаб на карте
Это вроде обычное поведение для карты. Можно его заблокировать, но тогда расстроится ползователь который наоборот ожидает что скролл над картой это зум.
>В разделе Sponsors стрелки прокручивают блок в противоположную сторону
Левая стрелка - влево, правая - вправо, так и задумано/
>У тебя нет ни индикации прогресса, ни обработки ошибок, ни блокировки кнопки отправки.
Лень было, да и там всего по три поля.
>Но слайдеры в мобильной версии получились неудачные. например, в разделе Speakers ты можешь прокрутить до фото и переклчюать их, но при этом не видно что меняется блок с описанием
Да, это так. Но а как их еще сделаешь? И блок с текстом и картинку рядом на 320рх не уместишь, только друг под другом. Можно уменьшить высоту картинки чтобы с бОльшей вероятностью оба блока оказались перед глазами при листании, но во-первых все равно не факт что пользователь не пролистнет до момента когда блок с текстом окажется вне вьюпорта, а во-вторых фотки будут мелкие.
>Анимация привлекает внимание, но какой смысл привлекать внимание к блокам, которые только что выехали снизу? Я и так вижу, что они выехали.
Просто нескучный эффект. Часто вижу на промо-сайтах
>попробовать выделить слово
Когда-то натыкался на это, забыл исправить.
>По моему, заголовок страницы "London Business Conference", а не "London"
Может быть.
>ты везде используешь SVG. А изучал ли ты уровень поддержки SVG?
Читал попутно. Я намерено использовал самый крутой и моднейший способ вставки без оглядки на совместимость. Он позволяет рулить svg из css, например изменять цвет заливки фигуры. Вот у меня там несколько одинаковых по форме, но разного цвета иконок социальных в разных местах встречаются. В png-спрайт надо добавлять три разных иконки разных цветов и потом их крутить в background-position. При вставке через <svg><use ...sprite.svg#icon-facebook></svg> в самом svg-спрайте такая иконка-фигура всего одна и ее можно раскрашивать в css.
Зачем так? Показать на собеседовании какой я крутой. Вот смотрите, даже на ютабе, в гугле и вк иконокнет и мыло за 2300, а я вот так умею. Разумеется с оговоркой что если в ТЗ будут старые браузеры я подсуну им png, просто тут лень было так как проект учебный.
>ЧТо за класс "p"?
Тоже самое что и тег р, paragraph, наиболее общие правила для абзацов. Что-то типа классов .link, .btn и т. д.
>Вообще, у тебя в яваскриптах какие-то стены кода без разбиения на части, например: http://w99953g4.bget.ru/evently/js/map.js
Эту дичь генерирует гугл и сервис который раскрашивает стандартную карту. Я там только добавил пару строк высичляющих координаты пина чтобы он на любом разрешении оказывался в центре
>Тут вообще eval: http://w99953g4.bget.ru/evently/js/smooth-scroll.js
Это сворованный скрипт, не читал его, только скорость подобрал.
>Твой код перезапишет его и он исчезнет. Почему не используешь add/removeClass?
Знаю про этом. Уже не помню какая именно но была сложность с add/remove.
>В мое время слайдер либо анимировал какой-нибудь scrollLeft либо просто переставлял классы на 2 элементах
Я и так и так делаю. Спикеры например листаются перестановкой классов, а споносоры смещением дивов. Вот слайдер споносоров на 2 пике, по-моему тут абсолютный минимум кода.
>>768318
Не, ну я не такой уж нуб) Нагуглить какое свойство где поддерживается могу.
Просто, блин, так мурыжить каждую кнопочку и блочок постоянно заглядывать в справочник и дописывать сопли для ослов 6-7 и отказываться от современных решений когда фолбек невозможен это отстой. Из средств позиционирования там вообще походу кроме флоатов а то и таблиц ничего не останется. Если на работе скажут - тогда да, а пока учусь ну его в пень. Ну и поглядывая на вакансии я все чаще вижу что пишут что-то типа 'мучать ие 8- не будем'. Да и в конце концов, я вообще не в версталы собираюсь, прост творческий кризис в php, решил отвлечься на фронт.
>> В задаче написано что множить сущности не желательно, это касается класса валидации для формы редактирования? Старый класс валидации всегда будет выдавать ошибку о том что такая почта уже занята, если её не поменять.
>Значит надо исправить его чтобы он не сравнивал свою почту со своей, а только с другими.
Тогда для метода валидатора нужно будет добавить аргумент с id.
А как будущие разработчики поймут зачем здесь этот аргумент? Даже если бы он был не обязательным, я бы не сразу понял зачем методу проверки имейла нужно что-то кроме имейла, пока не дошел бы до части с валидацией формы редактирования.
>>768312
>> Так же это касается паролей, даже если их заполнить теми же самыми данным, то всё равно сгенерируется новый хэш и токен, и авторизация сломается, потому что в куках находиться не актуальный токен. Как обойти обновление полей которые мы не хотим обновлять?
>Сделать параметр какой-то который предается валидатору.
А что за параметр? Который сообщает игнорировать те или иные поля? Так придётся переделывать весь класс валидации, не проще ли помножить сущность и сделать новый?
Да и дело даже не в самом в валидаторе, а в запросе БД, т.к. он составлен так, что нам всегда нужно будет отправлять все данные из формы\сущности (https://github.com/someApprentice/Students/blob/master/app/Model/Gateway/StudentGateway.php#L103-L114), а хотелось бы что если в форме какое-то поле было пустым, то и запрос в БД составлялся без этого поля. Как составляются такие запросы? Неужели нужно писать отдельный метод который на основе сущности будет составлять запрос? Да и как этот метод сделать универсальным, ведь запросы могут быть разными (UPDATE\INSERT\SELECT\...)? Принимать в аргументы тип запроса а потом через case "ТИПЗАПРОСА" составлять сам запрос?
>>768312
>Этот класс https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Search.php на мой взгляд не особо нужен.
Хм, значит сущности нужны только если у них есть какие-то особенные методы или много свойств?
>>768312
>> А разве это не обязанность контроллера работать с $_GET данными?
>Да, просто я имел в виду что index.php это и есть контроллер.
Получается что контроллер может находиться в любом месте директории и быть даже публичной страницей?
>>768312
>> а как быть с формами? Допустим есть форма поиска с методом get и, насколько я знаю, её нельзя заставить отправлять этот запрос с помощью urlencode().
>А зачем? Форму заполняет и отправляет пользователь ведь.
Оказалось что запрос сам как-то кодируется при отправке формы. Просто браузер в адресной строке отображал процентное кодирование в нормальном формате, и было не понятно почему при отправке формы в адресной строке можно использовать кириллицу, а в php нужно кодировать.
>> В задаче написано что множить сущности не желательно, это касается класса валидации для формы редактирования? Старый класс валидации всегда будет выдавать ошибку о том что такая почта уже занята, если её не поменять.
>Значит надо исправить его чтобы он не сравнивал свою почту со своей, а только с другими.
Тогда для метода валидатора нужно будет добавить аргумент с id.
А как будущие разработчики поймут зачем здесь этот аргумент? Даже если бы он был не обязательным, я бы не сразу понял зачем методу проверки имейла нужно что-то кроме имейла, пока не дошел бы до части с валидацией формы редактирования.
>>768312
>> Так же это касается паролей, даже если их заполнить теми же самыми данным, то всё равно сгенерируется новый хэш и токен, и авторизация сломается, потому что в куках находиться не актуальный токен. Как обойти обновление полей которые мы не хотим обновлять?
>Сделать параметр какой-то который предается валидатору.
А что за параметр? Который сообщает игнорировать те или иные поля? Так придётся переделывать весь класс валидации, не проще ли помножить сущность и сделать новый?
Да и дело даже не в самом в валидаторе, а в запросе БД, т.к. он составлен так, что нам всегда нужно будет отправлять все данные из формы\сущности (https://github.com/someApprentice/Students/blob/master/app/Model/Gateway/StudentGateway.php#L103-L114), а хотелось бы что если в форме какое-то поле было пустым, то и запрос в БД составлялся без этого поля. Как составляются такие запросы? Неужели нужно писать отдельный метод который на основе сущности будет составлять запрос? Да и как этот метод сделать универсальным, ведь запросы могут быть разными (UPDATE\INSERT\SELECT\...)? Принимать в аргументы тип запроса а потом через case "ТИПЗАПРОСА" составлять сам запрос?
>>768312
>Этот класс https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Search.php на мой взгляд не особо нужен.
Хм, значит сущности нужны только если у них есть какие-то особенные методы или много свойств?
>>768312
>> А разве это не обязанность контроллера работать с $_GET данными?
>Да, просто я имел в виду что index.php это и есть контроллер.
Получается что контроллер может находиться в любом месте директории и быть даже публичной страницей?
>>768312
>> а как быть с формами? Допустим есть форма поиска с методом get и, насколько я знаю, её нельзя заставить отправлять этот запрос с помощью urlencode().
>А зачем? Форму заполняет и отправляет пользователь ведь.
Оказалось что запрос сам как-то кодируется при отправке формы. Просто браузер в адресной строке отображал процентное кодирование в нормальном формате, и было не понятно почему при отправке формы в адресной строке можно использовать кириллицу, а в php нужно кодировать.
есть абстрактный класс Profession в котором есть поля:
private $salaryRate = 0; // базовая ставка
private $coffeeRate = 0; // кол-во выпиваемого кофе
От него наследуется класс Marketer, в котором переопределяются эти поля:
private $salaryRate = 400; // базовая ставка
private $coffeeRate = 15; // кол-во выпиваемого кофе
А var_dump показывает, что поля не переопределились а продублировались?
["salaryRate":"Marketer":private]=>
int(400)
["coffeeRate":"Marketer":private]=>
int(15)
["salaryRate":"Profession":private]=>
int(0)
["coffeeRate":"Profession":private]=>
int(0)
Приватное поле принадлежит только одному классу. Его нельзя переопрделеить. Потому это не одно переопределенное поле, а 2 разных поля с одинаковым названием.
>Правый маргин на флоате влияет на расталкивание текста и инлайн-блоков в элементе, поверх которого он находится. Но на блочном элементе правый маргин будет растягивать/сужать сам элемент.
Я протестировал. Одинаковое предсказуемое поведение вышло. Правый марджин на флоате также растягивает/сужает флоат-блок.
Плохо тестировал: https://jsfiddle.net/L41xne7f/
Увеличение правого маргина на обычном блоке сужает сам блок.
Увеличение правого маргина на флоате расталкивает текст дальше.
https://jsfiddle.net/hxxebptq/
Ну естественно сужает обычный блок так как у него ширина все место занимает, а у флоата ширина по контенту. Попробуй во флоат контент на всю ширину вставить.
Разница все же есть, когда контента мало. Обычный блок по умолчанию растягивается на максимальную ширину в родительском стакане, а флоат - на максимальную ширину своего содержимого с учетом ограничения родителя и минимальной ширины содержимого.
Но это уже float behavior идет, а не поведение марджинов.
>>757712
Что-то у тебя в README ничего не сказано про выбор dev/prod окружения. Где это задается. я помню, разворачивал твой проект, и по моему на это наткнулся, что не для того окружения что-то сделал. Например, композер генерирует bootstrap cache для dev, а сайт работает с prod, точнее не работает по этой причине.
И как его тестировать-то? Ни одна кнопка на главной не работает же. Попробовал загрузить данные из fixtures - получаю ошибку:
> [Doctrine\DBAL\Exception\SyntaxErrorException]
> An exception occurred while executing 'DELETE FROM user':
> SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near "user"
> LINE 1: DELETE FROM user
USER это ключевое слово, его в postgres надо как-то экранировать. По идее оно должно бы экранировать само, но по факту ничего не экранируется. В мануале доктрины что-то мутно упомянуто на эту тему: http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/basic-mapping.html#quoting-reserved-words
Также, есть такой баг: https://github.com/doctrine/doctrine2/issues/2409
Я попробовал в аннотации прописать имя таблицы user как ORM\Table(name="""user"""), но в ответ на это он начал падать с ошибкой
> SQLSTATE[42602]: Invalid name: 7 ERROR: invalid name syntax
> LINE 1: SELECT NEXTVAL('"user"_id_seq')
На этом месте я решил забить на попытку настроить постгрес и передать возможность исправления этой проблемы тебе.
Подключил mysql, загрузил тестовые данные, открыл https://nsdvw-testhub-codedokode.c9users.io/test/1/preface и нажатие кнопки ведет почему-то на https://nsdvw-testhub-codedokode.c9users.io/app_dev.php/test/1/start . Пришлось руками исправлять адрес в форме.
Вообще, я сразу советую сделать отдельный класс для генерации URL. Там в Симфони есть функция path() в твиге, которая генериурет УРЛ из роутера, но на мой взгляд свой класс дает гораздо больше гибкости, например ты можешь писать штуки вроде function getStartTestUrl(Test $test).
Команда
> Run tests
> $ php phpunit.phar
Тоже не заработала, так как не скачан phpunit. Вообще, его можно указать в композере в require-dev, либо предупредить что надо его скачать.
В общем, после ковыряния настроек тесты выполнились успешно.
Также, я смотрю, продакшен и дев-конфиги оба используют parameters.yml, а тестовый - не использует. Почему так? Какая вообще логика в разнесении параметров?
Далее, вопрос с логгированием. Надо проверить как и куда логгируются следующие вещи:
- PHP Notice, пример: $a = []; $a['test'];
- PHP Error, пример: callNonExistentFunction()
- Exception, пример throw new Exception("test");
Проверить надо в dev, test и prod окружениях, для CLI команд и веб-скриптов. Если ты точно знаешь ответы, что и куда логгируется, то конечно можно не проверять. Я например не знаю. На практике очень часто сталкиваюсь что в разных проектах логи идут непонятно куда, никто их не смотрит и ошибки не исправляет.
Вообще, мне не нравится идея что ошибки пишутся непонятно куда. Чем тебе стандартный лог PHP не нравится?
В ридми ты пишешь:
> Create database
> $ php bin/console doctrine:database:create
Но у пользователя, под которым я развернул сайт, нет прав на создание базы данных. Обычно никто на продакшене не дает рутовый доступ к БД, обычно создают БД и пользователя имеющего доступ к ней. Иногда даже еще более серьезно разграниивают.
В конфиге я вижу:
> twig:
> debug: "%kernel.debug%"
> strict_variables: "%kernel.debug%"
strict_variables определяет считать ли ошибкой обращение к несуществующей переменной. Почему в продакшене выбрана опция "и так сойдет"? Что, это уже не ошибка считается?
В формах можно бы использовать инпуты нужных типов (для чисел) и HTML5 валидацию. На мобильных устройствах например тип инпута влияет на вид клавитатуры.
Обрабатывается ли у тебя ввод дробных чисел через запятую, как принято в России? через точку?
https://github.com/nsdvw/TestHub/blob/master/app/Resources/views/test/preface.html.twig#L19
> В тесте {{ wordCase(questionsCount, ['вопрос', 'вопроса', 'вопросов']) }},
О, велосипед для склонения слов. Вот я уже писал на эту тему в посте >>761042
--------
Кроме варианта писать функцию склонения руками, есть еще вариант раскурить расширение Intl: http://php.net/manual/ru/book.intl.php
Оно скопировано с Явы и там есть функция, которая позволяет делать в строку подстановки с учетом правил языка. Например: http://php.net/manual/ru/messageformatter.formatmessage.php
--------
Преимущество подхода с Intl в том, что это позволит затем сделать нормальную локализацию, то есть перевод сообщений вместе с плейсхолдерами (так как в других языках другие правила склонения слов). А в твоем нынешнем варианте локализовать разорванное на куски предложение нормально нельзя.
Вообще, я бы тебе советовал немного изучить локализацию, библиотеки gettext и Intl.
Также, у тебя там есть недостаток, что цифра всегда подставляется вперед слова, что не позволяет нам писать фразы вроде "пройден 1 тест"/"пройдено 2 теста"/"не пройдено ни одного теста".
https://github.com/nsdvw/TestHub/blob/master/app/Resources/views/test/question.html.twig#L27
> {% if question.type is same as('text') %}
> {% include 'test/text_question.html.twig' %}
Не удобнее ли использовать макросы для разных типов форм вместо инклюдов?
В контроллере https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Controller/TestController.php ты бы мог вместо постоянного написания $this->getDoctrine()->getManager(); сделать поле $this->em, аналогично для других популярных сервисов.
Не очень понял это место в startAction. Стоило бы добавить комментарий (о, и кстати, стоило бы наверно покрыть эту фичу тестом, так как это неочевидная штука которую легко сломать).
> if ($this->get('test_service')->hasActiveAttempt($user, $test)) {
> return $this->redirectToRoute('preface', ['testID' => $test->getId()]);
Ты создаешь временного пользователя в prefaceAction. Но по моему логичнее создавать его в startAction, так как это ПОСТ запрос, и логичнее изменения какие-то в базу вносить в нем. Для тестирования кук можно просто сделать куку has_cookies=1 или даже вынести это в отдельный мини-сервис CookieTester.
>>757712
Что-то у тебя в README ничего не сказано про выбор dev/prod окружения. Где это задается. я помню, разворачивал твой проект, и по моему на это наткнулся, что не для того окружения что-то сделал. Например, композер генерирует bootstrap cache для dev, а сайт работает с prod, точнее не работает по этой причине.
И как его тестировать-то? Ни одна кнопка на главной не работает же. Попробовал загрузить данные из fixtures - получаю ошибку:
> [Doctrine\DBAL\Exception\SyntaxErrorException]
> An exception occurred while executing 'DELETE FROM user':
> SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near "user"
> LINE 1: DELETE FROM user
USER это ключевое слово, его в postgres надо как-то экранировать. По идее оно должно бы экранировать само, но по факту ничего не экранируется. В мануале доктрины что-то мутно упомянуто на эту тему: http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/basic-mapping.html#quoting-reserved-words
Также, есть такой баг: https://github.com/doctrine/doctrine2/issues/2409
Я попробовал в аннотации прописать имя таблицы user как ORM\Table(name="""user"""), но в ответ на это он начал падать с ошибкой
> SQLSTATE[42602]: Invalid name: 7 ERROR: invalid name syntax
> LINE 1: SELECT NEXTVAL('"user"_id_seq')
На этом месте я решил забить на попытку настроить постгрес и передать возможность исправления этой проблемы тебе.
Подключил mysql, загрузил тестовые данные, открыл https://nsdvw-testhub-codedokode.c9users.io/test/1/preface и нажатие кнопки ведет почему-то на https://nsdvw-testhub-codedokode.c9users.io/app_dev.php/test/1/start . Пришлось руками исправлять адрес в форме.
Вообще, я сразу советую сделать отдельный класс для генерации URL. Там в Симфони есть функция path() в твиге, которая генериурет УРЛ из роутера, но на мой взгляд свой класс дает гораздо больше гибкости, например ты можешь писать штуки вроде function getStartTestUrl(Test $test).
Команда
> Run tests
> $ php phpunit.phar
Тоже не заработала, так как не скачан phpunit. Вообще, его можно указать в композере в require-dev, либо предупредить что надо его скачать.
В общем, после ковыряния настроек тесты выполнились успешно.
Также, я смотрю, продакшен и дев-конфиги оба используют parameters.yml, а тестовый - не использует. Почему так? Какая вообще логика в разнесении параметров?
Далее, вопрос с логгированием. Надо проверить как и куда логгируются следующие вещи:
- PHP Notice, пример: $a = []; $a['test'];
- PHP Error, пример: callNonExistentFunction()
- Exception, пример throw new Exception("test");
Проверить надо в dev, test и prod окружениях, для CLI команд и веб-скриптов. Если ты точно знаешь ответы, что и куда логгируется, то конечно можно не проверять. Я например не знаю. На практике очень часто сталкиваюсь что в разных проектах логи идут непонятно куда, никто их не смотрит и ошибки не исправляет.
Вообще, мне не нравится идея что ошибки пишутся непонятно куда. Чем тебе стандартный лог PHP не нравится?
В ридми ты пишешь:
> Create database
> $ php bin/console doctrine:database:create
Но у пользователя, под которым я развернул сайт, нет прав на создание базы данных. Обычно никто на продакшене не дает рутовый доступ к БД, обычно создают БД и пользователя имеющего доступ к ней. Иногда даже еще более серьезно разграниивают.
В конфиге я вижу:
> twig:
> debug: "%kernel.debug%"
> strict_variables: "%kernel.debug%"
strict_variables определяет считать ли ошибкой обращение к несуществующей переменной. Почему в продакшене выбрана опция "и так сойдет"? Что, это уже не ошибка считается?
В формах можно бы использовать инпуты нужных типов (для чисел) и HTML5 валидацию. На мобильных устройствах например тип инпута влияет на вид клавитатуры.
Обрабатывается ли у тебя ввод дробных чисел через запятую, как принято в России? через точку?
https://github.com/nsdvw/TestHub/blob/master/app/Resources/views/test/preface.html.twig#L19
> В тесте {{ wordCase(questionsCount, ['вопрос', 'вопроса', 'вопросов']) }},
О, велосипед для склонения слов. Вот я уже писал на эту тему в посте >>761042
--------
Кроме варианта писать функцию склонения руками, есть еще вариант раскурить расширение Intl: http://php.net/manual/ru/book.intl.php
Оно скопировано с Явы и там есть функция, которая позволяет делать в строку подстановки с учетом правил языка. Например: http://php.net/manual/ru/messageformatter.formatmessage.php
--------
Преимущество подхода с Intl в том, что это позволит затем сделать нормальную локализацию, то есть перевод сообщений вместе с плейсхолдерами (так как в других языках другие правила склонения слов). А в твоем нынешнем варианте локализовать разорванное на куски предложение нормально нельзя.
Вообще, я бы тебе советовал немного изучить локализацию, библиотеки gettext и Intl.
Также, у тебя там есть недостаток, что цифра всегда подставляется вперед слова, что не позволяет нам писать фразы вроде "пройден 1 тест"/"пройдено 2 теста"/"не пройдено ни одного теста".
https://github.com/nsdvw/TestHub/blob/master/app/Resources/views/test/question.html.twig#L27
> {% if question.type is same as('text') %}
> {% include 'test/text_question.html.twig' %}
Не удобнее ли использовать макросы для разных типов форм вместо инклюдов?
В контроллере https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Controller/TestController.php ты бы мог вместо постоянного написания $this->getDoctrine()->getManager(); сделать поле $this->em, аналогично для других популярных сервисов.
Не очень понял это место в startAction. Стоило бы добавить комментарий (о, и кстати, стоило бы наверно покрыть эту фичу тестом, так как это неочевидная штука которую легко сломать).
> if ($this->get('test_service')->hasActiveAttempt($user, $test)) {
> return $this->redirectToRoute('preface', ['testID' => $test->getId()]);
Ты создаешь временного пользователя в prefaceAction. Но по моему логичнее создавать его в startAction, так как это ПОСТ запрос, и логичнее изменения какие-то в базу вносить в нем. Для тестирования кук можно просто сделать куку has_cookies=1 или даже вынести это в отдельный мини-сервис CookieTester.
>>757712
Некоторые вещи вроде этого
> $questionsCount = $this->get('test_service')->getQuestionsCount($test);
наверно было бы удобнее сделать не в сервисе, а в самом классе Test
> $form = $this->resolveForm($question, ['attempt_id' => $attemptID]);
здесь кстати интересный вопрос, а нельзя ли решить это на уровне FormType, то есть создать свой Type который поддерживает все виды вопросов и динамически отключает/включает поля в зависимости от типа вопроса. Либо подключает одну из 4 вложенных форм для разных типов ответов. Ты не рассматривал такой вариант?
$formType = new AnswerForm($answer);
$form = $this->createForm($formType, ....); // не помню точный синтаксис
А там примерно такой код:
1) первый вариант
if ($this->type == QUESTION_WITH_NUMBER) {
// для числового ответа подключаем поле ввода числа
$builder->add('numericAnswer', 'number', ...);
}
2) второй вариант с подформами:
$builder->add('answerType', 'hidden', ...);
if ($this->type == QUESTION_WITH_NUMBER) {
// для числового ответа подключаем соотв. форму
$builder->add('numericAnswer', new NumericAnswerType, ...);
}
Второй вариает потребует разобраться с особенностями маппинга полей формы на сущности. Тут тебе надо замапить numericAnswer на ту же сущность, а answerType не мапить вообще.
Это позволит привязать формы к сущностям Answer, а то сейчас у тебя все на массивах работает и ты вручную занимаешься тем, что можно автоматизировать.
И еще странно, что у тебя в результате ответа на вопрос получается массив Answers. Мне казалось, там по логике должен быть один Answer, у которого может быть несколько AnswerChoices для ответа с чекбоксами.
Насчет flush - ты ставишь его везде, но ведь по идее это коммит транзакции. Может он должен быть только в конце action в контроллере? Вот в этом цикле
> foreach ($answers as $answer) {
> $repo->save($answer);
Явно делается не одна, а много транзакций, что плохо. Мне вообще этот метод save не нравится, по идее в случае с ORM нам достаточно изменить граф объектов в памяти и вызвать flush(). Правда не очень понятно, должны ли функции создания всяких объектов вызывать persist.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/DBAL/Platform.php
Тут есть ли правильное экранирование имени таблицы?
Насчет Fixtures - имей в виду что есть и другие способы описывать эти тестовые данные, например в YML: https://github.com/khepin/KhepinYamlFixturesBundle - может это позволит записывать их более лаконично (хотя и не позволяет вызывать сервисы).
И кстати, как я помню, еще Faker умеет заполнять модели как-то автоматически.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Repository/AnswerRepository.php#L65
Микрооптимизация: если сущность нужна только для простановки ссылки, можно загружать только прокси от нее через $em->getReference().
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Attempt.php#L16
Такую константу лучше назвать STATUS_ACTIVE. И использовать ее ниже:
> private $status = "active";
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Question.php#L88
> if ($this->variants->toArray() === []) {
там же можно вызвать $this->variants->count()
> $variants = $this->getVariants()->toArray();
> foreach ($variants as $variant) {
Можно по моему напрямую перебирать ArrayCollection так как она поддерживает Iterator или что-то в этом роде, проверь исходники.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Question.php#L145
> public function setVariants($variants)
Это не setVariants, а addVariants
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Question.php#L129
> public function setAnswers($answers)
Очень неудачная функция, в нее нельзя передать массив например. Лучше делать clear() и затем через foreach добавлять.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Test.php#L42
> @ORM\Column(name="time_limit", type="integer", options={"default"=0})
> private $timeLimit;
Если по умолчанию 0, то и выставь такое значение для поля
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/User.php
Тут заданы уникальные ключи, например для email? да и просто индексы для поиска?
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Variant.php#L138
> @return string
> public function getIsRight()
по моему тут логичнее возвращать bool.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Form/Type/VariantAnswerForm.php#L11
> $data = $builder->getData();
Не обязательно использовать тут массив $data. можно просто сделать у класса методы вроде setIsMultiple() или передавать эти переменные в конструктор. Ты можешь вручную создать свой FormType:
$formType = new VariantAnswerForm(....);
и передать любые параметры.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Service/Calculator.php#L91
Не логичнее ли этот метод было сделать в классе Question или Answer? Чтобы методы проверки для разных классов были бы в них самих?
Вот все эти if/esleif плохи тем, что увеличивают объем работы при добавлении нового типа вопроса.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Twig/AppExtension.php#L81
Что-то у тебя тут сложно сделано определение формы слов. Там 3 или 4 ветки всего достаточно.
https://github.com/nsdvw/TestHub/blob/master/tests/TestHubBundle/TestCase.php
Тут ты делаешь doctrine:fixtures:load перед каждым тестом, но для многих тестов это не нужно. Наверно стоит сделать разные базовые классы либо отдельный метод который вызывается явно.
И кстати эту команду наверняка можно вызвать и напрямую, не через указание командной строки, а получив нужный сервис.
https://github.com/nsdvw/TestHub/blob/master/tests/TestHubBundle/Service/CalculatorTest.php
здесь я вижу такие слабые места:
- id тестов в базе жестко прописан
- если поменять данные в fixtures, тесты отвалятся
Возможно выгоднее было бы в тесте создавать нужные данные.
> $this->assertInternalType('integer', $maxMark);
> $this->assertEquals(30, $maxMark)
Вместо 2 строк можно использовать одну: https://phpunit.de/manual/current/en/appendixes.assertions.html#appendixes.assertions.assertSame
>>757712
> что такое REST
можно почитать тут: https://ru.wikipedia.org/wiki/REST
Идея там не только в УРЛ, но еще и например в том, что все нужные данные передаются явно, и надо избегать всяких сессий и подобных способов сохранять состояние.
>>757712
Некоторые вещи вроде этого
> $questionsCount = $this->get('test_service')->getQuestionsCount($test);
наверно было бы удобнее сделать не в сервисе, а в самом классе Test
> $form = $this->resolveForm($question, ['attempt_id' => $attemptID]);
здесь кстати интересный вопрос, а нельзя ли решить это на уровне FormType, то есть создать свой Type который поддерживает все виды вопросов и динамически отключает/включает поля в зависимости от типа вопроса. Либо подключает одну из 4 вложенных форм для разных типов ответов. Ты не рассматривал такой вариант?
$formType = new AnswerForm($answer);
$form = $this->createForm($formType, ....); // не помню точный синтаксис
А там примерно такой код:
1) первый вариант
if ($this->type == QUESTION_WITH_NUMBER) {
// для числового ответа подключаем поле ввода числа
$builder->add('numericAnswer', 'number', ...);
}
2) второй вариант с подформами:
$builder->add('answerType', 'hidden', ...);
if ($this->type == QUESTION_WITH_NUMBER) {
// для числового ответа подключаем соотв. форму
$builder->add('numericAnswer', new NumericAnswerType, ...);
}
Второй вариает потребует разобраться с особенностями маппинга полей формы на сущности. Тут тебе надо замапить numericAnswer на ту же сущность, а answerType не мапить вообще.
Это позволит привязать формы к сущностям Answer, а то сейчас у тебя все на массивах работает и ты вручную занимаешься тем, что можно автоматизировать.
И еще странно, что у тебя в результате ответа на вопрос получается массив Answers. Мне казалось, там по логике должен быть один Answer, у которого может быть несколько AnswerChoices для ответа с чекбоксами.
Насчет flush - ты ставишь его везде, но ведь по идее это коммит транзакции. Может он должен быть только в конце action в контроллере? Вот в этом цикле
> foreach ($answers as $answer) {
> $repo->save($answer);
Явно делается не одна, а много транзакций, что плохо. Мне вообще этот метод save не нравится, по идее в случае с ORM нам достаточно изменить граф объектов в памяти и вызвать flush(). Правда не очень понятно, должны ли функции создания всяких объектов вызывать persist.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/DBAL/Platform.php
Тут есть ли правильное экранирование имени таблицы?
Насчет Fixtures - имей в виду что есть и другие способы описывать эти тестовые данные, например в YML: https://github.com/khepin/KhepinYamlFixturesBundle - может это позволит записывать их более лаконично (хотя и не позволяет вызывать сервисы).
И кстати, как я помню, еще Faker умеет заполнять модели как-то автоматически.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Repository/AnswerRepository.php#L65
Микрооптимизация: если сущность нужна только для простановки ссылки, можно загружать только прокси от нее через $em->getReference().
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Attempt.php#L16
Такую константу лучше назвать STATUS_ACTIVE. И использовать ее ниже:
> private $status = "active";
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Question.php#L88
> if ($this->variants->toArray() === []) {
там же можно вызвать $this->variants->count()
> $variants = $this->getVariants()->toArray();
> foreach ($variants as $variant) {
Можно по моему напрямую перебирать ArrayCollection так как она поддерживает Iterator или что-то в этом роде, проверь исходники.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Question.php#L145
> public function setVariants($variants)
Это не setVariants, а addVariants
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Question.php#L129
> public function setAnswers($answers)
Очень неудачная функция, в нее нельзя передать массив например. Лучше делать clear() и затем через foreach добавлять.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Test.php#L42
> @ORM\Column(name="time_limit", type="integer", options={"default"=0})
> private $timeLimit;
Если по умолчанию 0, то и выставь такое значение для поля
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/User.php
Тут заданы уникальные ключи, например для email? да и просто индексы для поиска?
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Entity/Variant.php#L138
> @return string
> public function getIsRight()
по моему тут логичнее возвращать bool.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Form/Type/VariantAnswerForm.php#L11
> $data = $builder->getData();
Не обязательно использовать тут массив $data. можно просто сделать у класса методы вроде setIsMultiple() или передавать эти переменные в конструктор. Ты можешь вручную создать свой FormType:
$formType = new VariantAnswerForm(....);
и передать любые параметры.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Service/Calculator.php#L91
Не логичнее ли этот метод было сделать в классе Question или Answer? Чтобы методы проверки для разных классов были бы в них самих?
Вот все эти if/esleif плохи тем, что увеличивают объем работы при добавлении нового типа вопроса.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Twig/AppExtension.php#L81
Что-то у тебя тут сложно сделано определение формы слов. Там 3 или 4 ветки всего достаточно.
https://github.com/nsdvw/TestHub/blob/master/tests/TestHubBundle/TestCase.php
Тут ты делаешь doctrine:fixtures:load перед каждым тестом, но для многих тестов это не нужно. Наверно стоит сделать разные базовые классы либо отдельный метод который вызывается явно.
И кстати эту команду наверняка можно вызвать и напрямую, не через указание командной строки, а получив нужный сервис.
https://github.com/nsdvw/TestHub/blob/master/tests/TestHubBundle/Service/CalculatorTest.php
здесь я вижу такие слабые места:
- id тестов в базе жестко прописан
- если поменять данные в fixtures, тесты отвалятся
Возможно выгоднее было бы в тесте создавать нужные данные.
> $this->assertInternalType('integer', $maxMark);
> $this->assertEquals(30, $maxMark)
Вместо 2 строк можно использовать одну: https://phpunit.de/manual/current/en/appendixes.assertions.html#appendixes.assertions.assertSame
>>757712
> что такое REST
можно почитать тут: https://ru.wikipedia.org/wiki/REST
Идея там не только в УРЛ, но еще и например в том, что все нужные данные передаются явно, и надо избегать всяких сессий и подобных способов сохранять состояние.
Просто берешь и вкатываешься, ничего сложного же. А вообще да, придется учить ООП, MVC, красоту кода, архитектуру и другие сложные вещи. Теперь большой секрет - самый ценный ресурс это твое время, пока оно у тебя есть, можно выучить хоть что угодно и пройти любое собеседование. На работе у тебя этого времени уже не будет, а выполнять ты будешь по большей части однообразные задачи вроде натягивания шкур на вордпресс, которые тебя никак не продвинут. Поэтому многие программисты на работах застревают как в болоте, просто времени нет пробовать все новое и современное. Так что пока у тебя время есть, пользуйся, осваивай технологии из вакансий, читай книжки (в шапке кстати хорошие) и пиши красивый код с нормальной архитектурой. Потом тупо идешь, и рассказываешь о своих навыках на собеседовании, показываешь тестовые проекты на гитхабе, и тебя берут. Так и становятся программистами.
http://ideone.com/8PBPes // Антикризиные меры (версия 2)
Внес изменения в Антикризисные меры, по прошлым комментариям. Убрал новый класс NewAnalyst, который использовал для повышения ставки. Сделал с помощью метода переназначения поля salaryRate внутри Profession.
Убрал статические методы, попробовал парсить регуляркой ранг при создании сотрудников.
Изменил алгоритм в первом наборе антикризисных мер, выглядит сложнее, т.к. пришлось сортировать массив работников по рангу. И т.д.
Вот у Фаулера три типа наследования, и какой мне выбрать?
Выбираю sti, потому что одна таблица без всяких джойнов.
Зачем нужны остальные типы наследования (concrete и class ti), в чем преимущество?
Предположим, избегают null-полей. Встает вопрос, чем плохи null-поля?
Погуглил, оказывается в mysql null таки занимает место (8 штук null в одной записи занимает 1 байт).
Кроме этой оптимизации может есть еще какие-нибудь соображения в пользу паттернов с кучей таблиц?
Хорошо, допустим нас не устраивает количество места, занимаемое на диске null-значениями (1 мегабайт на миллион записей это очень много, у нас ведь дискета вместо ssd).
Что дальше, какой из двух многотабличных паттернов выбрать?
В случае class ti (если я правильно понял дурацкую схему с какими-то футболистами и крикетистами) есть одна таблица, куда сохраняются общие для всех классов поля, и по одной дополнительной на каждый класс. Это получается нужно сначала вставить часть данных в эту общую таблицу, затем оставшиеся данные вместе с полученным идентификатором вставить в соответствующую классу таблицу? То есть тут вдобавок еще и транзакция.
В случае concrete ti нет никаких общих таблиц, для каждого класса отдельная таблица, где хранятся все данные.
Тогда какой смысл вообще в существовании class ti? Тут и транзакция, и внешний ключ, еще один джойн.
Я просто пытаюсь понять, в какой ситуации какой патерн применять, не тыкать же наугад.
update: пришлось лезть в первоисточник, то есть к Фаулеру.
Говорит, проблема с concrete ti в том, что при изменении класса модели придется править структуру всех связанных таблиц.
class ti - много джойнов.
sti - wasted space (неужели это так существенно?)
Наверное, на практике нужно не полениться создать все 3 варианта и протестировать производительность, потому что пока непонятно что брать.
class ti это когда на каждый класс, включая базовые, делается таблица. Поля не дублируются (поле name хранится в таблице базового класса). concrete ti это когда таблица делается только для конкретных классов, а не для базовых. Поля базового класса (вроде name) дублируются во всех наследниках.
> Какими соображениями руководствоваться
Реально ли его реализовать в используемом фреймворке, поддерживаются ли внешние ключи и ограничения на уровне базы, защищающие от ошибок, сложность написания запросов.
> Зачем нужны остальные типы наследования (concrete и class ti), в чем преимущество?
Например можно сделать внешний ключ, позволяющий ссылаться только на запись определенного типа. Или невозможность заполнить поле, которого нет у записей данного типа.
> Погуглил, оказывается в mysql null таки занимает место (8 штук null в одной записи занимает 1 байт).
Неправильно погуглил. 1 бит занимает флаг наличия NULL в определенной колонке. А также, нужно место под значение этой колонки. Например 4 байта для INT, 8 байтов для BIGINT, N x 3 для строки типа CHAR, и тд. То есть int(10) DEFAULT NULL для одной строки займет 4 байта + 1 бит на флаг наличия NULL.
В БД как правило используются записи фиксированного размера так, чтобы каждая строчка таблицы имела в файле одинаковую длину. Это позволяет быстро найти расположение N-й строки. То есть неважно, NULL там или не NULL, места это займет одинаково.
Однако динамические поля типа VARCHAR, TEXT, обычно реализуются так: в строке закладывается N байт для коротких текстов, если текст не вмещается туда, то он помещается в отдельную область.
Но вообще ты не должен особо беспокоиться. Поле INT занимает 4 байта, для миллиона строк это всего лишь 4 мегабайта. Не так и страшно. Также немного замедляется чтение данных с диска, но я не думаю, что это так уж серьезно.
> Тогда какой смысл вообще в существовании class ti? Тут и транзакция, и внешний ключ, еще один джойн.
Возможность разрешить ставить внешние ключи только на класс и его наследников. Отсутствие дублирования поля из базового класса во всех наследниках.
> Говорит, проблема с concrete ti в том, что при изменении класса модели придется править структуру всех связанных таблиц.
Не считаю это большой проблемой, так как это происходит редко, а не постоянно.
> sti - wasted space (неужели это так существенно?)
Зависит от состава полей. Сами по себе 4 или 40 мегабайт на диске не страшны. Но если лишних полей много, то все ухудшается. Строчки начинают занимать больше байт, дольше грузятся с диска, занимают больше памяти (и с большей вероятностью в нее не поместятся). То есть 2-3 поля это не страшно. А если там 30 лишних полей?
Я могу привести еще такой пример. Допустим у нас есть большая таблица с 50 полями (инфа о пользователе в соцсети). Хорошо ли это? Ведь когда ты пишешь SELECT * FROM ... выбираются все поля. Если ты используешь ORM вроде доктрины, то мапятся все поля, и ты вынужден выбирать сущность с 50 полями (что наверно будет медленнее чем сущность с 10 полями, хотя это стоит проверять тестом). То есть ты выбираешь пользователя, который оставил сообщение на стене, и выбираются все 50 полей, хотя нам нужны только id, имя и аватарка.
В такой ситуации может быть есть смысл разбить таблицу на 2 - базовая информация о пользователе (которая нужна почти везде) и подробная.
> и протестировать производительность, потому что пока непонятно что брать.
Ты смотри, как тебе удобно будет писать запросы, работает ли это с твоим фреймворком, удобно ли ставить внешние ключи и тд.
class ti это когда на каждый класс, включая базовые, делается таблица. Поля не дублируются (поле name хранится в таблице базового класса). concrete ti это когда таблица делается только для конкретных классов, а не для базовых. Поля базового класса (вроде name) дублируются во всех наследниках.
> Какими соображениями руководствоваться
Реально ли его реализовать в используемом фреймворке, поддерживаются ли внешние ключи и ограничения на уровне базы, защищающие от ошибок, сложность написания запросов.
> Зачем нужны остальные типы наследования (concrete и class ti), в чем преимущество?
Например можно сделать внешний ключ, позволяющий ссылаться только на запись определенного типа. Или невозможность заполнить поле, которого нет у записей данного типа.
> Погуглил, оказывается в mysql null таки занимает место (8 штук null в одной записи занимает 1 байт).
Неправильно погуглил. 1 бит занимает флаг наличия NULL в определенной колонке. А также, нужно место под значение этой колонки. Например 4 байта для INT, 8 байтов для BIGINT, N x 3 для строки типа CHAR, и тд. То есть int(10) DEFAULT NULL для одной строки займет 4 байта + 1 бит на флаг наличия NULL.
В БД как правило используются записи фиксированного размера так, чтобы каждая строчка таблицы имела в файле одинаковую длину. Это позволяет быстро найти расположение N-й строки. То есть неважно, NULL там или не NULL, места это займет одинаково.
Однако динамические поля типа VARCHAR, TEXT, обычно реализуются так: в строке закладывается N байт для коротких текстов, если текст не вмещается туда, то он помещается в отдельную область.
Но вообще ты не должен особо беспокоиться. Поле INT занимает 4 байта, для миллиона строк это всего лишь 4 мегабайта. Не так и страшно. Также немного замедляется чтение данных с диска, но я не думаю, что это так уж серьезно.
> Тогда какой смысл вообще в существовании class ti? Тут и транзакция, и внешний ключ, еще один джойн.
Возможность разрешить ставить внешние ключи только на класс и его наследников. Отсутствие дублирования поля из базового класса во всех наследниках.
> Говорит, проблема с concrete ti в том, что при изменении класса модели придется править структуру всех связанных таблиц.
Не считаю это большой проблемой, так как это происходит редко, а не постоянно.
> sti - wasted space (неужели это так существенно?)
Зависит от состава полей. Сами по себе 4 или 40 мегабайт на диске не страшны. Но если лишних полей много, то все ухудшается. Строчки начинают занимать больше байт, дольше грузятся с диска, занимают больше памяти (и с большей вероятностью в нее не поместятся). То есть 2-3 поля это не страшно. А если там 30 лишних полей?
Я могу привести еще такой пример. Допустим у нас есть большая таблица с 50 полями (инфа о пользователе в соцсети). Хорошо ли это? Ведь когда ты пишешь SELECT * FROM ... выбираются все поля. Если ты используешь ORM вроде доктрины, то мапятся все поля, и ты вынужден выбирать сущность с 50 полями (что наверно будет медленнее чем сущность с 10 полями, хотя это стоит проверять тестом). То есть ты выбираешь пользователя, который оставил сообщение на стене, и выбираются все 50 полей, хотя нам нужны только id, имя и аватарка.
В такой ситуации может быть есть смысл разбить таблицу на 2 - базовая информация о пользователе (которая нужна почти везде) и подробная.
> и протестировать производительность, потому что пока непонятно что брать.
Ты смотри, как тебе удобно будет писать запросы, работает ли это с твоим фреймворком, удобно ли ставить внешние ключи и тд.
>>760508
> W5.1(Циклы и айфон в кредит)
> Исправлено: http://ideone.com/L6aoa6
Верно, хотя можно еще заменить if на min/max и чуть сократить код.
> W5.2(Циклы и айфон в кредит)
> Исправлено: http://ideone.com/v5HZxH
Верно, хотя $bill += $bill*0.1; можно записать и через *=
> Строки, хакеры и шифровки (На словах ты Лев Толстой
> Исправлено: http://ideone.com/KRk3Cf
Верно
> Строки, хакеры и шифровки (Палиндром
> Исправлено: http://ideone.com/5e8zEf
Верно
> Функции и новый айпад http://ideone.com/shMxHY
> Исправлено: http://ideone.com/Ioflg2
Верно
> Регулярные выражения
> Исправлено: http://ideone.com/QqtN4d
Ох, опять не так. Я имел в виде выводить например такой список:
[✓] +7123456789 правильный
[✓] 123456789 неправильный
[❌] +723456789 неправильный
То есть видно, какие номера неправильно определились. А у тебя если он напишет что тест не пройден - непонятно, по какой причине и из-за какого номера.
Ну вообще, это мелочи, можешь просто учесть это на будущее.
> Регулярные выражения
> Исправлено: https://ideone.com/2fLm31
Ты перестарался - в именах доменах нет подчеркиваний, а вот в имени пользователя перед @ может быть и подчеркивание и плюс (и другие сложные знаки которые нас не интересуют). Если что, статьи по теме (делать то, что там описано, не надо!):
- https://habrahabr.ru/post/274985/
- https://habrahabr.ru/post/175375/
- https://habrahabr.ru/post/55820/
- https://habrahabr.ru/post/224623/
- https://habrahabr.ru/post/280798/
> Регулярные выражения https://ideone.com/gDM38S
Почти верно, но вот тут есть проблемка:
> * +(a |но )
Ты ждешь что после союза будет пробел, но там может быть знак вопроса, запятая, и тд.
> Регулярные выражения https://ideone.com/t8Mq2K
Тут слово "зделал" будет заменено даже если оно - част другого слова, например, "разделать тушу на части". И та же проблема с пробелом после а/но.
> Регулярные выражения http://ideone.com/BPGuYs
Вполне нормальное решение.
> Промежуточные версии:
на мой взгляд не очень удачно что там ограничен список латинских букв. Вдруг ты какую-то букву пропустишь.
>>760508
> W5.1(Циклы и айфон в кредит)
> Исправлено: http://ideone.com/L6aoa6
Верно, хотя можно еще заменить if на min/max и чуть сократить код.
> W5.2(Циклы и айфон в кредит)
> Исправлено: http://ideone.com/v5HZxH
Верно, хотя $bill += $bill*0.1; можно записать и через *=
> Строки, хакеры и шифровки (На словах ты Лев Толстой
> Исправлено: http://ideone.com/KRk3Cf
Верно
> Строки, хакеры и шифровки (Палиндром
> Исправлено: http://ideone.com/5e8zEf
Верно
> Функции и новый айпад http://ideone.com/shMxHY
> Исправлено: http://ideone.com/Ioflg2
Верно
> Регулярные выражения
> Исправлено: http://ideone.com/QqtN4d
Ох, опять не так. Я имел в виде выводить например такой список:
[✓] +7123456789 правильный
[✓] 123456789 неправильный
[❌] +723456789 неправильный
То есть видно, какие номера неправильно определились. А у тебя если он напишет что тест не пройден - непонятно, по какой причине и из-за какого номера.
Ну вообще, это мелочи, можешь просто учесть это на будущее.
> Регулярные выражения
> Исправлено: https://ideone.com/2fLm31
Ты перестарался - в именах доменах нет подчеркиваний, а вот в имени пользователя перед @ может быть и подчеркивание и плюс (и другие сложные знаки которые нас не интересуют). Если что, статьи по теме (делать то, что там описано, не надо!):
- https://habrahabr.ru/post/274985/
- https://habrahabr.ru/post/175375/
- https://habrahabr.ru/post/55820/
- https://habrahabr.ru/post/224623/
- https://habrahabr.ru/post/280798/
> Регулярные выражения https://ideone.com/gDM38S
Почти верно, но вот тут есть проблемка:
> * +(a |но )
Ты ждешь что после союза будет пробел, но там может быть знак вопроса, запятая, и тд.
> Регулярные выражения https://ideone.com/t8Mq2K
Тут слово "зделал" будет заменено даже если оно - част другого слова, например, "разделать тушу на части". И та же проблема с пробелом после а/но.
> Регулярные выражения http://ideone.com/BPGuYs
Вполне нормальное решение.
> Промежуточные версии:
на мой взгляд не очень удачно что там ограничен список латинских букв. Вдруг ты какую-то букву пропустишь.
> http://ideone.com/8PBPes // Антикризиные меры (версия 2)
> // Присвоение профессии сотруднику
> $this->profession = $profession;
если ты это для себя писал то ок, но в общем, это комментарии "капитана очевидность", так как их может написать любой, понимающий код. В комментариях лучше писать почему ты делаешь так или иначе, какие-то неочевидные вещи. А еще лучше конечно писать очевидный код.
Хуже того, там ниже комментарии не соответствуют функциям.
> public function __construct($profession
Тут бы нужен тайп хинт
> $tmpxRank = $this->rankSalary[$this->rank];
Название плохое, почему не просто salaryMultiplier ?
>class Manager extends Profession
> protected $salaryRate = 500; // базовая ставка
> public function getProduct()
А почему одно из значений сделано абстрактным методом, а другое - полем? Разве не логично для всех 3 полей сделать абстр. методы? Чтобы гарантированно не забыли их переопределить?
> public function findDepartamentByName($name = null)
> {
> $result; // ссылка на департамент или массив департаментов
Что это за синтаксис такой? На мой взгляд, это обращение к несуществующей пока переменной $result.
> // Если сотрудник соответствует профессии
> if ($employee->getProfession()->getName() == $profession) {
Если у тебя профессия - это объект, может логично передавать для поиска не название, а этот объект? Или хотя бы сделать константы для обозначений профессий. Потому что строки это в общем дурацкая идея: в них легко опечататься (например перепутать маленькую букву с заглавной или поставить лишний пробел), не очевидно какие строки вообще есть в программе, и тд.
> $profession != 'all'
По идее эту строку тоже надо бы заменить на константу например с названием FIND_ANY
> class AnticrisisCommittee
Тут мне не очень нравится идея передавать компанию через отдельный метод. ладно, ты бы передавал ее через конструктор. Но тут у тебя создается риск что антикризисный метод будет вызван без компании. То лучше передавать компанию в метод через аргументы или конструктор.
> // Клонирует компанию
> public function cloneCompany($company)
Мне кажется, клонировать компании - это не задача антикризисного комитета, это не требуется для его работы, и этого кода в классе быть не должно.
> // Запрашивавем все департаменты в компании
> $departaments = $this->companyClone->findDepartamentByName();
Тут название не соответствует смыслу функции. Наверно лучше было сделать отдельный метод getAllDepartments.
> // Исключаем из списка босса
> foreach ($engineers as $key => $engineer) {
Это можно еще сделать через array_filter: $x = array_filter($x, function (...) {...});
> sort($engineers);
Очень странная сортировка. Объекты это не числа, чтобы их так сортировать. В данном случае логичнее применить usort где будет указана функция с критерием сортировки по рангу. Или, если ты хотел перенумеровать массив (а зачем?), то надо использовать более быстрый array_values.
> // Сортируем массив работников по рангу по возрастанию
> // Внешний цикл повторяется до тех пор,
пузырьковая сортировка? Это медленно и годится только для задач с собеседования. лучше использовать usort которая позволяет указать критерий сортировки. Не надо писать свой алгоритм сортировки, когда есть готовый.
> / Берем первые 40% через array_slice
> $engineers = array_slice($engineers, $dismissingEngineers);
Неправильно, так ты отберешь 60%
> public function setSalaryRate($salary)
> {
> $this->profession->setSalaryRate($salary);
Вот здесь у нас серьезная проблема. Что, если все работники содержат ссылку на один и тот же объект профессии? Тогда этот метод меняет зарплату всех этих работников. Я вижу, у тебя так и сделано.
Ты читал внимательно раздел про копирование объектов? Когда ты передаешь объект професссии, его копии не делается, все работники содержат ссылку на один и тот же объект.
Ты можешь конечно создавать каждому работнику новый объект профессии. Но это не очень соответствует логике ООП (почему одна профессия представлена множеством, а не одним объектом?), хотя в данной задаче и приемлемо, если объявить, что это не "профессия", а "информация о должности работника". Но тогда остается другая проблема: а как гарантировать что пользователь не передаст один объект профессии нескольким работникам? Я думаю, выходом могла бы быть замена агрегации и композиции. Если ты читал мой урок по ООП то должен помнить, что разница между ними в том, где создаются вложенные объекты и могут ли они существовать отдельно.
> // Если таковых нет, переходим к следующему департаменту
> if (!$analysts) continue;
В общем-то эта строка не нужна.
> // Находим аналитика самого высокого ранга
> // Повторяем для всех рангов, начиная с самого высокого
> for ($rank = 3; $rank > 0; $rank--) {
по моему проще взять всех аналитиков, отсортировать по рангу и взять верхнего
> // Назначаем Аналитика шефом
> $analyst->setСhief(1);
> // Снимаем с должности текущего шефа
> $chiefEmployee->setСhief(0);
Это явно нарушение инкапсуляции. Разве замена босса - это не задача департамента? Ты же вместо этого должен копировать сложный код каждый раз как захочешь его поменять.
Я кстати подумал, что поиск работников можно бы упростить с использованием анонимной функции в качестве критерия:
// Ищем всех работников выше 2 ранга
$people = $department->findPeople(function ($e) { return $e->getRank() > 2; })
Как ты думаешь? А для поиска босса наверно лучше сделать отдельную функцию.
> // Выводим статистику на печать
> public function printStat($message)
Это не задача комитета. Ты сделал у класса Антикризисный Комитет лишнюю зависимость от функции печати статистики, которая ему даром не нужна.
> // Достаем название профессии из вакансии
> $professionIndex = mb_substr($vacancyName, 0, 2);
> $profession = $professions[$professionIndex];
> // Достаем ранг и из вакансии
> preg_match ('/\d/', $vacancyName, $matches1);
Вообще регуляркой можно достать сразу название, ранг и количество из строки вроде '3me1boss' - через $matches[1], [2] и тд
> http://ideone.com/8PBPes // Антикризиные меры (версия 2)
> // Присвоение профессии сотруднику
> $this->profession = $profession;
если ты это для себя писал то ок, но в общем, это комментарии "капитана очевидность", так как их может написать любой, понимающий код. В комментариях лучше писать почему ты делаешь так или иначе, какие-то неочевидные вещи. А еще лучше конечно писать очевидный код.
Хуже того, там ниже комментарии не соответствуют функциям.
> public function __construct($profession
Тут бы нужен тайп хинт
> $tmpxRank = $this->rankSalary[$this->rank];
Название плохое, почему не просто salaryMultiplier ?
>class Manager extends Profession
> protected $salaryRate = 500; // базовая ставка
> public function getProduct()
А почему одно из значений сделано абстрактным методом, а другое - полем? Разве не логично для всех 3 полей сделать абстр. методы? Чтобы гарантированно не забыли их переопределить?
> public function findDepartamentByName($name = null)
> {
> $result; // ссылка на департамент или массив департаментов
Что это за синтаксис такой? На мой взгляд, это обращение к несуществующей пока переменной $result.
> // Если сотрудник соответствует профессии
> if ($employee->getProfession()->getName() == $profession) {
Если у тебя профессия - это объект, может логично передавать для поиска не название, а этот объект? Или хотя бы сделать константы для обозначений профессий. Потому что строки это в общем дурацкая идея: в них легко опечататься (например перепутать маленькую букву с заглавной или поставить лишний пробел), не очевидно какие строки вообще есть в программе, и тд.
> $profession != 'all'
По идее эту строку тоже надо бы заменить на константу например с названием FIND_ANY
> class AnticrisisCommittee
Тут мне не очень нравится идея передавать компанию через отдельный метод. ладно, ты бы передавал ее через конструктор. Но тут у тебя создается риск что антикризисный метод будет вызван без компании. То лучше передавать компанию в метод через аргументы или конструктор.
> // Клонирует компанию
> public function cloneCompany($company)
Мне кажется, клонировать компании - это не задача антикризисного комитета, это не требуется для его работы, и этого кода в классе быть не должно.
> // Запрашивавем все департаменты в компании
> $departaments = $this->companyClone->findDepartamentByName();
Тут название не соответствует смыслу функции. Наверно лучше было сделать отдельный метод getAllDepartments.
> // Исключаем из списка босса
> foreach ($engineers as $key => $engineer) {
Это можно еще сделать через array_filter: $x = array_filter($x, function (...) {...});
> sort($engineers);
Очень странная сортировка. Объекты это не числа, чтобы их так сортировать. В данном случае логичнее применить usort где будет указана функция с критерием сортировки по рангу. Или, если ты хотел перенумеровать массив (а зачем?), то надо использовать более быстрый array_values.
> // Сортируем массив работников по рангу по возрастанию
> // Внешний цикл повторяется до тех пор,
пузырьковая сортировка? Это медленно и годится только для задач с собеседования. лучше использовать usort которая позволяет указать критерий сортировки. Не надо писать свой алгоритм сортировки, когда есть готовый.
> / Берем первые 40% через array_slice
> $engineers = array_slice($engineers, $dismissingEngineers);
Неправильно, так ты отберешь 60%
> public function setSalaryRate($salary)
> {
> $this->profession->setSalaryRate($salary);
Вот здесь у нас серьезная проблема. Что, если все работники содержат ссылку на один и тот же объект профессии? Тогда этот метод меняет зарплату всех этих работников. Я вижу, у тебя так и сделано.
Ты читал внимательно раздел про копирование объектов? Когда ты передаешь объект професссии, его копии не делается, все работники содержат ссылку на один и тот же объект.
Ты можешь конечно создавать каждому работнику новый объект профессии. Но это не очень соответствует логике ООП (почему одна профессия представлена множеством, а не одним объектом?), хотя в данной задаче и приемлемо, если объявить, что это не "профессия", а "информация о должности работника". Но тогда остается другая проблема: а как гарантировать что пользователь не передаст один объект профессии нескольким работникам? Я думаю, выходом могла бы быть замена агрегации и композиции. Если ты читал мой урок по ООП то должен помнить, что разница между ними в том, где создаются вложенные объекты и могут ли они существовать отдельно.
> // Если таковых нет, переходим к следующему департаменту
> if (!$analysts) continue;
В общем-то эта строка не нужна.
> // Находим аналитика самого высокого ранга
> // Повторяем для всех рангов, начиная с самого высокого
> for ($rank = 3; $rank > 0; $rank--) {
по моему проще взять всех аналитиков, отсортировать по рангу и взять верхнего
> // Назначаем Аналитика шефом
> $analyst->setСhief(1);
> // Снимаем с должности текущего шефа
> $chiefEmployee->setСhief(0);
Это явно нарушение инкапсуляции. Разве замена босса - это не задача департамента? Ты же вместо этого должен копировать сложный код каждый раз как захочешь его поменять.
Я кстати подумал, что поиск работников можно бы упростить с использованием анонимной функции в качестве критерия:
// Ищем всех работников выше 2 ранга
$people = $department->findPeople(function ($e) { return $e->getRank() > 2; })
Как ты думаешь? А для поиска босса наверно лучше сделать отдельную функцию.
> // Выводим статистику на печать
> public function printStat($message)
Это не задача комитета. Ты сделал у класса Антикризисный Комитет лишнюю зависимость от функции печати статистики, которая ему даром не нужна.
> // Достаем название профессии из вакансии
> $professionIndex = mb_substr($vacancyName, 0, 2);
> $profession = $professions[$professionIndex];
> // Достаем ранг и из вакансии
> preg_match ('/\d/', $vacancyName, $matches1);
Вообще регуляркой можно достать сразу название, ранг и количество из строки вроде '3me1boss' - через $matches[1], [2] и тд
> Тогда для метода валидатора нужно будет добавить аргумент с id.
> А как будущие разработчики поймут зачем здесь этот аргумент? Даже если бы он был не обязательным, я бы не сразу понял зачем методу проверки имейла нужно что-то кроме имейла
назови его "пропуститьПользователяСИд" и напиши комментарий перед функцией.
> А что за параметр? Который сообщает игнорировать те или иные поля? Так придётся переделывать весь класс валидации, не проще ли помножить сущность и сделать новый?
Ты можешь назвать параметр "разрешитьПустойПароль" либо параметр, указывающий режим валидации - регистрация или редактирование.
> Да и дело даже не в самом в валидаторе, а в запросе БД, т.к. он составлен так, что нам всегда нужно будет отправлять все данные из формы\сущности
Значит надо его исправить.
> а хотелось бы что если в форме какое-то поле было пустым, то и запрос в БД составлялся без этого поля.
Ну пустым не может быть любое поле, а только пароль вообще-то. Более того, ты можешь выбрать сущность из базы, не менять ей хеш (при пустом пароле) и передать на сохранение - тогда старый хеш в базу и запишется.
> Как составляются такие запросы? Неужели нужно писать отдельный метод который на основе сущности будет составлять запрос?
Вообще, в данном случае можно написать вспомогательный метод вида
updateRecord($id, [
'name' => ...,
'birthday' => ...
]);
Который сгенерирует запрос в зависимости от аргументов. А вообще, для случаев, когда запрос строится в зависимости от условий, есть паттерн Query Builder, хотя на мой взгляд он тут не нужен.
В твоем случае проще сделать так: при пустом пароле не обновлять хеши и токены в модели студента, чтобы в базу записались старые данные.
В твоем случае есть такой контроллер: https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php (кстати редактирование и регистрацию надо бы объединить в один метод). Там есть код
> $registerStudentForm = new RegisterStudentForm();
> $registerStudentForm->setStudent($student);
....
> $registerStudentForm->fillDataFromArray($_POST);
> $errors = $this->validations->validRegisterStudentForm($registerStudentForm);
Очевидно надо сделать либо у формы, либо у валидатора параметр "режим редактирования" либо "разрешить не менять пароль" и подправить логику поведения если этот параметр включен.
"режимы" вполне нормальная вещь - например, может быть такое, что пользователь может редактировать один набор полей, а модератор - другой. Не делать же 2 формы для такого, проще сделать 2 режима для одной формы или валидатора.
Плюс, ниже, где стоит
> $registerStudentForm->setStudentPassword();
Надо добавить проверку
Если (форма->парольУказан) {
форма->поменятьПароль;
авторизатор->обновитьАвтоизацию;
}
И как я сказал выше, объединить функции редактирования и регистрации в одну. А то копипаста получается.
> Хм, значит сущности нужны только если у них есть какие-то особенные методы или много свойств?
Тут важно обходиться без фанатизма. В теории, мы можем сделать каждую строку отдельной сущностью:
$name = new Name('Иванов');
$year = new Year(1980);
Но какой в этом смысл? Это просто усложнение кода.
Что касается сущности Search, то ее можно оставить. Если в будущем будут появляться дополнительные критерии поиска, то в нее удобно будет их добавлять в виде новых полей и сущность будет представлять собой информацию о поисковом запросе.
Но вот нужно ли там поле result- я не уверен. Вот смотри, из 2 вариантов кода:
$query = new Search(....);
.....
$searchEngine->find($query);
и такого:
$results = $searchEngine->find($query);
Мне больше нравится второй, так как он как-то логичнее. Мы даем запрос и получаем ответ на него. Сразу видно, куда он идет. Ну хотя, тут опять же можно вспомнить то, что я написал выше, и сказать, что это переусложнение и хватит одной сущности.
В общем, я думаю, это можно оставить как есть.
> Получается что контроллер может находиться в любом месте директории и быть даже публичной страницей?
MVC не требует ООП. Ты можешь сделать MVC на функциях или даже файла, подключаемых через инклуд. но это будет довольно коряво.
А так да, почему, нет? В простом приложении можно писать код контрллера прямо в register.php:
require __DIR__....bootstrap.php
$students = $mapper->getStudents(...);
$view->render('students.phtml');
Но например при таком подходе нельзя разбить контроллер на несколько методов, нельзя использовать наследование и тд.
В более сложных приложениях конечно ты будешь использовать роутер и классы-контроллеры.
> Оказалось что запрос сам как-то кодируется при отправке формы. Просто браузер в адресной строке отображал процентное кодирование в нормальном формате, и было не понятно почему при отправке формы в адресной строке можно использовать кириллицу, а в php нужно кодировать.
Браузер сам кодирует и ГЕТ и ПОСТ формы, а PHP их прозрачно для тебя раскодирует и кладет в $_GET/$_POST. Не надо ничего делать для этого.
И да, ты прав, браузер может отображать УРЛ в искаженном виде. Чтобы увидеть полный УРЛ, скопируй его и вставь в текстовый редактор.
> Тогда для метода валидатора нужно будет добавить аргумент с id.
> А как будущие разработчики поймут зачем здесь этот аргумент? Даже если бы он был не обязательным, я бы не сразу понял зачем методу проверки имейла нужно что-то кроме имейла
назови его "пропуститьПользователяСИд" и напиши комментарий перед функцией.
> А что за параметр? Который сообщает игнорировать те или иные поля? Так придётся переделывать весь класс валидации, не проще ли помножить сущность и сделать новый?
Ты можешь назвать параметр "разрешитьПустойПароль" либо параметр, указывающий режим валидации - регистрация или редактирование.
> Да и дело даже не в самом в валидаторе, а в запросе БД, т.к. он составлен так, что нам всегда нужно будет отправлять все данные из формы\сущности
Значит надо его исправить.
> а хотелось бы что если в форме какое-то поле было пустым, то и запрос в БД составлялся без этого поля.
Ну пустым не может быть любое поле, а только пароль вообще-то. Более того, ты можешь выбрать сущность из базы, не менять ей хеш (при пустом пароле) и передать на сохранение - тогда старый хеш в базу и запишется.
> Как составляются такие запросы? Неужели нужно писать отдельный метод который на основе сущности будет составлять запрос?
Вообще, в данном случае можно написать вспомогательный метод вида
updateRecord($id, [
'name' => ...,
'birthday' => ...
]);
Который сгенерирует запрос в зависимости от аргументов. А вообще, для случаев, когда запрос строится в зависимости от условий, есть паттерн Query Builder, хотя на мой взгляд он тут не нужен.
В твоем случае проще сделать так: при пустом пароле не обновлять хеши и токены в модели студента, чтобы в базу записались старые данные.
В твоем случае есть такой контроллер: https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php (кстати редактирование и регистрацию надо бы объединить в один метод). Там есть код
> $registerStudentForm = new RegisterStudentForm();
> $registerStudentForm->setStudent($student);
....
> $registerStudentForm->fillDataFromArray($_POST);
> $errors = $this->validations->validRegisterStudentForm($registerStudentForm);
Очевидно надо сделать либо у формы, либо у валидатора параметр "режим редактирования" либо "разрешить не менять пароль" и подправить логику поведения если этот параметр включен.
"режимы" вполне нормальная вещь - например, может быть такое, что пользователь может редактировать один набор полей, а модератор - другой. Не делать же 2 формы для такого, проще сделать 2 режима для одной формы или валидатора.
Плюс, ниже, где стоит
> $registerStudentForm->setStudentPassword();
Надо добавить проверку
Если (форма->парольУказан) {
форма->поменятьПароль;
авторизатор->обновитьАвтоизацию;
}
И как я сказал выше, объединить функции редактирования и регистрации в одну. А то копипаста получается.
> Хм, значит сущности нужны только если у них есть какие-то особенные методы или много свойств?
Тут важно обходиться без фанатизма. В теории, мы можем сделать каждую строку отдельной сущностью:
$name = new Name('Иванов');
$year = new Year(1980);
Но какой в этом смысл? Это просто усложнение кода.
Что касается сущности Search, то ее можно оставить. Если в будущем будут появляться дополнительные критерии поиска, то в нее удобно будет их добавлять в виде новых полей и сущность будет представлять собой информацию о поисковом запросе.
Но вот нужно ли там поле result- я не уверен. Вот смотри, из 2 вариантов кода:
$query = new Search(....);
.....
$searchEngine->find($query);
и такого:
$results = $searchEngine->find($query);
Мне больше нравится второй, так как он как-то логичнее. Мы даем запрос и получаем ответ на него. Сразу видно, куда он идет. Ну хотя, тут опять же можно вспомнить то, что я написал выше, и сказать, что это переусложнение и хватит одной сущности.
В общем, я думаю, это можно оставить как есть.
> Получается что контроллер может находиться в любом месте директории и быть даже публичной страницей?
MVC не требует ООП. Ты можешь сделать MVC на функциях или даже файла, подключаемых через инклуд. но это будет довольно коряво.
А так да, почему, нет? В простом приложении можно писать код контрллера прямо в register.php:
require __DIR__....bootstrap.php
$students = $mapper->getStudents(...);
$view->render('students.phtml');
Но например при таком подходе нельзя разбить контроллер на несколько методов, нельзя использовать наследование и тд.
В более сложных приложениях конечно ты будешь использовать роутер и классы-контроллеры.
> Оказалось что запрос сам как-то кодируется при отправке формы. Просто браузер в адресной строке отображал процентное кодирование в нормальном формате, и было не понятно почему при отправке формы в адресной строке можно использовать кириллицу, а в php нужно кодировать.
Браузер сам кодирует и ГЕТ и ПОСТ формы, а PHP их прозрачно для тебя раскодирует и кладет в $_GET/$_POST. Не надо ничего делать для этого.
И да, ты прав, браузер может отображать УРЛ в искаженном виде. Чтобы увидеть полный УРЛ, скопируй его и вставь в текстовый редактор.
> Это готовый плагин так работает,
Это плагин так работает, но для заказчика или пользователя будет выглядеть что это ты криворукий. Значит надо выбрать другой плагин или написать свой. Ну и вообще, например в Хроме есть встроенные средства для кастомизации линейки прокрутки. Ты бы мог использовать в нем их, вообще не вешая яваскриптов. А в других браузрах - вешать.
> >Когда я прокручиваю страницу и курсор оказывается над картой, прокрутка перестант работать и вместо этого уменьшается масштаб на карте
> Это вроде обычное поведение для карты.
Нет, от этого по моему все уже давно отказываются. Потому что неудобно прокручивать. лучше сделать кнопки масштаба.
Ну сам подумай, где тут логика, ты прокручиваешь страницу, а она вдруг стопорится. Кому это понравится?
>>У тебя нет ни индикации прогресса, ни обработки ошибок, ни блокировки кнопки отправки.
> Лень было, да и там всего по три поля.
Лень сделать нормально? Это плохо, тем более что ты еще начинающий. вот подумай сам, ты наверняка много труда вложил в этот макет, почему бы и аякс нормально не сделать?
Отсутствие индикации это ненормально. Вот ты нажал кнопку и ничего не происходит - как понять, что это значит? Система должна реагировать на действия пользователя иначе создается ощущение что сайт "тормозит", "глючит" и тд.
> Да, это так. Но а как их еще сделаешь?
Поместить текст поверх картинки? Уменьшить картинку? Ты верстальщик, ты и думай как. Мне кажется, эта картинка там не очень и важна, ее можно уменьшить.
> но во-первых все равно не факт что пользователь не пролистнет до момента когда блок с текстом окажется вне вьюпорта, а во-вторых фотки будут мелкие.
ну выбери несклоько вариантов, сравни плюсы и минусы и выбери оптимальный. В практических задачах не всегда есть очевидный единственный ответ.
> Я намерено использовал самый крутой и моднейший способ вставки без оглядки на совместимость.
Ты сайт верстаешь для пользователей или для себя одного? Такое допустимо только когда есть контролируемая среда - например, встроенный в приложение браузер известной версии или корпоративное правило запрещающее использовать что-то кроме ИЕ9.
У неправильное мышление, на мой взгляд. HTML и CSS создавались чтобы обеспечить отображение документа на как можно большем числе устройств. Ты поступаешь ровно наоборот. В верстке ты можешь использовать модные возможности, но не забудь тогда сделать фоллбек для старых браузеров.
> Зачем так? Показать на собеседовании какой я крутой.
Ты же понимаешь. что "крутым" это посчитает только тот, кто плохо понимает принципы HTML. Я например помню как в этом разделе кто-то жаловался, что в Яндексе от него требовали верстать пример под максимальное число браузеров и спрашивали про особенности старой Оперы.
Искусство именно в том, чтобы делать "крутые" вещи не жертвуя другими принципами. SVG на страницу поставить любой может. А сделать чтобы это в итоге везде работало без багов и код не выглядел как лапша - не любой.
> >ЧТо за класс "p"?
> Тоже самое что и тег р, paragraph, наиболее общие правила для абзацов.
Тогда по моему проще использовать тег p в селекторе, возможно в сочетании с родительским классом.
> Это сворованный скрипт, не читал его, только скорость подобрал.
Воровал бы хорошие скрипты хотя бы, а лучше -писал бы свои. Я когда был начинающим, старался по хардкору все на чистом JS писать. С поддержкой ИЕ разумеется.
> Просто, блин, так мурыжить каждую кнопочку и блочок постоянно заглядывать в справочник
Со временем научишься без заглядывания в справочник понимать как лучше сделать
> Ну и поглядывая на вакансии я все чаще вижу что пишут что-то типа 'мучать ие 8- не будем'.
Это можно прочесть как "в ИЕ9 все должно работать"
Вообще, что касается древних ИЕ, у меня когда-то была такая мысль - чтобы не мучаться, просто отдавать им голый ХТМЛ без КСС и ЖС. Прочесть тексты можно (если верстка семантичная), картинки посмотреть можно, а остальное это уже проблемы юзеров этого антиквариата.
И еще был какой-то проект, который умел из сайта сделать версию то ли из картинок, то ли на одном абсолютном позиционировании, которая в старых браузерах корректно отображалась. Но это конечно уже усложнение, но если очень надо - может быть помогло бы.
Еще, я помню, я когда-то заморачивался с написанием генератора CSS для кнопок, там идея была такая: для новых браузеров делаем кнопки через CSS3, для старья - генерируем на основе CSS3 картинку с тенями, скруглениями и тд. Это может и перебор, но если постоянно занимаешься версткой - почему бы и не автоматизировать свой труд.
> Это готовый плагин так работает,
Это плагин так работает, но для заказчика или пользователя будет выглядеть что это ты криворукий. Значит надо выбрать другой плагин или написать свой. Ну и вообще, например в Хроме есть встроенные средства для кастомизации линейки прокрутки. Ты бы мог использовать в нем их, вообще не вешая яваскриптов. А в других браузрах - вешать.
> >Когда я прокручиваю страницу и курсор оказывается над картой, прокрутка перестант работать и вместо этого уменьшается масштаб на карте
> Это вроде обычное поведение для карты.
Нет, от этого по моему все уже давно отказываются. Потому что неудобно прокручивать. лучше сделать кнопки масштаба.
Ну сам подумай, где тут логика, ты прокручиваешь страницу, а она вдруг стопорится. Кому это понравится?
>>У тебя нет ни индикации прогресса, ни обработки ошибок, ни блокировки кнопки отправки.
> Лень было, да и там всего по три поля.
Лень сделать нормально? Это плохо, тем более что ты еще начинающий. вот подумай сам, ты наверняка много труда вложил в этот макет, почему бы и аякс нормально не сделать?
Отсутствие индикации это ненормально. Вот ты нажал кнопку и ничего не происходит - как понять, что это значит? Система должна реагировать на действия пользователя иначе создается ощущение что сайт "тормозит", "глючит" и тд.
> Да, это так. Но а как их еще сделаешь?
Поместить текст поверх картинки? Уменьшить картинку? Ты верстальщик, ты и думай как. Мне кажется, эта картинка там не очень и важна, ее можно уменьшить.
> но во-первых все равно не факт что пользователь не пролистнет до момента когда блок с текстом окажется вне вьюпорта, а во-вторых фотки будут мелкие.
ну выбери несклоько вариантов, сравни плюсы и минусы и выбери оптимальный. В практических задачах не всегда есть очевидный единственный ответ.
> Я намерено использовал самый крутой и моднейший способ вставки без оглядки на совместимость.
Ты сайт верстаешь для пользователей или для себя одного? Такое допустимо только когда есть контролируемая среда - например, встроенный в приложение браузер известной версии или корпоративное правило запрещающее использовать что-то кроме ИЕ9.
У неправильное мышление, на мой взгляд. HTML и CSS создавались чтобы обеспечить отображение документа на как можно большем числе устройств. Ты поступаешь ровно наоборот. В верстке ты можешь использовать модные возможности, но не забудь тогда сделать фоллбек для старых браузеров.
> Зачем так? Показать на собеседовании какой я крутой.
Ты же понимаешь. что "крутым" это посчитает только тот, кто плохо понимает принципы HTML. Я например помню как в этом разделе кто-то жаловался, что в Яндексе от него требовали верстать пример под максимальное число браузеров и спрашивали про особенности старой Оперы.
Искусство именно в том, чтобы делать "крутые" вещи не жертвуя другими принципами. SVG на страницу поставить любой может. А сделать чтобы это в итоге везде работало без багов и код не выглядел как лапша - не любой.
> >ЧТо за класс "p"?
> Тоже самое что и тег р, paragraph, наиболее общие правила для абзацов.
Тогда по моему проще использовать тег p в селекторе, возможно в сочетании с родительским классом.
> Это сворованный скрипт, не читал его, только скорость подобрал.
Воровал бы хорошие скрипты хотя бы, а лучше -писал бы свои. Я когда был начинающим, старался по хардкору все на чистом JS писать. С поддержкой ИЕ разумеется.
> Просто, блин, так мурыжить каждую кнопочку и блочок постоянно заглядывать в справочник
Со временем научишься без заглядывания в справочник понимать как лучше сделать
> Ну и поглядывая на вакансии я все чаще вижу что пишут что-то типа 'мучать ие 8- не будем'.
Это можно прочесть как "в ИЕ9 все должно работать"
Вообще, что касается древних ИЕ, у меня когда-то была такая мысль - чтобы не мучаться, просто отдавать им голый ХТМЛ без КСС и ЖС. Прочесть тексты можно (если верстка семантичная), картинки посмотреть можно, а остальное это уже проблемы юзеров этого антиквариата.
И еще был какой-то проект, который умел из сайта сделать версию то ли из картинок, то ли на одном абсолютном позиционировании, которая в старых браузерах корректно отображалась. Но это конечно уже усложнение, но если очень надо - может быть помогло бы.
Еще, я помню, я когда-то заморачивался с написанием генератора CSS для кнопок, там идея была такая: для новых браузеров делаем кнопки через CSS3, для старья - генерируем на основе CSS3 картинку с тенями, скруглениями и тд. Это может и перебор, но если постоянно занимаешься версткой - почему бы и не автоматизировать свой труд.
насчет свг: масштабирование это круто, и вообще, за вектором будущее, но надо искать способы подключения, которые работают максимально широко и не создают проблем.
Я пока варианта лучше чем "генерировать пнг из свг" не нашел для себя.
В коде работы со спонсорами мне не нравится что там при инициализации ставится маргин и может произойти скачок верстки (или там абсол. поз.? ). Также нет проверок, если спонсоров мало то не надо делать скроллинг. Также, у тебя там жестко заложено что видно ровно 3 логотипа, это неадаптивно.
Также, для анимации лучше использовать requestAnimationFrame, а двигать объекты с помощью translate() - на некотоых устройствах это позволяет использовать видеочип для перемещения элементов на экране. Но конечно надо думать и о тех браузерах, в которых этого нет.
В случае же с маргином, не знаю, будет ли задействовано аппаратное ускорение.
А, вспомнил еще интересную проблему с СВГ: если ты исплоьзуешь нестандартный шрифт, то у пользователя текст выведется другим шрифтом или вообще не выведется. Ты можешь превратить буквы в кривые, но тогда текст нельзя будет редактировать и нельзя выделять в браузере. То есть СВГ на мой взгляд это формат для создания контента, но для публикации можно использовать только его ограниченное подмножество (значит нужен конвертор), и нужны фоллбеки (например через пнг).
Это конечно сложно, вот тут я пробовал например экспериментировать с разными способами вставки картинок: http://codedokode.github.io/files/2/
Если тебе все это интересно, я тебе тоже советую разобраться с особенностями SVG, версиями, поддержкой, способами вставки и тд. А просто вставить картинку, скопировав код, любой может.
> Я не очень понимаю как в таком случае переводить эти строки через gettext. Сейчас у меня вот эта строка
>>_("{0, plural, =0{No downloads} one{# download} other{# downloads}}")
> Получается через gettext и при разных языках она будет разная. А если делать такой объект, то функция форматирования будет выглядеть как-то так
gettext нужен только для получения перевода строки. В нем нет нормальной возможности склонять или менять слова.
Тут есть 2 варианта - либо ты вызываешь gettext вручную:
$tr->format(_('text', $args);
Либо это делает format()
$tr->format('text', $args);
От этого будет зависеть алгоритм работы сборщика строк для перевода, как их искать в коде.
я кстати еще вспомнил интересную штуку: фраза может меняться в зависимости от пола пользователя:
Ты получил подарок от Ивана
Ты получила подарок от Ивана
Вроде TextFormatter такое позволяет. Я кстати когда-то придумывал свой синтаксис и свой форматтер, то ли тогда не было Intl то ли я о нем не знал.
> Единственный вариант который могу придумать это такой:
>>{{ tr.format('{0, plural, =0{No downloads} one{# download} other{# downloads}}', { number: 2}) }}
> Он подойдет?
Подойдет.
>>767669
> Мне в начале каждого адреса такую шнагу прописывать __DIR__.'/../ ?
Либо прописывать, либо сделать функцию например так:
$helper->getAbsolutePath('/config.json');
PathHelper::getAbsolutePath('/confog.json');
>>767581
Сомневаюсь что это хорошая идея. Никто на них не пишет. Либо Qt либо встроенный в приложение браузер (Electron) + HTML.
>>767522
Все для одного и того же - веб приложений.
>>767366
> $maxlength = mb_strlen($lines[0]);
Сложновато. Тут лучше использовать функции max и array_map например. Или array_reduce, если ты смел (она эффективнее из-за отсутствия промежуточного массива)
> $lines[$column] = mb_substr($lines[$column], 1);
Постоянно разрезать строки не очень эффективно и не очень читабельно. Лучше брать N-ю функцию, не меняя исходную строку.
> Я не очень понимаю как в таком случае переводить эти строки через gettext. Сейчас у меня вот эта строка
>>_("{0, plural, =0{No downloads} one{# download} other{# downloads}}")
> Получается через gettext и при разных языках она будет разная. А если делать такой объект, то функция форматирования будет выглядеть как-то так
gettext нужен только для получения перевода строки. В нем нет нормальной возможности склонять или менять слова.
Тут есть 2 варианта - либо ты вызываешь gettext вручную:
$tr->format(_('text', $args);
Либо это делает format()
$tr->format('text', $args);
От этого будет зависеть алгоритм работы сборщика строк для перевода, как их искать в коде.
я кстати еще вспомнил интересную штуку: фраза может меняться в зависимости от пола пользователя:
Ты получил подарок от Ивана
Ты получила подарок от Ивана
Вроде TextFormatter такое позволяет. Я кстати когда-то придумывал свой синтаксис и свой форматтер, то ли тогда не было Intl то ли я о нем не знал.
> Единственный вариант который могу придумать это такой:
>>{{ tr.format('{0, plural, =0{No downloads} one{# download} other{# downloads}}', { number: 2}) }}
> Он подойдет?
Подойдет.
>>767669
> Мне в начале каждого адреса такую шнагу прописывать __DIR__.'/../ ?
Либо прописывать, либо сделать функцию например так:
$helper->getAbsolutePath('/config.json');
PathHelper::getAbsolutePath('/confog.json');
>>767581
Сомневаюсь что это хорошая идея. Никто на них не пишет. Либо Qt либо встроенный в приложение браузер (Electron) + HTML.
>>767522
Все для одного и того же - веб приложений.
>>767366
> $maxlength = mb_strlen($lines[0]);
Сложновато. Тут лучше использовать функции max и array_map например. Или array_reduce, если ты смел (она эффективнее из-за отсутствия промежуточного массива)
> $lines[$column] = mb_substr($lines[$column], 1);
Постоянно разрезать строки не очень эффективно и не очень читабельно. Лучше брать N-ю функцию, не меняя исходную строку.
ставишь обработчик в set_exception_hadnler (либо пишешь try/catch в фронт контроллере). В нем:
- логгируешь ошибку через error_log, тестируешь это
- выдаешь один из HTTP кодов 5xx
- выдаешь HTML страницу с сообщением для пользователя
Дополнительно можно написать обработчик котоый превратит обычные ошибки в исключения, смотри ErrorException в мануале, там есть код.
Это все работает если ошибка произошла до вывода текста. Если вывод уже начат, то ничего не поделать.
Ты можешь определить это например проверяя http://php.net/manual/en/function.headers-sent.php
В принципе есть возможность включить буферизацию вывода и предотвратить вывод текста до полного формирования страницы, но по моему оно того не стоит.
>>766467
С фатальными ошибками ничего не поделать. можно их ловить через shutdown_handler, но по моему оно того не стоит. А в лог они пишутся, пхп это сам делает.
Должен ловить. Перепроверь, все ли верно? ЧТо будет если просто написать внизу throw new ErrorException(...) ?
Заметьте, что используется SQL-подобный синтаксис. Знание SQL поможет вам не раз. ОП учит вас просто-таки жемчужинам из мира технологий.
>> $result; // ссылка на департамент или массив департаментов
> Что это за синтаксис такой? На мой взгляд, это обращение к несуществующей пока переменной $result.
Иногда вначале функций обозначаю переменные, стоит ли так делать? Вот так будет лучше $result = null ?
> Тут мне не очень нравится идея передавать компанию через отдельный метод. ладно, ты бы передавал ее через конструктор. Но тут у тебя создается риск что антикризисный метод будет вызван без компании. То лучше передавать компанию в метод через аргументы или конструктор.
> Мне кажется, клонировать компании - это не задача антикризисного комитета, это не требуется для его работы, и этого кода в классе быть не должно.
Если я передаю компанию через конструктор, то затем нужно ее клонировать(внутри комитета?), чтобы применить 3 набора АМ. Если я клонирую компанию вне комитета, то мне нужно передать ее комитету три раза, как это сделать если не через аргумент? Вижу единственный выход, создать 3 комитета, каждый со своим набором АМ (клонирование снаружи и передачи в конструктор). Или есть еще варианты?
> Вот здесь у нас серьезная проблема. Что, если все работники содержат ссылку на один и тот же объект профессии? Тогда этот метод меняет зарплату всех этих работников. Я вижу, у тебя так и сделано.
Я так вижу, лол. Сделал это намеренно. Я понял условия так, что между аналитиками нет разницы. Поэтому зарплату поднял в профессии, которая общая для всех работников, а не у конкретных работников определенной должности. Имелось ввиду поднятие ставки у одного работника?
Остальное понятно, спасибо )
"Это по идее должна быть ошибка. У тебя наверно не включен вывод ошибок и ты ее не видишь." - хотел я написать, но решил проверить, запустив код в командной строке:
$ php -r 'error_reporting(-1); $result;'
(ничего)
Действительно, ошибки нет. Но я первый раз вижу такую конструкцию. Что она значит? У тебя есть ссылка на мануал с объяснением?
Кстати, если добавить любую операцию, то ошибка будет:
$ php -r '$result + 1;'
PHP Notice: Undefined variable: result in Command line code on line 1
Не, если ты хочешь "обозначить" переменную, лучше присвоить ей какой-то значение. А такую конструкцию я вижу в первый раз.
> Вижу единственный выход, создать 3 комитета, каждый со своим набором АМ (клонирование снаружи и передачи в конструктор). Или есть еще варианты?
Да, либо 3 комитета, либо передавать через аргументы .
Такой подход, когда одна функция задает аругмент, другая делает какое-то действие, он в общем плох, так как всегда есть шансы:
- забыть задать аргумент
- не догадаться что нужно задать аргумент сначала
- забыть задать аргумент при обработке другой компании и получить результаты от старой компании
Я не вижу никаких преимуществ у твоего способа перед простой передачей компании в функцию явно:
$comittee->doSomething($company);
> Я понял условия так, что между аналитиками нет разницы. Поэтому зарплату поднял в профессии, которая общая для всех работников, а не у конкретных работников определенной должности. Имелось ввиду поднятие ставки у одного работника?
А, точно, поднять всем. Но тогда логичнее сделать по-другому: не в цикле увеличивать кждому зарплату, а взять объект профессии и увеличить на нем.
"Это по идее должна быть ошибка. У тебя наверно не включен вывод ошибок и ты ее не видишь." - хотел я написать, но решил проверить, запустив код в командной строке:
$ php -r 'error_reporting(-1); $result;'
(ничего)
Действительно, ошибки нет. Но я первый раз вижу такую конструкцию. Что она значит? У тебя есть ссылка на мануал с объяснением?
Кстати, если добавить любую операцию, то ошибка будет:
$ php -r '$result + 1;'
PHP Notice: Undefined variable: result in Command line code on line 1
Не, если ты хочешь "обозначить" переменную, лучше присвоить ей какой-то значение. А такую конструкцию я вижу в первый раз.
> Вижу единственный выход, создать 3 комитета, каждый со своим набором АМ (клонирование снаружи и передачи в конструктор). Или есть еще варианты?
Да, либо 3 комитета, либо передавать через аргументы .
Такой подход, когда одна функция задает аругмент, другая делает какое-то действие, он в общем плох, так как всегда есть шансы:
- забыть задать аргумент
- не догадаться что нужно задать аргумент сначала
- забыть задать аргумент при обработке другой компании и получить результаты от старой компании
Я не вижу никаких преимуществ у твоего способа перед простой передачей компании в функцию явно:
$comittee->doSomething($company);
> Я понял условия так, что между аналитиками нет разницы. Поэтому зарплату поднял в профессии, которая общая для всех работников, а не у конкретных работников определенной должности. Имелось ввиду поднятие ставки у одного работника?
А, точно, поднять всем. Но тогда логичнее сделать по-другому: не в цикле увеличивать кждому зарплату, а взять объект профессии и увеличить на нем.
Посмотрел я этот SOAP, выглядит как говно мамонта. Алсо, что по SOAP, что по REST есть циклы статей на tutorialspoint.
Что ещё спрашивали?
Шторм летает у меня.
64 винда, i7, amd radeon, ssd
Пользуясь случаем, спрошу. Кто обновлялся на Шторме? Не слетит ли сервер для авторизации?
> $db=new DataBase($config['db']);
> Это раскидано в нескольких местах кода. Вообще-то идея была, чтобы в bootstrap создать нужные объекты один раз. Ты создаешь несколько соединений с базой данных например, несколько StudentDataGateway. Это не очень логично.
Идея нравится. Но вот не пойму, если я пропишу $db=new DataBase($config['db']); в bootstrap.php, как мне к ней обращаться в FrontController и в других классах?
>> // Если сотрудник соответствует профессии
>> if ($employee->getProfession()->getName() == $profession) {
>Если у тебя профессия - это объект, может логично передавать для поиска не название, а этот объект?
Для того, чтобы передать объект как аргумент в функцию поиска, нужно создать этот объект? Я попробовал передать имя класса, которому принадлежит объект и получил ошибку "Argument 1 passed to Departament::findEmployees() must be an instance of Profession, string given". Получается, каждый раз когда используется функция, нужно будет создать объект определенного класса и передать его в качестве аргумента?
ОП, а объясни условие в задаче про Антикризисные меры:
> В тех департаментах, где руководитель не является аналитиком,
> заменить его на аналитика самого высшего ранга из этого департамента
> (а бывшего руководителя вернуть к обычной работе)
А что делать в департаментах, где вообще нет аналитиков (по условию аналитики только в Департаменте продаж)? Нужно ли возвращать шефа к обычной работе? Или вообще ничего не делать с этими департаментами?
Если более хорошего кандидаты в шефы нет, оставить старого.
>>770821
Вообще, по идее у тебя должен быть ровно 1 объект для каждой профессии и передавать надо именно тот же экземпляр. Другой экземпляр, пусть даже с таким же названием - это именно другой объект и не годится.
То есть у объекта есть "идентичность". Мы можем с помощью === сравнить, являются ли объекты одним и тем же. == сравнивает класс и поля объектов на равенство, а вот === проверяет именно идентичность. Благодаря этому нам не нужны какие-то дополнительные идентификаторы, чтобы понять тот же это объект или нет, объект это сам по себе уникальный идентификатор.
И в ООП у нас не должно несколько копий объекта-профессии без веской причины.
Правда, это все усложняет - надо как-то хранить эти объекты. Если это сложно, можно сделать константы для обозначения классов профессиий и проверять их. В качестве констант удобно использоваь имя класса. В PHP запись SomeClass::class возвращает строку с именем класса. То есть будет Manager::class и тд.
> Для того, чтобы передать объект как аргумент в функцию поиска, нужно создать этот объект? Я попробовал передать имя класса, которому принадлежит объект
Ну так ты указал что нужен объект, а передаешь строку.
Тут есть разные варианты. Самый простой - забить на эту проблему и сказать что в контроллере можно создавать оьъекты, но это имеет недостатки. Например каждый новый объект PDO создает соединение с БД.
Второй вариант - сделать какое-то хранилище (контейнер) для объектов. Самый просто вариант - массив:
$services['pdo] = new PDO...
или объект:
$container->add('pdo', new PDO...);
А затем передать контейнер в контроллер через конструктор.
Третий вариант - передавать сервисы в конструктор контроллера по отдельности.
Урок по теме: https://github.com/codedokode/pasta/blob/master/arch/di.md
Я советую не делать слишком сложных решений. Для простой задачи наверно и массив сойдет.
К тому моменту я многое успею исправить
вроде и красивая девушка слева, а что-то в ней есть странное, тоьлко мне кажется? Какая-то она... не сформулирую.
Хрен с ним.
Я не оч пойму как генерировать схему таблицы для ОРМ.
> Yii, Symphony, Doctrine, Slim, Лар(в)ав(р)ель, апи вордпресса, не дохуища ли? Это все взаимозаменяемые фреймворки об одном и том же или они для разных задач?
> Все для одного и того же - веб приложений.
Я имел ввиду, их функционал пересекаются, дублируется или они для разного и их реально все-все надо знать? Я почти ничего не знаю о фреймворках кроме рельс для руби или кроме джанги для питона. Все остальные - либо хипстерские откровенно, либо узкоспециализированные. А для пхп нужно все-все это знать? Почему так?
>Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
Читаю в маршрутке перед работой. Что еще посоветуете полезного?
>Для установки MySQL на Debian войдите в систему под учетной записью root и наберите команду:
>apt-get install mysql-server
Почему это не работает? Помогите начинающему линуксоиду.
Не работает и с sudo.
Пишет:
>bash: sudo команда не найдена
Что делать, как дальше жить?
Debian 8 на VirtualBox.
sudo - команда которая позволяет запускать программы с правами другого пользователя, обычно это суперпользователь (root). Если ты уже залогинился под рутом писать sudo не нужно. Не копируй бездумно команды из интернета, старайся сам гуглить и понимать что они значат перед выполнением.
>>apt-get install mysql-server
>Почему это не работает?
Должно работать, какую именно ошибку пишет?
Т.к. у тебя все стоит на виртуалбоксе - поиграю в вангу и предположу что ты неправильно настроил сеть и у машины нет доступа в интернет.
Получается FC вызывает MC, а в MC я вызываю $auth=FC->checkAuth(), на основании значения которой представление будет решать, что показать
Хотя для этого нужно создавать новый экземпляр FC в MC, что плохо.
Такая дилема, некоторым контроллерам нужно получать значение переменной $auth из FrontController'a, а некоторым не нужно. Как это можно реализовать?
ну сделай для начала apt-get install sudo из-под рута
Если ты сидишь под пользователем root, sudo ты можешь не писать
а ты во фронт-котроллере не можешь почекать свой checkUath и уже вызывать тот контроллер, которые тебе нуджен?
Зависит от того, какой у тебя ORM. Иногда они могут генерировать SQL код сами, иногда надо его самому писать, второй вариант почти всегда предпочтительнее, так как то, что по умолчанию генерируется, подходит только для простых случаев.
>>771843
Вообще, это не совсем одинаковые вещи. Доктрина например это ORM, а не фреймворк. Ты бы мог погуглить и хотя бы в википедии или на хабре про них прочитать.
Различаются они масштабом - Слим очень маленький и годится для простых приложений, Симфони для больших и сложных.
Что надо знать - смотри описание вакансий.
>>772493
"Совершенный код" например.
>>772750
Надо sudo сначала установить
Также, не копируй команды, найди где-нибудь и изучи синтаксис команд apt-cache и apt-get, научись устанавливать любые пакеты.
>>772812
Вообще, нет. Для авторизации логичнее сделать отдельный класс-сервис, а не пихать все в фронт контроллер. В соответствии с приницпом единой обязанности каждый класс должен заниматься своим делом.
Зависит от того, какой у тебя ORM. Иногда они могут генерировать SQL код сами, иногда надо его самому писать, второй вариант почти всегда предпочтительнее, так как то, что по умолчанию генерируется, подходит только для простых случаев.
>>771843
Вообще, это не совсем одинаковые вещи. Доктрина например это ORM, а не фреймворк. Ты бы мог погуглить и хотя бы в википедии или на хабре про них прочитать.
Различаются они масштабом - Слим очень маленький и годится для простых приложений, Симфони для больших и сложных.
Что надо знать - смотри описание вакансий.
>>772493
"Совершенный код" например.
>>772750
Надо sudo сначала установить
Также, не копируй команды, найди где-нибудь и изучи синтаксис команд apt-cache и apt-get, научись устанавливать любые пакеты.
>>772812
Вообще, нет. Для авторизации логичнее сделать отдельный класс-сервис, а не пихать все в фронт контроллер. В соответствии с приницпом единой обязанности каждый класс должен заниматься своим делом.
запихнул checkAuth в abstract class Controller, который наследуется остальными контроллерами
>>иногда надо его самому писать, второй вариант почти всегда предпочтительнее, так как то, что по умолчанию генерируется, подходит только для простых случаев.
Нафиг она тогда нужна-то? Ну я понимаю, сложные мени-ту-мени отношения можно и переписать, но если у тебя в базе данных пицот таблиц, каждую описывать вручную можно же умом поехать, мне казалось ОРМ и должна освобождать от рутинного sql дрочения
Правильнее сделать отдельный сервис авторизации, а в базовый контроллер ставить в лучшем случае его вызов.
>>772926
Индексы например, как ставить? Добавлять всякие ограничения. Использовать text вместо varchar, и тд. Комментарии к полям добавлять. Все это ORM не сделает.
И почему ты так недоволен? Ты плохо знаешь SQL и тебе тяжело написать CREATE TABLE? Тогда стоит лучше его изучить, ORM это помощник в работе с базой данных, но он не отменяет необходимость знать SQL.
Я, конечно, может чего не понимаю, но, тупо из википедии берём цитату:
>>если программист хочет создать пользователя в базе данных, он может больше не использовать SQL, а написать следующий PHP код:
[далее код доктрины]
Я могу описать некую таблицу, без вопросов, но неужели суть ОРМ не в том, чтобы избавить меня хотя бы от написания банального КРУДа?
сначала пример кода (он не рабочий, прост пример)
Есть класс для работы с таблицей
class News {
public $date;
public $news;
public function __construct($date, $news) {
$this->date = $date;
$this->news = $news;
}
//новая новость
public function save() {
// some mysqli shit
$query = "INSERT INTO news (date, news) VALUES (?,?)";
$sth = $dbh->prepare($query);
$sth->bind_param("is", $this->date, $this->news);
$sth->execute;
}
Вопрос
Как передовать хендлер базы данных в метод? В каждлом методе делать новый хендлер, по-новой соедниясь с БД?
В пхп ини прописал extension=mcrypt.so, заработало
Нет, доступ в Интернет есть, браузер был открыт, там как раз мануал по установке LAMP.
Просто я не как суперюзер вошёл и sudo не установил.
Вот сейчас пытаюсь установить, получаю прямо по лбу вот этим
<-----
Какой диск, какая Джесси, куда вставить - сие не понять.
>>772852
Вроде сидел под root, пока в этом просто ещё не разобрался.
А sudo пытаюсь вот только установить.
>>772877
>найди где-нибудь и изучи синтаксис команд apt-cache и apt-get, научись устанавливать любые пакеты.
Придётся, конечно.
Я просто думал, что это будет работать всё. Я когда Дебиан на ВиртуалБокс стааааавил...
тяжело тебе будет без знаения хотя бы основ unix-like систем
вот ты говоришь ты под рутом сидел. А зачем тогда sudo вызывал? Судо в данном нужно только для того, чтобы запустить команду от имени рута. Потому что пакет, в данном случае = сервер бд, может установить только рут.
>тяжело тебе будет без знаения хотя бы основ unix-like систем
Надеюсь, что не тяжелее, чем без знания основ программирования полгода назад...
Спасибо, немного прояснело.
А с установкой вот что: надо было в ВиртуалБоксе просто снова направить на дистрибутив Дебиан путь в "Носителях".
Я думал, что не нужно это повторять, если один раз уже прописал путь, но вот так.
Дальше пытаюсь.
Отпишусь по результатам, постараюсь держать в курсе!
чо? каких носителях?
отредактируй /etc/apt/sources.list и удали
оттуда всё к ебеням, оставь только
deb http://mirror.mephi.ru/debian/ jessie main
deb-src http://mirror.mephi.ru/debian/ jessie main
deb http://security.debian.org/ jessie/updates main
deb-src http://security.debian.org/ jessie/updates main
# jessie-updates, previously known as 'volatile'
deb http://mirror.mephi.ru/debian/ jessie-updates main
deb-src http://mirror.mephi.ru/debian/ jessie-updates main
>Какой диск, какая Джесси, куда вставить - сие не понять.
Это происходит потому что в списке репозиториев у тебя находится установочный диск, и так как он не смонтирован то установщик пакетов тебя просит его смонтировать. Вообще установщик дебиана, насколько я помню после установки советует убрать образ из списка репозиториев, может ты просто это пропустил. Как его убрать, тебе уже написали тут >>773121
>>770003
>редактирование и регистрацию надо бы объединить в один метод
Я много думал как это сделать и даже заглядывал в код к другим анонам, и мои мысли подтвердились - нужно в ключевых моментах делать проверку на залогинивание. Но в моем случае тут несколько сложнее - из-за того что я использую пароль приходиться делать валидацию по другому, и чтобы это сделать нужно делать лишний раз проверку на залогинивание. Получается что мы копипасту меняем на копипасту
Старая копипаста
https://github.com/someApprentice/Students/blob/1b6864801a038c4619701ccb96c43144ea91c5b3/app/Controller/RegisterAction.php
Новая копипаста
https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L33
https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L43
https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L50
К тому же, из-за того что я сократил проверку токена, если не будет параметра $_GET['token'] в адресной строке, то залогиненый пользователь увидит форму регистрации. Логично что для такого случая нужно делать проверку и вбрасывать исключения или делать ридерект, но это приведет к еще большей копипасте.
Вконтакте, Фейсбуке и Твиттере почти для каждого поля своя форма редактирования. У них, наверно, для этого отдельный от регистрации контроллер и отдельный валидатор?
Естественно не только проверил, включена ли она (включена по-умолчанию), но и хочу разобраться, за что собственно отвечает.
>Данная директива включает поддержку оберток URL (URL wrappers), которые позволяют работать с объектами URL как с обычными файлами. Обертки, доступные по умолчанию, служат для работы с удаленными файлами с использованием ftp или http протокола. Некоторые расширения, например, zlib, могут регистрировать собственные обертки.
Что такое "обертки URL", и как их активация/отключение изменит работу приложения?
Разве php и так не работает с url как с обычными файлами, зачем нужны эти "обертки"?
На форумах суппорта не найдешь, одни мудаки или тролли (поэтому спрашиваю на дваче, да).
Вот чел приводит пример, когда подключается внешний скрипт из гет-переменной(!).
http://phpclub.ru/talk/threads/Опция-allow_url_fopen-вопрос-по-безопасности.51669/#post-444000
Резонный вопрос, а есть ли уязвимость, если не использовать непроверенные данные от пользователя?
Тем более что за инклюды с 5.2 теперь отвечает отдельная настройка allow_url_include
В оф.документации непонятная лабуда, на форумах мудаки. Куда гуглить? Хочу научиться самостоятельно разбираться в подобных вопросах.
Подозреваю, что у меня пробел в понимании, как php работает с файлами и прочими ресурсами, как составить правильный поисковый запрос?
Запрос "как php работает с ресурсами" не выдал удовлетворительных ссылок.
https://habrahabr.ru/post/61429/
>А какаие противоречия у них есть? У одного потенциальная дыра открыта, у другого прикрыта. Выбирайте что вам нужнее :)
Безграмотный смайлофаг. Вопрос был, в чем состоит уязвимость.
>может, один эту дыру за дыру не считает? или другой, наоборот, видит дыру там, где её нету?
Агностик.
>Потенциально (!) это однозначно дыра, зависит исключительно от скриптов, которые реализуют некий функционал.
Не приводит пример скрипта, реализующего некий потенциал, и в котором включение настройки становится "дырой".
>Давненько я не встречал платный виртуальный хостинг с отключенным allow_url_fopen.
Напиши еще что-нибудь из своей биографии.
>Не понимаю сути вопроса.
Тупой.
>У вас есть задача и вам нужно ее решить, значит используйте allow_url_fopen.
Мне для этого нужно понять, что делает эта опция, и какие могут быть подводные камни.
>Вся проблема заключается в том как использовать, больше внимания защите приложения и все будет нормально.
Сука, напиши как использовать, и опиши как защитить приложение.
>если скрипты безопасны (возмем идеальный вариант) 100%, то это не дыра.
Как сделать "скрипты" (процедурщики?) безопасными?
Поэтому не люблю хабрахабр, это же обычная соцсеть, или в крайнем случае тухлый блог, куда репостят несвежие переводы.
http://phpclub.ru/detail/article/2003-09-23
>Для увеличения функциональности и упрощения кодирования, разработчики php сделали такую особенность в функциях fopen, file, include и прочих. >Если имя файла начинается с "http://", сервер выполнит HTTP-запрос, скачает страницу, и запишет в переменную как из обычного файла. Аналогично работают префиксы "ftp://", "php://" (последний предназначен для чтения и записи в stdin, stdout и stderr).
Чтобы создать пользователя, тебе не надо писать рутинный однотипный SQL код. А вот чтобы создать таблицу - надо, так как там часто требуется что-то нестандартное или то, что ORM не сгенерирует.
Использование ORM вообще не значит, что SQL можно не изучать. Он избавляет от рутинных запросов, а не от необходимости знать SQL.
>>773031
во-первых, CGI это несерьезно. Он делался скорее для каких-то любительских вещей. Сейчас используют либо php как модуль Апача, либо php-fpm, взаимодействующий с сервером через FCGI.
Модули php это просто динамические библиотеки. Ты их компилируешь (или устанавливаешь пакетным менеджером, или качаешь готовые) и подключаешь в php.ini. Часть модулей уже встроена в пхп и подключается в php.ini.
конкретные инструкции зависят от модуля и версии ОС, смотри в мануале. Например для модуля gd: http://php.net/manual/ru/image.installation.php
Для расширений pecl: http://php.net/manual/ru/install.pecl.php
> Как передовать хендлер базы данных в метод?
Читай урок по DI https://github.com/codedokode/pasta/blob/master/arch/di.md
Если кратко - через конструктор.
>>773098
Странно. Тебе надо проверить конфиг в файле /etc/apt/sources.list (редактировать его можно редактором nano, набрав nano /etc/apt/s.... )
В нем указывается, откуда брать пакеты. У тебя почему-то указан только cdrom (почему? ты наверно при установке что-то не то указал). Надо раскомментировать или добавить туда официальный репозитоий дебиана. Адреса можно взять в генераторе sources.list: https://debgen.simplylinux.ch/
Выбираешь страну, версию ОС, ставишь все 10 галочек, при желании дополнительные галочки в 3rdparties, но это необязательно и всегда можно добавить потом. Получаешь конфги, копипастишь его в sources.list
Обновляешь список командой apt-get update
После этого должно ставить пакеты из интернета.
Также почитай статью https://wiki.debian.org/ru/SourcesList
Лучше не давать готовый список, а ссылку на генератор soures.list и мануалы. А то человек ничему не научится.
>>773236
Эта опция позволяет писать такое
$content = file_get_contents('http://example.com/file.txt');
Во многих местах в пхп функции могут работать не только с файлами на диске, но и с удаленными файлами по сети, и много еще с чем. Мануал:
http://php.net/manual/ru/wrappers.php
http://php.net/manual/ru/book.stream.php
> Разве php и так не работает с url как с обычными файлами, зачем нужны эти "обертки"?
Это отключается/включается в конфиге.
> Резонный вопрос, а есть ли уязвимость, если не использовать непроверенные данные от пользователя?
Есть ли уязвимость зависит от кода. Если в коде делается например fopen() с данными от пользователя то конечно поддержка врапперов расширяет возможности для атаки. Если код написан грамотно и ответственно (что случается редко) то проблем нет.
>>773247
Да, статьи разные по качеству. Но например у Яндекса обычно качественные статьи. А вот по пхп - скажем так, ... разные.
Лучше не давать готовый список, а ссылку на генератор soures.list и мануалы. А то человек ничему не научится.
>>773236
Эта опция позволяет писать такое
$content = file_get_contents('http://example.com/file.txt');
Во многих местах в пхп функции могут работать не только с файлами на диске, но и с удаленными файлами по сети, и много еще с чем. Мануал:
http://php.net/manual/ru/wrappers.php
http://php.net/manual/ru/book.stream.php
> Разве php и так не работает с url как с обычными файлами, зачем нужны эти "обертки"?
Это отключается/включается в конфиге.
> Резонный вопрос, а есть ли уязвимость, если не использовать непроверенные данные от пользователя?
Есть ли уязвимость зависит от кода. Если в коде делается например fopen() с данными от пользователя то конечно поддержка врапперов расширяет возможности для атаки. Если код написан грамотно и ответственно (что случается редко) то проблем нет.
>>773247
Да, статьи разные по качеству. Но например у Яндекса обычно качественные статьи. А вот по пхп - скажем так, ... разные.
> Но в моем случае тут несколько сложнее - из-за того что я использую пароль приходиться делать валидацию по другому, и чтобы это сделать нужно делать лишний раз проверку на залогинивание.
Не вижу тут особой сложности.
> К тому же, из-за того что я сократил проверку токена, если не будет параметра $_GET['token'] в адресной строке, то залогиненый пользователь увидит форму регистрации. Логично что для такого случая нужно делать проверку и вбрасывать исключения или делать ридерект, но это приведет к еще большей копипасте.
Думаю, можно как-то решить эту проблему. Я посмотрел контроллер регистрации и там можно полностью избавиться от копипасты строк кода.
> Вконтакте, Фейсбуке и Твиттере почти для каждого поля своя форма редактирования. У них, наверно, для этого отдельный от регистрации контроллер и отдельный валидатор?
Своя форма для каждого поля - это как? А, в смысле, можно отдельно редактировать имя, фамилию и тд? Ну, у них там просто сделано по-другому. Не знаю, отдельный ли контроллер, я бы сделал один контроллер, который проверяет имя поля и в зависимости от этого что-то меняет.
-----------
Вместо того, чтобы копипастить условие в if:
> if (Helper::validCSRFtoken($_GET['token']) and $this->loginAction->isLoggedIn()) {
Можно завести булеву переменную $isEdit:
$isEdit = $this->loginAction->isLoggedIn();
И передавать ее куда надо, например:
$errors = $this->validations->validRegisterStudentForm($registerStudentForm, $isEdit);
> if ($registerStudentForm->getPassword() != "") {
> $registerStudentForm->setStudentPassword();
Вообще, это можно было бы делать в классе формы в методе fillDataFromArray
>if (Helper::validCSRFtoken($_GET['token']) and $this->loginAction->isLoggedIn()) {
> $student = $this->studentGateway->getStudentByСolumn('id', $_COOKIE['id']);
Вот здесь странно. Если у тебя есть сервис авторизации и ты через него проверяешь залогиненность, то разве не логично через него же и получать текущего пользователя? Почему у тебя проверка залогиненности в одном месте, а получение пользователя - в другом?
Насчет XSRF, у тебя при несовпадении токена делается регистрация. Но правильнее вообще не обрабатывать форму. А то злоумышленник может от имени пользователя регистрировать аккаунты.
А, еще, я думал, loginAction это сервис, а это контроллер. Тогда это странный код:
> $this->loginAction->isLoggedIn()
ты пытаешься использовать контроллер как сервис, но это в общем плохая идея, так как контроллер не для этого. Он для обработки запроса пользователя. В данном случае он ничего не обрабатывает и используется не по назначению.
Более того, проверяешь авторизацию ты контроллером, а залогинаваешь пользователя в классе со странным названием StudentCookies.
По моему это нелогично, что код, отвечающий за авторизацию разбросан по всему приложению. Мне кажется, логичнее сделать единый сервис авторизации, который умеет:
- залогинивать пользователя (ставить нужные куки)
- разлогинивать
- проверять залогинен ли пользователь
Это позволит убрать все, что связано с авторизационными куками, в один класс.
Проверка CSRF сделана неправильно.
https://github.com/someApprentice/Students/blob/master/app/Model/Helper/Helper.php#L15
> if ($token == $_COOKIE['token']) {
Получается если токен пустой и в куках пусто, то проверка сработает. Более того, у тебя тут нестрогое сравнение через == вместо === что добавляет шансы на ложные совпадения.
Далее, нет логики в наследовании хелперов. Вот у тебя есть Helper и есть LoginHelper, который его наследует. Непонятно, а зачем? Там все равно почти все методы статические. Точнее, часть статическая, а часть нет, и почему, непонятно.
Непонятно, зачем нужен этот пустой класс: https://github.com/someApprentice/Students/blob/master/app/Model/Helper/RegistrationHelper.php
Ты вызываешь статически не-статический метод Helper::validCSRFtoken
https://github.com/someApprentice/Students/blob/master/app/init.php#L21
> $config = parse_ini_file('config.ini');
Тут используется относительное имя файла. Это ненадежно, так как правильно ли сработает код, зависит от того, какая директория текущая (прочти-ка про текущую директорию https://ru.wikipedia.org/wiki/Рабочий_каталог )
https://github.com/someApprentice/Students/blob/master/app/Model/Cookies/StudentCookies.php
Тут по моему ты радикально все переусложнил. По моему, установку кук можно было просто сделать 2 строчками setcookie и незачем ради этого было городить интерфейсы. Также, ты указал для кук в качестве домена 'localhost' и получается что на другом домене твое приложение не будет работать (не будет ставить куки).
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Search.php#L11
> if (is_scalar($query)) {
А если передан неправильные данные, то что? Притворимся что все в порядке? Не лучше ли исключение выбрасывать?
https://github.com/someApprentice/Students/tree/master/app/Model/Entity
Я думаю, формы лучше было бы конечно вынести в отдельную папку.
В студенте, непонятно, а зачем у тебя там и токен, и пароль? Если ты исплоьзуешь вход по паролю, то токен по моему не нужен.
https://github.com/someApprentice/Students/blob/master/app/Model/Gateway/TableDataGateway.php#L15
Непонятно что в базовом классе делается функция adduser. Разве она не в StudentTDG должна быть?
В классах валидации наследование реализовано неправильно. Вот, как я понимаю:
- есть базовый класс валидации с общими методами вроде "провеhить длину", не привязанный ни к студенту, ни к формам
- есть его наследники: класс для валидации стдента, класс для валидации формы регистрации
Но у тебя это нарушается. В базовом классе зачем-то есть константы (которые относятся к модели студента, а не к валидации), базовый класс через конструктор получает StudentGateway, там есть метод isGenderInvalid($gender), который предназначен только для студента, там же есть метод validateLoginStudentForm.
Нужно это исправить. Либо у тебя есть общий базовый класс, не завязанный на студента, либо, получается, надо делать вместо двух классов один.
И наконец, в некоторых местах, ты обращаешься к массивам вроде $_GET или $_COOKIE, не проверяя перед этим, есть ли в них такой элемент:
Helper::redirect($_GET['go']);
Но ведь он может и отсутствовать. Лучше использовать функцию, вроде $this->getQuery('go'), которая делает нужную проверку. Редирект, кстати, я подумал, логичнее было бы поместить в базовый класс контроллера. Тогда редиректить можно будет только из контроллеров.
Вместо include __DIR__ . '/../../templates/registration.phtml'; лучше было бы сделать метод $this->render('templates/registration.phtml')
В общем, как всегда, я тут написал много замечаний, может даже немного запутанно, так что, если что, задавай уточняющие вопросы.
> Но в моем случае тут несколько сложнее - из-за того что я использую пароль приходиться делать валидацию по другому, и чтобы это сделать нужно делать лишний раз проверку на залогинивание.
Не вижу тут особой сложности.
> К тому же, из-за того что я сократил проверку токена, если не будет параметра $_GET['token'] в адресной строке, то залогиненый пользователь увидит форму регистрации. Логично что для такого случая нужно делать проверку и вбрасывать исключения или делать ридерект, но это приведет к еще большей копипасте.
Думаю, можно как-то решить эту проблему. Я посмотрел контроллер регистрации и там можно полностью избавиться от копипасты строк кода.
> Вконтакте, Фейсбуке и Твиттере почти для каждого поля своя форма редактирования. У них, наверно, для этого отдельный от регистрации контроллер и отдельный валидатор?
Своя форма для каждого поля - это как? А, в смысле, можно отдельно редактировать имя, фамилию и тд? Ну, у них там просто сделано по-другому. Не знаю, отдельный ли контроллер, я бы сделал один контроллер, который проверяет имя поля и в зависимости от этого что-то меняет.
-----------
Вместо того, чтобы копипастить условие в if:
> if (Helper::validCSRFtoken($_GET['token']) and $this->loginAction->isLoggedIn()) {
Можно завести булеву переменную $isEdit:
$isEdit = $this->loginAction->isLoggedIn();
И передавать ее куда надо, например:
$errors = $this->validations->validRegisterStudentForm($registerStudentForm, $isEdit);
> if ($registerStudentForm->getPassword() != "") {
> $registerStudentForm->setStudentPassword();
Вообще, это можно было бы делать в классе формы в методе fillDataFromArray
>if (Helper::validCSRFtoken($_GET['token']) and $this->loginAction->isLoggedIn()) {
> $student = $this->studentGateway->getStudentByСolumn('id', $_COOKIE['id']);
Вот здесь странно. Если у тебя есть сервис авторизации и ты через него проверяешь залогиненность, то разве не логично через него же и получать текущего пользователя? Почему у тебя проверка залогиненности в одном месте, а получение пользователя - в другом?
Насчет XSRF, у тебя при несовпадении токена делается регистрация. Но правильнее вообще не обрабатывать форму. А то злоумышленник может от имени пользователя регистрировать аккаунты.
А, еще, я думал, loginAction это сервис, а это контроллер. Тогда это странный код:
> $this->loginAction->isLoggedIn()
ты пытаешься использовать контроллер как сервис, но это в общем плохая идея, так как контроллер не для этого. Он для обработки запроса пользователя. В данном случае он ничего не обрабатывает и используется не по назначению.
Более того, проверяешь авторизацию ты контроллером, а залогинаваешь пользователя в классе со странным названием StudentCookies.
По моему это нелогично, что код, отвечающий за авторизацию разбросан по всему приложению. Мне кажется, логичнее сделать единый сервис авторизации, который умеет:
- залогинивать пользователя (ставить нужные куки)
- разлогинивать
- проверять залогинен ли пользователь
Это позволит убрать все, что связано с авторизационными куками, в один класс.
Проверка CSRF сделана неправильно.
https://github.com/someApprentice/Students/blob/master/app/Model/Helper/Helper.php#L15
> if ($token == $_COOKIE['token']) {
Получается если токен пустой и в куках пусто, то проверка сработает. Более того, у тебя тут нестрогое сравнение через == вместо === что добавляет шансы на ложные совпадения.
Далее, нет логики в наследовании хелперов. Вот у тебя есть Helper и есть LoginHelper, который его наследует. Непонятно, а зачем? Там все равно почти все методы статические. Точнее, часть статическая, а часть нет, и почему, непонятно.
Непонятно, зачем нужен этот пустой класс: https://github.com/someApprentice/Students/blob/master/app/Model/Helper/RegistrationHelper.php
Ты вызываешь статически не-статический метод Helper::validCSRFtoken
https://github.com/someApprentice/Students/blob/master/app/init.php#L21
> $config = parse_ini_file('config.ini');
Тут используется относительное имя файла. Это ненадежно, так как правильно ли сработает код, зависит от того, какая директория текущая (прочти-ка про текущую директорию https://ru.wikipedia.org/wiki/Рабочий_каталог )
https://github.com/someApprentice/Students/blob/master/app/Model/Cookies/StudentCookies.php
Тут по моему ты радикально все переусложнил. По моему, установку кук можно было просто сделать 2 строчками setcookie и незачем ради этого было городить интерфейсы. Также, ты указал для кук в качестве домена 'localhost' и получается что на другом домене твое приложение не будет работать (не будет ставить куки).
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Search.php#L11
> if (is_scalar($query)) {
А если передан неправильные данные, то что? Притворимся что все в порядке? Не лучше ли исключение выбрасывать?
https://github.com/someApprentice/Students/tree/master/app/Model/Entity
Я думаю, формы лучше было бы конечно вынести в отдельную папку.
В студенте, непонятно, а зачем у тебя там и токен, и пароль? Если ты исплоьзуешь вход по паролю, то токен по моему не нужен.
https://github.com/someApprentice/Students/blob/master/app/Model/Gateway/TableDataGateway.php#L15
Непонятно что в базовом классе делается функция adduser. Разве она не в StudentTDG должна быть?
В классах валидации наследование реализовано неправильно. Вот, как я понимаю:
- есть базовый класс валидации с общими методами вроде "провеhить длину", не привязанный ни к студенту, ни к формам
- есть его наследники: класс для валидации стдента, класс для валидации формы регистрации
Но у тебя это нарушается. В базовом классе зачем-то есть константы (которые относятся к модели студента, а не к валидации), базовый класс через конструктор получает StudentGateway, там есть метод isGenderInvalid($gender), который предназначен только для студента, там же есть метод validateLoginStudentForm.
Нужно это исправить. Либо у тебя есть общий базовый класс, не завязанный на студента, либо, получается, надо делать вместо двух классов один.
И наконец, в некоторых местах, ты обращаешься к массивам вроде $_GET или $_COOKIE, не проверяя перед этим, есть ли в них такой элемент:
Helper::redirect($_GET['go']);
Но ведь он может и отсутствовать. Лучше использовать функцию, вроде $this->getQuery('go'), которая делает нужную проверку. Редирект, кстати, я подумал, логичнее было бы поместить в базовый класс контроллера. Тогда редиректить можно будет только из контроллеров.
Вместо include __DIR__ . '/../../templates/registration.phtml'; лучше было бы сделать метод $this->render('templates/registration.phtml')
В общем, как всегда, я тут написал много замечаний, может даже немного запутанно, так что, если что, задавай уточняющие вопросы.
>а бекслеш пишется один раз (например: \d). Писать ^ и $ для привязки выражения к краям не требуется.
Это все различия с PCRE? Я не нагуглил больше. Для получения регулярок из валидатора в шаблон сделал так:
https://github.com/applejacky/students/blob/master/app/Lib/StudentValidator.php#L87
2. Подсветка найденного реализована вот так:
https://github.com/applejacky/students/blob/master/app/functions.php#L11
В шаблонах вызывается так: https://github.com/applejacky/students/blob/master/app/View/student/index.phtml#L44
Так пойдёт или я схалтурил и нужно писать какой-то сложный парсер массива объектов Student, проверяя свойства объекта (только определённые) на соответствие поиску?
3. Нагуглил ещё Laravel Auth Scaffold. Там роуты, связанные с аутентификацией, выглядят так:
$this->get('login', 'Auth\AuthController@showLoginForm');
$this->post('login', 'Auth\AuthController@login');
$this->get('logout', 'Auth\AuthController@logout');
$this->get('register', 'Auth\AuthController@showRegistrationForm');
$this->post('register', 'Auth\AuthController@register');
И всё-таки я склонился именно к такому варианту и у себя:
https://github.com/applejacky/students/blob/master/app/routes.php
Это только в Laravel так или в некоторых других фреймворках тоже?
4. Насколько хорошо нужно знать Bash джуну?
Здесь вбрасывали ссылку на задание по SF2, там в конце было указано написать скрипт развёртывания проекта на локалхосте. Попытался сделать для студентов такой:
https://github.com/applejacky/students/blob/master/util/deploy_to_localhost.sh
И сложилось впечатление, что Bash это язык вообще не для людей. Хочется верить, что это всё-таки я плохо Bash изучил и на нём можно писать по-человечески. Пока вот что обнаружил:
- Функции могут возвращать только код операции. Всё остальное нужно выплёвать в stdout с помощью echo.
- Все переменные по умолчанию глобальные, поэтому передавая переменную в функцию, я, по сути, передаю туда глобальную переменную, которая и так была бы доступна для функции. Думаю, что для читаемости, всё-таки стоит явно передавать параметры?
- В определении функции не указываются названия параметров. Для того, чтобы разобраться в каше из $1, $2, $3 нужно искать вызов этой функции или костылями присваивать значения параметров новым переменным: https://github.com/applejacky/students/blob/master/util/deploy_to_localhost.sh#L49
- В функции нельзя полностью выйти из сценария, так как функция это сабшелл и выход из неё возвращает в родительский. Нужно делать костыль с убийством процесса для скрипта:
https://github.com/applejacky/students/blob/master/util/deploy_to_localhost.sh#L18
Молчу про исключения и наркоманские диалекты регулярок в sed и grep.
5. Платина: куда это халявно задеплоить? На heroku используется postgresql; разбираюсь с openshift, но там сейчас какие-то глюки с регистрацией.
6. Что должен уметь файлообменник, чтобы его можно было отдавать тебе на ревью? Стоит ли ради древовидных комментариев брать ORM или хватит мапперов и паттернов для работы с древовидными структурами, описанными в твоей пасте?
>а бекслеш пишется один раз (например: \d). Писать ^ и $ для привязки выражения к краям не требуется.
Это все различия с PCRE? Я не нагуглил больше. Для получения регулярок из валидатора в шаблон сделал так:
https://github.com/applejacky/students/blob/master/app/Lib/StudentValidator.php#L87
2. Подсветка найденного реализована вот так:
https://github.com/applejacky/students/blob/master/app/functions.php#L11
В шаблонах вызывается так: https://github.com/applejacky/students/blob/master/app/View/student/index.phtml#L44
Так пойдёт или я схалтурил и нужно писать какой-то сложный парсер массива объектов Student, проверяя свойства объекта (только определённые) на соответствие поиску?
3. Нагуглил ещё Laravel Auth Scaffold. Там роуты, связанные с аутентификацией, выглядят так:
$this->get('login', 'Auth\AuthController@showLoginForm');
$this->post('login', 'Auth\AuthController@login');
$this->get('logout', 'Auth\AuthController@logout');
$this->get('register', 'Auth\AuthController@showRegistrationForm');
$this->post('register', 'Auth\AuthController@register');
И всё-таки я склонился именно к такому варианту и у себя:
https://github.com/applejacky/students/blob/master/app/routes.php
Это только в Laravel так или в некоторых других фреймворках тоже?
4. Насколько хорошо нужно знать Bash джуну?
Здесь вбрасывали ссылку на задание по SF2, там в конце было указано написать скрипт развёртывания проекта на локалхосте. Попытался сделать для студентов такой:
https://github.com/applejacky/students/blob/master/util/deploy_to_localhost.sh
И сложилось впечатление, что Bash это язык вообще не для людей. Хочется верить, что это всё-таки я плохо Bash изучил и на нём можно писать по-человечески. Пока вот что обнаружил:
- Функции могут возвращать только код операции. Всё остальное нужно выплёвать в stdout с помощью echo.
- Все переменные по умолчанию глобальные, поэтому передавая переменную в функцию, я, по сути, передаю туда глобальную переменную, которая и так была бы доступна для функции. Думаю, что для читаемости, всё-таки стоит явно передавать параметры?
- В определении функции не указываются названия параметров. Для того, чтобы разобраться в каше из $1, $2, $3 нужно искать вызов этой функции или костылями присваивать значения параметров новым переменным: https://github.com/applejacky/students/blob/master/util/deploy_to_localhost.sh#L49
- В функции нельзя полностью выйти из сценария, так как функция это сабшелл и выход из неё возвращает в родительский. Нужно делать костыль с убийством процесса для скрипта:
https://github.com/applejacky/students/blob/master/util/deploy_to_localhost.sh#L18
Молчу про исключения и наркоманские диалекты регулярок в sed и grep.
5. Платина: куда это халявно задеплоить? На heroku используется postgresql; разбираюсь с openshift, но там сейчас какие-то глюки с регистрацией.
6. Что должен уметь файлообменник, чтобы его можно было отдавать тебе на ревью? Стоит ли ради древовидных комментариев брать ORM или хватит мапперов и паттернов для работы с древовидными структурами, описанными в твоей пасте?
> Это все различия с PCRE? Я не нагуглил больше.
не знаю, есть ли где список различий, но я знаю, где есть описание синтаксиса.
В спецификации (https://www.w3.org/TR/html5/forms.html#the-pattern-attribute) написано:
> the attribute's value must match the JavaScript Pattern production. [ECMA262]
Используются рег. выражения из яваскрипта. Их описание лучше всего конечно прочитать в стандарте ECMA, но в более понятном виде его можно например найти тут:
- https://developer.mozilla.org/ru/docs/Web/JavaScript/Guide/Regular_Expressions
- https://learn.javascript.ru/regular-expressions-javascript
Также, в стандарте написано:
> This implies that the regular expression language used for this attribute is the same as that used in JavaScript, except that the pattern attribute is matched against the entire value, not just any subset (somewhat as if it implied a ^(?: at the start of the pattern and a )$ at the end).
Написано, что к выражению добавляются условия, привязывающие его к краям текста (то есть весь текст должен совпасть с регуляркой, а не часть).
Ну а теперь, если ты хочешь иметь список отличий, то можешь взять мануал по PCRE и по JS выражениям и составить этот список. Ну или может он где-то есть.
> convertPcreRegexToHtml5($pcreRegex)
> Это все различия с PCRE? Я не нагуглил больше.
не знаю, есть ли где список различий, но я знаю, где есть описание синтаксиса.
В спецификации (https://www.w3.org/TR/html5/forms.html#the-pattern-attribute) написано:
> the attribute's value must match the JavaScript Pattern production. [ECMA262]
Используются рег. выражения из яваскрипта. Их описание лучше всего конечно прочитать в стандарте ECMA, но в более понятном виде его можно например найти тут:
- https://developer.mozilla.org/ru/docs/Web/JavaScript/Guide/Regular_Expressions
- https://learn.javascript.ru/regular-expressions-javascript
Также, в стандарте написано:
> This implies that the regular expression language used for this attribute is the same as that used in JavaScript, except that the pattern attribute is matched against the entire value, not just any subset (somewhat as if it implied a ^(?: at the start of the pattern and a )$ at the end).
Написано, что к выражению добавляются условия, привязывающие его к краям текста (то есть весь текст должен совпасть с регуляркой, а не часть).
Ну а теперь, если ты хочешь иметь список отличий, то можешь взять мануал по PCRE и по JS выражениям и составить этот список. Ну или может он где-то есть.
> convertPcreRegexToHtml5($pcreRegex)
>>> Как передовать хендлер базы данных в метод?
Читай урок по DI https://github.com/codedokode/pasta/blob/master/arch/di.md
>>Если кратко - через конструктор.
Бро, а ты можешь привести простой пример?
То есть как я понимаю слежуеь созать класс о статиечскеим методом получения хендлера и вызывать этот метод в других методах?
> convertPcreRegexToHtml5($pcreRegex)
Ты серьезно? Одной маленькой регуляркой ты преобразуешь один синтаксис в другой? Не, это не будет работать.
> Подсветка найденного
такой вариант подходит
> Так пойдёт или я схалтурил и нужно писать какой-то сложный парсер массива объектов Student, проверяя свойства объекта (только определённые) на соответствие поиску?
это переусложнение
> Нагуглил ещё Laravel Auth Scaffold.
Имей в виду что scaffold это процесс генерации файлов с кодом по шаблону.
> Это только в Laravel так или в некоторых других фреймворках тоже?
Это их особенности. Вот как выглядят роуты в Симфони:
http://symfony.com/doc/current/components/routing/introduction.html
Там роуты описываются в YAML файле, а на его основе уже автоматически создаются нужные объекты. Плюс, для оптимизации все это компилируется в один пхп файл.
> 4. Насколько хорошо нужно знать Bash джуну?
уметь запускать команды и может написать простой скрипт.
> там в конце было указано написать скрипт развёртывания проекта на локалхосте. Попытался сделать для студентов такой:
Ну, у тебя и алгоритм относительно сложный. Баш лучше подходит для более простых вещей, когда надо просто вызывать несколько команд подряд.
Вообще, есть системы вроде ansible, которые позволяют разворачивать проект на удаленных машинах, но там по сути будет то же самое, только в еще более неудобном синтаксисе. Они незаменимы, когда у тебя много серверов, а когда один, не знаю, есть ли смысл.
Плюс, он почему-то работает из-под рута, что опасно, чуть что не так и легко разломать систему.
Вместо проверки наличия гита можно было наверно просто написать apt-get install git mysql compser
Зачем-то прописывает записи в hosts. А на продакшене ты тоже будешь адрес домена на 127.0.0.1 направлять?
> function die {
> kill $$
Ой, как все сложно и запутанно.
> echo "Type dbname: "
> read dbname;
Это же неудобно, надо руками вводить все эти параметры каждый раз. Это не автоматизация, а что-то противоположное.
Проще сказать пользователю отредактировать конфиг или предоставить файл локального конфига.
И кстати, твой скрипт не такой и универсальный. В разных дистрибутивах линукса конфиги Апача могут быть организованы по-разному и храниться в разных папках.
И кстати у тебя не везде стоят кавычки, так что скрипт сломается если в пути к папке есть пробелы. Экранирование это проблема баша, так как там используется принцип "замены и подстановки" при интерпретации команд.
> https://github.com/applejacky/students/blob/master/app/Model/Student.php#L21
Тут слишком много аргументов. Нет смысла делать больше 4-5 аргументов у функции, так как в них не разобраться. В твоем случае можно либо вообще убрать аргументы либо передавать массив.
> Здесь вбрасывали ссылку на задание по SF2, там в конце было указано написать скрипт развёртывания проекта на локалхосте.
Думаю, имелось в виду что-то проще.
> Функции могут возвращать только код операции. Всё остальное нужно выплёвать в stdout с помощью echo.
А они не имеют доступа к каким-нибудь глобальным переменным? Можно через них вернуть. Вообще, баш скрипты - это просто способ записать последовательность команд, что-то сложное там писать не стоит.
> Платина: куда это халявно задеплоить? На heroku используется postgresql; разбираюсь с openshift, но там сейчас какие-то глюки с регистрацией.
Есть c9.io, на нем дают рутовый доступ, но сайт доступен только пока IDE открыта в браузере. Были когда-то free vps на каком-то западном freewebhost или как-то так, попробуй погуглить. Есть бесплатные хостинги, западные даже без рекламы, но там не дают рута, ssh, и менять настройки Апача.
> 6. Что должен уметь файлообменник, чтобы его можно было отдавать тебе на ревью? Стоит ли ради древовидных комментариев брать ORM или хватит мапперов и паттернов для работы с древовидными структурами, описанными в твоей пасте?
Не знаю, что хочешь то и делай, я наверно в любом случае проверю. Чем больше, тем лучше. Насчет ОРМ - сам решай, можно сделать и на мапперах + materialized path. Комментарии хорошо бы сделать с поддержкой аякса и вообще как-то продуманно сделать.
> convertPcreRegexToHtml5($pcreRegex)
Ты серьезно? Одной маленькой регуляркой ты преобразуешь один синтаксис в другой? Не, это не будет работать.
> Подсветка найденного
такой вариант подходит
> Так пойдёт или я схалтурил и нужно писать какой-то сложный парсер массива объектов Student, проверяя свойства объекта (только определённые) на соответствие поиску?
это переусложнение
> Нагуглил ещё Laravel Auth Scaffold.
Имей в виду что scaffold это процесс генерации файлов с кодом по шаблону.
> Это только в Laravel так или в некоторых других фреймворках тоже?
Это их особенности. Вот как выглядят роуты в Симфони:
http://symfony.com/doc/current/components/routing/introduction.html
Там роуты описываются в YAML файле, а на его основе уже автоматически создаются нужные объекты. Плюс, для оптимизации все это компилируется в один пхп файл.
> 4. Насколько хорошо нужно знать Bash джуну?
уметь запускать команды и может написать простой скрипт.
> там в конце было указано написать скрипт развёртывания проекта на локалхосте. Попытался сделать для студентов такой:
Ну, у тебя и алгоритм относительно сложный. Баш лучше подходит для более простых вещей, когда надо просто вызывать несколько команд подряд.
Вообще, есть системы вроде ansible, которые позволяют разворачивать проект на удаленных машинах, но там по сути будет то же самое, только в еще более неудобном синтаксисе. Они незаменимы, когда у тебя много серверов, а когда один, не знаю, есть ли смысл.
Плюс, он почему-то работает из-под рута, что опасно, чуть что не так и легко разломать систему.
Вместо проверки наличия гита можно было наверно просто написать apt-get install git mysql compser
Зачем-то прописывает записи в hosts. А на продакшене ты тоже будешь адрес домена на 127.0.0.1 направлять?
> function die {
> kill $$
Ой, как все сложно и запутанно.
> echo "Type dbname: "
> read dbname;
Это же неудобно, надо руками вводить все эти параметры каждый раз. Это не автоматизация, а что-то противоположное.
Проще сказать пользователю отредактировать конфиг или предоставить файл локального конфига.
И кстати, твой скрипт не такой и универсальный. В разных дистрибутивах линукса конфиги Апача могут быть организованы по-разному и храниться в разных папках.
И кстати у тебя не везде стоят кавычки, так что скрипт сломается если в пути к папке есть пробелы. Экранирование это проблема баша, так как там используется принцип "замены и подстановки" при интерпретации команд.
> https://github.com/applejacky/students/blob/master/app/Model/Student.php#L21
Тут слишком много аргументов. Нет смысла делать больше 4-5 аргументов у функции, так как в них не разобраться. В твоем случае можно либо вообще убрать аргументы либо передавать массив.
> Здесь вбрасывали ссылку на задание по SF2, там в конце было указано написать скрипт развёртывания проекта на локалхосте.
Думаю, имелось в виду что-то проще.
> Функции могут возвращать только код операции. Всё остальное нужно выплёвать в stdout с помощью echo.
А они не имеют доступа к каким-нибудь глобальным переменным? Можно через них вернуть. Вообще, баш скрипты - это просто способ записать последовательность команд, что-то сложное там писать не стоит.
> Платина: куда это халявно задеплоить? На heroku используется postgresql; разбираюсь с openshift, но там сейчас какие-то глюки с регистрацией.
Есть c9.io, на нем дают рутовый доступ, но сайт доступен только пока IDE открыта в браузере. Были когда-то free vps на каком-то западном freewebhost или как-то так, попробуй погуглить. Есть бесплатные хостинги, западные даже без рекламы, но там не дают рута, ssh, и менять настройки Апача.
> 6. Что должен уметь файлообменник, чтобы его можно было отдавать тебе на ревью? Стоит ли ради древовидных комментариев брать ORM или хватит мапперов и паттернов для работы с древовидными структурами, описанными в твоей пасте?
Не знаю, что хочешь то и делай, я наверно в любом случае проверю. Чем больше, тем лучше. Насчет ОРМ - сам решай, можно сделать и на мапперах + materialized path. Комментарии хорошо бы сделать с поддержкой аякса и вообще как-то продуманно сделать.
Ты прочел урок? для начала прочти и разберись в том, что там написано. Вообще, у меня появилось ощущение что ты любишь при изучении пропускать некоторые темы, где изучают основы. Это плохая идея.
> То есть как я понимаю слежуеь созать класс о статиечскеим методом получения хендлера и вызывать этот метод в других методах?
То есть ты не прочел урок, потому что там написано что статические методы это плохо.
Если у тебя ActveRecord то он плохо работает c DI. Разве что только так (тогда DI можно использовать с $newsTable):
$news = $newsTable->create();
А если ты пишешь
$news = new News();
То разумеется DI тут нет и придется какими-то плохими методами вроде статических методов получать нужные зависимости.
>>773736
Ну я надеюсь что он прочитает где-нибудь про apt
>во-первых, CGI это несерьезно. Он делался скорее для каких-то любительских вещей. Сейчас используют либо php как модуль Апача, либо php-fpm, взаимодействующий с сервером через FCGI.
Мне помимо 7.0, который как модуль апача стоит, нужны 5.2 и 5.3, для запуска мамонтов. Вот таким способом и запускаю, лучше вариантов не нашел.
>Ты их компилируешь (или устанавливаешь пакетным менеджером, или качаешь готовые) и подключаешь в php.ini.
А вот как мне например pecl'ом получить gd.so с поддержкой freetype? Как-то это все сложно и в гугле очень скупо на эту тему.
CGI очень неэффективен. В твоем случае гораздо логичнее либо использовать php-fpm либо несколько Апачей с разными версиями пхп. И по моему даже есть какая-то штука, чтобы один Апач мог работать с разными версиями.
> А вот как мне например pecl'ом получить gd.so с поддержкой freetype? Как-то это все сложно и в гугле очень скупо на эту тему.
В мануале написано что gd входит в состав php. Следовательно, нужная поддержка включается флагами (наверно в ./configure) при сборке php.
Более того, ты не прочел даже мануал который я дал.
http://php.net/manual/ru/image.installation.php
Тут написано про freetype. Ну и никто тебе не мешает сделать ./configure --help
>> придется какими-то плохими методами вроде статических методов получать нужные зависимости.
так а как следует поступать? и что делает методы, о которых ты пишешь _плохими_? Это объективные данные?
да тут не с апта надо начинаит, а надо с... а бог знает, с чего.
Вот там выше советовали хендбук генту. Как бы жутко это ни звучало, но это, наверное, неплохое решение.
Ну, и практика, посоны. Без практики...
Сойдет, хотя я бы написал как "[^"]*?" - так надежнее по моему
>>753870
увы, не знаю, надо смотреть документацию
>>755563
Сейчас все пишут на фреймоврках, а там сплошной ООП. Точную цифру я не назову, думаю что несколько тысяч строк.
>>755864
Не знаю, документацию может быть, но не знаю, есть ли там то, что нужно.
>>756017
композер ставит все в vendor в текущую папку + держит кеш домашней директории пользователя
>>756490
Объекты животных, например:
$animal = new Cat;
$animals[] = $animal;
>>756755
Поищи.
>>757866
не понял вопрос. Что значит "забивалось в html-овский insert"?
Сойдет, хотя я бы написал как "[^"]*?" - так надежнее по моему
>>753870
увы, не знаю, надо смотреть документацию
>>755563
Сейчас все пишут на фреймоврках, а там сплошной ООП. Точную цифру я не назову, думаю что несколько тысяч строк.
>>755864
Не знаю, документацию может быть, но не знаю, есть ли там то, что нужно.
>>756017
композер ставит все в vendor в текущую папку + держит кеш домашней директории пользователя
>>756490
Объекты животных, например:
$animal = new Cat;
$animals[] = $animal;
>>756755
Поищи.
>>757866
не понял вопрос. Что значит "забивалось в html-овский insert"?
> public function addEmployee($employee)
> {
> if(class_exists($employee[0]))
Неправильно тут сделано. Ты возлагаешь на департамент обязанность создавать объекты, но это не его задача. Эта функция должна принимать уже готовый объект Employee.
>public function fireEmployee($employee)
> $employeeKey = array_search($employee, $this->employees);
почитай мануал по array-search. У тебя он может уволить не того работника, что передан. Там есть дополнительный параметр.
> public function countEmployees($position)
Вообще эта функция смотрится очень специализированно (заточена под антикризисную задачу), а не универсальной и что-то мне кажется, что она должна быть не в департаменте. Также, она смотрится переусложненной. Чтобы отобрать N% работников определенной професссии, проще взять всех работников, соответствующих условиям, отсорттировать по рангу и взять первые N% из списка.
> public function findEmployee($position, $boss, $rank)
Для поиска по произвольному критерию удобнее использовать анонимные функции:
find(function ($e) { return $e->rank > 1; });
Для департамента не определен __clone, потому он при клонировании не создает клоны работников, а сохраняет ссылки на старые объекты.
> $employee->boss = true;
> $i+=1;
> }
> if(max(array_flip($analystQuantity)) > $employee->rank && $employee->boss == true)
> $employee->boss = false;
Смена босса должна делаться методом в департаменте, а не вот такими вот костылями. Ты вместо выделения отдельных операций в методы пишешь код сплошной стеной из отдельных команд.
Код лучше было бы написать так:
- найти босса
- если он не аналитик, то найти аналитика высшего ранга
- назначить его боссом
То есть более прямолинейный алгоритм, а не сложный цикл, где надо долго проверять согласованность всех условий и гадать, а верно ли он работает.
> Опять возникла проблема с клонированием: клоны перезаписывают оригинал с каждым использованием. И почему-то количество работников в норме, а вот зарплата, кофе, страницы меняются.
Потому что ты не клонируешь работников вместе с департаментом
> public function addEmployee($employee)
> {
> if(class_exists($employee[0]))
Неправильно тут сделано. Ты возлагаешь на департамент обязанность создавать объекты, но это не его задача. Эта функция должна принимать уже готовый объект Employee.
>public function fireEmployee($employee)
> $employeeKey = array_search($employee, $this->employees);
почитай мануал по array-search. У тебя он может уволить не того работника, что передан. Там есть дополнительный параметр.
> public function countEmployees($position)
Вообще эта функция смотрится очень специализированно (заточена под антикризисную задачу), а не универсальной и что-то мне кажется, что она должна быть не в департаменте. Также, она смотрится переусложненной. Чтобы отобрать N% работников определенной професссии, проще взять всех работников, соответствующих условиям, отсорттировать по рангу и взять первые N% из списка.
> public function findEmployee($position, $boss, $rank)
Для поиска по произвольному критерию удобнее использовать анонимные функции:
find(function ($e) { return $e->rank > 1; });
Для департамента не определен __clone, потому он при клонировании не создает клоны работников, а сохраняет ссылки на старые объекты.
> $employee->boss = true;
> $i+=1;
> }
> if(max(array_flip($analystQuantity)) > $employee->rank && $employee->boss == true)
> $employee->boss = false;
Смена босса должна делаться методом в департаменте, а не вот такими вот костылями. Ты вместо выделения отдельных операций в методы пишешь код сплошной стеной из отдельных команд.
Код лучше было бы написать так:
- найти босса
- если он не аналитик, то найти аналитика высшего ранга
- назначить его боссом
То есть более прямолинейный алгоритм, а не сложный цикл, где надо долго проверять согласованность всех условий и гадать, а верно ли он работает.
> Опять возникла проблема с клонированием: клоны перезаписывают оригинал с каждым использованием. И почему-то количество работников в норме, а вот зарплата, кофе, страницы меняются.
Потому что ты не клонируешь работников вместе с департаментом
Можно в конфиге в файле, если он один раз туда впишется и больше не используется. Также, доступ можно закрыть через http-авторизацию.
>>762026
Надо читать мануалы. learn.javascript.ru, MDN, caniuse и так далее.
Ну и ты сам ведь написал, в чем различия:
> Да, понятно, что нужны фолбеки для определения события для разных браузеров, поддержка скролла с клавы и на мобилах, заворачивание в плагин и т. д.
Попробуй все это реализовать, при этом соблюдая приницпы вроде:
- не должно быть копипасты
- не должно быть длинных полотен кода в одной функции
- разные опции должны настраиваться
- поддержка разных версий браузеров
И может у тебя выйдет примерно то же.
Твой код по моему вообще некорректный, так как ты думаешь что один поворот колеса мыши генерирует одно событие. По моему, это может быть и не так. У меня например есть скролл через тачпад и мне кажется, он генерирует несколько событий.
Или например у тебя есть такая штука:
> [id^="page"]
получается если на странице есть такие id то твой код на них повлияет.
Вообще, его код выглядит каким-то переусложненным. Мне он не особо нравится. А твой выглядит вообще непригодным для реального использования.
>>762131
Если нет АПИ то значит нельзя. Если это программа для управления множеством аккаунтов то такое у нас не обсуждается.
Можно в конфиге в файле, если он один раз туда впишется и больше не используется. Также, доступ можно закрыть через http-авторизацию.
>>762026
Надо читать мануалы. learn.javascript.ru, MDN, caniuse и так далее.
Ну и ты сам ведь написал, в чем различия:
> Да, понятно, что нужны фолбеки для определения события для разных браузеров, поддержка скролла с клавы и на мобилах, заворачивание в плагин и т. д.
Попробуй все это реализовать, при этом соблюдая приницпы вроде:
- не должно быть копипасты
- не должно быть длинных полотен кода в одной функции
- разные опции должны настраиваться
- поддержка разных версий браузеров
И может у тебя выйдет примерно то же.
Твой код по моему вообще некорректный, так как ты думаешь что один поворот колеса мыши генерирует одно событие. По моему, это может быть и не так. У меня например есть скролл через тачпад и мне кажется, он генерирует несколько событий.
Или например у тебя есть такая штука:
> [id^="page"]
получается если на странице есть такие id то твой код на них повлияет.
Вообще, его код выглядит каким-то переусложненным. Мне он не особо нравится. А твой выглядит вообще непригодным для реального использования.
>>762131
Если нет АПИ то значит нельзя. Если это программа для управления множеством аккаунтов то такое у нас не обсуждается.
Оставь в покое скролл. Сколько не видел решений с изменением скролла - все жутко неудобные и меняют функциональность в худшую сторону, причем взамен ничего не дают. Браузеры-операционки-разрешения-драйверы мыши не у всех одинаковые, и то, что у тебя нормально выглядит, на другом компе будет крайне уебищно смотреться и мешать навигации по сайту. С этим то же самое. Руки бы отрывал, кто в продакшн менялку скролла ставит. Что касается JS, то он там довольно стандартный, любая книга по JS тебя такому научит. Причем автор явно тоже не заморачивался чтением книг по JS, у него куски кода там просто тупо скопипасчены из stackoverflow c указанием откуда вставлял. Размер большой из-за обратной совместимости с разными браузерами, которой в твоем коде нет, плюс всяческие настройки и хаки.
>не пойму как писать такой профессионально выглядящий нихера непонятный код вместо
Это как раз признак плохого кода. Функции и переменные названы как попало, кругом навставлены куски всего подряд, связи проследить сразу трудно, jsdoc или внятных комментов нет, про solid автор судя по всему вообще не слышал, зачем-то написано, что pure javascript, хотя используется jquery. Писать такой код научиться не очень сложно, открой руководства по Javascript, раздел ООП и колбэки, и доку по Jquery/плагинам к нему, остальное догугли по именам функций.
Сейчас у меня Uncaught Error: Access to undeclared static property: Container::$config
>>Неправильно тут сделано. Ты возлагаешь на департамент обязанность создавать объекты, но это не его задача.
Сделал отдел кадров и унаследовал от него департамент
>>почитай мануал по array-search. У тебя он может уволить не того работника, что передан. Там есть дополнительный параметр.
Ты хочешь сказать, что array_search выдает ключ первого попавшегося подходящего значения? Если так, то я передаю ему объект и он ищет его в массиве и все норм вроде.
>>Для поиска по произвольному критерию удобнее использовать анонимные функции
Я задумывался над тем, чтобы использовать их в этом методе с самого начала, но мне кажется, что это будет выглядеть сложнее.
>>Для департамента не определен __clone
исправил
>>Смена босса должна делаться методом в департаменте, а не вот такими вот костылями.
исправил
Есть вопрос по Кошкам-Мышкам:
Как найти самую ближнюю кошку? Нужно как-то сравнить высоту и ширину на поле, но я пока не понял как. Попробовал воспользоваться сложением высоты и ширины и сравнением этой суммы мышки и кошки, но в некоторых случаях этот способ дает неверный результат.
Вектор http://ideone.com/H5DCfT
Набросок Кошек-Мышек http://ideone.com/f1aQaw
Там по моему есть __callStatic, но я хочу предупредить что ты занимаешься плохими вещами:
- 2 контейнера это плохо
- статические методы это плохо в данном случае
- писать код внутри строки это очень плохо
>>774244
> Сделал отдел кадров и унаследовал от него департамент
Нельзя наследовать что угодно от чего угодно. Наследование - это отношение "A является B" (is-a relation). Например: "Кошка является Животным" или "Танк является СредствомПередвижения".
Департамент - это не улучшенная версия отдела кадров.
> Ты хочешь сказать, что array_search выдает ключ первого попавшегося подходящего значения? Если так, то я передаю ему объект и он ищет его в массиве и все норм вроде.
Ты читал мануал? Есть 2 способа сравнения объектов: по содержимому полей и по идентичности и почти всегда нужно последнее.
> Я задумывался над тем, чтобы использовать их в этом методе с самого начала, но мне кажется, что это будет выглядеть сложнее.
А ты попробуй - скорее будет проще
> Как найти самую ближнюю кошку?
Взять список кошек, найти в цикле расстояния до каждой и взять ту, до которой расстояние меньшее.
Там правда есть два разных расстояни:
- декартово - вычисляется через корень - реальное расстояние между точками на плоскости
- "игровое" - минимальное число ходов, нужное чтобы дойти из точки A в B.
Например, если кошка и мышка стоят по диагонали друг от друга, то декартово расстояние = корень из 2 ~ 1.41, а игровое - 1, так как кошка может за 1 ход дойти до клеточки с мышкой.
Там по моему есть __callStatic, но я хочу предупредить что ты занимаешься плохими вещами:
- 2 контейнера это плохо
- статические методы это плохо в данном случае
- писать код внутри строки это очень плохо
>>774244
> Сделал отдел кадров и унаследовал от него департамент
Нельзя наследовать что угодно от чего угодно. Наследование - это отношение "A является B" (is-a relation). Например: "Кошка является Животным" или "Танк является СредствомПередвижения".
Департамент - это не улучшенная версия отдела кадров.
> Ты хочешь сказать, что array_search выдает ключ первого попавшегося подходящего значения? Если так, то я передаю ему объект и он ищет его в массиве и все норм вроде.
Ты читал мануал? Есть 2 способа сравнения объектов: по содержимому полей и по идентичности и почти всегда нужно последнее.
> Я задумывался над тем, чтобы использовать их в этом методе с самого начала, но мне кажется, что это будет выглядеть сложнее.
А ты попробуй - скорее будет проще
> Как найти самую ближнюю кошку?
Взять список кошек, найти в цикле расстояния до каждой и взять ту, до которой расстояние меньшее.
Там правда есть два разных расстояни:
- декартово - вычисляется через корень - реальное расстояние между точками на плоскости
- "игровое" - минимальное число ходов, нужное чтобы дойти из точки A в B.
Например, если кошка и мышка стоят по диагонали друг от друга, то декартово расстояние = корень из 2 ~ 1.41, а игровое - 1, так как кошка может за 1 ход дойти до клеточки с мышкой.
>> if ($registerStudentForm->getPassword() != "") {
>> $registerStudentForm->setStudentPassword();
>Вообще, это можно было бы делать в классе формы в методе fillDataFromArray
Прежде чем задавать студенту хэш нужно проверить пароль на валидность. Из названии функции наверно кажется что я меня именно пароль, а не создаю хеши?
>>773648
>>if (Helper::validCSRFtoken($_GET['token']) and $this->loginAction->isLoggedIn()) {
>> $student = $this->studentGateway->getStudentByСolumn('id', $_COOKIE['id']);
>Вот здесь странно. Если у тебя есть сервис авторизации и ты через него проверяешь залогиненность, то разве не логично через него же и получать текущего пользователя? Почему у тебя проверка залогиненности в одном месте, а получение пользователя - в другом?
Не знаю, мне казалось что если есть какая-то проверка на что-то, то результат должен быть true\false и не более.
Вообще это очевидно что результат должен быть в зависимости от требований задачи, но я и представить не мог что такое где-нибудь понадобиться. То есть я думал что этот метод будет использоваться только в проверках, и в моем случае изначально после проверки на залогиненность была проверка на токен а потом уже получение пользователя.
>Можно завести булеву переменную $isEdit:
>$isEdit = $this->loginAction->isLoggedIn();
Если результат этого метода будет сущность студента, то ничего страшного если переменная $isEdit будет содержать не булевое значение? Ведь на практике это можно применить точно так же.
>>773648
>Более того, проверяешь авторизацию ты контроллером, а залогинаваешь пользователя в классе со странным названием StudentCookies.
В каком именно месте это не правильно? В месте где я перезалогиниваю пользователя после редактирования?
Вообще я залогиниваю пользователя с помощью контроллера https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L41
Просто в том месте с редактированием был баг из-за которого не выводилось сообщение об успешном редактировании, потому что контроллере залогинивания свой метод ридеректа. Я думал о том что ему нужно передавать параметр с сообщением об успешном редактировании, но мне показалось это усложнением, и я решил обойтись просто обновив куки хотя у меня были сомнения и насчет этого способа.
>>773648
>По моему это нелогично, что код, отвечающий за авторизацию разбросан по всему приложению. Мне кажется, логичнее сделать единый сервис авторизации, который умеет:
>
>- залогинивать пользователя (ставить нужные куки)
>- разлогинивать
>- проверять залогинен ли пользователь
>
>Это позволит убрать все, что связано с авторизационными куками, в один класс.
Но ведь уже есть контроллер который занимается этим. Или ты имеешь ввиду переделать так чтобы контроллер пользовался вышеперечисленными методами сервиса?
>>773648
>Далее, нет логики в наследовании хелперов. Вот у тебя есть Helper и есть LoginHelper, который его наследует. Непонятно, а зачем? Там все равно почти все методы статические. Точнее, часть статическая, а часть нет, и почему, непонятно.
Раньше LoginHelper наследовался чтобы избавиться от копипасты метода перенаправления.
А разве в одном классе все методы должны быть либо статические либо не статические? Почему?
>>773648
>Также, ты указал для кук в качестве домена 'localhost' и получается что на другом домене твое приложение не будет работать (не будет ставить куки).
У меня без этого куки не работали почему-то - при разлогинивании не удалялись и при повторном залогинивании создавалась еще одна копия. Сейчас снова поставил этот параметр на null и все работает. Странно.
>>773648
>В студенте, непонятно, а зачем у тебя там и токен, и пароль? Если ты исплоьзуешь вход по паролю, то токен по моему не нужен.
Так токен нужен же для защиты от CSRF. Его удобно держать в студенте, потому что мы создаем куки на его основе.
>> if ($registerStudentForm->getPassword() != "") {
>> $registerStudentForm->setStudentPassword();
>Вообще, это можно было бы делать в классе формы в методе fillDataFromArray
Прежде чем задавать студенту хэш нужно проверить пароль на валидность. Из названии функции наверно кажется что я меня именно пароль, а не создаю хеши?
>>773648
>>if (Helper::validCSRFtoken($_GET['token']) and $this->loginAction->isLoggedIn()) {
>> $student = $this->studentGateway->getStudentByСolumn('id', $_COOKIE['id']);
>Вот здесь странно. Если у тебя есть сервис авторизации и ты через него проверяешь залогиненность, то разве не логично через него же и получать текущего пользователя? Почему у тебя проверка залогиненности в одном месте, а получение пользователя - в другом?
Не знаю, мне казалось что если есть какая-то проверка на что-то, то результат должен быть true\false и не более.
Вообще это очевидно что результат должен быть в зависимости от требований задачи, но я и представить не мог что такое где-нибудь понадобиться. То есть я думал что этот метод будет использоваться только в проверках, и в моем случае изначально после проверки на залогиненность была проверка на токен а потом уже получение пользователя.
>Можно завести булеву переменную $isEdit:
>$isEdit = $this->loginAction->isLoggedIn();
Если результат этого метода будет сущность студента, то ничего страшного если переменная $isEdit будет содержать не булевое значение? Ведь на практике это можно применить точно так же.
>>773648
>Более того, проверяешь авторизацию ты контроллером, а залогинаваешь пользователя в классе со странным названием StudentCookies.
В каком именно месте это не правильно? В месте где я перезалогиниваю пользователя после редактирования?
Вообще я залогиниваю пользователя с помощью контроллера https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L41
Просто в том месте с редактированием был баг из-за которого не выводилось сообщение об успешном редактировании, потому что контроллере залогинивания свой метод ридеректа. Я думал о том что ему нужно передавать параметр с сообщением об успешном редактировании, но мне показалось это усложнением, и я решил обойтись просто обновив куки хотя у меня были сомнения и насчет этого способа.
>>773648
>По моему это нелогично, что код, отвечающий за авторизацию разбросан по всему приложению. Мне кажется, логичнее сделать единый сервис авторизации, который умеет:
>
>- залогинивать пользователя (ставить нужные куки)
>- разлогинивать
>- проверять залогинен ли пользователь
>
>Это позволит убрать все, что связано с авторизационными куками, в один класс.
Но ведь уже есть контроллер который занимается этим. Или ты имеешь ввиду переделать так чтобы контроллер пользовался вышеперечисленными методами сервиса?
>>773648
>Далее, нет логики в наследовании хелперов. Вот у тебя есть Helper и есть LoginHelper, который его наследует. Непонятно, а зачем? Там все равно почти все методы статические. Точнее, часть статическая, а часть нет, и почему, непонятно.
Раньше LoginHelper наследовался чтобы избавиться от копипасты метода перенаправления.
А разве в одном классе все методы должны быть либо статические либо не статические? Почему?
>>773648
>Также, ты указал для кук в качестве домена 'localhost' и получается что на другом домене твое приложение не будет работать (не будет ставить куки).
У меня без этого куки не работали почему-то - при разлогинивании не удалялись и при повторном залогинивании создавалась еще одна копия. Сейчас снова поставил этот параметр на null и все работает. Странно.
>>773648
>В студенте, непонятно, а зачем у тебя там и токен, и пароль? Если ты исплоьзуешь вход по паролю, то токен по моему не нужен.
Так токен нужен же для защиты от CSRF. Его удобно держать в студенте, потому что мы создаем куки на его основе.
Установил MySQL и Apache.
Не получается установить РНР.
Вот что выдаёт
<----
Делают вот по тому, что пацанчик описал вот тут: https://codebeer.ru/ustanovka-php-7-v-debian-8/
Скачиваем и распаковываем исходники PHP 7.0.4:
>wget http://de1.php.net/get/php-7.0.4.tar.bz2/from/this/mirror -O php-7.0.4.tar.bz2
>tar -xvjf php-7.0.4.tar.bz2
Переходим в каталог с исходниками PHP 7:
>cd php-7.0.4
Для того чтобы установить PHP 7, нам необходимо выполнить компиляцию из исходников.
./buildconf --force
CONFIGURE_STRING="--prefix=/usr/local/php-fpm
--enable-fpm
--enable-mysqlnd
--enable-mbstring
--disable-pdo
--disable-phar
--with-config-file-scan-dir=/usr/local/php-fpm/etc/conf.d
--with-curl
--with-gd
--with-fpm-user=www-data
--with-fpm-group=www-data
--with-mysql-sock=/var/run/mysqld/mysqld.sock
--with-mysqli=mysqlnd
--with-zlib
--without-sqlite3
--without-pdo-sqlite"
Далее делаю
./configure $CONFIGURE_STRING
make && make install
и получаю вот то, что на скриншоте.
Какой permission denied, о чём ты несёшь?..
Спасибо, попробую это сделать тоже.
>>773121
>>773139
>>773553
Обновлю sourcelist. Но вроде проблем же не возникло с установкой Мускула и Апача, что теперь надо где указать - может, подскажете? Заранее благодарен.
Установил MySQL и Apache.
Не получается установить РНР.
Вот что выдаёт
<----
Делают вот по тому, что пацанчик описал вот тут: https://codebeer.ru/ustanovka-php-7-v-debian-8/
Скачиваем и распаковываем исходники PHP 7.0.4:
>wget http://de1.php.net/get/php-7.0.4.tar.bz2/from/this/mirror -O php-7.0.4.tar.bz2
>tar -xvjf php-7.0.4.tar.bz2
Переходим в каталог с исходниками PHP 7:
>cd php-7.0.4
Для того чтобы установить PHP 7, нам необходимо выполнить компиляцию из исходников.
./buildconf --force
CONFIGURE_STRING="--prefix=/usr/local/php-fpm
--enable-fpm
--enable-mysqlnd
--enable-mbstring
--disable-pdo
--disable-phar
--with-config-file-scan-dir=/usr/local/php-fpm/etc/conf.d
--with-curl
--with-gd
--with-fpm-user=www-data
--with-fpm-group=www-data
--with-mysql-sock=/var/run/mysqld/mysqld.sock
--with-mysqli=mysqlnd
--with-zlib
--without-sqlite3
--without-pdo-sqlite"
Далее делаю
./configure $CONFIGURE_STRING
make && make install
и получаю вот то, что на скриншоте.
Какой permission denied, о чём ты несёшь?..
Спасибо, попробую это сделать тоже.
>>773121
>>773139
>>773553
Обновлю sourcelist. Но вроде проблем же не возникло с установкой Мускула и Апача, что теперь надо где указать - может, подскажете? Заранее благодарен.
Так и не нашёл нормальной информации для установки РНР7.
Придётся окунаться в эти экскременты доисторических животных.
Честно говоря, жалею, что вообще затеял установку Дебиан и так далее. Надо было просто искать аналоги для cmd.exe, хотя бы попробовать.
Кто-то может пояснить такой синтаксис? Почему перед аргументом конструктора какой-то непнятный bareword?
мерси
make install надо делать от рута (а вот остальное не стоит, чтобы при ошибке не разломать себе систему)
Ну подумай сам, install копирует собранные бинарники в системные директории.
Тебе стоило бы вообще с линуксом познакомиться получше, изучить структуру файловой системы, права на файлы, основные команды.
>>774700
Плохо искал. В дебиане софт всегда в стабильной ветке не новый. для новых версий надо подключать сторонний репозиторий вроде dotdeb.
(обработку ошибок для краткости опустил)
# Класс объекта для работы с БД
class Handler {
public $hostname;
public $username;
public $password;
public $databaseName;
public function __construct($hostname, $username, $password, $databaseName ) {
$this->hostname = $hostname;
$this->username = $username;
$this->password = $password;
$this->databaseName = $databaseName;
}
public function dbConnect() {
$dbh = mysqli_connect($this->hostname, $this->username, $this->password, $this->databaseName);
return $dbh;
}
#касс, описывающий таблицу users
class Users {
public $uid;
public $ulogin;
public $upass;
public $handler;
## handler передаём в конструкторе
public function __construct(Mysqli $handler) {
$this->handler = $handler;
}
# простая выборка логина по ID
public function getUserNameById($uid) {
$dbh = $this->handler;
$stmt = $dbh->prepare("SELECT ulogin, upass FROM authorise WHERE uid=?");
$stmt->bind_param("i", $uid);
$stmt->execute();
$result = $stmt->get_result();
return $result;
}
}
# код теста всего хозяйства
$dbh = new Handler("localhost", "user", "password", "db_name"); ## конечно они будут передаваться не так, а в переменных. Это для краткости
$users = new Users($dbh->dbConnect());
$user = $users->getUserNameById(2);
$user1 = $user->fetch_assoc();
echo $user1["ulogin"];
данный код работает корректно, но кошерен ли он с точки зрения правиьного программирования?
(обработку ошибок для краткости опустил)
# Класс объекта для работы с БД
class Handler {
public $hostname;
public $username;
public $password;
public $databaseName;
public function __construct($hostname, $username, $password, $databaseName ) {
$this->hostname = $hostname;
$this->username = $username;
$this->password = $password;
$this->databaseName = $databaseName;
}
public function dbConnect() {
$dbh = mysqli_connect($this->hostname, $this->username, $this->password, $this->databaseName);
return $dbh;
}
#касс, описывающий таблицу users
class Users {
public $uid;
public $ulogin;
public $upass;
public $handler;
## handler передаём в конструкторе
public function __construct(Mysqli $handler) {
$this->handler = $handler;
}
# простая выборка логина по ID
public function getUserNameById($uid) {
$dbh = $this->handler;
$stmt = $dbh->prepare("SELECT ulogin, upass FROM authorise WHERE uid=?");
$stmt->bind_param("i", $uid);
$stmt->execute();
$result = $stmt->get_result();
return $result;
}
}
# код теста всего хозяйства
$dbh = new Handler("localhost", "user", "password", "db_name"); ## конечно они будут передаваться не так, а в переменных. Это для краткости
$users = new Users($dbh->dbConnect());
$user = $users->getUserNameById(2);
$user1 = $user->fetch_assoc();
echo $user1["ulogin"];
данный код работает корректно, но кошерен ли он с точки зрения правиьного программирования?
При использовании mysqli надо после каждого действия проверять нет ли ошибок, так как сам он их никак не обрабатывает. Открой мануал по mysqli и посмотри примеры.
Если ты хочешь использовать mysqli то (тебе лично) надо прочитать как минимум введение целиком и просмотреть мануал по всем ее функциям сначала.
Класс Handler непонятно зачем нужен и вообще выглядит бесполезно.
Почему mysqli_connect, а не new mysqli?
Названия классов неудачные.
Users -> USersTable, UsersTableGateway
> $user = $users->getUserNameById(2);
> $user1 = $user->fetch_assoc();
Это неправильнро. Судя по названию, функция getUserNameById должна вернуть строку с именем. А у тебя возвращается непонятно что. Вся работа с базой данных должна быть внутри TDG а если ты возвращаешь объект mysqli то это значит что это правило не соблюдается.
Плохо что ты используешь результат в виде массива, а не сделал модель дял пользователя.
Так, отдаленно напоминает TDG.
А, еще непонятно что в классе USers делают поля $ulogin, $upass. Для чего у тебя этот класс? Что представляет его объект? Пользователя? Тогда в методе getUserNameById не должно быть аргумента $uid.
По моему ты плоховато понял суть паттерна. Объект TDG это объект, представляющий интерфейс (шлюз) к базе данных. Он не хранит внутри информацию о пользователях.
Ты же просто намешал в классе все, что придет в голову. У меня ощущение что ты слабовато понимаешь ООП. В ООП нельзя просто так сваливать в класс любые поля и методы, там есть определенная логика. Каждый класс выполняет какую-то свою задачу.
Такой if/else делать нужно, так как $search может быть пустым и тогда матчится пустая строка, на выходе каждый символ становится разделённым тегом b.
Я ковырял preg_replace_callback, но получилось ещё запутаннее.
Тупанул
Та норм всё, я тебя понял. А флагов там нет каких-то, по типу PREG_SPLIT_NO_EMPTY?
Не знаю. А первый вариант лучше был, читабельней.
Первый блин деплоя стал комом: https://secondtryphp-veryfirtsapp.rhcloud.com/
Если у кого есть желание потестить и сообщить о неисправностях - то буду вам очень благодарен.
SSH-сессия очень кастрированная (на 1-м пике список всех доступных команд), но зато бесплатно 3 постоянно доступных сайта.
>> convertPcreRegexToHtml5($pcreRegex)
> Ты серьезно? Одной маленькой регуляркой ты преобразуешь один синтаксис в другой? Не, это не будет работать.
Максимум, что я могу сделать - изменить название функции с пафосного на более приземлённое - deleteStartEndAnchors. Я почитал про регулярки в JS и так и не понял, что мне нужно менять в этих: https://github.com/applejacky/students/blob/6d2bea191317e4195c9e5f8aa61a1088b298bce6/app/Lib/StudentValidator.pбылhp#L11
Исключая привязку по краям, разумеется. На пике 2 - то, что выводится на страницу, хотя ты и так можешь по верхней ссылке глянуть.
>Плюс, он почему-то работает из-под рута, что опасно, чуть что не так и легко разломать систему.
Мне самому эту штуку сразу было страшно запускать. Но я всё протестировал на голой убунте. Ну и не знаю как по-другому. Без прав суперпользователя в изкоробочной убунте нельзя добавлять виртуальные хосты. Разумеется, ещё ограничен доступ к менеджеру пакетов и /etc/hosts
>Зачем-то прописывает записи в hosts. А на продакшене ты тоже будешь адрес домена на 127.0.0.1 направлять?
Вынес в переменную ip_addr. Скрипт только для использования на локалхосте. Я не знаю как на продакшене должно быть.
>> function die {
>> kill $$
> Ой, как все сложно и запутанно.
Я объяснил в предыдущем посте, почему так сделал.
>Проще сказать пользователю отредактировать конфиг или предоставить файл локального конфига.
Не понимаю. То есть ты предлагаешь такую последовательность действий для развёртывания на локалхосте:
1. Склонировать репозиторий;
2. Найти конфиг и поправить его;
3. Запустить скрипт, который будет создавать виртуальный хост, включать апач и прочее.
Я же предлагаю сделать так:
1. Скачать скрипт и запустить его. В процессе установки cкрипт склонирует репозиторий и предложит ввести данные для подлючения к бд, которые запишутся в config.ini
Всё в одном месте, неужели так хуже?
>В разных дистрибутивах линукса конфиги Апача могут быть организованы по-разному и храниться в разных папках.
Насколько я помню, то для Arch (и, скорее всего для Red Hat based) нужно поменять apache_root с '/var/www' на '/srv/http', а виртуальные хосты класть в /etc/httpd/conf/extra, что в общем-то неважно, так как путь можно любой указывать. Другое дело, что там нет a2ensite/a2enmode, то есть мне придётся шаманить sed'ом подключение виртульных хостов в httpd.conf
Хорошо, я займусь этим, но попозже.
Первый блин деплоя стал комом: https://secondtryphp-veryfirtsapp.rhcloud.com/
Если у кого есть желание потестить и сообщить о неисправностях - то буду вам очень благодарен.
SSH-сессия очень кастрированная (на 1-м пике список всех доступных команд), но зато бесплатно 3 постоянно доступных сайта.
>> convertPcreRegexToHtml5($pcreRegex)
> Ты серьезно? Одной маленькой регуляркой ты преобразуешь один синтаксис в другой? Не, это не будет работать.
Максимум, что я могу сделать - изменить название функции с пафосного на более приземлённое - deleteStartEndAnchors. Я почитал про регулярки в JS и так и не понял, что мне нужно менять в этих: https://github.com/applejacky/students/blob/6d2bea191317e4195c9e5f8aa61a1088b298bce6/app/Lib/StudentValidator.pбылhp#L11
Исключая привязку по краям, разумеется. На пике 2 - то, что выводится на страницу, хотя ты и так можешь по верхней ссылке глянуть.
>Плюс, он почему-то работает из-под рута, что опасно, чуть что не так и легко разломать систему.
Мне самому эту штуку сразу было страшно запускать. Но я всё протестировал на голой убунте. Ну и не знаю как по-другому. Без прав суперпользователя в изкоробочной убунте нельзя добавлять виртуальные хосты. Разумеется, ещё ограничен доступ к менеджеру пакетов и /etc/hosts
>Зачем-то прописывает записи в hosts. А на продакшене ты тоже будешь адрес домена на 127.0.0.1 направлять?
Вынес в переменную ip_addr. Скрипт только для использования на локалхосте. Я не знаю как на продакшене должно быть.
>> function die {
>> kill $$
> Ой, как все сложно и запутанно.
Я объяснил в предыдущем посте, почему так сделал.
>Проще сказать пользователю отредактировать конфиг или предоставить файл локального конфига.
Не понимаю. То есть ты предлагаешь такую последовательность действий для развёртывания на локалхосте:
1. Склонировать репозиторий;
2. Найти конфиг и поправить его;
3. Запустить скрипт, который будет создавать виртуальный хост, включать апач и прочее.
Я же предлагаю сделать так:
1. Скачать скрипт и запустить его. В процессе установки cкрипт склонирует репозиторий и предложит ввести данные для подлючения к бд, которые запишутся в config.ini
Всё в одном месте, неужели так хуже?
>В разных дистрибутивах линукса конфиги Апача могут быть организованы по-разному и храниться в разных папках.
Насколько я помню, то для Arch (и, скорее всего для Red Hat based) нужно поменять apache_root с '/var/www' на '/srv/http', а виртуальные хосты класть в /etc/httpd/conf/extra, что в общем-то неважно, так как путь можно любой указывать. Другое дело, что там нет a2ensite/a2enmode, то есть мне придётся шаманить sed'ом подключение виртульных хостов в httpd.conf
Хорошо, я займусь этим, но попозже.
спасибо ОП
буду думать
А что можешь посоветовать почитать по философии ООП в целом? Я похоже и правда не до конца понимаю суть ооп
>>надо прочитать как минимум введение целиком и просмотреть мануал по всем ее функциям сначала.
Ты о каком введении говоришь?
Cкорее всего ОП тебе посоветует почитать его пасту по ООП: http://archive-ipq-co.narod.ru/l1/pasta.html и решить задачки на вектор и студентов. Так что начинай уже сейчас.
Спасибо!
Теперь попробую обновиться до РНР7.1.0 чуть позже.
Теперь, благодаря тебе, знаю про Dotdeb и sourcelist.
Про make install понял, спасибо.
Так, если в код не заглядывать, впечатление хорошее.
На форме регистрации нет подтверждения пароля. Это плохо, так как легко опечататься или ввексти не в той раскладке. пароль должен либо быть виден либо потверждаться.
Текст ошибки "CSRF-токены не совпадают" надо подправить, уменьшив объем технических подробностей в пользу советов, что делать пользователю.
> Я не знаю как на продакшене должно быть.
На продакшене хостс трогать не надо так как домен обслуживается ДНС сервером.
> Я же предлагаю сделать так:
> 1. Скачать скрипт и запустить его. В процессе установки cкрипт склонирует репозиторий и предложит ввести данные для подлючения к бд, которые запишутся в config.ini
ну не знаю, а если ты после каждого изменения код вызываешь этот скрипт, тебе не надоест одно и то же вводить? Для меня конечно конфиг отредактировать удобнее.
> Хорошо, я займусь этим, но попозже.
Не надо делать поддерждку арча, я думаю. Я просто хотел сказать что скрипты деплоя вряд ли получится сделать кроссплатформенными.
> >> convertPcreRegexToHtml5($pcreRegex)
> Ты серьезно? Одной маленькой регуляркой ты преобразуешь один синтаксис в другой? Не, это не будет работать.
> Я почитал про регулярки в JS и так и не понял, что мне нужно менять в этих:
я имел в виду что синтаксис pcre нельзя преобразовать в синтаксис js выражений так просто. Значит, твой код работает только в части случаев. Если мы сейчас исправим регулярку, добавив в нее что-то pcre-специфичное, то этот код начнет выдавать некорретные регулярки. Какие я вижу решения? Например, сделать 2 регулярки вручную.
На форме логина https://secondtryphp-veryfirtsapp.rhcloud.com/auth/login стоило хотя бы проставить тип email и required.
Просто не представляю, что подразумевают под "знанием" cms. Полистал документацию - элементарщина, заглянул в код - каша конечно, но разобраться можно.
Нужны конкретные практические задачи, потому что пока не представляю, что нужно будет делать. Натянуть верстку само собой, но это не имеет отношения к программированию. А еще?
Написать плагин/модуль, понятно, но что конкретно должен делать плагин?
Короче перечислите типичные задачи под вордпресс и магазины (opencart/prestashop).
Потому что пока боязно, наверное недостаточно полистать документацию чтобы заносить в резюме знание cms, нужно наверное сначала что-то сделать на них, только не представляю что.
>Короче перечислите типичные задачи под вордпресс и магазины (opencart/prestashop).
Короче заходишь на фриланс биржу и смотришь.
Например, такой плагин для WP, назовём его CodeInjection:
1. Сколь угодно можно создавать меню, просто текст (в который можно код рекламы запихнуть). Все они расположены списком на странице в админке.
2. Нажимаешь в админке на блок с меню, текстом или кодом - получаешь список всех страниц и записей сайта, рядом с каждой находится чекбокс. Ставишь "галочку" - указанные тобой меню или текст, код появятся на этой странице в сайдбаре.
3. То же самое - для разных частей самих статей: в верху, в середине, в низу статей. Тоже для размещения кода рекламы было бы удобно.
Не пишите здесь больше. Если я вас пропустил и не ответил, напомните о себе в новом треде.
Здесь сегодня-завтра напишу ответы на старые посты.
На 32-битных системах число типа int может принимать значение от -2 млрд до 2 млрд. Операция % тоже поддерживает корректно только числа типа int. Если ты на 32-битной системе делишь числа выходящие за разрешенный предел, результат получается неверный (по хорошему конечно надо в таких случаях выдавать ошибку, но с праивльной работой с ошибками в пхп все плохо).
>>763366
> Как можно описать в regex игнорирование символов " -()", чтобы была возможность указать кол-во цифр в телефоне равное 10?
Можно написать так:
(1 цифра, за ней любое число минусов, скобок, пробелов) повторяется 10 раз
>>763547
> Проблема в том что внутри группы подгруппы выделяются только один раз, в параметре $replacement функции preg_replace() нельзя вывести несколько подгрупп с одинаковыми номерами и совпавшими с разными участками текста.
Можно использовать разные регулярки для поиска слов и для выделения букв в них.
>>763827
program can't start часто бывает из-за того что не установлена нужная версия Visual Studio C++ Runtime (это не сама студия, а небольшая библиотека на несколько мегабайт, качается с оф сайта майкрософт). Посмотри где ты скачивал свой пхп и там наверняка есть упоминание этой библиотеки и какая версия нужна.
>>764269
Если в коде нет поддержки загрузки нескольких файлов, надо либо ее как-то прикрутить либо отказаться от идеи.
> Может быть я тупой и в функции привязанной к хуку save_post надо сделать как-то в цикле вызов другой функции в зависимости от колличества прикрепленных файлов, ибо я сохраняю мету в бд вордпресса и что-то мне подсказывает, что там должна быть мета одного файла и $id одного только post.
Я не могу ничем помочь так как не читал код вордпресса. Это можешь сделать только ты.
>>764664
В задаче про студентов из ОП поста много комментариев. Что значит "правильный"? В разных фреймворках это сделано по разному (Юи, Ларавель, симфони).
Также, у меня есть урок про общие принципы обработки форм: https://github.com/codedokode/pasta/blob/master/forms.md
Вообще, я тебе советую сделать сначала задачу про студентов. У меня ощущение (по формулировке вопроса) что ты довольно много теории пропустил или не знаешь и надо ее наверстать. Там в студентах как раз и формы, и база данных имеется.
>>764916
Честно говоря не вижу разницы. Раньше было:
- приходит запрос, ищутся нужные данные, отдается ответ
Сейчас:
- приходит запрос, отдается статическая страница
- приходит запрос, ищутся данные. отдается джейсон
Итог:
- грузится медленнее
- нагрузка на БД по идее такая же так как такие же данные выбираются
- менее надежно, при ошибке пользователеь ничего не увидит
- больше трудозатрат
По моему ты где-то сильно ошибаешься. Выше ты думал что ob_start медленный, но тест показал что это не так.
Также, ты мельком упомянул что у тебя там выводится 400 кб данных. Я думаю, проблема может быть в этом - слишком много данных выбирается и выводится при каждой загрузке страницы. Уверен что это не требуется - пользователь все равно не способен прочитать 400 Кб текста.
Профилируй, измеряй, логгируй, ищи реальные проблемы производительности и проверяй это тестами. А не пытайся решать проблемы которые тебе мерещатся.
Ну и насчет кеша - ты писал что у тебя там миллиард страниц - на таком количестве страниц любой кеш будет неэффективен так как запросы скорее всего идут по большому числу страниц и редко повторяются.
На 32-битных системах число типа int может принимать значение от -2 млрд до 2 млрд. Операция % тоже поддерживает корректно только числа типа int. Если ты на 32-битной системе делишь числа выходящие за разрешенный предел, результат получается неверный (по хорошему конечно надо в таких случаях выдавать ошибку, но с праивльной работой с ошибками в пхп все плохо).
>>763366
> Как можно описать в regex игнорирование символов " -()", чтобы была возможность указать кол-во цифр в телефоне равное 10?
Можно написать так:
(1 цифра, за ней любое число минусов, скобок, пробелов) повторяется 10 раз
>>763547
> Проблема в том что внутри группы подгруппы выделяются только один раз, в параметре $replacement функции preg_replace() нельзя вывести несколько подгрупп с одинаковыми номерами и совпавшими с разными участками текста.
Можно использовать разные регулярки для поиска слов и для выделения букв в них.
>>763827
program can't start часто бывает из-за того что не установлена нужная версия Visual Studio C++ Runtime (это не сама студия, а небольшая библиотека на несколько мегабайт, качается с оф сайта майкрософт). Посмотри где ты скачивал свой пхп и там наверняка есть упоминание этой библиотеки и какая версия нужна.
>>764269
Если в коде нет поддержки загрузки нескольких файлов, надо либо ее как-то прикрутить либо отказаться от идеи.
> Может быть я тупой и в функции привязанной к хуку save_post надо сделать как-то в цикле вызов другой функции в зависимости от колличества прикрепленных файлов, ибо я сохраняю мету в бд вордпресса и что-то мне подсказывает, что там должна быть мета одного файла и $id одного только post.
Я не могу ничем помочь так как не читал код вордпресса. Это можешь сделать только ты.
>>764664
В задаче про студентов из ОП поста много комментариев. Что значит "правильный"? В разных фреймворках это сделано по разному (Юи, Ларавель, симфони).
Также, у меня есть урок про общие принципы обработки форм: https://github.com/codedokode/pasta/blob/master/forms.md
Вообще, я тебе советую сделать сначала задачу про студентов. У меня ощущение (по формулировке вопроса) что ты довольно много теории пропустил или не знаешь и надо ее наверстать. Там в студентах как раз и формы, и база данных имеется.
>>764916
Честно говоря не вижу разницы. Раньше было:
- приходит запрос, ищутся нужные данные, отдается ответ
Сейчас:
- приходит запрос, отдается статическая страница
- приходит запрос, ищутся данные. отдается джейсон
Итог:
- грузится медленнее
- нагрузка на БД по идее такая же так как такие же данные выбираются
- менее надежно, при ошибке пользователеь ничего не увидит
- больше трудозатрат
По моему ты где-то сильно ошибаешься. Выше ты думал что ob_start медленный, но тест показал что это не так.
Также, ты мельком упомянул что у тебя там выводится 400 кб данных. Я думаю, проблема может быть в этом - слишком много данных выбирается и выводится при каждой загрузке страницы. Уверен что это не требуется - пользователь все равно не способен прочитать 400 Кб текста.
Профилируй, измеряй, логгируй, ищи реальные проблемы производительности и проверяй это тестами. А не пытайся решать проблемы которые тебе мерещатся.
Ну и насчет кеша - ты писал что у тебя там миллиард страниц - на таком количестве страниц любой кеш будет неэффективен так как запросы скорее всего идут по большому числу страниц и редко повторяются.
Ок, все верно.
> По регуляркам куча материала осталась. Про то как их составлять. Изучить это или можно двигаться дальше?
Стоит хотя бы прочитать это.
>>764951
> $timers = $mapper->getTimers();
Сдампь $timers и посмотри что в ней
>>765627
> Просто мне кажется, вы усложняете многое правильным и последовательным подходом
Праивльный и последовательный подход поможет много раз. А костыльное решение - только один.
>>766323
Доки почитал бы
>>767064
Нет проверки на ничью, а в остальном все правильно.
>>Вообще, это можно было бы делать в классе формы в методе fillDataFromArray
> Прежде чем задавать студенту хэш нужно проверить пароль на валидность. Из названии функции наверно кажется что я меня именно пароль, а не создаю хеши?
Вообще, отчасти ты прав. Но с другой стороны, если введено неправильное имя. ты ведь сначала записываешь его в студента, а только потом проверяешь? Тут можно поступить и так, и так:
- записывать хеш независимо от того праивльный ли пароль
- записывать хеш только после успешной проверки
>>>if (Helper::validCSRFtoken($_GET['token']) and $this->loginAction->isLoggedIn()) {
>>> $student = $this->studentGateway->getStudentByСolumn('id', $_COOKIE['id']);
>>Вот здесь странно. Если у тебя есть сервис авторизации и ты через него проверяешь залогиненность, то разве не логично через него же и получать текущего пользователя? Почему у тебя проверка залогиненности в одном месте, а получение пользователя - в другом?
> Не знаю, мне казалось что если есть какая-то проверка на что-то, то результат должен быть true\false и не более.
> Вообще это очевидно что результат должен быть в зависимости от требований задачи, но я и представить не мог что такое где-нибудь понадобиться. То есть я думал что этот метод будет использоваться только в проверках, и в моем случае изначально после проверки на залогиненность была проверка на токен а потом уже получение пользователя.
Да ошибка не в этом. Ошибка в том что проверяешь залогиненность ты методом класса loginAction, а получаешь залогиненного пользователя другим путем. Один класс должен отвечать и за проверку залогиненности и за получение текущего пользователя. Потому что это части одной задачи.
Ну то есть если сформулировать по другому: работа с кукой 'id' должна быть собрана только в одном классе. Больше она не должна нигде использоваться.
И это должен быть сервис (хелпер), а не контроллер. А ты пытаешься использовать контроллер как будто это сервис.
>>$isEdit = $this->loginAction->isLoggedIn();
> Если результат этого метода будет сущность студента, то ничего страшного если переменная $isEdit будет содержать не булевое значение? Ведь на практике это можно применить точно так же.
Можно но тогда надо метод переименовать.
>>Более того, проверяешь авторизацию ты контроллером, а залогинаваешь пользователя в классе со странным названием StudentCookies.
> В каком именно месте это не правильно? В месте где я перезалогиниваю пользователя после редактирования?
1) код в контроллере не предназначен для повторного использования. для этого есть хелперы. Ты поместил код не туда (в контроллер вместо хелпера) и из-за этого вынужден использовать контроллер как хелпер.
2) каждый класс занимается своим делом. Работать с авторизационными куками должен один класс, который за это отвечает. У тебя работа с ними размазана по всей программе.
>>По моему это нелогично, что код, отвечающий за авторизацию разбросан по всему приложению. Мне кажется, логичнее сделать единый сервис авторизации, который умеет:
> Но ведь уже есть контроллер который занимается этим. Или ты имеешь ввиду переделать так чтобы контроллер пользовался вышеперечисленными методами сервиса?
Код в контроллере не стоит повторно использовать, например вызывать из других мест. Это не соответствует идее MVC. Контроллер это тот, кто управляет обработкой запроса от пользователя. Ты не должен создавать контроллер для каких-то других целей, например, чтобы узнать текущего залогиненного пользователя.
Надо было код отвечающий за авторизацию вынести в хелпер. ты вместо этого раскидал его по разным контроллерам.
>>Далее, нет логики в наследовании хелперов. Вот у тебя есть Helper и есть LoginHelper, который его наследует. Непонятно, а зачем? Там все равно почти все методы статические. Точнее, часть статическая, а часть нет, и почему, непонятно.
> А разве в одном классе все методы должны быть либо статические либо не статические? Почему?
Такого ограничения нет. Но должна быть какая-то логика по которой ты делаешь метод статическим или нет. Например, одинаково ли его поведение всегда или разное для разных объектов. Можно ли его вызывать не имея объекта или наоборот, он имеет смысл только если есть объект. У тебя там такой логики нету, слово static поставлено произвольно.
Пример. Допустим мы делаем класс для работы с файлом, имя файла передается в конструктор. Метод "удалить файл" очевидно должен быть нестатическим, так как он работает с объектом, представляющим файл. А вот метод "перевести размер из байтов в мегабайты" логично сделать статическим так как ему в принципе объект не нужен, он всегда одинаково работает, и может вообще использоваться не для файла, а для чего-то еще.
>>В студенте, непонятно, а зачем у тебя там и токен, и пароль? Если ты исплоьзуешь вход по паролю, то токен по моему не нужен.
> Так токен нужен же для защиты от CSRF. Его удобно держать в студенте, потому что мы создаем куки на его основе.
Токен для защиты от CSRF не имеет никакого отношения к студенту. Потому что:
- он может использоваться для защиты форм (например формы регистрации) даже когда пользователь не залогинен и никакого объекта студента нет
- он ограничен по времени и кука с ним удаляется через какое-то время. А записи в базе хранятся вечно (если их не удалять). Нелогично хранить токен, кука с которым давно умерла
Ты точно читал урок про защиту от CSRF? Токен CSRF сохранять в базу не нужно. Одна копия токена хранится в куке, другая - в скрытом поле формы. База данных тут не нужна.
>>Вообще, это можно было бы делать в классе формы в методе fillDataFromArray
> Прежде чем задавать студенту хэш нужно проверить пароль на валидность. Из названии функции наверно кажется что я меня именно пароль, а не создаю хеши?
Вообще, отчасти ты прав. Но с другой стороны, если введено неправильное имя. ты ведь сначала записываешь его в студента, а только потом проверяешь? Тут можно поступить и так, и так:
- записывать хеш независимо от того праивльный ли пароль
- записывать хеш только после успешной проверки
>>>if (Helper::validCSRFtoken($_GET['token']) and $this->loginAction->isLoggedIn()) {
>>> $student = $this->studentGateway->getStudentByСolumn('id', $_COOKIE['id']);
>>Вот здесь странно. Если у тебя есть сервис авторизации и ты через него проверяешь залогиненность, то разве не логично через него же и получать текущего пользователя? Почему у тебя проверка залогиненности в одном месте, а получение пользователя - в другом?
> Не знаю, мне казалось что если есть какая-то проверка на что-то, то результат должен быть true\false и не более.
> Вообще это очевидно что результат должен быть в зависимости от требований задачи, но я и представить не мог что такое где-нибудь понадобиться. То есть я думал что этот метод будет использоваться только в проверках, и в моем случае изначально после проверки на залогиненность была проверка на токен а потом уже получение пользователя.
Да ошибка не в этом. Ошибка в том что проверяешь залогиненность ты методом класса loginAction, а получаешь залогиненного пользователя другим путем. Один класс должен отвечать и за проверку залогиненности и за получение текущего пользователя. Потому что это части одной задачи.
Ну то есть если сформулировать по другому: работа с кукой 'id' должна быть собрана только в одном классе. Больше она не должна нигде использоваться.
И это должен быть сервис (хелпер), а не контроллер. А ты пытаешься использовать контроллер как будто это сервис.
>>$isEdit = $this->loginAction->isLoggedIn();
> Если результат этого метода будет сущность студента, то ничего страшного если переменная $isEdit будет содержать не булевое значение? Ведь на практике это можно применить точно так же.
Можно но тогда надо метод переименовать.
>>Более того, проверяешь авторизацию ты контроллером, а залогинаваешь пользователя в классе со странным названием StudentCookies.
> В каком именно месте это не правильно? В месте где я перезалогиниваю пользователя после редактирования?
1) код в контроллере не предназначен для повторного использования. для этого есть хелперы. Ты поместил код не туда (в контроллер вместо хелпера) и из-за этого вынужден использовать контроллер как хелпер.
2) каждый класс занимается своим делом. Работать с авторизационными куками должен один класс, который за это отвечает. У тебя работа с ними размазана по всей программе.
>>По моему это нелогично, что код, отвечающий за авторизацию разбросан по всему приложению. Мне кажется, логичнее сделать единый сервис авторизации, который умеет:
> Но ведь уже есть контроллер который занимается этим. Или ты имеешь ввиду переделать так чтобы контроллер пользовался вышеперечисленными методами сервиса?
Код в контроллере не стоит повторно использовать, например вызывать из других мест. Это не соответствует идее MVC. Контроллер это тот, кто управляет обработкой запроса от пользователя. Ты не должен создавать контроллер для каких-то других целей, например, чтобы узнать текущего залогиненного пользователя.
Надо было код отвечающий за авторизацию вынести в хелпер. ты вместо этого раскидал его по разным контроллерам.
>>Далее, нет логики в наследовании хелперов. Вот у тебя есть Helper и есть LoginHelper, который его наследует. Непонятно, а зачем? Там все равно почти все методы статические. Точнее, часть статическая, а часть нет, и почему, непонятно.
> А разве в одном классе все методы должны быть либо статические либо не статические? Почему?
Такого ограничения нет. Но должна быть какая-то логика по которой ты делаешь метод статическим или нет. Например, одинаково ли его поведение всегда или разное для разных объектов. Можно ли его вызывать не имея объекта или наоборот, он имеет смысл только если есть объект. У тебя там такой логики нету, слово static поставлено произвольно.
Пример. Допустим мы делаем класс для работы с файлом, имя файла передается в конструктор. Метод "удалить файл" очевидно должен быть нестатическим, так как он работает с объектом, представляющим файл. А вот метод "перевести размер из байтов в мегабайты" логично сделать статическим так как ему в принципе объект не нужен, он всегда одинаково работает, и может вообще использоваться не для файла, а для чего-то еще.
>>В студенте, непонятно, а зачем у тебя там и токен, и пароль? Если ты исплоьзуешь вход по паролю, то токен по моему не нужен.
> Так токен нужен же для защиты от CSRF. Его удобно держать в студенте, потому что мы создаем куки на его основе.
Токен для защиты от CSRF не имеет никакого отношения к студенту. Потому что:
- он может использоваться для защиты форм (например формы регистрации) даже когда пользователь не залогинен и никакого объекта студента нет
- он ограничен по времени и кука с ним удаляется через какое-то время. А записи в базе хранятся вечно (если их не удалять). Нелогично хранить токен, кука с которым давно умерла
Ты точно читал урок про защиту от CSRF? Токен CSRF сохранять в базу не нужно. Одна копия токена хранится в куке, другая - в скрытом поле формы. База данных тут не нужна.
Про ООП рассказывается в том числе в книгах которые есть в ОП посте (Зандстра, Шлосснейгл)
>>775013
Вот этот раздел официального мануала прочитать: http://php.net/manual/ru/book.mysqli.php
Наизусть запоминать ничего не надо, только почитать.
Переходите в новый тред >>769611 (OP)
Если я вам не ответил, не проверил задачу, пропустил - напомните о себе в новом треде.
Это копия, сохраненная 28 июня 2016 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.