Вы видите копию треда, сохраненную 15 марта 2017 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Пожалуйста, пишите один большой пост вместо нескольких маленьких и не флудите не по теме. ОПу ведь все это читать придется.
Это тред для начинающих. Не написал за свою жизнь ни одной программы и имеешь тройку по математике? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Visual Studio Code, Netbeans PHP или PhpStorm (с ним будет удобнее).
Предыдущий тред был тут: >>898502 (OP)
Еще предыдущие треды в архиве:
- 83: https://2ch.hk/pr/arch/2016-12-26/res/880700.html (М)
- 82: https://2ch.hk/pr/arch/2016-12-01/res/864640.html (М)
- другие: https://www.google.ru/search?q=site:2ch.hk/pr/+клуб+php
Мейлач лежит? Есть запасной тред: http://dobrochan.org/s/res/23225.xhtml#i46467
Что самое главное для программиста? Умение аккуратно оформлять код (читай второй пост, прежде чем писать код).
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП заходит редко, где-то раз в 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/Symfony: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование https://gist.github.com/codedokode/a455bde7d0748c0a351a
- Если ты все решил, переходи к 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://github.com/codedokode/pasta/blob/master/db/databases.md
Что почитать
- Мануал по PHP — http://www.php.net/manual/ru/langref.php
- Сайт phptherightway (перевод на русский: http://getjump.me/ru-php-the-right-way/ )
- По PHP: Профессиональное программирование на PHP Джордж Шлосснейгл
- По PHP: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- JS: learn.javascript.ru
- Про Git: https://git-scm.com/book/ru/v1
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
У ОПа нет аккаунтов и групп вконтакте, в фейсбуке, в твиттере, все "пхп-треды" там поддельные.
Платиновые вопросы
- Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко.
- Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета)
- Что надо знать чтобы найти работу - разработчику: PHP, SQL, HTML/CSS, JS, ООП, Git, композер, MVC, фреймворк. Верстальщику - HTML/CSS, JS, jQuery
- Можно подробнее про поиск работы, собеседования - нет, ОП писать не будет, но может кто из анонов захочет рассказать. Поищите тред перезвонивших, а также раздел /wrk/.
- Сколько времени надо изучать все это? - все зависит от тебя, но не меньше 6-8 месяцев
- Посоветуйте редактор кода - Sublime Text 3, Notepad++, PhpStorm
- Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию.
- Что самое главное для программиста? Умение аккуратно оформлять код.
- ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет.
- Подскажи сайты для поиска работы, я не умею гуглить? — 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/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию 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/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского или русского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
--
Хорошая задача на ООП с собеседования: http://www.cyberforum.ru/php-oop/thread1459985.html
Вы должны решить как минимум Вектор, чтобы взяться за нее. Если вы сделали студентов или файлообменник - эту задачу должны решить легко.
ОПчик, почему тут цикл состоит только из 1 значения?
http://ideone.com/lcS8qQ
Дело в continue? Мысль была такова, что при положительном итоге проверки на наличие купюр, он их просто пропускает и переходит к следующему номиналу.
Я уже начал делать MVC сапера отсюда https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md , смысл делать позер-MVC, когда можно полноценное. Вопрос такой, ты помогал советами анонам писать event dispatcher для паттерна observer? Или они брали готовый код (глянул на гитхабе енота, вроде свой). Можешь ли ты подробней расписать про этот паттерн, а то я пока что не понял как работают его методы и как будет устроен класс, понял только что он будет следить за изменением модели за счет addEventListener. Там вроде есть ссылки на эти классы, но мне кажется если я их посмотрю, это будет как подглядывание.
Класс очень простой, он по сути позволяет сделать 2 вещи:
- добавить обработчик для какого-то события (addEventListener)
- вызвать все зарегистрированные обработчики и может быть передать им параметры события (triggerEvent)
Примерно так:
var ed = new EventDispatcher;
ed.addEventListener(SOME_EVENT, function (event) { ... });
ed.addEventListener(SOME_EVENT, function (event) { ... });
ev.triggerEvent(SOME_EVENT, { data: ... });
Есть еще альтернативный подход, когда класс представляет диспетчер только для одного события (то есть на каждое событие создается отдельный объект):
var someEvent = new EventDispatcher;
someEvent.subscribe(function (event) { ... });
someEvent.trigger({ data: ... });
Ты также можешь посмотреть:
- как устроен встроенный в браузер диспетчер событий: https://developer.mozilla.org/ru/docs/Web/Guide/Events/Создание_и_вызов_событий
- обзор разных подходов (англ): https://github.com/millermedeiros/js-signals/wiki/Comparison-between-different-Observer-Pattern-implementations
Методы иногда называют немного по-другому (notify/dispatchEvent/raiseEvent/fireEvent), но суть та же.
Также можешь погуглить по словам Observer и Event Emitter/Event Dispatcher.
Класс очень простой, он по сути позволяет сделать 2 вещи:
- добавить обработчик для какого-то события (addEventListener)
- вызвать все зарегистрированные обработчики и может быть передать им параметры события (triggerEvent)
Примерно так:
var ed = new EventDispatcher;
ed.addEventListener(SOME_EVENT, function (event) { ... });
ed.addEventListener(SOME_EVENT, function (event) { ... });
ev.triggerEvent(SOME_EVENT, { data: ... });
Есть еще альтернативный подход, когда класс представляет диспетчер только для одного события (то есть на каждое событие создается отдельный объект):
var someEvent = new EventDispatcher;
someEvent.subscribe(function (event) { ... });
someEvent.trigger({ data: ... });
Ты также можешь посмотреть:
- как устроен встроенный в браузер диспетчер событий: https://developer.mozilla.org/ru/docs/Web/Guide/Events/Создание_и_вызов_событий
- обзор разных подходов (англ): https://github.com/millermedeiros/js-signals/wiki/Comparison-between-different-Observer-Pattern-implementations
Методы иногда называют немного по-другому (notify/dispatchEvent/raiseEvent/fireEvent), но суть та же.
Также можешь погуглить по словам Observer и Event Emitter/Event Dispatcher.
for ($i = 0; $i < 1.02; $i += 0.17) {
$a[$i] = $i;
}
echo count($a);
Господа, поясните, почему получается 1?
Все, я понял. Спасибо.
ОП, я разобрался сам, не трать время на тот код. Вот посмотри пожалуйста этот, вроде, как сделал: http://ideone.com/Vl5GJ4
>wireshark
>прокси на php
Какие прекрасные подарки вы мне сделали перед НГ, это просто благословение!
https://arhivach.org/thread/216627/#899868
>> .header h1 {
>> font-family: ReklameScriptRegularDEMO, Cambria
>В конце списка шрифтов надо указывать бы стандартный (serif и подобный). Cambria есть только на windows.
Вы кидали сайт где указывались шрифты и статистика использования на разных платформах. Выбрал Кабмрию потому что там было 90%+ и для Эпл и красивенько выглядел.
>> .services .content .box .consectetur::before {
>> background-position: 0px 0px;
>> width: 47px;
>> height: 54px;
>Вот тут проще было для 4 блоков указать одинаковый размер картинки, а не различающиеся на 2-3 пикселя.
В фш так вырезалось что все картинки разных размеров...
>> .services .content .box .fermentum::before {
>я бы убрал тут .content, он выглядит лишним
Как понять 'выглядит'? Разве не нужно соблюдать иерархию дерева? Знаю что не обязательно, но моё субъективное восприятие подсказывает что так выглядит лучше.
>В общем, я вижу, что верстка хорошо сделана (еще бы, после нескольких итераций), единственнй баг - это бекслеши и не работающие в ФФ картинки. Их исправь, а остальное - если будет желание. Если будешь сдавать на проверку, напомни, что единственная претензия у меня была к картинкам.
Исправил: https://www.browserstack.com/screenshots/634b220454c5dd8901b415de1440461f2a47865f/win10_firefox_44.0.jpg
https://github.com/someApprentice/maintaskforlayout/
$i=1;
foreach($breadcrumbs_array as $id => $title) {
$string ="<a href='?category=$id'>$title</a> / ";
if($i==count($breadcrumbs_array)) {
$string = $title;
}
$breadcrumbs.= $string;
$i++;
}
http://ideone.com/3PFkTf
Логика моего решения примерно такова:
создаем пустой массив длиной в 30 символов
пока длина этого массива больше чем количество слогов:
начинаем считать сначала, находим первый элемент который делится на 5 без остатка ( элемент % 5 == 0)
как только находим его, делаем новый массив в который добавляем все числа которые идут после него и до конца.
затем к этому новому массиву добавляем те элементы которые шли сначала и до этого элемента
в итоге получаем массив начинающийся с числа 6 до 30, а далее 1,2,3,4. Это для того чтобы считать дальше начиная со следующего элемента.
скопировать значения из этого нового массива в финальный, чтобы пройтись по нему таким же алгоритмом снова
как только в массиве остается 4 числа, выводим их в ответ
У меня нихуя не получается переназначить первоначальный массив, он всегда остается таким же как был.
Судя по примерному коду ОПа из этой задачи, у него решение четкое логичное и аккуратное, у меня же как говно. Скорее всего сам алгоритм неверен.
http://ideone.com/cWWTRU
Я хочу, чтобы this._field (он представлен в виде массива [{x:1, y:1}, {x:2, y:1} ...etc] остался в таком виде по возрастанию. Если я сделаю на него shuffle, то он пересортируется случайно. Поэтому я делаю неглубокию копию массив и воздействую на элементы массива - объекты, которые соответственно будут меняться и в оригинальном массиве. Вот такой неочевидный абуз. Я получаю, то что мне нужно (минирую поле) и массив по прежнему в порядке возрастания. Наверное надо делать метод setMine и передавать в него элемент скопированного массива, чтобы good code? А этот метод будет минировать поле field.
Спасибо, ОП, за перекат самого годного треда доски. Ради него сюда и захожу еще ради хаскеля
И еще я думаю как хранить мины, флажки и открытые клетки. Два варианта. Либо это boolean свойства объекта Cell, либо они хранятся в свойствах-массивах this._flags, this._mines, this._openCells. В последнем случае будет еще существовать мета-массив field, в котором будут все клеточки. Получится так, что элементы этих массивов будут ссылаться на одни и те же объекты Cell. Это нормально или делать глубокое копирование?
Так же вопрос, можно ли данный метод http://ideone.com/8v7B0B вставить в ConsoleController ? Или в модель это пойдет. Суть его в том, чтобы команды вроде 'a2' из консоли переводить в объекты Cell, ведь наверное нельзя передавать аргументы в контроллер вот так game.setFlag(new Cell(1,5), верно?
я знаю жс(+jquery+react+react-native+ember),html,css на уровне 3-4 месяцев практики, сделал тройку сайтов для себя + игру на андроид, и сейчас выбираю между пхп и питоном. На жс+верстке не въехть из за обилия макак, которые имеют сотни отзывов и опыт на сайте, а мой профиль без рейтинга никому не нужен, поэтому хочу попробовать что-то еще. Ну и типа фуллстак же буду.
Пока ты не начнешь выгрызать и находить себе заказы, ты так и будешь выбирать между пхп и питоном, потом еще из какой нить дрочи и везде будут "обилие макак".
Нельзя просто взять и попробовать Яву.
https://github.com/masssn/students.loc 29.12 >>903250
https://github.com/anotherCodeMunkey/fileshare (это ведь на проверку?) 07.01 >>907902
Если вы что-то писали в старом треде, и я не ответил, не проверил, напомните о себе тут.
Также, напомню, что в старом треде опубликованы ответы:
>>919067 - про задачу про ООП и скидки
>>919065 про JS задачу на DOM номер 4
>>919061 клавиша шифт, йода, сумма прописью
>>919060 про сумму прописью
>>919059 про калькулятор
>>919058 про preg_match
> В каком вашем мануале можно прочитать про организацию роутера для URL, контроллеры по умолчанию, index.php в корне сайта?
У нас такой статьи нет. Но можно оизучать фреймворуи вроде Slim, Yii2, Symfony и посмотреть, как роутинг сделан там.
Обычно ислоьзуются такие подходы или их сочетание:
- конфиг, где прописаны шаблоны УРЛ и соответствующие им контроллеры
- шаблоны УРЛ и их обработчики явно прописываются в коде:
$app->get('/about', function () { ... });
$app->get('/help', 'HelpController::indexAction');
- используется какой-то стандартный шаблон URL по умолчанию вроде /контроллер/действие/имя параметра/значение параметра
Ну и процитирую еще пост другого анона с ответом на этот вопрос:
>>908528
> Первый ответ: https://toster.ru/q/321702
> Читать исходники готовых роутеров:
>>909285
>>Ты по моему не очень понимаешь, что такое AR. AR это когда класс/объект представляет и строку из базы данных, и содержит методы для ее загрузки/сохранения в БД. Ты уверен, что ничего не перепутал?
> Просто я видел такую фишку, когда делали абстрактный класс model в котором просто были сделаны операции типа insert и delete, а под конкретную типа student от него наследовались, называли это актив рекорд.
Нет, наследование тут не при чем. Главная особенность Active Record - то, что класс-модель сам умеет себя сохранять в базу:
$student = new Student;
$student->name = 'Ivan';
$student->save();
В других паттернах обычно 2 класса: класс-модель, редставляющий сущность и класс-сохранятель этой сущности в базу.
>>Лучше иметь сервис, в котором есть методы для проверки и сохранения файла. Чтобы мы всегда могли бы программно сохранить файл (даже если мы не собираемся это делать, просто чтобы код был логически разделен на част).
> О, отлично. Только это же все равно логически относится к моделям, правда?
Да, в MVC это модель.
>>безопасность, чтобы нельзя было сохранить файл с расширением php или именем htaccess
> Такие файлы стоит просто запрещать загружать или же менять им расширение?
Удобнее разрешать загружать, но при сохранении менять расширение на безопасное вроде .txt
> В каком вашем мануале можно прочитать про организацию роутера для URL, контроллеры по умолчанию, index.php в корне сайта?
У нас такой статьи нет. Но можно оизучать фреймворуи вроде Slim, Yii2, Symfony и посмотреть, как роутинг сделан там.
Обычно ислоьзуются такие подходы или их сочетание:
- конфиг, где прописаны шаблоны УРЛ и соответствующие им контроллеры
- шаблоны УРЛ и их обработчики явно прописываются в коде:
$app->get('/about', function () { ... });
$app->get('/help', 'HelpController::indexAction');
- используется какой-то стандартный шаблон URL по умолчанию вроде /контроллер/действие/имя параметра/значение параметра
Ну и процитирую еще пост другого анона с ответом на этот вопрос:
>>908528
> Первый ответ: https://toster.ru/q/321702
> Читать исходники готовых роутеров:
>>909285
>>Ты по моему не очень понимаешь, что такое AR. AR это когда класс/объект представляет и строку из базы данных, и содержит методы для ее загрузки/сохранения в БД. Ты уверен, что ничего не перепутал?
> Просто я видел такую фишку, когда делали абстрактный класс model в котором просто были сделаны операции типа insert и delete, а под конкретную типа student от него наследовались, называли это актив рекорд.
Нет, наследование тут не при чем. Главная особенность Active Record - то, что класс-модель сам умеет себя сохранять в базу:
$student = new Student;
$student->name = 'Ivan';
$student->save();
В других паттернах обычно 2 класса: класс-модель, редставляющий сущность и класс-сохранятель этой сущности в базу.
>>Лучше иметь сервис, в котором есть методы для проверки и сохранения файла. Чтобы мы всегда могли бы программно сохранить файл (даже если мы не собираемся это делать, просто чтобы код был логически разделен на част).
> О, отлично. Только это же все равно логически относится к моделям, правда?
Да, в MVC это модель.
>>безопасность, чтобы нельзя было сохранить файл с расширением php или именем htaccess
> Такие файлы стоит просто запрещать загружать или же менять им расширение?
Удобнее разрешать загружать, но при сохранении менять расширение на безопасное вроде .txt
> Начал писать свой пикчехостинг на ларавеле. Вопрос следующий: у меня есть возможность загружать картинку не только с компа, но и по урл. Написал свой формреквест, форма валидируется нормально, но пропускает одновременно оба заполненных инпута, с файлом и урлом (нужен xor). Вопрос: как лучше организовать проверку на xor, писать свой Rule или делать это на стороне контроллера?
Я не особо разбираюсь в этом фреймворке. Я думаю, что стоит сделать оба поля необязательными, а затем написать правило, проверяющее, что хотя бы одно заполнено. Лучше конечно делать это в форме, а не в контроллере.
>>909411
>>909412
> Можете подсказать, почему конечный результат отличается?
Ты в последний месяц, когда там остается выплатить около 4000, выплачиваешь их сразу ( else {$paymentTotal = $paymentTotal + $creditBalance; ), а ведь на них сначала должны начислиться проценты и комиссия. И выйдет больше.
>>909492
> Где phpMyAdmin хранит комментарии к ячейкам таблицы?
phpMyAdmin сам их не хранит, а передает на сервер MySQL для хранения. Эти же комментарии можно добавить и без phpMyADmin, через SQL запрос вида:
CREATE TABLE x (
y INT COMMENT 'Примечание'
) COMMENT 'Примечание';
> Участвуют ли они в запросах?
> Есть ли разница в производительности запросов с ними / без?
Думаю, нет. Но разбираться в таблицах с комментариями гораздо удобнее.
> Какой движок (MyIsam, InnoDb, другой?) подойдет лучше для ситуации:
Почитай про различия между движками, гугли "отличия innodb и myisam"
> Не будут ли тормозить запросы в конце года?
100 000 не так и много, думаю, при правильно поставленных индексах все будет хорошо. Но конечно, если ты выбираешь все 100 000 записей, очень быстро сделать это не получится просто из-за большого объема передаваемых данных.
Вообще, тебе лучше бы просто сделать таблицу, скрипт выборки и померять время.
> Начал писать свой пикчехостинг на ларавеле. Вопрос следующий: у меня есть возможность загружать картинку не только с компа, но и по урл. Написал свой формреквест, форма валидируется нормально, но пропускает одновременно оба заполненных инпута, с файлом и урлом (нужен xor). Вопрос: как лучше организовать проверку на xor, писать свой Rule или делать это на стороне контроллера?
Я не особо разбираюсь в этом фреймворке. Я думаю, что стоит сделать оба поля необязательными, а затем написать правило, проверяющее, что хотя бы одно заполнено. Лучше конечно делать это в форме, а не в контроллере.
>>909411
>>909412
> Можете подсказать, почему конечный результат отличается?
Ты в последний месяц, когда там остается выплатить около 4000, выплачиваешь их сразу ( else {$paymentTotal = $paymentTotal + $creditBalance; ), а ведь на них сначала должны начислиться проценты и комиссия. И выйдет больше.
>>909492
> Где phpMyAdmin хранит комментарии к ячейкам таблицы?
phpMyAdmin сам их не хранит, а передает на сервер MySQL для хранения. Эти же комментарии можно добавить и без phpMyADmin, через SQL запрос вида:
CREATE TABLE x (
y INT COMMENT 'Примечание'
) COMMENT 'Примечание';
> Участвуют ли они в запросах?
> Есть ли разница в производительности запросов с ними / без?
Думаю, нет. Но разбираться в таблицах с комментариями гораздо удобнее.
> Какой движок (MyIsam, InnoDb, другой?) подойдет лучше для ситуации:
Почитай про различия между движками, гугли "отличия innodb и myisam"
> Не будут ли тормозить запросы в конце года?
100 000 не так и много, думаю, при правильно поставленных индексах все будет хорошо. Но конечно, если ты выбираешь все 100 000 записей, очень быстро сделать это не получится просто из-за большого объема передаваемых данных.
Вообще, тебе лучше бы просто сделать таблицу, скрипт выборки и померять время.
$b можно было бы вычислять из $i ($b = длина - $i - 1 или даже просто $b = - $i - 1), но твое решение в общем-то тоже верное.
>>919083
>>Конечно, копирует ссылку на функцию.
> Но функция находиться же в объекте obj! В чём подвох?
var o = {};
o.fn = function() {};
Функция o.fn сама по себе никак к этому объекту не привязана. Объект хранит в себе ссылку на функцию, а функция на объект - нет. Потому если мы копируем ее в отдельную переменную, и вызываем, то она про исходный объект ничего уже не знает и this указывает на window:
var f = o.fn;
f();
А вот если мы пишем
o.fn();
То интерпретатор JS видит, что функция вызвыается на объекте, и подсовывает его в качестве this.
Чтобы она "запомнила" свой объект, мы должны использовать bind:
var f = o.fn.bind(o);
f();
>>Задача 1: написать функцию bindContext(fn, that). Она создает новую функцию, которая при вызове вызывает fn с указанным this и переданным аргументами. То по сути есть привязывает произвольное значение this к функции.
Все верно.
>>Задача 2: сделать функцию addProperty(object, name, initialValue) для создания приватных свойств с геттерами и сеттерами на объекте или прототипе объекта.
Тут опечатка в коде:
> addProperty(obj, 'test', 10);
> console.log(obj.getName());
Свойство test, а вызывается getName вместо getTest().
По коду: в общем, верный подход, мы прячем переменную в замыкании, но вот не очень понял, зачем использовать defineProperty. Функции get/set можно просто добавить через obj[funcName] = function ...
> name[0].toUpperCase() + name.slice(1)
Это конечно лучше было вынести в вспомогательную функцию.
Задача про фастфуд
> Почему-то не получается https://jsfiddle.net/y28h2o2b/3/
Там надо открыть отладчик (Ctrl + SHift + I) и включить режим "останавливаться на исключении". И там будет видно, что this.menu не определено, отсюда и ошибка.
Ест статья про отладку: http://learn.javascript.ru/debugging-chrome
Аналогичный отладчик есть и в Фаерфоксе, и в Сафари.
На jsfiddle отладка может и не заработать (хотя у меня работало), в этом случае надо просто сделать свой HTML файл, открыть в браузере и отлаживать его.
Электросеть
> ...только мне теперь кажется логичным что у каждого элемента сети должен быть один параметр, это мощность, и этот элемент должен сам определять дальнейшие условия определения этого параметра в соответствии\зависимости со средой (определение дневного\ночного времени).
Тут есть разные варианты, надо просто выбрать такой, какой удобнее:
- можно вообще не делать свойств в базовом классе, и в каждом наследнике написать свой метод getPower() со своей логикой. Соответственно, наследник определяет, какие будут свойства (вроде числа квартир) и как из них вычисляется потребление.
- можно сделать в базовом классе свойства dayPower/nightPower и метод getPower, который их возвращает. А логику вычисления этих свойств писать в конструкторах наследников. И не делать свойств вроде числа квартир.
Первый подход наверно гибче.
> //Нужен ли тут гетер, если к свойству можно обратиться через точку?
мы можем договориться, что свойство приватное, чтобы в него напрямую не лезли, и тогда геттер будет нужен.
> Лучше исключить её из перебора потому, что ЛЭП тоже может вернуть какое-то количество мощности.
Мне кажется, надежнее прописать там ноль, так как мы не знаем, какая у нее мощность. Ну или вообще не делать в ней метода getPower(), чтобы его не пытались даже вызывать.
> С другой стороны электросеть не знает о ценах и о внутреннем устройстве ЛЭП. Моя идея в том, баланс можно просто передать в условный счетчик. Ведь это для ЛЭП свойственно иметь и считать цену.
Электросеть не знает цены и пропускной способности, но она всегда может ее у ЛЭП спросить. ЛЭП сама не может ничего посчитать, так как не знает баланс энергии во всей сети, и не может решить, сколько надо закупать. Потому расчет, сколько энергии купить, должен быть в электросети.
> this.elements.sort(function (a, b) {
> if (a.price > b.price) {
Вот тут я вижу неточность: в коде предполагается, что у всех элементов сети есть свойство "цена". Но оно есть только у ЛЭП. Потому мы должны отобрать ЛЭП и сортировать только их.
Также, sort меняет порядок следования элементов в this.elements, что может где-то быть нежелательно. Ведь получается что метод с названием get Something фактически изменяет внутреннее состояние объекта, что неожиданно. Лучше получить отдельный массив ЛЭП и сортировать его.
> if (this.elements instanceof PowerLine) {
> price += this.elements.countPrice(balance);
> balance = this.elements.countPower(balance);
Тут нет расчета, сколько именно нужно купить. Нужно покупать не всю доступную энергию, а только ту, что не удалось произвести.
> function Electrostation(power) {
> if (power >= 1 Math.pow(10, 6) && power <= 100 Math.pow(10, 6)) {
> this.power = power;
> this.nightpower = power;
Тут лучше не задавать свойства вручную, а вызывать конструктор предка. Может потом туда допишут еще какой-то код, и тут он не сработает. Принято всегда вызвать конструктор предка, так как именно он отвечает за инициализацию предка. И если его не вызвать, то что-то из задуманного может не выполниться.
> function PowerLine(power, price) {
> this.power = power;
> this.price = price;
> }
Мне кажется, тут неправильно используется свойство power, так как у ЛЭП это не фактически переданная мощность, а пропускная способность (сколько максимум можно купить/продать), и логично для нее использовать другое название, чтобы не было путаницы.
> PowerLine.prototype.countPower = function(power) {
> var thispower = this.power;
> for (var i = 0; i < thispower; thispower--) {
> if (power == 0) {
> break;
> }
> power += power / -Math.abs(power);
что-то я не могу толком понять, что тут происходит. Зачем мы power делим саму на себя? Получится ведь либо 1, либо -1. Тут явно ошибка.
Определение типа переменной
> Что подрузумевается здесь под словом 'хеш'?
Хеш, он же словарь, он же ассоциативный массив, он же объект - это вот это:
var x = { 1: 2, 'x': 'y' };
Я просто написал что вместо switch можно было сделать объект, в ключах которого хранятся значения вроде '[object Function]'. И искать тип в этом объекте.
>>Недостаточно, надо бы проверить что там есть свойства от 0 до length - 1.
>>от 0 до length - 1
> А как это выразить в условии
Написать цикл, проверяющий наличие свойств с помощью оператора in.
15. Напиши функцию неглубокого копирования объектов и массивов
> Опять же, не могу понять что с этим не так: Если это клон объекта, то этот клон должен иметь тот же прототип что и "донор"(?).
Не, до такой степени скопировать объект мы вряд ли сможем. Достаточно копировать только свойства самого объекта, копировать прототип не требуется.
Вообще, в JS не все можно скопировать полностью. Некоторые встроенные объекты, например, массив, функцию-замыкание с захваченными переменными, или исключение, идеально скопировать может не получиться (насчет массива: он ведь мог быть создан в другом ифрейме, тогда копирование через var copy = [] не даст точную копию). Потому в задаче и не требуется все копировать полностью, а только то, что описано в условии.
> Лучше использовать for ... object.length вместо for in?
У объектов нету object.length, length есть только у массивов. Лучше использовать либо for .. of из ES5, либо проверку через hasOwnProperty() из ES3.
Тут в коде, я вижу, есть поддержка копирования объектов Date и обычных объектов, а что с массивами? Для них ведь надо изначально создавать пустой массив и копировать элементы. Иначе тест вроде Array.isArray() не пройдет на копии.
Глубокое копирование
> var clone = new object.constructor;
Неприавльно вызывать конструктор. Мы ведь не знаем, какие аргументы у него есть и что в них надо передать. Надо делать так:
- если источник - это Date, то создать новый Date, можно сразу передать правильное значение даты
- если источник - массив, создать новый массив и скопировать элементы
- если источник - другой объект создать пустой объект и скопировать все его свойства, не относящиеся к прототипам
- иначе, если источник - не объект (а число, строка, null и тд), можно просто вернуть его
Тут из-за кучи цитат становится уже трудно понимать некоторые ответы, если что-то непонятно, можно просто задать вопрос отдельно.
$b можно было бы вычислять из $i ($b = длина - $i - 1 или даже просто $b = - $i - 1), но твое решение в общем-то тоже верное.
>>919083
>>Конечно, копирует ссылку на функцию.
> Но функция находиться же в объекте obj! В чём подвох?
var o = {};
o.fn = function() {};
Функция o.fn сама по себе никак к этому объекту не привязана. Объект хранит в себе ссылку на функцию, а функция на объект - нет. Потому если мы копируем ее в отдельную переменную, и вызываем, то она про исходный объект ничего уже не знает и this указывает на window:
var f = o.fn;
f();
А вот если мы пишем
o.fn();
То интерпретатор JS видит, что функция вызвыается на объекте, и подсовывает его в качестве this.
Чтобы она "запомнила" свой объект, мы должны использовать bind:
var f = o.fn.bind(o);
f();
>>Задача 1: написать функцию bindContext(fn, that). Она создает новую функцию, которая при вызове вызывает fn с указанным this и переданным аргументами. То по сути есть привязывает произвольное значение this к функции.
Все верно.
>>Задача 2: сделать функцию addProperty(object, name, initialValue) для создания приватных свойств с геттерами и сеттерами на объекте или прототипе объекта.
Тут опечатка в коде:
> addProperty(obj, 'test', 10);
> console.log(obj.getName());
Свойство test, а вызывается getName вместо getTest().
По коду: в общем, верный подход, мы прячем переменную в замыкании, но вот не очень понял, зачем использовать defineProperty. Функции get/set можно просто добавить через obj[funcName] = function ...
> name[0].toUpperCase() + name.slice(1)
Это конечно лучше было вынести в вспомогательную функцию.
Задача про фастфуд
> Почему-то не получается https://jsfiddle.net/y28h2o2b/3/
Там надо открыть отладчик (Ctrl + SHift + I) и включить режим "останавливаться на исключении". И там будет видно, что this.menu не определено, отсюда и ошибка.
Ест статья про отладку: http://learn.javascript.ru/debugging-chrome
Аналогичный отладчик есть и в Фаерфоксе, и в Сафари.
На jsfiddle отладка может и не заработать (хотя у меня работало), в этом случае надо просто сделать свой HTML файл, открыть в браузере и отлаживать его.
Электросеть
> ...только мне теперь кажется логичным что у каждого элемента сети должен быть один параметр, это мощность, и этот элемент должен сам определять дальнейшие условия определения этого параметра в соответствии\зависимости со средой (определение дневного\ночного времени).
Тут есть разные варианты, надо просто выбрать такой, какой удобнее:
- можно вообще не делать свойств в базовом классе, и в каждом наследнике написать свой метод getPower() со своей логикой. Соответственно, наследник определяет, какие будут свойства (вроде числа квартир) и как из них вычисляется потребление.
- можно сделать в базовом классе свойства dayPower/nightPower и метод getPower, который их возвращает. А логику вычисления этих свойств писать в конструкторах наследников. И не делать свойств вроде числа квартир.
Первый подход наверно гибче.
> //Нужен ли тут гетер, если к свойству можно обратиться через точку?
мы можем договориться, что свойство приватное, чтобы в него напрямую не лезли, и тогда геттер будет нужен.
> Лучше исключить её из перебора потому, что ЛЭП тоже может вернуть какое-то количество мощности.
Мне кажется, надежнее прописать там ноль, так как мы не знаем, какая у нее мощность. Ну или вообще не делать в ней метода getPower(), чтобы его не пытались даже вызывать.
> С другой стороны электросеть не знает о ценах и о внутреннем устройстве ЛЭП. Моя идея в том, баланс можно просто передать в условный счетчик. Ведь это для ЛЭП свойственно иметь и считать цену.
Электросеть не знает цены и пропускной способности, но она всегда может ее у ЛЭП спросить. ЛЭП сама не может ничего посчитать, так как не знает баланс энергии во всей сети, и не может решить, сколько надо закупать. Потому расчет, сколько энергии купить, должен быть в электросети.
> this.elements.sort(function (a, b) {
> if (a.price > b.price) {
Вот тут я вижу неточность: в коде предполагается, что у всех элементов сети есть свойство "цена". Но оно есть только у ЛЭП. Потому мы должны отобрать ЛЭП и сортировать только их.
Также, sort меняет порядок следования элементов в this.elements, что может где-то быть нежелательно. Ведь получается что метод с названием get Something фактически изменяет внутреннее состояние объекта, что неожиданно. Лучше получить отдельный массив ЛЭП и сортировать его.
> if (this.elements instanceof PowerLine) {
> price += this.elements.countPrice(balance);
> balance = this.elements.countPower(balance);
Тут нет расчета, сколько именно нужно купить. Нужно покупать не всю доступную энергию, а только ту, что не удалось произвести.
> function Electrostation(power) {
> if (power >= 1 Math.pow(10, 6) && power <= 100 Math.pow(10, 6)) {
> this.power = power;
> this.nightpower = power;
Тут лучше не задавать свойства вручную, а вызывать конструктор предка. Может потом туда допишут еще какой-то код, и тут он не сработает. Принято всегда вызвать конструктор предка, так как именно он отвечает за инициализацию предка. И если его не вызвать, то что-то из задуманного может не выполниться.
> function PowerLine(power, price) {
> this.power = power;
> this.price = price;
> }
Мне кажется, тут неправильно используется свойство power, так как у ЛЭП это не фактически переданная мощность, а пропускная способность (сколько максимум можно купить/продать), и логично для нее использовать другое название, чтобы не было путаницы.
> PowerLine.prototype.countPower = function(power) {
> var thispower = this.power;
> for (var i = 0; i < thispower; thispower--) {
> if (power == 0) {
> break;
> }
> power += power / -Math.abs(power);
что-то я не могу толком понять, что тут происходит. Зачем мы power делим саму на себя? Получится ведь либо 1, либо -1. Тут явно ошибка.
Определение типа переменной
> Что подрузумевается здесь под словом 'хеш'?
Хеш, он же словарь, он же ассоциативный массив, он же объект - это вот это:
var x = { 1: 2, 'x': 'y' };
Я просто написал что вместо switch можно было сделать объект, в ключах которого хранятся значения вроде '[object Function]'. И искать тип в этом объекте.
>>Недостаточно, надо бы проверить что там есть свойства от 0 до length - 1.
>>от 0 до length - 1
> А как это выразить в условии
Написать цикл, проверяющий наличие свойств с помощью оператора in.
15. Напиши функцию неглубокого копирования объектов и массивов
> Опять же, не могу понять что с этим не так: Если это клон объекта, то этот клон должен иметь тот же прототип что и "донор"(?).
Не, до такой степени скопировать объект мы вряд ли сможем. Достаточно копировать только свойства самого объекта, копировать прототип не требуется.
Вообще, в JS не все можно скопировать полностью. Некоторые встроенные объекты, например, массив, функцию-замыкание с захваченными переменными, или исключение, идеально скопировать может не получиться (насчет массива: он ведь мог быть создан в другом ифрейме, тогда копирование через var copy = [] не даст точную копию). Потому в задаче и не требуется все копировать полностью, а только то, что описано в условии.
> Лучше использовать for ... object.length вместо for in?
У объектов нету object.length, length есть только у массивов. Лучше использовать либо for .. of из ES5, либо проверку через hasOwnProperty() из ES3.
Тут в коде, я вижу, есть поддержка копирования объектов Date и обычных объектов, а что с массивами? Для них ведь надо изначально создавать пустой массив и копировать элементы. Иначе тест вроде Array.isArray() не пройдет на копии.
Глубокое копирование
> var clone = new object.constructor;
Неприавльно вызывать конструктор. Мы ведь не знаем, какие аргументы у него есть и что в них надо передать. Надо делать так:
- если источник - это Date, то создать новый Date, можно сразу передать правильное значение даты
- если источник - массив, создать новый массив и скопировать элементы
- если источник - другой объект создать пустой объект и скопировать все его свойства, не относящиеся к прототипам
- иначе, если источник - не объект (а число, строка, null и тд), можно просто вернуть его
Тут из-за кучи цитат становится уже трудно понимать некоторые ответы, если что-то непонятно, можно просто задать вопрос отдельно.
> Спасибо. Будет интересно для практики сделать статус-бар загрузки контента не НА сервер, а С сервера.
В этом нет особого смысла, так как данных там немного и фактически большую часть времени там идет ожидание ответа сервера. А его длительность угадать нельзя и сделать правильный прогресс-бар не получится. Потому лучше просто крутящийся индикатор, что идет загрузка.
> А ещё интересно было бы узнать, как по уму реализовывать хранение конфига системы в базе данных (как у всех популярных cms).
Сделать таблицу вида key | value, то есть 1 строка = 1 настройка. Если данные разных типов, то можно сделать key | int_value | string_value и тд.
Тут важно понимать, какие нам нужны настройки? Если они для менеджеров и меняются через админку - то в таблице, если для сисадмина или разработчика и меняются редко - может, удобнее в файл поместить.
> отдельная модель UserConfig с отдельной таблицей в БД, сохраняющая при каждом изменении данные в кэш.
Ты усложняешь раньше времени. Кеш можно добавить, но тогда надо решать, как именно его делать: 1 ключ для всех настроек или для каждой настройки свой ключ. И на малонагруженном сайте скорее всего с кешем будет примерно такая же скорость, что и с БД.
Насчет кода - да, хорошо бы реализацию спрятать в классе, чтобы код только вызывал нужный метод и не знал, как именно там все реализовано.
> Всё это дело выполняет отдельный миддлвэр для всех запросов к серверу.
middleware - это в смысле что-то аналогичное middleware в Слиме? Оно явно не для настроек и конфигов предназначно.
> для всех запросов к серверу.
Вообще, логичнее читать конфиг, только если он нужен.
Скорее всего, дробные числовые индексы у массивов округляются до целого.
>>919179
Там кто-то пишет сам, кто-то берет готовую библиотеку для событий.
>>919233
> while (($amount >= $value) and ($count > 0)) {
> $amount -= $value;
> $countBills += 1;
Цикл с вычитанием нужно заменить на деление. Учесть ограничение на число имеющихся банкнот можно с помощью min/max.
>>919568
Проще наверно вести счетчик. Регулярными выражением точно ничего подчищать не стоит.
Также тут есть еще идеи:
http://stackoverflow.com/questions/1070244/how-to-determine-the-first-and-last-iteration-in-a-foreach-loop
> end($array);
> $lastKey = key($array);
...
> if ($key === $lastKey)
Ну и я бы советовал вынести HTML в шаблон. И не писать array в названии переменной.
>>919968
А чем создание каталогов отличается от каких-то других сайтов? Ну не знаю даже что, сделай каталог уроков по программированию например, если своих идей нет.
>>920065
Можно сделать мультиинсертом, если надо вставить несколько записей за раз, но обновить несколько записей можно только циклом. Желательно обернуть цикл в транзакцию, чтобы запросы могли выполниться только целиком.
>>920199
Тут надо правильно выбрать формат массива. Например, можно сделать так:
- ключ обозначает текущий порядковый номер девочки от 0 до N (и ключи пересчитываются заново при удалении с помощью array_values/array_splice), а значение - исходный номер (когда еще все были в круге). На определенном шаге мы имеем такой массив:
[0] => [2]
[1] => [3]
[2] => [5]
[3] => [7]
...
Тогда нам достаточно иметь просто счетчик, который мы увеличиваем на X с переполнением, и удаляем девочку с таким индексом (например, через unset или array_splice). Переполнение счетчика делается через взятие остатка от деления на текущий размер массива.
- можно сделать массив, где в значении хранится исходный номер, и ключи не пересчитываются (они тут вообще не важны). Вместо этого мы просто делаем цикл foreach по массиву и увеличиваем счетчик на каждом шаге на 1, когда он дойдет до определенного значения - удаляем текущий элемент (через unset, не пересчитывая ключи) и сбрасываем счетчик
И изучи функцию array_splice, она может быть полезна.
Скорее всего, дробные числовые индексы у массивов округляются до целого.
>>919179
Там кто-то пишет сам, кто-то берет готовую библиотеку для событий.
>>919233
> while (($amount >= $value) and ($count > 0)) {
> $amount -= $value;
> $countBills += 1;
Цикл с вычитанием нужно заменить на деление. Учесть ограничение на число имеющихся банкнот можно с помощью min/max.
>>919568
Проще наверно вести счетчик. Регулярными выражением точно ничего подчищать не стоит.
Также тут есть еще идеи:
http://stackoverflow.com/questions/1070244/how-to-determine-the-first-and-last-iteration-in-a-foreach-loop
> end($array);
> $lastKey = key($array);
...
> if ($key === $lastKey)
Ну и я бы советовал вынести HTML в шаблон. И не писать array в названии переменной.
>>919968
А чем создание каталогов отличается от каких-то других сайтов? Ну не знаю даже что, сделай каталог уроков по программированию например, если своих идей нет.
>>920065
Можно сделать мультиинсертом, если надо вставить несколько записей за раз, но обновить несколько записей можно только циклом. Желательно обернуть цикл в транзакцию, чтобы запросы могли выполниться только целиком.
>>920199
Тут надо правильно выбрать формат массива. Например, можно сделать так:
- ключ обозначает текущий порядковый номер девочки от 0 до N (и ключи пересчитываются заново при удалении с помощью array_values/array_splice), а значение - исходный номер (когда еще все были в круге). На определенном шаге мы имеем такой массив:
[0] => [2]
[1] => [3]
[2] => [5]
[3] => [7]
...
Тогда нам достаточно иметь просто счетчик, который мы увеличиваем на X с переполнением, и удаляем девочку с таким индексом (например, через unset или array_splice). Переполнение счетчика делается через взятие остатка от деления на текущий размер массива.
- можно сделать массив, где в значении хранится исходный номер, и ключи не пересчитываются (они тут вообще не важны). Вместо этого мы просто делаем цикл foreach по массиву и увеличиваем счетчик на каждом шаге на 1, когда он дойдет до определенного значения - удаляем текущий элемент (через unset, не пересчитывая ключи) и сбрасываем счетчик
И изучи функцию array_splice, она может быть полезна.
> Поэтому я делаю неглубокию копию массив и воздействую на элементы массива - объекты, которые соответственно будут меняться и в оригинальном массиве
Можно так сделать.
И в коде конечно ошибки, например:
> if (typeof x != "number" || y != "number") {
Должно быть typeof y
Также, не уверен, что нужно свойство nearMines у Cell. Зачем оно там?
Также, в твоем массиве не очень удобно искать ячейку по координатам.
>>920988
Ну тут надо думать, какой подход будет удобнее. Оба подхода имеют право на жизнь. Главное отличие, что в случае массивов this._mines, this._flags информация хранится в классе Field, а не в объектах Cell.
Тут надо сравнивать такие вещи:
- удобно ли найти информацию о клеточке
- удобно ли отслеживать изменения и генерировать события?
- есть ли возможность менять свойства в обход класса, не генерируя события? Это может вести к багам
Вообще, в сложных моделях данные не получится все хранить в одном объекте и придется их разносить по отдельным объектам, которые образуют дерево или граф. Ну представь, например, текстовый редактор, где модель документа содержит объекты-заголовки, -картинки, -абзацы, -таблицы, и какие-то из них внутри еще содержат другие объекты. Все это может меняться, и все изменения надо как-то отслеживать.
>>921117
Лучше всего вынести тот файл из публичной папки, инклудить его и вызывать нужную функцию из скрипта-обработчика при соблюдении условий.
Яваскрипт-проверка легко обходится злоумышленником.
>>921197
Можно в контроллер, можно в вспомогательный utility класс.
> var regex = new RegExp("^\\w(\\d){1," + Util.getNumberLength(this._minesweeperGame.MAX_HEIGHT) + "}$", "i");
Это уже переулсожнение, проще просто потом проверить значение числа.
> ведь наверное нельзя передавать аргументы в контроллер вот так game.setFlag(new Cell(1,5), верно?
Да, в условиях задачи требуется передавать человекопонятные обозначения.
> Поэтому я делаю неглубокию копию массив и воздействую на элементы массива - объекты, которые соответственно будут меняться и в оригинальном массиве
Можно так сделать.
И в коде конечно ошибки, например:
> if (typeof x != "number" || y != "number") {
Должно быть typeof y
Также, не уверен, что нужно свойство nearMines у Cell. Зачем оно там?
Также, в твоем массиве не очень удобно искать ячейку по координатам.
>>920988
Ну тут надо думать, какой подход будет удобнее. Оба подхода имеют право на жизнь. Главное отличие, что в случае массивов this._mines, this._flags информация хранится в классе Field, а не в объектах Cell.
Тут надо сравнивать такие вещи:
- удобно ли найти информацию о клеточке
- удобно ли отслеживать изменения и генерировать события?
- есть ли возможность менять свойства в обход класса, не генерируя события? Это может вести к багам
Вообще, в сложных моделях данные не получится все хранить в одном объекте и придется их разносить по отдельным объектам, которые образуют дерево или граф. Ну представь, например, текстовый редактор, где модель документа содержит объекты-заголовки, -картинки, -абзацы, -таблицы, и какие-то из них внутри еще содержат другие объекты. Все это может меняться, и все изменения надо как-то отслеживать.
>>921117
Лучше всего вынести тот файл из публичной папки, инклудить его и вызывать нужную функцию из скрипта-обработчика при соблюдении условий.
Яваскрипт-проверка легко обходится злоумышленником.
>>921197
Можно в контроллер, можно в вспомогательный utility класс.
> var regex = new RegExp("^\\w(\\d){1," + Util.getNumberLength(this._minesweeperGame.MAX_HEIGHT) + "}$", "i");
Это уже переулсожнение, проще просто потом проверить значение числа.
> ведь наверное нельзя передавать аргументы в контроллер вот так game.setFlag(new Cell(1,5), верно?
Да, в условиях задачи требуется передавать человекопонятные обозначения.
>Также, в твоем массиве не очень удобно искать ячейку по координатам.
Это связано что я выбрать подход с объектом Cell, а не с двумерным массивом поля? Если да, то ты же сам в задаче предлагаешь такой вариант.
Написана может не очень хорошо, но неплохие практики, перед прочтением пройдите туториал от ОПа.
>Бретт Маклафлин - PHP & MySQL. The Missing Manual, 2nd Edition / PHP и MySQL. Исчерпывающее руководство, 2-е издание
>>это ведь на проверку?
Возможно. Кажется, я как-то неправильно использую кастомные ограничители в валидаторе. Получается гораздо запутанней, чем если бы я написал свой класс валидатор. Стандартные ассерты, определенные аннотациями не работают, возможно из-за кастомного constraint validator. Я так понимаю, что нужно использовать для создания формы entity класс, но в форме всего 2-3 поля, а в классе их больше, к тому-же хочется проверять сразу класс, а не поля отдельно друг от друга. Я создал отдельные классы форм, это выглядит как костыль. По какой-то причине ошибки, выдаваемые валидатором при использовании метода getMessage() не имеют ключей и просто выдают месседжи об ошибке без привязки к полю объекта формы. Если тянуть ошибки из формы, то там их вообще нет. Я где-то накосячил и не пойму где.
В общем нужны советы и мб какието пояснения по коду. Про то, что нужно сделать бутстрап файл, я уже догадался. И возникла странная проблема: если поместить сонфиг подключения к бд в ини файл и сделать parse_ini_file, вставив значения в DoctrineServiceProvider(), то консоль не может подключиться к базе данных, хотя приложение ее видит и сайт работает.
>mysql_connect, ни слова про ООП, html+php в одну кучу в 2k17
>неплохие практики
Это ты так пытаешься конкуренцию на рынке труда снизить?
kek. Если бы я нашел что-то лучше...
Просто того же Робина читал, у него вперемешку представление и код, тут хотя бы разделение, обрезание пробелов, что-то еще, что довольно неплохо. Нет ООП, да, это плохо, но хотя бы учат писать код правильно (до того места, где я дочитал).
Есть еще книга украинского программиста, но такое ощущение, что она писалась в разное время разными людьми, он более-менее кода, до вырвиглазного.
Из нормального мне понравилась только книга с рецептами, но там же не учат с нуля. Да и вообще актуальных книг не могу найти.
Значит стоит смириться и не ставить ифы для проверки типа переданных аргументов и выброса исключения?
Это первая задача (не считая самые базовые), которую сделал полностью сам, не подсматривая код вообще нигде. Приятное чувство )
все задачки переделываю раза по два, потому что по привычке ебашу var/let и без $, особенно в цикле фор.
> и отсутствие объявления
Это реально пиздец. Даже в древнем бейсике (Visual Basic 6 1998-го года) можно было включить режим обязательного объявления. А вот в PHP 2017-го года - нет. Воистину СЛАДКИЙ ХЛЕБУШЕК
ну давай, скажи, что жс - язык унтерменьшей для создания алертов на одностраничниках.
Человеческого объявления переменных с областями видимости и все такое. Сразу писать х = "оп-хуй" - неприятно и >>922743 согласен.
еще не очень нравится, что СКЛАДЫВАНИЕ строк происходит ТОЧКОЙ, но это уже хуй с ним.
язык как язык, не знаю, чому его критикой до уровня 1С опускают
и да, если ты подразумеваешь, что все жс-юзеры дрочат $$$$$$ в жквери, то ты ошибаешься. У нас там уже реакт, нода, и куча всего, чем успешный говнокодер может лепить говносайты и взламывать глаза и логику хороших кодеров.
for (let i = 0; i < array.length; i++) {}
for ($i = 0; $i < $length; $i++) {}
1. читабельно без $$$$, переменная i создается на время действие цикла и выгружается из памяти после его конца, есть отдельный метод, читающий длинну строки, массива, аллаха.
2. я очень люблю доллары, но тут они не настоящие, чтоб читать длинну массива, надо ее записать в переменную, либо делать огромное условие.
Объявление переменных просто логичнее и приятнее. Мы создаем меременную, потом присваиваем ей значение, меняем, уничтожаем. В пхп все не так гладко.
и да, foreach применяется не везде, так что .length - важная фича.
еще в пхп ругается интерпретатор, если я задаю переменную внутри чего-то, того же цикла. Надо как дебилу сначала до него написать $x = 0 или "".
> читабельно без $$$$
Исключительно вкусовщина.
>for ($i = 0; $i < count($array); $i++) {// переменная доступна только в теле цикла, после его завершения память освобождается.}
Проблемы?
> foreach применяется не везде
В php 99% это foreach.
>>922771
>еще в пхп ругается интерпретатор, если я задаю переменную внутри чего-то, того же цикла. Надо как дебилу сначала до него написать $x = 0 или "".
Шо? Это как?.
ну, типа я пишу тот де форич иии { $буква = значение... }, и потом смотрю, что это надо было вынести и поставить до начала функции.
В общем, забей, это просто мои барибухи с непривычки, вот и все.
Это не баг, а фитча.
Область видимости переменной не может выходить за рамки контекста в котором она определена.( локальные переменные )
Можно конечно продлить жизнь временным данным, но это уже другая история
Мне надо не рассусоливать о том как круты паттерны проектирования, а что: вот есть у нас проблема, и вот смотри какой есть элегантный способ её решить, го расскажу!
>Кевин Янк - PHP и MySQL. От новичка к профессионалу - 2013
Тут вместо mysql_connect ПДО.
Типа такого кода:
> $obj = array('show' => function() { echo 'test'; });
> $obj = (object) $obj;
> $obj->show(); // кидает ошибку `Call to undefined method stdClass::show`
.
Зачем? Почему? Там ведь даже контекст не нужен (`$this` не используется), так почему в языке так нельзя?
>ты из руби?
Нет, из JS, лол.
>У нас тут не принято манкипатчить
Дык я не собирался.
Пришлось в коде конвертировать объект в массив, вспомнил о том, как когда-то давно с этим экспериментировал и как обнаружил эту странность. Решил спросить.
>сall_user_func
Ну так оно, по сути, работает уже с переданной лямбдой. Так не интересно.
Короче, как я понял, эта дрянь формируется где-то на уровне транслятора, а потом можно только "вызвать метод strClass::show в контексте объекта obj", но не "получить свойство show объекта obj и вызвать его".
Видимо, я мыслю слишком "по-JSному". Охуевал когда узнал, что, по сути, в PHP нет большой разницы между статичными методами и обычными.
В любом случае, лучей добра тебе ^_^
Я об этом-же.
PHP позволяет
>"вызвать метод strClass::show в контексте объекта obj"
вместо
>"получить свойство show объекта obj и вызвать его"
. Хрен знает почему, но так, видимо, нужней. Ну и хуй с ним.
Анончик там лимит на оперативку и свопится начинает? Оперативки на сервере 8 гигов
>Собственно прочитал гайд и понял, что нужно пошагово как в гайде учиться.верно?
Да.
>И сильно я сосну с маком? Надо ли накатывать винду на него?
С ним даже удобнее будет.
То есть смещается вправо или влево? Это про запятую, то есть было 1, стало 100?
Нет, это про биты.
Почитай об этом лучше в учебнике по JS: learn.javascript.ru/bitwise-operators - там намного лучше описано что это и где применяется, нежели в документации PHP.
Похоже здесь какие-то циклы разрастаются, в результате этот единственный процесс как было 90% процессорного времени занимал, так и сейчас занимает, но скорость обновления товара все сильнее падает. Анончик помоги это выше моего уровня
foreach($rows as $r => $row){
$i++;
if($i < 0) {
// continue;
}
print $r.' - '.round(memory_get_peak_usage()/1024/1024) . PHP_EOL;
$item = explode(';',$row);
foreach($item as $k=>$v){
$v = ltrim($v, '"');
$v = rtrim($v, '"');
$item[$k] = $v;
}
if(is_numeric($item[0])){
import_product($item);
}
$row = null;
$item = null;
}
В общем путем расставления микротаймов выявил тормоз в левой функции, а конкретнее в этом участке,
if(!$product){
$product = commerce_product_new('product');
$product->status = '1';
$product->uid = '1';
$product->created = $product->changed = time();
$product->is_new = TRUE;
//print 'Creating new product!'.PHP_EOL;
}
Вот какого хуя антош? какого хуя? Исполнение функции дальше не проходит! все стопается на этапе сравнения!
UPD. Если у тебя $product - это строка, то выводится его длина и выполнение программы прекращается.
Сделай отступы и переходы строки нормальные, а то как долбоеб.
$entity_type - это commerce_product
$controllers[$entity_type]; - не содержит ничего
count ($controllers[$entity_type];) - не содержит ничего
Не знаю почему участок тормозит.
Вот вся цепочка которую я раскручивал:
---------------
function import_product($item){
$product = commerce_product_load_by_sku($item[14]);
----------------
function commerce_product_load_by_sku($sku) {
$start = microtime(true); //начало измерения
print "^^^^ 0 $start ";
$products = commerce_product_load_multiple(array(), array('sku' => $sku));
$end3 = microtime(true); //конец измерения
print "3 $end3 !!!";
return $products ? reset($products) : FALSE;
}
-----------------
function commerce_product_load_multiple($product_ids = array(), $conditions = array(), $reset = FALSE) {
$end2 = microtime(true); //конец измерения
print "2 $end2 !!!";
if (empty($product_ids) && empty($conditions)) {
return array();
}
return entity_load('commerce_product', $product_ids, $conditions, $reset);
}
-------------------
function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) {
$start = microtime(true); //начало измерения
print "^^^^ 5 $start ";
if ($reset) {
$end2 = microtime(true); //конец измерения
print "6 $end2 !!!";
entity_get_controller($entity_type)->resetCache();
$end3 = microtime(true); //конец измерения
print "7 $end3 !!!";
}
$end8 = microtime(true); //конец измерения
print "8 $end8 !!!";
return entity_get_controller($entity_type)->load($ids, $conditions);
}
$entity_type - это commerce_product
$controllers[$entity_type]; - не содержит ничего
count ($controllers[$entity_type];) - не содержит ничего
Не знаю почему участок тормозит.
Вот вся цепочка которую я раскручивал:
---------------
function import_product($item){
$product = commerce_product_load_by_sku($item[14]);
----------------
function commerce_product_load_by_sku($sku) {
$start = microtime(true); //начало измерения
print "^^^^ 0 $start ";
$products = commerce_product_load_multiple(array(), array('sku' => $sku));
$end3 = microtime(true); //конец измерения
print "3 $end3 !!!";
return $products ? reset($products) : FALSE;
}
-----------------
function commerce_product_load_multiple($product_ids = array(), $conditions = array(), $reset = FALSE) {
$end2 = microtime(true); //конец измерения
print "2 $end2 !!!";
if (empty($product_ids) && empty($conditions)) {
return array();
}
return entity_load('commerce_product', $product_ids, $conditions, $reset);
}
-------------------
function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) {
$start = microtime(true); //начало измерения
print "^^^^ 5 $start ";
if ($reset) {
$end2 = microtime(true); //конец измерения
print "6 $end2 !!!";
entity_get_controller($entity_type)->resetCache();
$end3 = microtime(true); //конец измерения
print "7 $end3 !!!";
}
$end8 = microtime(true); //конец измерения
print "8 $end8 !!!";
return entity_get_controller($entity_type)->load($ids, $conditions);
}
Лучше спросить что непонятно, чем дожидаться поддержки здесь
Да, ухахатываюсь просто.
По своей сути это что-то что позволяет общаться друг с другом различным программам и не только...
Я точнее хотел спросить -API можно предоставить только посредством JS или есть ещё какие-то другие варианты?
API обычно это либо набор функций в библиотеке, либо (в случае веб-сервисов) набор URL, к которым можно обратиться. Яваскрипт тут вообще не при чем. Это можно делать на любом языке программирования.
>Я точнее хотел спросить -API можно предоставить только посредством JS или есть ещё какие-то другие варианты?
Твое API что должно делать? Ты знаешь различия между front end и back end? Твоему API нужны какие данные, которые необходимо формировать на стороне сервера?
Вообще ничего? Вот открыл книгу для самых новичков и с первой же страницы ничего не понял? Нет, это ненормально.
Я маки терпеть не могу, но стоит признать, что как ОС она в чем-то намного более прямая и правильная. Возможно, будут проблемы с выбором инструментария разного рода - на винде арсенал шире в разы.
вряд ли будут у него проблемы с этим, лол.
Все хорошие IDE и редакторы кода прекрасно там работают + есть терминал.
Сам себя запутал. Я видел пример как подключают js файл в шапке html документа по ссылке, и подумал что уже этот js файл взаимодействует с сервером где находиться этот файл.
Значит подключение API зависит от среды в которой его нужно подключить и каждая API это каждый отдельный случай?
Всё равно не понятно каким образом API взаимодействует с отдельным сервером.
Допустим я хочу воспользоваться API от сайта example.com на своем сайте mysite.org
Что должен предоставить сайт example.com чтобы предоставить свой функционал для моего сайта?
>(в случае веб-сервисов) набор URL, к которым можно обратиться
Этим пользуются с помощью html-тэга iframe? Есть ещё какие-нибудь варианты?
>>923921
У меня не какой-то конкретный случай. Пытаюсь разобраться как работает любое API.
Какие ЯП ты изучал/знаешь?
Реализовать доступ к API можно по-разному. Кто-то подключает js-файл на веб-страницу и скрипты на странице вызывают функции в том файле. А кто-то обращается к API со своего сервера или из мобильного приложения, и там они могут использовать любой язык программирования.
Если ты хочешь выполнять код в браузере пользователя то да, нужен яваскрипт (API тут не при чем, просто большинство браузеров поддерживают только JS). Если ты не ограничен браузером, то можно использовать любые языки.
>>924104
типа считать теги на странице, которую ему дадут. Вроде реализуемо и даже прогресс есть, но я ебал такие тестовые делать для каждой вакансии, блядь (это не единственный вопрос, как вы, наверное, уже догадались)
>типа считать теги на странице, которую ему дадут.
Просто получить количество тегов на страничке? А какие-то требования были дополнительные? Ну потому что вроде тут из сложнго только подумать в каком виде представить базу данных с тегами. Я конечно не делал, но так вроде с помощью парочки регулярок и циклов можно сделать.
>А кто-то обращается к API
Но как можно обратиться к нему, если оно находиться на стороннем сервере? Как, например, посредством php получить доступ к функции, например, getAllPost(...) из этого API, которое находиться на другом сервере? Или как это делается. Я это понять не могу.
Это я как раз уже сделал, но из требований: оформить всё в правильный ООП, чтобы решение было расширяемым, мать его. Ну и всякие по мелочи отдать в виде xml и html (не понимаю, как можно отдать результаты поиска тегов в виде хтмл?) короче, не имея опыта с этой хуйнёй чувствую, что написал полное дерьмо
>Кевин Янк - PHP и MySQL. От новичка к профессионалу - 2013
Что хорошо:
пхп код отделен от HTML;
Рассказывают о том, данные от пользователя не безопасны;
Для полдключения к бд используется PDO, как им пользоваться, основные команды.
Например по книге делаем сборник анекдотов: создаем таблицы с шутками, авторами, у шуток есть категории. Все это вместе соединяем, создаем, редактируем и тому подобное.
В книге не разъясняется синтаксис html и css, азовые вещи скорее всего тоже пропущены, но я сам начал смотреть книгу где-то с 3 главы.
По беглому взгляду не нашел MVC, паттерны, продвинутое ООП и так далее.
Книга подойдет начинающим, если вы до этого что-то читали.
>оформить всё в правильный ООП
Ну тут хз конечно, но видимо они хотят что бы ты сделал так, что бы например каждый класс выполнял только одну роль и все такое.
>чтобы решение было расширяемым, мать его.
Это о том, что твой код должен быть простым и очевидным настолько, насколько это возможно без ущерба функциональности и стандартам.
>не понимаю, как можно отдать результаты поиска тегов в виде хтмл?
В смысле? Тебе список тегов надо вернуть в итоге? Ну так почитай как сохранять инфу в виде хмл.
Ну я правильно понимаю, что если мне надо вернуть список "тег" - "колво" в HTML формате, то я внутри php скрипта генерю html страницу в виду гигансткого string'a и в него в <table> загоняю свои значения? Или это не так делается?
и если правильно понимаю, то надо ли отдавать только <table>куча инфы</table> или надо отдавать строку аж с !DOCTYPE ?
да не хотелось бы палить текст задачи, гуглиться же на раз будет, поймут сразу, что я на форуме для девочек ответы спрашивал
Привет, аноны.
Какой лучше всего способ использовать для подключения к дб?
<?php
require_once 'login.php';
$conn = new mysqli($hn, $un, $pw, $db);
if ($conn->connect_error) die($conn->connect_error);
?>
Или $connect = mysqli_connect('localhost', 'root', '12345', 'mydb');
Вообще будет ли это юзаться где или же сразу надо копать какой-нибудь нетбим ОРМ?
>PDO
Есть какой самоучитель хороший или прямо http://phpfaq.ru/pdo тут начинать? Почему не стоит через ORM?
>Кевин Янк - PHP и MySQL
Благодарю, анон, сейчас гляну. Много книг смотрю и одна хуйня, ей богу
Причем тут ORM? ORM это уже архитектурные изыски. Ты явно путаешь это с драйвером БД. PDO это просто самая удобная на данный момент обвязка драйвера БД.
Как причем? Коннектиться с помощью орм, через её код, и всё, самое безопасное подключение с помощью ретбин настолько я знаю
и die
Я так понял это что-то типа break?
Ты все таки хуиты где-то нахватался. Тот же mysqli это просто расширение для работы с бд в пхп, это еще не ORM в полном виде. ORM в целом это уже нечто посложнее, погугли что такое active record, data mapper и т.п, освоишь это а там уже до доктрины какой-нибудь дорастешь. Если же тебе интересно какое расширение лучше - сейчас почти все юзают пдо.
public function upload(\Slim\Http\UploadedFile $file)
{
$params = $this->getFileParams($file);
$file->moveTo($params['link']);
}
В парамсе у меня хранятся данные о файле, но не суть. Что интересно, кириличные имена файлов с помощью mb_detect_encoding() определяются как ютф-8, а латинца - асц2. Как решить проблему? В гугле все советуют не париться и латинизировать названия, но мне бы хотелось все таки сохранять в кирилице. Алсо в БД имена сохраняются как положено.
Юзай ООП.
>tinyint(1)
значит, что я могу хранить только 1 или 0 в этой колонке, или что могу хранить числа 0-9?
Логично было бы предположить, что 1/0, но всё-таки.
Можешь хранить числа в ренже -128..127. И ничего не спрашивай больше, храни просто и все.
сделать свой
Но так как мне немного лениво и от крудов тошнит, да и с php я хорошо знаком, писал уже на Yii, то реквестирую более-менее готовый каркас для создания магазинов или crm на симфони. Есть же, наверное, какой-то открытый компонент такой на гитхабе с базовым функционалом магазина? Чтобы можно было доделать его, добавить какую-то фичу по желанию, управление акциями, например.
Так намного удобней код редактировать.
А как по-твоему браузер загружает страницы? И как яваскрипт обращается к API на удаленном сервере?
Две программы, расположенные на разных компьютерах, могут установить сетевое соединение через Интернет и обмениваться данными. Обычно для соединения используют протокол TCP, поверх которого работает какой-нибудь высокоуровневый протокол вроде HTTP.
Вот пример АПИ, которое предоставляют Яндекс-карты (API геокодера). Оно позволяет определить по адресу координаты точки или наоборот: https://tech.yandex.ru/maps/doc/geocoder/desc/concepts/input_params-docpage/
Там в самом начале написано:
> Запрос к геокодеру представляет собой обращение по протоколу HTTPS к URL https://geocode-maps.yandex.ru/1.x/.
То есть необходимо установить сетевое соединение с узлом geocode-maps.yandex.ru и далее отправить по нему HTTPS-запрос.
Я тебе советую почитать про такие темы:
- протокол TCP/IP
- протокол HTTP
- сокеты (не вебсокеты, а сокеты Беркли)
Очевидно, они хотят посмотреть, как ты справишься с этой задачей и какой подход выберешь. Знаешь ли ты про шаблоны или нет. Видимо не знаешь, судя по твоему вопросу.
Уточняющие вопросы по задаче логичнее задавать тому, кто ее дал.
>>924203
Оператор обращения к полю объекта или вызова метода объекта. Если ты не знаешь, что такое классы и объекты, тебе надо их сначала изучить, например в учебнике в ОП посте есть глава про ООП, и в других учебниках наверно тоже есть.
Про die можно прочесть в мануале.
>>924227
> Что интересно, кириличные имена файлов с помощью mb_detect_encoding() определяются как ютф-8, а латинца - асц2.
Эта функция не работает и возвращает случайные значения. Почитай например мой урок про кодировки и сам поймешь, почему в принципе невозможно 100% точно определить кодировку (можно только угадывать с какой-то вероятностью): https://github.com/codedokode/pasta/blob/master/cs/strings.md
Насчет имен файлов - тут такая ситуация:
Имена файлов скорее всего приходят в $_FILES в той кодировке, которая исользовалась на HTML-странице. Насколько я знаю, браузер вообще все данные форм ($_POST, $_FILES) кодирует кодировкой страницы, где эта форма расположена. Данные в _GET - не уверен, то ли кодировкой страницы, то ли всегда utf-8
Функции работы с файлами вроде fopen, file_put_contents, move_uploaded_file принимают имена в той кодировке, которая определяется ОС и ее настройками:
- в Windows они принимают имена в 8-битной кодировке, зависящей от языка системы. Для русского языка это Windows-1251
- в Mac они принимают имена в utf-8
- в Linux имена файлов не имеют определенной кодировки. Но отображение имен файлов использует заданную в системе кодировку, как правило, это utf-8. То есть в linux скорее всего надо передавать имена в utf-8
Теперь у тебя есть понимание, как решить проблему?
Очевидно, они хотят посмотреть, как ты справишься с этой задачей и какой подход выберешь. Знаешь ли ты про шаблоны или нет. Видимо не знаешь, судя по твоему вопросу.
Уточняющие вопросы по задаче логичнее задавать тому, кто ее дал.
>>924203
Оператор обращения к полю объекта или вызова метода объекта. Если ты не знаешь, что такое классы и объекты, тебе надо их сначала изучить, например в учебнике в ОП посте есть глава про ООП, и в других учебниках наверно тоже есть.
Про die можно прочесть в мануале.
>>924227
> Что интересно, кириличные имена файлов с помощью mb_detect_encoding() определяются как ютф-8, а латинца - асц2.
Эта функция не работает и возвращает случайные значения. Почитай например мой урок про кодировки и сам поймешь, почему в принципе невозможно 100% точно определить кодировку (можно только угадывать с какой-то вероятностью): https://github.com/codedokode/pasta/blob/master/cs/strings.md
Насчет имен файлов - тут такая ситуация:
Имена файлов скорее всего приходят в $_FILES в той кодировке, которая исользовалась на HTML-странице. Насколько я знаю, браузер вообще все данные форм ($_POST, $_FILES) кодирует кодировкой страницы, где эта форма расположена. Данные в _GET - не уверен, то ли кодировкой страницы, то ли всегда utf-8
Функции работы с файлами вроде fopen, file_put_contents, move_uploaded_file принимают имена в той кодировке, которая определяется ОС и ее настройками:
- в Windows они принимают имена в 8-битной кодировке, зависящей от языка системы. Для русского языка это Windows-1251
- в Mac они принимают имена в utf-8
- в Linux имена файлов не имеют определенной кодировки. Но отображение имен файлов использует заданную в системе кодировку, как правило, это utf-8. То есть в linux скорее всего надо передавать имена в utf-8
Теперь у тебя есть понимание, как решить проблему?
Нетбинс спрашивает, как у тебя организовано обновление и просмотр сайта. Выбранный тобой вариант подразумевает, что у тебя на компьютере запущен веб-сервер, URL которого предлагается указать в настройках. Также есть опция при деплое копировать файлы в другую папку, которую использует этот веб-сервер.
Разберись с тем, какие варианты есть. Если что, они тут на англ. описаны: https://netbeans.org/kb/docs/php/project-setup.html#runConfiguration
Вообще, если ты совсем начинающий, то ты можешь пока решить проблему без нетбинз - например, поднять Апач и руками копировать файлы в папку Апача или запустить встроенный в PHP-веб-сервер. Или даже запускать скрипты в командной строке.
А позже, когда поймешь, что у тебя как работает, уже настроить нетбинз. Тут важно понимать, за что отвечает какая настройка и что происходит, а не тыкать настройки наугад.
Ты вообще знаешь, что такое веб-сервер и как запустить PHP скрипт в командной строке? Если нет, то надо разобраться, если да, то хорошо, прочитай документацию нетбинза, и если что-то осталось непонятно, я могу пояснить.
>>924341
Я знаю, что есть фреймворк Magento, но он не на Симфони.
>>924306
В ОП посте есть задачи на разработку файлообменника, сайта TestHub.
>>924249
Цифра задает рекомендуемую ширину колонки для вывода чисел. На хранение в базе данных это никак не влияет.
Но хорошо, что ты задаешь такие вопросы, многие ведь даже не пытаются разобраться.
Вот офиц. мануал на англ, проясняющий этот вопрос:
- https://dev.mysql.com/doc/refman/5.7/en/integer-types.html
- https://dev.mysql.com/doc/refman/5.7/en/numeric-type-attributes.html
Обрати внимание, что для дробных чисел цифры в скобках имеют другой смысл:
- https://dev.mysql.com/doc/refman/5.7/en/floating-point-types.html
- https://dev.mysql.com/doc/refman/5.7/en/fixed-point-types.html
>>924253
Что значит "не спрашивай"? Как раз надо спрашивать, если что-то непонятно.
>>924188
Ты путаешь. Использование ORM никак не влияет на "безопасность" подключения.
>>924166
А ты понимаешь разницу между этими вариантами кода? У меня ощущение, что тебе рановато браться за базы данных и надо подучить сам язык.
>>924128
Проверяют, знаешь ли ты ООП.
>>924132
> Рассказывают о том, данные от пользователя не безопасны;
А что с этим делать и как решать эту проблему, рассказывают?
> В книге не разъясняется синтаксис html и css,
Предполагается что ты уже их знаешь.
>>924085
Вообще, в PHP уже есть готовые расширения для парсинга HTML, так что не думаю, что это сложно. Или тут именно свой код нужен? Тогда чуть сложнее, конечно.
>>923981
Ну смотри сколько постов тут нафлудили. Конечно, есть.
>>923924
Ну не знаю, я использовал когда-то хакинтош и у меня осталось ощущение, что он тормозной. Конечно, может это из-за того, что это хакинтош, но у меня ощущение, что оно рассчитано на мощное железо и например на стареньком Core 2 duo с медленным HDD быстро не заработает. А Windows XP - заработает.
И там вроде до сих пор не научились раскрывать окна на весь экран?
>>923731
Ну может ты просто взял учебник не для совсем начинающих? Там в начале не написано, что надо знать? Или чего-то не знаешь? Или просто надо сбавить темп изучения и повторить тему еще раз?
В любом случае, надо полностью разобраться в том, что ты изучаешь.
>>923725
Видимо, ты что-то считаешь неправильно. Также, надо учесть что под виндой точность функции microtime(true) составляет около 15 мс, то есть время может получиться либо 0 либо 15 мс, но не 1 или 2 мс например. И если ты попадешь на границу между 2 интервалами то вернется 15 мс даже если в реальности там было намного меньше.
Лучше бы использовать профайлинг в xdebug.
> return $controllers[$entity_type];
Если $controllers - это массив, то это мнговенно выполнится, но если это объект с магическими методами или ArrayAccess, то конечно возможны варианты.
Нетбинс спрашивает, как у тебя организовано обновление и просмотр сайта. Выбранный тобой вариант подразумевает, что у тебя на компьютере запущен веб-сервер, URL которого предлагается указать в настройках. Также есть опция при деплое копировать файлы в другую папку, которую использует этот веб-сервер.
Разберись с тем, какие варианты есть. Если что, они тут на англ. описаны: https://netbeans.org/kb/docs/php/project-setup.html#runConfiguration
Вообще, если ты совсем начинающий, то ты можешь пока решить проблему без нетбинз - например, поднять Апач и руками копировать файлы в папку Апача или запустить встроенный в PHP-веб-сервер. Или даже запускать скрипты в командной строке.
А позже, когда поймешь, что у тебя как работает, уже настроить нетбинз. Тут важно понимать, за что отвечает какая настройка и что происходит, а не тыкать настройки наугад.
Ты вообще знаешь, что такое веб-сервер и как запустить PHP скрипт в командной строке? Если нет, то надо разобраться, если да, то хорошо, прочитай документацию нетбинза, и если что-то осталось непонятно, я могу пояснить.
>>924341
Я знаю, что есть фреймворк Magento, но он не на Симфони.
>>924306
В ОП посте есть задачи на разработку файлообменника, сайта TestHub.
>>924249
Цифра задает рекомендуемую ширину колонки для вывода чисел. На хранение в базе данных это никак не влияет.
Но хорошо, что ты задаешь такие вопросы, многие ведь даже не пытаются разобраться.
Вот офиц. мануал на англ, проясняющий этот вопрос:
- https://dev.mysql.com/doc/refman/5.7/en/integer-types.html
- https://dev.mysql.com/doc/refman/5.7/en/numeric-type-attributes.html
Обрати внимание, что для дробных чисел цифры в скобках имеют другой смысл:
- https://dev.mysql.com/doc/refman/5.7/en/floating-point-types.html
- https://dev.mysql.com/doc/refman/5.7/en/fixed-point-types.html
>>924253
Что значит "не спрашивай"? Как раз надо спрашивать, если что-то непонятно.
>>924188
Ты путаешь. Использование ORM никак не влияет на "безопасность" подключения.
>>924166
А ты понимаешь разницу между этими вариантами кода? У меня ощущение, что тебе рановато браться за базы данных и надо подучить сам язык.
>>924128
Проверяют, знаешь ли ты ООП.
>>924132
> Рассказывают о том, данные от пользователя не безопасны;
А что с этим делать и как решать эту проблему, рассказывают?
> В книге не разъясняется синтаксис html и css,
Предполагается что ты уже их знаешь.
>>924085
Вообще, в PHP уже есть готовые расширения для парсинга HTML, так что не думаю, что это сложно. Или тут именно свой код нужен? Тогда чуть сложнее, конечно.
>>923981
Ну смотри сколько постов тут нафлудили. Конечно, есть.
>>923924
Ну не знаю, я использовал когда-то хакинтош и у меня осталось ощущение, что он тормозной. Конечно, может это из-за того, что это хакинтош, но у меня ощущение, что оно рассчитано на мощное железо и например на стареньком Core 2 duo с медленным HDD быстро не заработает. А Windows XP - заработает.
И там вроде до сих пор не научились раскрывать окна на весь экран?
>>923731
Ну может ты просто взял учебник не для совсем начинающих? Там в начале не написано, что надо знать? Или чего-то не знаешь? Или просто надо сбавить темп изучения и повторить тему еще раз?
В любом случае, надо полностью разобраться в том, что ты изучаешь.
>>923725
Видимо, ты что-то считаешь неправильно. Также, надо учесть что под виндой точность функции microtime(true) составляет около 15 мс, то есть время может получиться либо 0 либо 15 мс, но не 1 или 2 мс например. И если ты попадешь на границу между 2 интервалами то вернется 15 мс даже если в реальности там было намного меньше.
Лучше бы использовать профайлинг в xdebug.
> return $controllers[$entity_type];
Если $controllers - это массив, то это мнговенно выполнится, но если это объект с магическими методами или ArrayAccess, то конечно возможны варианты.
У тебя в коде ошибка. Переменная $size создается внутри блока if, но используется снаружи, и возможно, что она в некоторых случаях даже не создается (или выводится старое значение). То есть тебе стоило бы язык PHP подучить для начала.
>>923565
Открой мануал http://php.net/manual/ru/language.operators.logical.php
>>923513
Тут используются внешние функции, чей код мы не видим. Выясни время их выполнения.
>>923499
Битовые операторы работают с представление числа в двоичном виде (из нулей и единиц). Сдвигается именно число в двоичном представлении. Начни с изучения двоичной системы счисления:
- https://ru.wikipedia.org/wiki/Двоичная_система_счисления
- https://ru.wikipedia.org/wiki/Битовые_операции
На первый взгляд тебе может это показаться запутанным (зачем преобразовывать число в двоичный вид, делать что-то с ним, преобразовывать обратно), но тут важно помнить, что внутри компьютера числа как раз хранятся и обрабатываются в двоичном виде, и для процессора это как раз простые операции.
На практике они редко нужны, если только тебе не надо работать с отдельными битами в числах.
Когда-то их еще использовали для оптимизации (сдвиг влево на 1 разряд равносилен умножению на 2, только выполнялся быстрее), но это неактуально для высокоуровневых языков вроде PHP/JS и не дает никакой выгоды.
Проверить, свопится или нет, можно так:
- командами top, htop, free посмотреть сколько памяти используется в свопе, сколько свободно
- посмотреть процент использования CPU процессом - при своппинге он падает и становится намного меньше 100% (хотя это может быть вызвано и другими причинами: чтение файлов, ожидание данных из сети).
>>923235
Можно, только надо использовать синтаксис, не похожий на вызов метода:
$fn = $obj->fn;
$fn();
или ($fn->obj)();
То, что ты пишешь, $obj->fn() - это вызов метода fn, которого у тебя нет, отсюда ошибка.
Ну и ты конечно глупостью занимаешься, в твоей ситуации массив нужен, а не объект.
>>923274
JS тут плохая аналогия, так как в нем полноценных классов и ООП вообще нет. Это скорее проблема в JS, что для него метод и записанная в поле анонимная функция - одно и то же.
> по сути, в PHP нет большой разницы между статичными методами и обычными.
Разница есть, они работают как и принято в ООП.
>>922793
Для ООП есть глава в нашем учебнике из ОП поста.
Для паттернов надо взять код, где они используются, например, компоненты Symfony Forms, Symfony Validation, Doctrine, и изучать их параллельно с паттернами. Так, просто в теории их изучать смысла нет.
>>922771
Если он ругается, то значит ты неправильно написал код.
Скорее всего ты пытаешься использовать еще не созданную переменную, отсюда и ошибка.
>>922770
Если ты хочешь тут поискать претензии к PHP, советую сразу брать статью https://habrahabr.ru/post/315152/
Ты к ерунде придираешься, а реальных проблем не видишь. Ну и в JS их не меньше, кстати. Чего стоит только отсутствие нормального ООП.
> выгружается из памяти после его конца
Сомневаюсь. Это конечно зависит от реализации, но в реальности, думаю, ничего никуда не выгружается, чтобы не тратить время.
> я очень люблю доллары, но тут они не настоящие, чтоб читать длинну массива, надо ее записать в переменную, либо делать огромное условие.
> Объявление переменных просто логичнее и приятнее. Мы создаем меременную, потом присваиваем ей значение, меняем, уничтожаем. В пхп все не так гладко.
Так в PHP даже короче получается.
Проверить, свопится или нет, можно так:
- командами top, htop, free посмотреть сколько памяти используется в свопе, сколько свободно
- посмотреть процент использования CPU процессом - при своппинге он падает и становится намного меньше 100% (хотя это может быть вызвано и другими причинами: чтение файлов, ожидание данных из сети).
>>923235
Можно, только надо использовать синтаксис, не похожий на вызов метода:
$fn = $obj->fn;
$fn();
или ($fn->obj)();
То, что ты пишешь, $obj->fn() - это вызов метода fn, которого у тебя нет, отсюда ошибка.
Ну и ты конечно глупостью занимаешься, в твоей ситуации массив нужен, а не объект.
>>923274
JS тут плохая аналогия, так как в нем полноценных классов и ООП вообще нет. Это скорее проблема в JS, что для него метод и записанная в поле анонимная функция - одно и то же.
> по сути, в PHP нет большой разницы между статичными методами и обычными.
Разница есть, они работают как и принято в ООП.
>>922793
Для ООП есть глава в нашем учебнике из ОП поста.
Для паттернов надо взять код, где они используются, например, компоненты Symfony Forms, Symfony Validation, Doctrine, и изучать их параллельно с паттернами. Так, просто в теории их изучать смысла нет.
>>922771
Если он ругается, то значит ты неправильно написал код.
Скорее всего ты пытаешься использовать еще не созданную переменную, отсюда и ошибка.
>>922770
Если ты хочешь тут поискать претензии к PHP, советую сразу брать статью https://habrahabr.ru/post/315152/
Ты к ерунде придираешься, а реальных проблем не видишь. Ну и в JS их не меньше, кстати. Чего стоит только отсутствие нормального ООП.
> выгружается из памяти после его конца
Сомневаюсь. Это конечно зависит от реализации, но в реальности, думаю, ничего никуда не выгружается, чтобы не тратить время.
> я очень люблю доллары, но тут они не настоящие, чтоб читать длинну массива, надо ее записать в переменную, либо делать огромное условие.
> Объявление переменных просто логичнее и приятнее. Мы создаем меременную, потом присваиваем ей значение, меняем, уничтожаем. В пхп все не так гладко.
Так в PHP даже короче получается.
Молодец, решено правильно.
>>922454
Справедливости ради, NaN это разновидность дробного числа в стандарте IEEE https://habrahabr.ru/post/112953/
>>922034
Можно проверять, хуже не будет, наоборот, поможет найти ошибку. Но может тебе будет удобнее использовать языки поверх JS с проверкой типов на этапе компиляции.
>>922390
Можно ставить, хуже не будет.
>>922276
Документация.
>>922138
> Стандартные ассерты, определенные аннотациями не работают, возможно из-за кастомного constraint validator.
Чтобы аннотации работали, надо их прочитать. Возможно, что это делается в Симфони, а при использовании отдельного компонента - не делается. В таком случае тебе надо найти код для чтения этих валидаций и вызвать его самому.
Вот тут в документации, например, ограничения задаются статическим методом, а не аннотациями: http://symfony.com/doc/current/components/validator/metadata.html
А вот тут описывается, какие способ загрузки аннотаций есть: http://symfony.com/doc/current/components/validator/resources.html
Ты используешь AnnotationLoader?
Далее ты должен передать правильно настроенный Валидатор в FormFactoryBuilder как в примере тут: http://symfony.com/doc/current/components/form.html#validation
Я вижу, ты используешь SilexFormProvider. Выясни, как именно он настраивает компонент форм и валидатор.
> Получается гораздо запутанней, чем если бы я написал свой класс валидатор.
Возможно, он рассчитан на использование совместно с аннотациями, тогда ведь получается проще? Или с каким-то конфигом, где описаны ограничения.
Такая штука есть во всех фреймворках, в Юи например ограничения описываются массивом: https://yiiframework.com.ua/ru/doc/guide/2/input-validation/
Так как в больших приложениях много форм, и неинтересно под каждую писать однотипный класс валидации. Проще описать только правила проверки.
При написании своего валидатора тебе ведь придется многое повторять, те же проверки на не-пустоту, длину строки и тд. То есть ты можешь написать свой класс-валидатор, но даже в этом случае, возможно, стоит как-то поддерживать и стандартные ограничения.
Вообще, есть альтернативы. Я вижу, ты создаешь форму прямо в контроллере. Но возможно, удобнее было бы сделать отдельный класс формы LoginType (не модели данных для формы) и настраивать список полей и ограничения в нем. Там же можно прописать кастомную валидацию.
Либо же разобраться с аннотациями и как-то брать описание формы из класса модели формы.
Также, ты вызываешь валидацию вручную через $this->validate(). Но формы интегрируются с валидатором и умеют сами его вызывать.
Что касается регистрации - регистрация это создание пользователя, и наверно, там в качестве модели для формы выгоднее использовать сразу модель пользователя, нет?
> Я так понимаю, что нужно использовать для создания формы entity класс, но в форме всего 2-3 поля, а в классе их больше, к тому-же хочется проверять сразу класс, а не поля отдельно друг от друга
Не обязательно. В самой Симфони форма может использовать в качестве модели массив, а не Entity. Или просто обычный класс, не связанный с доктриной. То есть там как раз все максимально гибко сделано. И ты можешь привязать и форму симфони, и валидацию к обычному классу или даже массиву.
Насчет этого: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Validator/ValidatorServiceProvider/LoginValidatorServiceProvider.php
Не очень понял, а зачем тут провайдер? Почему нельзя записать что-то в контейнер $app напрямую? Провайдеры обычно используют для каких-то библиотек, чтобы достаточно было добавить в прилоежние один провайдер и не надо было описывать инициализацию библиотеки, и каждый сервис из библиотеки прописывать в DI контейнер вручную. Но у тебя там всего один сервис и объявляется (validator.login), и он не универсальный, его нельзя использовать в другом приложении.
То есть как я понял, провайдер используется как этакий "мост" для простого подключения сторонней библиотеки в Silex.
В документации написано: http://silex.sensiolabs.org/doc/2.0/providers.html
> Providers allow the developer to reuse parts of an application into another one.
Вот видишь, "reuse parts of an application".
Хотя конечно, можно использовать их и для других целей, например, чтобы в большом приложении вынести часть функционала в внутреннюю библиотеку.
Насчет вот этого: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Validator/Constraints/LoginConstraintValidator.php
Я все же думаю, что это уже не задача формы - проверять логин. Задача формы:
- принять данные от пользователя
- проверить их на соответствие заданному формату и ограничениям
Но проверять логин и пароль - это уже задача какого-то другого кода, например, сервиса проверки авторизации или еще чего-нибудь. У тебя форма берет на себя слишком много и занимается не своим делом.
Должно быть примерно так:
if ($form->validate()) {
$email = $form->email;
$password = $form->password;
if (!validateEmail($email, $password)) {
$form->addViolation('неправильный логин или пароль');
}
}
Насчет этого: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Validator/Constraints/RegisterConstraint.php
Я думаю, тут должно быть 2 класса-ограничения: одно для проверки логина, другое для email. А может какое-то более уникальное ограничение для проверки уникальности любого поля. Так как по задумке Constraint - это одно ограничение, а не много.
Ты используешь constraint неправильно, это именно объект, представляющий одно ограничение, а не валидатор формы. Если тебе нужен валидатор формы - сделай класс и там работай с формой (или ее моделью) напрямую:
if ($form->password == ...) {
$form->addViolation(..);
}
if ($model->email == ....) {
return new ValidationError(....);
}
Ну и представь, что ты сделаешь форму восстановления пароля. Там тебе надо будет проверять, что введенный email есть в таблице пользователей. Там можно было бы переиспользовать ограничение для проверки существования email.
Обрати внимание, что для произвольных ограничений есть такие варианты:
- свой constraint
- использовать CallbackConstaint
- попробовать использовать события https://symfony.com/doc/current/form/events.html
Еще у тебя я вижу в коде стремление писать все в контроллерах и не использовать сервисы. Ну например, регистрация/авторизация/получение текущего пользователя - для этого нет готовых методов. Нет сервиса для работы с комментариями. То есть ты вместо MVC пишешь толстые контроллеры.
Тут вообще HTML-код в контроллере вместо использования шаблона: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Controller/CommentController.php#L39
Я предлагаю для начала разобраться с формами, моделями и валидацией, а дальше посмотрим остальные проблемы в коде. Я тут написал много текста, так что уточняй, если что-то неясно.
Молодец, решено правильно.
>>922454
Справедливости ради, NaN это разновидность дробного числа в стандарте IEEE https://habrahabr.ru/post/112953/
>>922034
Можно проверять, хуже не будет, наоборот, поможет найти ошибку. Но может тебе будет удобнее использовать языки поверх JS с проверкой типов на этапе компиляции.
>>922390
Можно ставить, хуже не будет.
>>922276
Документация.
>>922138
> Стандартные ассерты, определенные аннотациями не работают, возможно из-за кастомного constraint validator.
Чтобы аннотации работали, надо их прочитать. Возможно, что это делается в Симфони, а при использовании отдельного компонента - не делается. В таком случае тебе надо найти код для чтения этих валидаций и вызвать его самому.
Вот тут в документации, например, ограничения задаются статическим методом, а не аннотациями: http://symfony.com/doc/current/components/validator/metadata.html
А вот тут описывается, какие способ загрузки аннотаций есть: http://symfony.com/doc/current/components/validator/resources.html
Ты используешь AnnotationLoader?
Далее ты должен передать правильно настроенный Валидатор в FormFactoryBuilder как в примере тут: http://symfony.com/doc/current/components/form.html#validation
Я вижу, ты используешь SilexFormProvider. Выясни, как именно он настраивает компонент форм и валидатор.
> Получается гораздо запутанней, чем если бы я написал свой класс валидатор.
Возможно, он рассчитан на использование совместно с аннотациями, тогда ведь получается проще? Или с каким-то конфигом, где описаны ограничения.
Такая штука есть во всех фреймворках, в Юи например ограничения описываются массивом: https://yiiframework.com.ua/ru/doc/guide/2/input-validation/
Так как в больших приложениях много форм, и неинтересно под каждую писать однотипный класс валидации. Проще описать только правила проверки.
При написании своего валидатора тебе ведь придется многое повторять, те же проверки на не-пустоту, длину строки и тд. То есть ты можешь написать свой класс-валидатор, но даже в этом случае, возможно, стоит как-то поддерживать и стандартные ограничения.
Вообще, есть альтернативы. Я вижу, ты создаешь форму прямо в контроллере. Но возможно, удобнее было бы сделать отдельный класс формы LoginType (не модели данных для формы) и настраивать список полей и ограничения в нем. Там же можно прописать кастомную валидацию.
Либо же разобраться с аннотациями и как-то брать описание формы из класса модели формы.
Также, ты вызываешь валидацию вручную через $this->validate(). Но формы интегрируются с валидатором и умеют сами его вызывать.
Что касается регистрации - регистрация это создание пользователя, и наверно, там в качестве модели для формы выгоднее использовать сразу модель пользователя, нет?
> Я так понимаю, что нужно использовать для создания формы entity класс, но в форме всего 2-3 поля, а в классе их больше, к тому-же хочется проверять сразу класс, а не поля отдельно друг от друга
Не обязательно. В самой Симфони форма может использовать в качестве модели массив, а не Entity. Или просто обычный класс, не связанный с доктриной. То есть там как раз все максимально гибко сделано. И ты можешь привязать и форму симфони, и валидацию к обычному классу или даже массиву.
Насчет этого: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Validator/ValidatorServiceProvider/LoginValidatorServiceProvider.php
Не очень понял, а зачем тут провайдер? Почему нельзя записать что-то в контейнер $app напрямую? Провайдеры обычно используют для каких-то библиотек, чтобы достаточно было добавить в прилоежние один провайдер и не надо было описывать инициализацию библиотеки, и каждый сервис из библиотеки прописывать в DI контейнер вручную. Но у тебя там всего один сервис и объявляется (validator.login), и он не универсальный, его нельзя использовать в другом приложении.
То есть как я понял, провайдер используется как этакий "мост" для простого подключения сторонней библиотеки в Silex.
В документации написано: http://silex.sensiolabs.org/doc/2.0/providers.html
> Providers allow the developer to reuse parts of an application into another one.
Вот видишь, "reuse parts of an application".
Хотя конечно, можно использовать их и для других целей, например, чтобы в большом приложении вынести часть функционала в внутреннюю библиотеку.
Насчет вот этого: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Validator/Constraints/LoginConstraintValidator.php
Я все же думаю, что это уже не задача формы - проверять логин. Задача формы:
- принять данные от пользователя
- проверить их на соответствие заданному формату и ограничениям
Но проверять логин и пароль - это уже задача какого-то другого кода, например, сервиса проверки авторизации или еще чего-нибудь. У тебя форма берет на себя слишком много и занимается не своим делом.
Должно быть примерно так:
if ($form->validate()) {
$email = $form->email;
$password = $form->password;
if (!validateEmail($email, $password)) {
$form->addViolation('неправильный логин или пароль');
}
}
Насчет этого: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Validator/Constraints/RegisterConstraint.php
Я думаю, тут должно быть 2 класса-ограничения: одно для проверки логина, другое для email. А может какое-то более уникальное ограничение для проверки уникальности любого поля. Так как по задумке Constraint - это одно ограничение, а не много.
Ты используешь constraint неправильно, это именно объект, представляющий одно ограничение, а не валидатор формы. Если тебе нужен валидатор формы - сделай класс и там работай с формой (или ее моделью) напрямую:
if ($form->password == ...) {
$form->addViolation(..);
}
if ($model->email == ....) {
return new ValidationError(....);
}
Ну и представь, что ты сделаешь форму восстановления пароля. Там тебе надо будет проверять, что введенный email есть в таблице пользователей. Там можно было бы переиспользовать ограничение для проверки существования email.
Обрати внимание, что для произвольных ограничений есть такие варианты:
- свой constraint
- использовать CallbackConstaint
- попробовать использовать события https://symfony.com/doc/current/form/events.html
Еще у тебя я вижу в коде стремление писать все в контроллерах и не использовать сервисы. Ну например, регистрация/авторизация/получение текущего пользователя - для этого нет готовых методов. Нет сервиса для работы с комментариями. То есть ты вместо MVC пишешь толстые контроллеры.
Тут вообще HTML-код в контроллере вместо использования шаблона: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Controller/CommentController.php#L39
Я предлагаю для начала разобраться с формами, моделями и валидацией, а дальше посмотрим остальные проблемы в коде. Я тут написал много текста, так что уточняй, если что-то неясно.
Я предлагал это как один из возможных вариантов. Если подумать, то логично как-то оптимизировать поиск объекта Cell по координатам, и тут есть варианты:
- хранить их в словаре вроде cells[y][x]
- хранить их в массиве в определенном порядке, чтобы их можно было найти по формуле вроде cells[y * columns + x]
Я не настаиваю на какой-то конкретной реализации. Просто подумай, удобно ли будет в том или ином случае искать нужную клеточку, отслеживать изменения состояния клеточки, простой ли получится код. Тут нет какого-то одного правильного ответа.
Ну вот смотри, сколько тут опций:
- можно хранить все клетки в виде объектов Cell, а можно хранить отдельно список мин, список открытых клеток, список флажков
- можно использовать массив, а можно словарь для поиска по координатам
- в случае объекта Cell, можно хранить информацию об открытых клетках и флажках в нем, а можно не в нем
- можно хранить объекты Cell внутри другого класса и не выдавать их наружу, а можно выдавать
Ты должен как-то взвесить все эти варианты и выбрать наиболее подходящий. Если совсем не уверен, что взять, выбери какой-то вариант, набросай несколько функций для работы с клеточками, и покажи, а я напишу, какие получаются недостатки.
>Теперь у тебя есть понимание, как решить проблему?
Т.е. надо на стороне винды поменять кодировку?
Это вряд ли реально. Проще подстроить приложение и для винды перекодировать имена в нужную ей кодировку. Для этого можно сделать слой совместимости, который скрывает эти особенности внутри.
Вообще, конечно, это большая проблема. Ведь в русской версии винды используется одна 8-битная кодировка, а в американской - другая. Почему разработчики PHP не хотят использовать юникодные функции или хотя бы сделать дополнительные функции для юникода (в идеале для utf-8)? PHP формально кроссплатформенный, но от решения этой проблемы он самоустранился.
>А что с этим делать и как решать эту проблему, рассказывают?
Ну да, вроде. Предлагают функцию с магическими кавычками, рассказывают, что если будет множество инклюдов и кавычек, то можно проблемы какие-нибудь получить.
На странице данные выводятся через htmlspecialchars, с константами (потом пишется обертка для нее).
В mysql данные передаются через псевдо-переменные PDO
>Проще подстроить приложение и для винды перекодировать имена в нужную ей кодировку. Для этого можно сделать слой совместимости, который скрывает эти особенности внутри.
Я нихуя не понял. И вообще, это в пхп работа с кодировками адов труд, или я слишком неофит?
Вместо того, чтобы вызывать функцию вроде file_put_contents напрямую, сделай свою функцию, которая проверяет ОС и если надо, перекодировывает имя файла.
Я догадывался что это выполняется посредством http-запроса, но мне не понятно как отправить его посредством, к примеру, php. Я видел как обрабатываются http запросы, но не разу не видел чтобы они отправлялись.
Хорошо знаком только с php.
код автора - http://pastebin.com/VJc7w0tp
страница управления авторами.
/admin/authors/
[Новый автор]
имя-фамилия [редактировать] [удалить]
Кнопка удалить работает.
Создать нового и редактировать - не работает.
При создании нового - переходит в admin/authors/?add
Открывается форма
label+input
[Добавить автора]
Ввожу данные, жму, попадаю на страницу admin/authors/addform
с ошибкой 404.
С редактированием аналогично, только переход на /editform
Код автора работает, книга Кевин Янк - PHP и MySQL. От новичка к профессионалу - 2013
7 глава.
Некоторые данные под себя делал, поэтому вариант скопировать код у автора не подходит, 3 раза прочитал, все нормально, не могу найти ошибки.
у автора книги скрипт не перенаправляет на /addform или /editform
>>Возможно, он рассчитан на использование совместно с аннотациями, тогда ведь получается проще? Или с каким-то конфигом, где описаны ограничения.
Да, в официальной документации приводятся примеры использования yaml конфига, как альтернатива аннотациям, но проблема в том, что я использую силекс, а не симфони и тут все не так, как в симфони. В сети не так уж много инфы по использованию некоторых модулей симфони с силексом. И хотя я не искал варианты использования yaml конфигов с силексом, в надежде на кастомные констранты, я пока не могу представить как это все будет выглядеть. Чем больше модулей симфони прикручивается к силексу, тем больше мне кажется, что он изначально не предназначался для этого.
>>Что касается регистрации - регистрация это создание пользователя, и наверно, там в качестве модели для формы выгоднее использовать сразу модель пользователя, нет?
Я об этом и хотел написать с самого начала. Если мы используем модель пользователя для регистрации, то зачем создавать отдельную модель для логина, ведь можно использовать ту же модель пользователя. И тут можно использовать уже используемый доктриной entity класс, ведь нам всеравно нужно при логине получить данные из бд о пользователе, которого логиним.
>>То есть как я понял, провайдер используется как этакий "мост" для простого подключения сторонней библиотеки в Silex.
Да, это так. Я много гуглил этот вопрос и нагуглил то, что если я собираюсь использовать констранты с кастомным валидатором, ведь мне нужно впихнуть в валидатор entity manager, то мне придется зарегистрировать эти валидаторы как отдельный сервис.
>>Я все же думаю, что это уже не задача формы - проверять логин.
Но это же не форма. Это валидатор, в который я передаю форму с констрантой. Ну и если это все-таки форма, то я тогда ничего не понимаю. Этот класс создан специально для того, чтобы проверить есть ли такая связка логин+пароль в базе, и если этот класс не может этого сделать, то зачем он тогда нужен?
>>Еще у тебя я вижу в коде стремление писать все в контроллерах и не использовать сервисы. Ну например, регистрация/авторизация/получение текущего пользователя - для этого нет готовых методов. Нет сервиса для работы с комментариями. То есть ты вместо MVC пишешь толстые контроллеры.
Что значит использовать сервисы? У тебя есть уроки использования или примеры таких сервисов?
>>Возможно, он рассчитан на использование совместно с аннотациями, тогда ведь получается проще? Или с каким-то конфигом, где описаны ограничения.
Да, в официальной документации приводятся примеры использования yaml конфига, как альтернатива аннотациям, но проблема в том, что я использую силекс, а не симфони и тут все не так, как в симфони. В сети не так уж много инфы по использованию некоторых модулей симфони с силексом. И хотя я не искал варианты использования yaml конфигов с силексом, в надежде на кастомные констранты, я пока не могу представить как это все будет выглядеть. Чем больше модулей симфони прикручивается к силексу, тем больше мне кажется, что он изначально не предназначался для этого.
>>Что касается регистрации - регистрация это создание пользователя, и наверно, там в качестве модели для формы выгоднее использовать сразу модель пользователя, нет?
Я об этом и хотел написать с самого начала. Если мы используем модель пользователя для регистрации, то зачем создавать отдельную модель для логина, ведь можно использовать ту же модель пользователя. И тут можно использовать уже используемый доктриной entity класс, ведь нам всеравно нужно при логине получить данные из бд о пользователе, которого логиним.
>>То есть как я понял, провайдер используется как этакий "мост" для простого подключения сторонней библиотеки в Silex.
Да, это так. Я много гуглил этот вопрос и нагуглил то, что если я собираюсь использовать констранты с кастомным валидатором, ведь мне нужно впихнуть в валидатор entity manager, то мне придется зарегистрировать эти валидаторы как отдельный сервис.
>>Я все же думаю, что это уже не задача формы - проверять логин.
Но это же не форма. Это валидатор, в который я передаю форму с констрантой. Ну и если это все-таки форма, то я тогда ничего не понимаю. Этот класс создан специально для того, чтобы проверить есть ли такая связка логин+пароль в базе, и если этот класс не может этого сделать, то зачем он тогда нужен?
>>Еще у тебя я вижу в коде стремление писать все в контроллерах и не использовать сервисы. Ну например, регистрация/авторизация/получение текущего пользователя - для этого нет готовых методов. Нет сервиса для работы с комментариями. То есть ты вместо MVC пишешь толстые контроллеры.
Что значит использовать сервисы? У тебя есть уроки использования или примеры таких сервисов?
Привет аноны.
Решил учить PHP, пока для себя, потом как получится. Несмотря на ококолоайтишную вышку, последний раз пытался погромировать в школе, на делфи.
Ну да ладно, делаю сейчас по вот этой ссылке - http://archive-ipq-co.narod.ru/, и такое чувство возникает странное, я вроде не совсем идиот, но в этих задачках больше сижу и думаю как как это реализовать в коде, а как хотя бы понять что вообще требуется и как бы я делал это допустим вручную. (например считал каждый месяц на калькуляторе в задачке про школьника). Я совсем конченый или еще нет? Что это? Проблема с воображением или что? С этим можно жить?
вот и задачка, мож кто посмотрит
http://ideone.com/TKF4VQ
Наверняка и тут хуйни понаписал, всмысле в посте.
В копилку про забавные вакансии.
Постарался еще сделать красивый вариант калькулятора. :З
http://ideone.com/q71hdO
интересная штука, оказалось, планирую прикрутить еще скобочки туда, а потом последовательность действий человеческую
Я зеленый нуб, пошел качать php для Windows для обучения и увидел вот такое:
Thread Safe и Non Thread Safe.
Начал искать информацию о разницы, на русском ничего не нашел. На англ не понял, ибо база знань информатики желает быть лучше.
Прошу совета.
Смысла нет?
1. Там старый софт.
2. Помогать тебе если что то не будет там работать не будут. Т.к. Что там авторы намутили всем лень разбираться.
Если ты почитаешь ссыли из шапки, то там есть гайд от ОПа по установке PHP и прочее в Windows. Вообще, там есть все что нужно человеку для начала изучения. + Мы всегда поможем. Только учти, что тред медленный.
Для использования вместе с Апачом под виндой нужен ThreadSafe. Для использования без Апача или под линуксом можно и Non-Thread-Safe. Это поддержка многопоточности.
Да, я видел шапку и внимательно ее прочитал, но все равно туго доходило. В общем, я пока прохожу:
1. на этом сайте php https://www.codecademy.com/ - немного сложновато в понимании информации и поэтому сижу со словарем. Но, главный минус - это примитивные задания.
2. ну и скачал книжку PHPTheRightWay.
3. сервер скачал отсюда http://www.wampserver.com (вроде уже не старые технологии), или это плохая идея и мне таки лучше всего уже самому учиться ставить апачи, пхп, mysql?
чем-то манить работать в направлении back end.
https://github.com/laravel/laravel/graphs/contributors
>>925115
Йобаный буллшит. Просто поставь себе Linux и caddyserver.
типа рейлс фор зомбиз. Там тебе и про руби, и про рейлс, и про архитектуру, а пока рассказывают, ты делаешь свой твиттер.
Большую часть книг и сайтов что я нашел можно выкинуть, т.к они втирают про сам язык и программирование, а я хочу про его применение в вебе - написать сайт с формами етц, опыт набивать короче.
Заранее спасибо большое. :3
Мне чисто из интереса.
\тот самый нуб-анон, который спрашивал про Thread Safe и Non Thread Safe, и про denwer\
Веб-сервис это что у тебя? Сделай задачку про студентов, а там уже можешь аналог твиттора пробовать пилить.
Помогите, пожалуйста, понять этот эльфийский:
preg_match('/^['.$Line['text'][0].']{3,}[ ]([\w-]+)?[ ]$/', $Line['text'], $matches)
Я вот почитал статью: http://archive-ipq-co.narod.ru/l1/regexp.html и расшифровал фразу как-то так:
Ищется точное совпадение шаблона в строке $Line['text'] (символы ^ в начале и $ в конце строки). В начале ищется повторение три и больше раз строки, которая находится в $Line['text'][0]. Потом идет много пробелов. Потом идет строка из одного или нескольких буквенных символов, которые могут встретиться или не встретиться. Потом идет один или несколько пробелов
И еще, стоит ли писать вот такую обертку вокруг метода реализации чтобы не кидать каждый раз свойства объекта ему в аргументы http://ideone.com/Bom4oP ?
это сайт, который включает похапе, например. А я в похапе умею только задачки решать, а не в похапе морской бой могу написать на крестах, лол.
>>925572
> Ну а применить знания из "рейлс фор зомбиз" в PHP нельзя? Там наверное рассказывают про HTTP, ну и в рельсах есть MVC, ActiveRecord, ServiceObjects. Всё это используется в PHP, просто открываешь доку по заинтересовавшему тебя фреймворку и пишешь приложение.
проблема в том, что я их не проходил, просто знаю, что там вот так, а не иначе. В серверном программировании я ноль полный.
я вот тут прикрутил, например. >>925011
Свитч повышает читаемость кода, а так можешь и ифы пихать.
Я что-то даже не понял, что твой код делает, а значить и не понял, почему там прикрутили switch-case. Можешь объяснить?
/я только, как 2 изучаю php/
Оп, а вот в этой строке здесь https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md :
> Тут есть 2 варианта, как это хранить: в виде 2-мерного массива, например this.openCells[y][x] = true помечает клеточку открытой ...
...ты имел ввиду действительно двумерный массив ([ [true, false], [false, false], [false, true], [true, false]) или словарь ( {1: {1: true, 2:false}, 2: {1: false, 2: false}, 3: {1:false, 2: true})? Мне кажется, ты имел ввиду первое, а я делаю через второе и в некоторых местах трудности (например мучаюсь как создать мины при такой структуре чтобы не было вечного рандома. Когда у меня был массив объектов Cell я просто делал на него shuffle и превращал в мины n-ое количество равное числу мин).
не хочу смущать тут всех своим вариантом, потому что я либо подскажу новичкам развернутое и полное решение, либо задурю им мозги. Напиши мне в телеграмм? Да и учить вместе веселее будет.@Kyooni
Я просто вообще не вдупляю как пэхапэ в общем связывается в коде с html и как его туда вставлять
А вот еще интересный список, на который я наткнулся в сети: http://reestr.rublacklist.net/distributors/
Посоветуйте пожалуйста книжку почитать, желательно современную, но на русише.
В программировании не новичок, с php уже дело имел, сейчас нужно все это вспомнить.
Картинка в посте очень правдоподобная :>
>Тут (англ) есть сложные пояснения: http://dev.mysql.com/doc/refman/5.7/en/charset-unicode-sets.html
>Тут немного на русском: http://gahcep.github.io/blog/2013/01/05/mysql-utf8/
А какие проблемы могут решить эти пояснения?
>https://github.com/someApprentice/Students/blob/master/app/init.php#L10
>> use App\Model\Helper\LoginHelper;
>> use App\Model\Helper\LoginHelper as Authorizer;
>Тут зачем-то класс 2 раза импортирован
Хотел задать пространство имен для ЛогинХелпера. Ведь это же и Авторайзер в то же время.
>https://github.com/someApprentice/Students/blob/master/app/Controller/Controller.php
>Вот тут конечно методы вроде getPageQuery или getSortQuery явно не очень правильно смотрятся. Ведь это базовый контроллер, и в него надо класть только то, что может пригодиться в любом контроллере, а это явно функции для вывода таблицы постранично. Возможно, стоило их поместить в унаследованный класс BaseTableController, возможно - вынести в Helper. В принципе, это исправлять не надо, но в дальнейшем надо задумываться, куда лучше поместить метод.
А я спрашивал насчет этих методов. Конечно, сейчас мне очевидно, что по-хорошему нужно сделать какой-нибудь дочерний контроллер для этих методов. Переносить их в Хелпер - плохая идея потому, что хелпер это модель, и заниматься GET-запросами должны контроллеры.
>Действия Search и просто вывод таблицы стоило бы объединить. Это ведь почти одно и то же, и там и там есть и сортировка, и пагинация, проще использовать один код и в нужных местах просто поставить пару if.
Не совсем понимаю как это сделать - согласен, и там и там есть общий код, но он минимален, и ведет себя как свойственно контроллеру:
1. получаем запросы\конфигурацию.
2. создаем паджер, обращаемся к БД.
3. выводим шаблон
Общие в этом только получение конфигураций и создание паджера. Конечно, я мог бы объеденить ещё объеденить метод обращения к БД, отправляя пустой запрос в метод поиска чтобы получить все записи, но помимого этого для шаблона нужен и паджер и записи, к тому же ему могут понадобиться и запросы. Получается замкнутый круг.
Или стоит как-то исхитриться? Ради чего?
>https://github.com/someApprentice/Students/blob/master/app/Controller/SearchAction.php#L19
>> if ($_GET) {
>Я бы не советовал так проверять, так как в теории в GET могут быть какие-то неотносящиеся к поиску параметры (их могут добавить позже). Лучше проверять, например, что $_GET['query'] не пуст.
А если пользователю захочется найти всех пользователей, т.е. сделать пустой запрос? Или, например, я хочу объеденить способ получения всех записей и поиска, передавая в метод тот же пустой запрос? Или, например, я хочу объеденить методы получения всех записей и поиска, опять же отправляя пустой запрос?
Обязательно ли вообще делать проверку на гет-запрос?
https://github.com/someApprentice/Students/commit/25476b949c720cc75eb8b3c8f03edb0b827ae196#diff-f2d1af9c4ccbbb1fb81e9cd1a194f5e3L19
>> $this->render('templates/search.phtml', compact('query', 'pager'));
>> } else {
>> $this->render('templates/search.phtml');
>Вот это тоже странно. Один и тот же шаблон может вызываться как с переменными, так и без. Как писать надежный код, если ты даже не знаешь, передана такая переменная или нет? Да и неудобно, перед любым использованием переменной надо писать if (isset()). Не стоит так делать, тут стоит оставить единственный вызов render, в который передаются все нужные переменные.
А как выводить один и тот же шаблон если к странице могут обратиться и без запроса? Этих переменных просто может не быть, потому что в них нету пока надобности.
>Теперь посмотрим на использование класса Pager. Это класс, в котором много неудачных решений:
>
>Во-вторых, почему-то для передачи аргументов в конструктор используется массив. Причем вместе идут как параметры, которые не зависят от номера страницы (query, sort), так и номер текущей страницы. Тоже нелогично, мне кажется, что номер страницы должен идти отдельно.
>Опять же, если мы хотим использовать класс, то неочевидно, что за массив надо ему передать, и что в нем должно быть.
Это не просто массив а массив запросов - $queries. По моему, по названии перемной должно быть понятно что это запросы. Я использовал такой подход в попытке сразу передать этот массив в метод http_build_query($queries) на практике ничего не получилось и, к тому же, не всегда известно какие запросы понадобиться передать ещё для формирования ссылок.
>Тут (англ) есть сложные пояснения: http://dev.mysql.com/doc/refman/5.7/en/charset-unicode-sets.html
>Тут немного на русском: http://gahcep.github.io/blog/2013/01/05/mysql-utf8/
А какие проблемы могут решить эти пояснения?
>https://github.com/someApprentice/Students/blob/master/app/init.php#L10
>> use App\Model\Helper\LoginHelper;
>> use App\Model\Helper\LoginHelper as Authorizer;
>Тут зачем-то класс 2 раза импортирован
Хотел задать пространство имен для ЛогинХелпера. Ведь это же и Авторайзер в то же время.
>https://github.com/someApprentice/Students/blob/master/app/Controller/Controller.php
>Вот тут конечно методы вроде getPageQuery или getSortQuery явно не очень правильно смотрятся. Ведь это базовый контроллер, и в него надо класть только то, что может пригодиться в любом контроллере, а это явно функции для вывода таблицы постранично. Возможно, стоило их поместить в унаследованный класс BaseTableController, возможно - вынести в Helper. В принципе, это исправлять не надо, но в дальнейшем надо задумываться, куда лучше поместить метод.
А я спрашивал насчет этих методов. Конечно, сейчас мне очевидно, что по-хорошему нужно сделать какой-нибудь дочерний контроллер для этих методов. Переносить их в Хелпер - плохая идея потому, что хелпер это модель, и заниматься GET-запросами должны контроллеры.
>Действия Search и просто вывод таблицы стоило бы объединить. Это ведь почти одно и то же, и там и там есть и сортировка, и пагинация, проще использовать один код и в нужных местах просто поставить пару if.
Не совсем понимаю как это сделать - согласен, и там и там есть общий код, но он минимален, и ведет себя как свойственно контроллеру:
1. получаем запросы\конфигурацию.
2. создаем паджер, обращаемся к БД.
3. выводим шаблон
Общие в этом только получение конфигураций и создание паджера. Конечно, я мог бы объеденить ещё объеденить метод обращения к БД, отправляя пустой запрос в метод поиска чтобы получить все записи, но помимого этого для шаблона нужен и паджер и записи, к тому же ему могут понадобиться и запросы. Получается замкнутый круг.
Или стоит как-то исхитриться? Ради чего?
>https://github.com/someApprentice/Students/blob/master/app/Controller/SearchAction.php#L19
>> if ($_GET) {
>Я бы не советовал так проверять, так как в теории в GET могут быть какие-то неотносящиеся к поиску параметры (их могут добавить позже). Лучше проверять, например, что $_GET['query'] не пуст.
А если пользователю захочется найти всех пользователей, т.е. сделать пустой запрос? Или, например, я хочу объеденить способ получения всех записей и поиска, передавая в метод тот же пустой запрос? Или, например, я хочу объеденить методы получения всех записей и поиска, опять же отправляя пустой запрос?
Обязательно ли вообще делать проверку на гет-запрос?
https://github.com/someApprentice/Students/commit/25476b949c720cc75eb8b3c8f03edb0b827ae196#diff-f2d1af9c4ccbbb1fb81e9cd1a194f5e3L19
>> $this->render('templates/search.phtml', compact('query', 'pager'));
>> } else {
>> $this->render('templates/search.phtml');
>Вот это тоже странно. Один и тот же шаблон может вызываться как с переменными, так и без. Как писать надежный код, если ты даже не знаешь, передана такая переменная или нет? Да и неудобно, перед любым использованием переменной надо писать if (isset()). Не стоит так делать, тут стоит оставить единственный вызов render, в который передаются все нужные переменные.
А как выводить один и тот же шаблон если к странице могут обратиться и без запроса? Этих переменных просто может не быть, потому что в них нету пока надобности.
>Теперь посмотрим на использование класса Pager. Это класс, в котором много неудачных решений:
>
>Во-вторых, почему-то для передачи аргументов в конструктор используется массив. Причем вместе идут как параметры, которые не зависят от номера страницы (query, sort), так и номер текущей страницы. Тоже нелогично, мне кажется, что номер страницы должен идти отдельно.
>Опять же, если мы хотим использовать класс, то неочевидно, что за массив надо ему передать, и что в нем должно быть.
Это не просто массив а массив запросов - $queries. По моему, по названии перемной должно быть понятно что это запросы. Я использовал такой подход в попытке сразу передать этот массив в метод http_build_query($queries) на практике ничего не получилось и, к тому же, не всегда известно какие запросы понадобиться передать ещё для формирования ссылок.
>https://github.com/someApprentice/Students/blob/master/templates/index.phtml#L10
>> <a href="logout.php?token=<?= $token ?>&go=/public/index.php">logout?</a>
>LogOut лучше бы делать отправкой POST-запроса (например, пустой формы с одной кнопкой). Это ведь изменяет состояние залогиненности, а GET запросы обычно не меняют состояние сервера.
А я спрашивал почему в популярных соц.сетях разлогинивание сделано через форму.
Нужно делать сущность LogoutForm со всеми вытекающими для этого?
Есть какой-нибудь css хинт чтобы форма с кнопкой выглядела как ссылка или как любой другой элемент?
>Это ведь изменяет состояние залогиненности, а GET запросы обычно не меняют состояние сервера.
А можно по подробней на этот моменте? Чем POST-запросы лучше GET-запросов? Ведь по сути они оба просто массивы.
И как разлогинивание меняет сосотяние сервера? Мы же не используем локальные кукисы и не добавляем ничего нового например в БД.
>Могу предложить посмотреть вот такую реализацию класса авторизации: https://github.com/kubk/students/blob/master/src/AuthService.php
https://github.com/kubk/students/blob/11bc17f3b87e48f04cebc2715dd415af89d2332b/src/AuthService.php
Там на Симфони написано - сложно определить в чём кардинальное различие. Подозреваю что там то же устройство, только другая технология. Можно подсказку на что мне нужно обратить внимание?
>use Symfony\Component\HttpFoundation\{ParameterBag, Cookie, ResponseHeaderBag};
Что означает тут синтаксис в литералах? Это перечисление разных классов в одной и той же директории?
>public function registerStudent(Student $student): Student
И тут, что означает : Student в объявлении функции?
Мне нравиться такой код. Я тоже научусь пользоваться фреймворками.
>К нему даже написан тест, который проверяет например, что если вызват метод залогинивания, получить куки и скормить их этому классу, то мы получим нужного пользователя:
У меня тоже можно написать такой тест, у меня есть функция которая возвращает пользователя по кукисам: https://github.com/someApprentice/Students/blob/master/app/Model/Helper/LoginHelper.php#L16
>Далее, тут https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L58 мы не завершаем контроллер после редиректа.
Про это я тоже спрашивал. Мне сразу показалось не естественно делать ридерект по серди класса, к тому же, как вы тогда ответили, нет смысла что-то делать после ридеректа. Нужно выполнить exit()\die() после него? Не совсем понимаю как правильно завершить работу контроллера.
>https://github.com/someApprentice/Students/blob/master/app/Model/Validators/StudentValidations.php
>Константы вроде const GENDER_MALE = "Man"; логичнее поместить в студента. Это ведь его свойства, а не свойства валидатора. В валидатор можно поместить константы вроде VALIDATE_MIN_LENGTH, относящиеся к валидации. Представь, что нам не нужна валидация и мы удалим этот класс. И вместе с ним удалятся константы, обозначающие пол, хотя они-то нам еще нужны.
А класс валидации не присваивает эти значения. Присваивает форма. Класс валидации только проверяет что значения правильные, и это его задача знать внутреннее устройство или механику проверки.
>А интересно, почему здесь StudentValidations передается как зависимость, а не наследуется? По идее ведь RegisterFormValiadtions мог бы расширять StudentValidations, или это неудобно?
Нет, вы правы. Это ошибка.
Просто на тот момент казалось, что это разные сущности и не могут наследоваться друг от друга.
>И тут получается похожий код:
>
>
>Может стоит сделать метод validateByRules($entity, $rules, $errorList) в базовом классе?
А может не нужно придумывать такую сложную функцию в таком маленьком задании? Я могу придумать её, только это сложно будет и отнимет временя и силы. Такая головоломка меня вряд ли чему-то научит.
Может даже функция не сложная, но не хочется даже думать как её сделать.
>Хотя, конечно, не очень понятно, как тогда там вставить код, меняющий правила проверки пароля:
Мне вообще кажется, что смена пароля должна осуществляться отдельно.
https://github.com/someApprentice/Students/blob/master/app/Controller/Controller.php#L11
Это ошибка отправлять пустую строку если нет запроса? А если бы это было отдельный случай к примеру получение запроса поиска?
https://github.com/someApprentice/Students/
>Ну и дальше стоит начинать изучать фреймворки, хотя бы микрофреймворки для начала, так как тут мы писали все с нуля, и на практике конечно эффективнее учиться использовать готовый код. Также потом стоит глянуть шаблонизатор twig и, может быть, библиотеку-data mapper для работы с БД под названием Doctrine.
Не терпиться уже начать!
>Doctrine
Даже после прочтения сложных определений в гугле не до конца понимают что это такое. Оно заменяет Mysql или любую другу БД?
....
Это нормально отвлекаться на постороние вещи во время изучения? Хоть я и настроен на продуктивную работу, всё равно, сложно сконцентрироваться на каком-то сложном вопросе. Я подумал, что мозг может работать в пассивном режиме, например, как ты когда что-то пытаешься вспомнить, и это потом сомо, через какое-то время, вспыхивает у тебя в голове. Ведь, в конце концов я самостоятельно решаю этот вопрос.
С этим всё нормально? Я хорошо справляюсь?
>https://github.com/someApprentice/Students/blob/master/templates/index.phtml#L10
>> <a href="logout.php?token=<?= $token ?>&go=/public/index.php">logout?</a>
>LogOut лучше бы делать отправкой POST-запроса (например, пустой формы с одной кнопкой). Это ведь изменяет состояние залогиненности, а GET запросы обычно не меняют состояние сервера.
А я спрашивал почему в популярных соц.сетях разлогинивание сделано через форму.
Нужно делать сущность LogoutForm со всеми вытекающими для этого?
Есть какой-нибудь css хинт чтобы форма с кнопкой выглядела как ссылка или как любой другой элемент?
>Это ведь изменяет состояние залогиненности, а GET запросы обычно не меняют состояние сервера.
А можно по подробней на этот моменте? Чем POST-запросы лучше GET-запросов? Ведь по сути они оба просто массивы.
И как разлогинивание меняет сосотяние сервера? Мы же не используем локальные кукисы и не добавляем ничего нового например в БД.
>Могу предложить посмотреть вот такую реализацию класса авторизации: https://github.com/kubk/students/blob/master/src/AuthService.php
https://github.com/kubk/students/blob/11bc17f3b87e48f04cebc2715dd415af89d2332b/src/AuthService.php
Там на Симфони написано - сложно определить в чём кардинальное различие. Подозреваю что там то же устройство, только другая технология. Можно подсказку на что мне нужно обратить внимание?
>use Symfony\Component\HttpFoundation\{ParameterBag, Cookie, ResponseHeaderBag};
Что означает тут синтаксис в литералах? Это перечисление разных классов в одной и той же директории?
>public function registerStudent(Student $student): Student
И тут, что означает : Student в объявлении функции?
Мне нравиться такой код. Я тоже научусь пользоваться фреймворками.
>К нему даже написан тест, который проверяет например, что если вызват метод залогинивания, получить куки и скормить их этому классу, то мы получим нужного пользователя:
У меня тоже можно написать такой тест, у меня есть функция которая возвращает пользователя по кукисам: https://github.com/someApprentice/Students/blob/master/app/Model/Helper/LoginHelper.php#L16
>Далее, тут https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L58 мы не завершаем контроллер после редиректа.
Про это я тоже спрашивал. Мне сразу показалось не естественно делать ридерект по серди класса, к тому же, как вы тогда ответили, нет смысла что-то делать после ридеректа. Нужно выполнить exit()\die() после него? Не совсем понимаю как правильно завершить работу контроллера.
>https://github.com/someApprentice/Students/blob/master/app/Model/Validators/StudentValidations.php
>Константы вроде const GENDER_MALE = "Man"; логичнее поместить в студента. Это ведь его свойства, а не свойства валидатора. В валидатор можно поместить константы вроде VALIDATE_MIN_LENGTH, относящиеся к валидации. Представь, что нам не нужна валидация и мы удалим этот класс. И вместе с ним удалятся константы, обозначающие пол, хотя они-то нам еще нужны.
А класс валидации не присваивает эти значения. Присваивает форма. Класс валидации только проверяет что значения правильные, и это его задача знать внутреннее устройство или механику проверки.
>А интересно, почему здесь StudentValidations передается как зависимость, а не наследуется? По идее ведь RegisterFormValiadtions мог бы расширять StudentValidations, или это неудобно?
Нет, вы правы. Это ошибка.
Просто на тот момент казалось, что это разные сущности и не могут наследоваться друг от друга.
>И тут получается похожий код:
>
>
>Может стоит сделать метод validateByRules($entity, $rules, $errorList) в базовом классе?
А может не нужно придумывать такую сложную функцию в таком маленьком задании? Я могу придумать её, только это сложно будет и отнимет временя и силы. Такая головоломка меня вряд ли чему-то научит.
Может даже функция не сложная, но не хочется даже думать как её сделать.
>Хотя, конечно, не очень понятно, как тогда там вставить код, меняющий правила проверки пароля:
Мне вообще кажется, что смена пароля должна осуществляться отдельно.
https://github.com/someApprentice/Students/blob/master/app/Controller/Controller.php#L11
Это ошибка отправлять пустую строку если нет запроса? А если бы это было отдельный случай к примеру получение запроса поиска?
https://github.com/someApprentice/Students/
>Ну и дальше стоит начинать изучать фреймворки, хотя бы микрофреймворки для начала, так как тут мы писали все с нуля, и на практике конечно эффективнее учиться использовать готовый код. Также потом стоит глянуть шаблонизатор twig и, может быть, библиотеку-data mapper для работы с БД под названием Doctrine.
Не терпиться уже начать!
>Doctrine
Даже после прочтения сложных определений в гугле не до конца понимают что это такое. Оно заменяет Mysql или любую другу БД?
....
Это нормально отвлекаться на постороние вещи во время изучения? Хоть я и настроен на продуктивную работу, всё равно, сложно сконцентрироваться на каком-то сложном вопросе. Я подумал, что мозг может работать в пассивном режиме, например, как ты когда что-то пытаешься вспомнить, и это потом сомо, через какое-то время, вспыхивает у тебя в голове. Ведь, в конце концов я самостоятельно решаю этот вопрос.
С этим всё нормально? Я хорошо справляюсь?
К чему эти коты в шапке, весь все знают, кто является настоящим маскотом языка PHP.
21 с лишним хромолет. Есть высокий интерес к этому, но даже школьного курса информатики не помню, могу только шиндовс переустановить и по интернету полазить. С чего начать и через какой срок уже можно начать парится макакой. Мамка гонит, тем не менее время есть и желание тоже. Сильно не обоссывайте. В шапке хорошая инфа, но мне хочется, чтобы аноны поделились со своим опытом, с чего начинать
http://ideone.com/qRGcXB - Компания Вектор.
http://ideone.com/WeGENR - Вопросы с абстрактными классами.
http://ideone.com/9wsu8g - Игра в кубики;
http://ideone.com/sRvu3e - мини "калькулятор";
http://ideone.com/8x1TeV - Игра в кубики с Пекой;
http://ideone.com/pJ4TO8 - Пхп без смс;
http://ideone.com/8GN4S3 - депозит;
http://ideone.com/O7tPEA - Айфон в кредит;
http://ideone.com/7SJzev - Задание со стихами;
http://ideone.com/MGrQkg - Регулярка на номера телефонов
Ебать, спасибо тебе за эти задания с циклом, ибо я чёт никак не мог вдуплить как оно работает, а теперь дошло.
Я даун и не так понял задания, лол.
Научился выводить скрипт на страницу и отправлять запросы в бд(MySQL) + немного работы через PhpMyAdmin.
Стоит ли мне сразу вскочить на фреймворк и набегать на создание сайтов, обмазываясь гайдами, т.е поступать в стиле руби он рейлс или надо сначала осознать как это все делать на чистом пхп, итд?
посмотрел тут решение задачки про студентов, охуел от количества файлов. Без фреймворков можно с ума сойти же.
Когда перейдешь к фреймворкам после задач, то понимаешь, что все твои решения задач это корявые велосипеды и в реальной разработке не нужны. Но они учат понимать язык и его возможности.
>>92659>>926597
нужно знать как работает инструмент, чтобы им пользоваться.
сначала сделай сам, потом можешь юзать фреймворки
Весьма исчерпывающие ответы - спасибо!
>PHP 7
У меня уже давно phpfmt выдает ошибку на старую версию - из-за чего везде на гитхабе у меня съежает оформление кода, потому что раньше для отступов я использовал символ табуляции (т.к. это стояло по умолчанию в настройках sublimetext): https://github.com/someApprentice/Students/blob/master/app/Controller/Controller.php
Ради фана или развития может быть, а по факту обычному разработчику этого знать не надо.
Тебе совершенно не надо знать как работает мешалка массива, когда тебе его тупо надо перемешать.
Или тебе должно быть похер как отправляются данные в БД, твоя задача лишь знать куда и что отправляешь, а как уже оно там будет работать тебе должно быть чхать, если оно правильно работает.
Кек, представил, если бы врачи работали по таким принципам:
- доктор, у меня болит голова
- ага ага, выпейте вот это, я не знаю как, но мне сказали что оно работает
Хотел порофлить но так походу в 95% случаях и происходит, лол
(No offence)
но ведь создавая с десяток MVC компонентов самостоятельно я убью неделю на то, что требует часа с фреймворком. Плюс, я могу просто посмотреть что создает именяет то или иное мое действие, и точно так же разобраться.
Ну и я не имею ввиду, что я просто научился писать if else и сразу ВБОЙ ЕБАНА. Я почитаю самые основы этой ебони, а дальше фреймворки.
>>926603
но зачем язык и возможности, если я буду знать возможности фреймворка?
в смысле, если я знаю максимум того, что выжму из фреймворка, мне не нужно знать то, что я могу выжать из пхп вне фреймворка, потому что это будет какой нибудь лютый велосипед для нестандартного случая.
>>926644
мрт - тот же фреймворк, который показывает что у тебя в голове, например. Врачу не нужно знать молекулярной биологии и прочих ужасов, чтоб посмотреть на снимок и сказать, что у тебя мозг рака.
батя врач, ты сказал чистую истину касательно 99% врачей
В общем, вы меня вдохновили на почитать основы, как минимум.
Подскажите еще, пожалуйста, какой фреймворк актуален в сфере зарабатывания денег при выполнении работы рабодниками на работе?
я так понимаю, что йии2 и симфони2 сейчас топовые, но который из них?
а что сложного в том, чтобы сделать на чистом пхп? никто же не заставляет нуба сразу все это на МВЦ ООП хуярить (хотя было бы неплохо)
то, что на MVC это логически проще, что ли. Ну или это потому что я уже почитал про мвц, а про то, как это сделать на чистом пхп я не могу сообразить, потому что в сети мало толковой информации про такое.
Ну и потому что это будет некрасивым, медленным велосипедом. Так то, я могу тебе и на реакте сейчас написать сайт со всем этим и уместить в одном index.js, но это будте жутко уродливо.
>Разница есть
Тогда почему обычный метод можно вызвать статический через ::, а статический обычным -> ?
Вопросы все еще по MVC саперу. А вот в какой-то момент я буду тестировать несколько domView, а там на html странице будут повторяющиеся id. От id в шаблонах наверное вообще отказаться тогда и заменить на классы?
>про то, как это сделать на чистом пхп я не могу сообразить
1) У тебя не должно быть глобальных переменных;
2) Только функции, принимающие данные на вход и бросающие в ответ изменённые данные;
3) Функции эти надо собрать в пространства имён, в виде дерева, а данные — в классы;
4) На вход им передавать другие функции, создавая таким образом композицiю их;
5) Логи и сообщения не должны оставаться всю ночь до утра;
6) Интерфейсы программ твоих приноси в дом господа, бога твоего;
7) Не еби TCP-сокет в контексте окружения его.
Аноны, почему не пашет у меня
https://github.com/codedokode/pasta/blob/master/soft/web-server.md
>Чтобы веб-сервер использовал скрипт маршрутизации, его надо запускать такой командой:
>php -S localost:9001 router.php
Запускаю не из цмд, а с опенсервера. Создал отдельные файлы, всё как надо, в результате нихуя!
хоспаде, туториал погугли (мвц на пхп), просто берешь и повторяешь. вообще умение хуярить мвц на чистом пхп - оно и не нужно по сути. Возьми фреймворк. А можешь даже попробовать взять цмску и поправить ее мвц код под свои нужды, у тебя сразу будет рабочий вариант с красивой админкой - такие задачи чаще всего и встречаются в рабоче пхпшника. Но перед этим лучше ООП поучить, про паттерны занстра, pdo почитать, а то с ходу в коде цмски не разберешься.
создай index.php, в нем скинь
$router = new Router();
$router->run();
в файле router.php опиши класс Роутер и его функционал через публичную функцию run(), и не надо пердолить сервер командами
забыл, в индексе еще ж надо файл запросить.
define('ROOT', dirname(__FILE__));
require_once(ROOT.'/components/router.php');
Что за хуйню ты ему втираешь?
Что значит "чистое пхп"? Как правило имеется ввиду отсутствие фремфорка.
ООП вообще часть языка блять.
А вот мвц это всего лишь патер, пусть пользует, хорошо и полезно.
>Как правило имеется ввиду отсутствие фремфорка
Как правило имеется в виду, что PHP сам себе сервер - без всяких Апачей.
Благодарю, анон, сейчас попробую
В фреймворках бывают недостатки и неудачные решения.
Например, в команде Yii есть какой то любитель пользовать указатели, что в пхп имхо не нужно вообще, и из за этого была большая ошибка в их RBAC модуле.
Возможности фремворка это расширение возможностей ПХП и не зная толком ПХП можно просто не понять или упустить многие возможности фреймворка.
>велосипед для нестандартного случая
Не для всех общих случае есть готовые хорошие решения, что говорить про нестандартные.
Вот хорошая статья на тему http://www.habrahabr.net/thread/1716
>мрт - тот же фреймворк
Плохой пример.
Мрт и врач это, как программист и монитор.
>Подскажите еще, пожалуйста, какой фреймворк актуален в сфере
Yii проще и популярнее в СНГ, Symfony сложнее, но вроде как более грамотно сделан.
Там еще кажется Symfony3 вышел, но я его не смотрел.
>в файле router.php опиши класс Роутер и его функционал через публичную функцию run()
Окей, а как это сделать?
И почему echo $_SERVER['HTTP_USER_AGENT'];
Показывает почему-то не мой браузер, а несколько штук?
>страницы сбрасывались значения передаваемые через гет
На PHP никак, кажется
>несколько штук
У тебя хром
http://webaim.org/blog/user-agent-string-history/
> and the user agent string was a complete mess, and near useless, and everyone pretended to be everyone else, and confusion abounded.
Именно так и вижу этот список.
Мельком прочитал эту новеллу, но все-таки как быстро узнавать какой браузер?
Благодарю, анон-с
понял, спасибо
Да. Денвер прост для установки, полно гайдов:
http://www.denwer.ru
http://www.denwer.ru/base.html
А вообще заимей хост, так проще.
Жаль что я еще и туповат. Был у меня давно хостинг+домен, хотел туда сайтик залить на джумле но так и неосилил.
Видел в какой-то книжке как делался запрос сам на себя здесь, стоит ли делать как там или и так пойдет?
Ну как я написал очевидно меньше писать придется, там же писалось что-то типа $_SERVER[EBALA_TYDA_SUDA];
опенсервер поставь. Там все включено(и пхп, и сикьюэль, и аллах) и он гипер-простой и бесплатный.
> Это нормально что он по 3 раза подряд одно и то же "случайно" выбирает? Или мой косяк?
разве array rand не возвращает случайное значение?
Попробуй выводить $рандом, а не массив[рандом]
Как последнюю задачу с куки делать то блять?
Не было ничего нихрена о ней сказано
$this->first_name = $first_name;
$this->last_name = $last_name;
}
Аноны, почему они не пишут сразу $this->$first_name?
Зачем писать first_name? какого значение этой хуйни? Она ведь нигде не употребляется больше, это не переменная, ничто
И далее
public function say_name() {
echo "My name is " . $this->first_name . " " . $this->last_name . ".\n";
}
Почему нельзя написать вместо $this->first_name просто $first_name?
Усложнено настолько, что это просто охерено упрощает остальные 100500 ситуаций.
Давай представим, что у тебя класс 40 свойств. И наизусть ты их не помнишь. Предположим, что ты добавляешь в класс новый метод, в котором ты создаешь новые переменные и работаешь с ними. Создал метод, все вроде работает, нормально вызывается, но иногда почему этот класс начинает работать непонятно как и в нем совершенно другие значения свойств, а не те, которые ты ожидаешь. Лезешь в код и видишь, что переменная $item, которую ты создал в методе оказывается перезаписала свойство $item у этого же класса, а ты не знал, что в классе такое свойство есть. И таким образом, взглянув на код ты не можешь сразу визуально определить в каком случае работа происходит с локальной переменной метода, а когда затрагивается свойство класса.
Кота зовут Лис.
Бухгалтер не я - моя тян.
А я - обычный вебмакак с котом, который не любит похапе и любит JS
Ты понимаешь, что обозначает $this->first_name? Это не то же самое, что переменная $first_name, это другая вещь. Это поле объекта. Соответственно, слева от стрелки указывается объект, а справа - название поля в нем.
Если что, в учебнике в ОП посте есть глава про ООП. Там это вроде объясняется.
>Не было ничего нихрена о ней сказано
Там же написано: "необходимо изучить куки, переменную $_COOKIE и функцию setcookie()". При запуске скрипта проверяешь, прислал ли тебя пользователь куку с именем, скажем, "hui". Если прислал, то выводишь сообщение с текущим значением, увеличиваешь ее значение на единицу и ставишь пользователю, если не прислал - то ставишь ему эту куку со значением 1. Все, блядь, больше вообще ничего не надо.
> информатика
> интерес
> PHP
Ты СОВЕРШЕННО не понимаешь в чем суть PHP. PHP это не SICP "о, привет чуваки, зацените решение задачки на скиме, писал два месяца". PHP это не псевдоинтеллектуальные обсуждаения алгоритмов и структур данных. PHP это не сложные вычисления, нейроночки или динамические интерфейсы. PHP это язык, где люди могут побыть говнокодерами — ужасными, тупыми, безразличными ко всему рабами галер, которыми они на самом деле и являются. json_decode возвращает null, а мы смеемся. Кусок стэйта рендерится на сервере в глобальной области видимости, а мы смеемся и обмазываемся миддлварями на ноде. Три сеньор разработчика спрыгнули с крыши, пытаясь отрефакторить движок корпоративной CMS на 4-й пыхе, а мы смеемся и просим еще. Лапшекод, регулярочки, критические уязвимости — мы смеемся. Мы бездушно подпишемся на любой проект, где нам заплатят дошираком, наши предпочтения не основаны на логике программирования, бесцельное внедрение сторонник библиотек — наша стихия, мы — истинное лицо вебдевелопмента.
Не буду кидать, ибо оффтоп.
А вообще - ОП-няша.
Добра ему, всех благ и в нелегком деле обучения ньюфагов.
Именно благодаря ему сей тред не тонет.
Домо, PHP-ОП-сан!
Не понимаю нихуя, вот и спросил
Потому что я ленивый славскам не очень хорошо знающий английский
Написано всё на modx, в котором нет человекочасов разбираться. Прикручивать отдельно фреймворк для апи тоже не буду отдельным костылем.
Поэтому буду делать просто php файлом в корне проекта.
Суть проблемы в следующем:
Например есть много эвентов, и сторонний сервис, который будет моё "апи" опрашивать, требует под каждый эвент отдельный урл.
Под каждый эвент я не хочу хуярить отдельный файл вот так:
site.ru/event1_scheduale.php
site.ru/event2_scheduale.php
site.ru/event3_scheduale.php
site.ru/event3_scheduale.php
На фреймворке с нормальным роутингом я бы наебенил очень просто через 1 метод в 1 классе всё и урлы бы были вида:
site.ru/api/sceduale/event1
site.ru/api/sceduale/event2
site.ru/api/sceduale/event3
Но как так изебнуться что бы сделать в 1 пхп файле так? Что бы не копипастить всё это дело?
Ебаться с htaccess?
Я бы очень хотел сделать 1 файл, что бы пусть даже был некрасивый урл типа такого:
site.ru/scheduale.php/event1
site.ru/scheduale.php/event2
site.ru/scheduale.php/event3
Написано всё на modx, в котором нет человекочасов разбираться. Прикручивать отдельно фреймворк для апи тоже не буду отдельным костылем.
Поэтому буду делать просто php файлом в корне проекта.
Суть проблемы в следующем:
Например есть много эвентов, и сторонний сервис, который будет моё "апи" опрашивать, требует под каждый эвент отдельный урл.
Под каждый эвент я не хочу хуярить отдельный файл вот так:
site.ru/event1_scheduale.php
site.ru/event2_scheduale.php
site.ru/event3_scheduale.php
site.ru/event3_scheduale.php
На фреймворке с нормальным роутингом я бы наебенил очень просто через 1 метод в 1 классе всё и урлы бы были вида:
site.ru/api/sceduale/event1
site.ru/api/sceduale/event2
site.ru/api/sceduale/event3
Но как так изебнуться что бы сделать в 1 пхп файле так? Что бы не копипастить всё это дело?
Ебаться с htaccess?
Я бы очень хотел сделать 1 файл, что бы пусть даже был некрасивый урл типа такого:
site.ru/scheduale.php/event1
site.ru/scheduale.php/event2
site.ru/scheduale.php/event3
>foreach ($question->answers as $letter => $answer) {
$question->answers должен быть ассивом или объектом, а у тебя
в описании класса там простая переменная
>public $answers; // варианты ответов
private $QueryFormPath = __DIR__ . '/' . 'queryform';
На что получаю сообщение об ошибке:
>Parse error: syntax error, unexpected '.', expecting ',' or ';'
Безобидное же действие, что не так?
Меня то они как бы устроят, но смотри я делаю вот что бы эти ребята могли опрашивать
http://mir-kvestov.ru/integration
вот их мануал:
https://docs.google.com/document/d/1b_MV2wfUgq0O9L-xjuPsbLlaVYJqNkSotaARvfBShS8/edit
Я сейчас конечно попробую протолкнуть им id квеста не в самом урле, а в гет части, но что-то мне кажется это мимо.
Особенно в той части в которой они будут делать бронь.
Одновременно гет и пост вообще работать будут?
site.ru/scheduale.php/event1
site.ru/scheduale.php/event2
site.ru/scheduale.php/event3
и буду из
$_SERVER дергать:
event1
event2
event3
кусочек. Должно сработать.
>AcceptPathInfo
Это ты про то, что бы никто не мог лазить по папкам и запускать скрипты которые не в корне лежат или другие файлы скачивать/смотреть вообще?
Спасибо за совет.
Нет, если эту хуиту не включить, то запрос вида /shitcode.php/another_shit будет просто отклонен и у тебя ничего не получится.
Но у меня уже работает, видимо для modx наворачивали, для чпу.
PHP исчерпывающее руководство
В свое время(годик назад) когда вообще ничего не понимал да и сейчас ничего не понимаю, дало неплохой старт, теперь могу спокойной пилить какие нибудь проектики для себя
А я не хвалю. Там первые несколько глав конечно хорошо подают основы ООП, а вот дальше начинается вода, так что я даже дропнул не дойдя до собственно паттернов, ну не смог я читать то, что тупо срет тебе в мозг без конкретики.
я тоже дропнул, хз, вообще оч скучно как то заходят книжочки всегда, единственное шо почти фулл прочитал, исчерпывающее руководство
ну, в голову напрашивается самое дефолтное из дефолтнейших решений
узнать сколько всего значений, потом rand, потом взять случайный элемент из массива
Конкретнее? Ты ведь не шифрование там пишешь наверно.
Если мне не изменяет память, функция rand() использует линейный генератор: https://ru.wikipedia.org/wiki/Линейный_конгруэнтный_метод
Он хоть и примитивный, но выдает довольно случайные значения. И как видно из статьи, применяется много где. Главные преимущества- очень простой и очень быстрый.
Также, есть улучшенный генератор (mt_rand) на основе Мерсенновского перемешивателя чисел: https://ru.wikipedia.org/wiki/Вихрь_Мерсенна
Он выдает более "случайные" числа. Но как и предыдущий метод, этот метод генерирует не по-настоящему случайные числа, а лишь математически вычисляемую последовательность.
Для критографии нужно использовать специальные особо надежные функции, вроде
http://php.net/manual/ru/function.random-bytes.php (PHP7+)
http://php.net/manual/ru/function.openssl-random-pseudo-bytes.php
Они используют функции ОС, на Линукс например это /dev/urandom, который собирает случайные значения из окружающей среды (время нажатия кнопок, прихода сетевых пакетов и тд). Надо понимать, что тут скорость генерации ограничена.
В серьезных системах могут использоваться аппаратные генераторы случайных чисел, которые генерируют совсем непредсказуемые последовательности, например, слушая тепловой шум (столкновения молекул): https://ru.wikipedia.org/wiki/Аппаратный_генератор_случайных_чисел
Аппаратный ГСЧ встроен в некоторые процессоры Intel: https://ru.wikipedia.org/wiki/RdRand
И если уж на то пошло, почему бы тебе не написать самому простой линейный ГСЧ и посмотреть на числа, которые он генерирует, чтобы проверить, насколько они получаются случайными? В статье википедии выше есть формулы и константы, если что. Знаний PHP особых не нужно, достаточно знать циклы.
array_rand это и делает, только она поддерживает массивы с любыми ключами, а не только с идущими подряд номерами.
>>924484
>>924780
Значит с формами и валидатором я разобрался. Хотел использовать entity класс для создания формы регистрации, но отказался и вот почему: 1 - я не храню пароль в базе, а в форме он должен быть 2 - для проверки полей на UniqueValue() нужно установить doctrine bridge и doctrine bundle и как-то это прикручивать к силексу и здесь смысл использования entity класса исчезает. Поэтому проще сделать отдельную форму и написать для нее свой unique constraint.
Все еще не ясно, что ты имеешь ввиду под использованием сервисов. В 83 треде ты писал, что:
> $app['validator'] = function() use($app){return new App\Helper\Validator($app);};
>Это по моему нарушение принципа DI, получается не DI, а Service Locator
Нужно регистрировать сервис провайдеры или что?
>Три сеньор разработчика спрыгнули с крыши, пытаясь отрефакторить движок корпоративной CMS на 4-й пыхе, а мы смеемся и просим еще
Проиграл бы если бы сам с таким не сталкивался.
Аноны, ну че вы ну, знаете ведь небось. Гет запрос аяксом по-любому все обрабатывали.
>Но роутинг осуществляется через якоря в урле
Ето невозможно
http://stackoverflow.com/questions/2317508/get-fragment-value-after-hash-from-a-url-in-php
Я работал с Yii-шным ActiveRecord и Doctrine 2, а также с чистым PDO в других проектах.
Для простых крудов да, ООП, вся фигня, код с объектвми выглядит сладко. Но описывать более сложные SQL-запросы (с подзапросами, джойнами..) через квери-билдеры в ORM - Я ЕБАЛ!!1 Это ж ебаный бойлерплейт!
->select('kokoko')
->from('yoba')
->where('1=1')
Куча ебаных геттеров-сеттеров! Структура базы, связи между таблицами описывается внутри doc-комментов! Это я про доктрину. В Yii вообще пиздец без identity map, апдейты по всем полям, даже если ты изменил одно поле.
Ну что за пиздец?
Что тебе мешает использовать DQL той-же доктрины? Как по мне, доктрина очень годная ORM. Анотации, консоль, DQL, query builder, интеграция в симфони и дохуя чего еще. Но можно просто обмазываться PDO. Никто не запрещает.
Dive into haskell
https://github.com/sylenien/php-news-site/
алсо дома лежит толстая здоровенная, сука, подробная книга по node.js, стоит ли начать читать ее вместо пыхи?
ЛЕЛ, скачал после своего вопроса точно такую же
Подскажи неосилятору как сделать эти умножения?
<?php
for($x = 1; $x <= 10; $x++){
echo "$x*$x=\n";
}
1×1 =
2×2 =
Схуяли там инъекция? Там intval, защищает от хуйни.
Схуяли sql в контроллере это не mvc? Обмажутся своими active record и ябут друг друга в жеппу! В идеале выделить работу с sql в прослойку - маппер, чтобы она не была в model. Но если нет, то где ей больше место - в model или controller?
Спасибо, не туда переменную ставил.
Не, недавно пак проебал.
>Зачем синглтон для соединения с БД?
Всмысле? А почему бы ему не быть синглтоном?
мимо-код-не-смотрел
Вроде как сделал через foreach и сомневаюсь, что так "правильно" - для одномерных массивов можно сделать array_map('trim', $array)
https://habrahabr.ru/post/13726/ -Безопасный метод авторизации на PHP
И имею вопрос. Вот пользователь. Он ввёл свои данные, скрипт поискал юзера, нашёл, сравнил пароли, при совпадении создал ему новый хеш, записал куки и перенаправил работу скрипту проверки. Скрипт проверки сравнивает то, что записано в куках с тем, что записано в базе (как я понял) и в случае совпадения, возвращает "ок".
А теперь дурацкий вопрос. Как этим пользоваться? Сами скрипты я повторил у себя на хостинге, они работают. Допустим, у меня есть ещё пара php файликов, которые хочу доверить только авторизованным пользователям. Как это сделать?
Немного исправлю ссылку: https://habrahabr.ru/post/13726/
И сразу ещё один вопрос.
Там используется конструкция
header("Location: check.php"); exit();
Я погуглил на тему header(), и везде пишут, что так отправляются заголовки html страницы. А в коде это используется для передачи управления другому скрипту. Как так-то?
Там смысл в том, что на страницах, которые ты хочешь отдавать только авторизованным пользователям, проверяются куки. Соответственно на любой такой странице тебе надо выполнять код скрипта проверки, если куки пользователя проходят проверку - отдавать содержимое, если нет - посылать нахуй или на страницу регистрации.
>>928103
Это не передача управления, а редирект на любую страницу, все правильно пишут. В статье просто демонстрируется проверка сразу после регистрации.
То есть, весь код из check.php должен выполняться перед любым кодом со страницы, (допустим, index.php), а вместо print "Привет, ".$userdata['user_login'].". Всё работает!"; писать тело страницы со всеми хидерами и футерами?
Спасибо за разъяснение.
https://laracasts.com/series/php-for-beginners
http://exercism.io/languages/php/exercises
https://code.tutsplus.com/articles/25-resources-to-get-you-started-with-php-from-scratch--net-2223
https://nixsolutions.github.io/design-patterns/
https://toster.ru/q/368853
http://onlinetestcentre.com/search/php.html
http://blog.nikolaposa.in.rs/2017/01/16/on-structuring-php-projects/
Делюсь полезными ссылками.
<a href="/{{file.link}}" download>Скачать бесплатно и без смс</a>
Или надо как-то сложнее изворачиваться?
Два массива со значениями от 1 до 10.
Если "введённое в поле" равно $arr1[$i] + $arr2[$o], то echo ссылка.
При обновлении страницы $i++, а $o+2 либо - array_rand.
Ну и вывод значений на странице соответствующий.
Не, ну это ненадежно. Пусть как у Гугла делает. "Отметьте картинки на которых есть Абу".
сделал метод getOvertimeHours(), но он выводит нули в массиве, и при выведении ошибка Notice: Array to string conversion
>Два массива со значениями от 1 до 10.
>Если "введённое в поле" равно $arr1[$i] + $arr2[$o], то echo ссылка.
>При обновлении страницы $i++, а $o+2 либо - array_rand.
>Ну и вывод значений на странице соответствующий.
Сложна. Это что, делать отдельный шаблон и перенаправлять на него для этой хуиты?
>>928336
>>928338
Это сложно? Я вот погуглил, вроде гугл капчу нельзя прикрутить на локалхост, не?
> Это что, делать отдельный шаблон и перенаправлять на него для этой хуиты?
Вроде того.
>Это сложно? Я вот погуглил, вроде гугл капчу нельзя прикрутить на локалхост, не?
Это сложнее. Прикрутить можно, если сервер на твоем локалхосте имеет доступ в Интернет.
>Вроде того.
Да не, это какой-то изъеб. Вот был же ex.ua, там можно было качать без капчи и регистраций, как они это делали не боясь что им завалят сервера?
>Это сложнее. Прикрутить можно, если сервер на твоем локалхосте имеет доступ в Интернет.
Ладно, только гугл подсказывает только сраные плагины на вордпресс.
Ну я хз, вот тут как-то сделано на жабаскрипте: http://biotualet1.ru/otzyvy.html
То, что я имел в виду.
Но там после ввода ответа и нажатия срабатывает POST, а ты бы просто ссылку выдавал.
Тебе не нужно геморроиться.
Только желающие повалить будут писать скрипты под тебя.
Ты можешь действия там менять рандомно (минус, плюс, деление, умножение) и т.п.
>928339
https://ideone.com/YfDjGW
да уже ни в чем, я просто невнимательно смотрел, вместо числа массив передавал
>Но там после ввода ответа и нажатия срабатывает POST, а ты бы просто ссылку выдавал.
В то то вот и дело, что я не знаю как это сделать.
>>928362
Ну как такое сделать я понимаю, но хотелось бы именно не "лишь бы работало", а правильно сделать.
>ex.ua
Там, где нет капчи, пользуются другими методами - типа блокировки известных прокси и ограничениями на количество запросов/одновременных подключений для остальных. Но это, вроде, уже к настройкам сервера ближе, а не PHP.
> POST
> не знаю
Прочитай первые главы учебника по пыхе "PHP 5 в подлиннике". Его принято поливать говном и считать за днище, однако это единственный учебник, где прежде, чем перейти к профессиональному программированию, освещается необходимый для этого дела минимум по запросам и формам.
Ты меня не понял. Я не хочу постоянно перенаправлять пользователя туда-сюда. Я хочу как-то впилить туда капчу.
Ну напиши простейший скрипт на JS, который AJAX запрос отправляет и получает ссылку на скачивание ответом.
>Ну напиши простейший скрипт на JS, который AJAX запрос отправляет и получает ссылку на скачивание ответом.
Я так и думал. Проблема в том, что я толком то JS не знаю, не то что бы аякс. Ну да ладно, будет как раз повод его освоить.
Вручную не нравится - классов полно, методов на вебсервисе тоже. Что хотелось бы - какую-то маппилку, которая просто берет готовые классы, маппит по какому-нибудь правилу или списку на классы из БД и все сама пишет в БД.
Измени условие на ошибочное и запусти. Это ведь Codeacademy? Днище, на самом деле, не всегда ясно, чего от тебя хотят, сам с этого охуевал. В данном случае, очевидно, хотят, чтобы ты сначала проверил верное утверждение ("3<7"), а потом изменил его на неверное ("3>7") и опять проверил.
Обзорно можно познакомиться например здесь: http://php720.com. Все довольно понятно, дизайн не выворачивает, читабельно.
я ничего не делаю, так как у меня простые пути. Тупо если запросили несуществующее id с базы например, то и хуй с ним. Будет тупо редирект на уровне контроллера если модель ничего не вернула на 404 или вообще нахуй в личный кабинет, ибо нехуй лазить по чужим заявкам/страницам/платежам и пр.
А записывать ты туда всякий мусор разрешаешь? Меня в данном случае сеттер волнует. Да конечно на сайте может быть валидация на стороне пользователя, но разве можно быть на 100% быть уверенным в том, что мы получили валидные данные от пользователя?
В принципе, там много неоднозначных ответов (в одном тесте например правильный ответ - может работать на любом вебсервере умеющем в похапе, в другом - бэкэнд это строго апач с похапе).
Дурдом, короче. Зато теперь я трижды сертифицированный говноед.
Четвертый курс (Huiload) сдавать буду на следующей неделе у них в офисе.
Не разрешаю, тоже анально игнорирую если у меня попытались хуйню протолкнуть. А вообще я даже логирую пользовательские действия, и если кто-то попытался отключить фронт-энд проверки и споткнулся на бек-энд проверках, то так и заношу в базу, что вот дескать этот хуй пытался МАМ,ХАКЕРИТЬ)))
Исключение конечно. Причем проверку не в сеттере лучше делать, а отдельный метод написать, который из сеттера вызывается. Вот метод этот уже true/false возвращает. А сам сеттер выглядит вроде этого:
if ($this->parameterIsValid($parameter)) {
$this->parameter = $parameter
}
throw new \UnexpectedValueException('Parameter is not valid');
return в if блоке забыл
ассемблер
В общем, есть на странице селект, содержимое которого берётся при генерации страницы из бд с помощью функции:
<div id="div_change">
<?php
list_selector($table, $column, 'list_id', 'list_name');
?>
</div>
Содержимое селекта - список предметов. И этот список можно менять, добавляя или удаляя предметы.
Сделал это через аякс: нажимаем кнопку, срабатывает js-крипт, запускает через ajax php-код, а тот прописывает добавление/удаление предмета в БД.
И тут мне восхотелось сделать так, чтобы список на странице тоже обновлялся без перезагрузки страницы. И сделал это так: как только у аякса срабатывает success, тут же запускает через ещё один аякс ещё один пхп-файл, который принимает через _POST данные для генерации list_selector и запускает его с этими данными. Функция возвращает собранный html-код в тот файл, который вызван через второй аякс, оттуда код возвращается в js-скрипт и селектор обновляется.
Кривость какая-то, но проще способа не нашёл. Это так и должно быть, или я уже шизею?
https://github.com/honeydev/s-blog
Пример для понажимать - http://honeytype_0ayl98.radius-host.net/
Ну хуй знает. Это при попытке создать новый пост с загруженной картинкой. Тестами покрывал?
Ну так на вскидку:
Нет публичной папки, весь код доступен пользователю.
Не используешь никаких паттернов управления данными. Все круды в контроллерах или что это там у тебя в перемешку с логикой.
4 класса в одном файле https://github.com/honeydev/s-blog/blob/master/app/src/authorization.php
Короче, может быть это и работает, но выглядит как свалка.
Нет, не покрывал не умею в функциональное тестирование.
>>928713
Просто у меня классы контроллеров не вынесены в отдельные файлы, но сами классы разделяются.
Короче буду все переделывать, спасибо. Все же нужно было изучать паттерны сначала, а не писать лижбы написать.
github/sylenien -> php site
почитай роутер.пхп. Знакомый похапе миддл одобрил, правда, с придирками.
Вообще, да. Можно. Но тогда через аякс нужно будет передавать все данные и для апдейта таблицы и для апдейта селектора. Надо будет подумать на свежую голову завтра.
Пикрелейтед ошибка:
А вот скрины SiteController, модель CategoryPhp, и вывод во вьюху.
Пробывал проверить через is_array, тогда просто ничего не выводит.
Дело в том что я прохожу по видеокурсам, и там на этом моменте все работает, даже дамп базы импортнул - все равно не пойму в чем проблема.
>Дело в том что я прохожу по видеокурсам
Какие-то хуевые видеокурсы. Чисто на первый взгляд
>include_once 'c:\openserver что-то там дальше
Во-первых, там и правда учат так пути прописывать? Во-вторых, инклюдить что-то в скрипте с объявлением класса это тихий ужас, это говнокодище за которое руки отрывать надо. Учи что такое неймспейсы и автозагрузка классов. В третьих, там какая-то дикая мешанина из статики и динамики. Короче исправь первые два пункта и потом попробуй написать тоже самое, только без статики.
Алсо нихуя не понял зачем ты там еще и тру возвращаешь. Это же пхп, тебе не надо париться по поводу того что ты там возвращаешь в методе/функции.
через PATH у меня почему-то не видит
Честно говоря, я не знаю как такая хуитень может работать без пространства имен.
Ошибки в коде ищи. Такая же параша была. Смотри, что было выше.
Ну теперь все встало на свои места. Хохол-быдлокодер хуже индуса.
Мой тебе совет:
Не надо проходить мутные курсы в стиле 'Магазин дилдаков за 24 наносекунды'.
В ОП посте есть учебник. Лучше порешай задачи из него и попробуй сделать студентов.
Сам не знаю с какой стороны подойти, но можно попробовать в тетради расписать свою архитектуру.
>>928999
Для начала стоит написать индексный файл и заинклюдить туда шаблон главной страницы, прикрутив к нему бутстрап. Потом можно уже добавлять функционал. Можете посмотреть гитхабы анонов уже сделавших студентов.
Но тогда получается, что одна страница будет лежать в нескольких колонках базы данных. А это множение колонок в таблице. ведь под каждый кусок шаблона должен выводиться определенный кусок из одной статьи.
<ul><?php
foreach($result as $item){
<li><?=$item?></li>
<?php } ?>
</ull>
<?php foreach($result2 as $text) {?>
<p class="hueta"><?=$text?></p>
<?php }?>
И подумайте сколько запросов к бд делать придется под такую жесть. И не рациональное использование таблиц получится (много таблиц с кусками одной страницы)
я тебе про сложную верстку поясняю маня.>>929072
А не одностраничники на яндекс.народе
Или мне весь текст прямо в шаблоне хранить ежели верстка сложная?
в оп посте нет ни одного гайда про веб-дев. Хохол-быдлокодер внятно объясняет азы, а в оп-посте просто блять задача в стиле "я научил тебя пользоваться палкой, иди строй ткацкий станок". Критикуешь этот курс - покажи хороший.
другой анон
Прохожу ООП по учебнику из ОП поста. Вроде не дурак, все разминочные задания сделал без особых проблем, а вот с последним (которое про компанию ВЕКТОР) впал в ступор.
Расписал абстрактные классы компания, департамент и сотрудник. Че дальше то делать ?
Нужно создавать класс для каждой профессии, или сделать профессию свойством сотрудника ?
Почему потребление кофе - не является свойством сотрудника ? Является ли свойством количество производимых страниц ? Если нет - почему ?
Где должны создаваться и добавляться в департамент сотрудники ? Это нужно делать в отдельном классе или прямо в классе департамент ?
Вопросов гораздо больше, но даже сформулировать их пока очень трудно. Помогите, прошу.
>Почему потребление кофе - не является свойством сотрудника ?
Потому что я блять хуй знает. Я ебал этой задачи рот.
Вообще обосрался на этой задаче, хотя что-то до этого получалось.
Слава богу кто-то подсказал, что так заморачиваться не надо - это нигде на хуй не нужно будет.
Синие узоры же на белом фоне. Дизайн наверное такой.
Это все потому, что ОП - няшка.
Ты слишком буквально пытаешься следовать тексту, а текст, к сожалению (а может и нет?), далек от академичности и однозначной трактовки.
> Почему потребление кофе - не является свойством сотрудника ?
Потому что потребление кофе вычисляется методом на основании профессии и ранга. То же самое с зарплатой и листами.
Текст храни в базе, разметку страницы в шаблонах.
Научись правильно оформлять запросы к БД.
Сделал запрос данных из таблицы, перевел полученное в массив, вывел массив на странице.
Все.
Компания и департамент-то с чего абстрактные? Они обычные классы.
>Че дальше то делать ?
Компанию заполняешь департаментами, департаменты сотрудниками. Пишешь методы подсчета для компании - они опрашивают департаменты. Потом для департаментов - они опрашивают сотрудников.
>Нужно создавать класс для каждой профессии, или сделать профессию свойством сотрудника ?
Нужно, там базовые ставки разные. Причем все эти классы должны наследовать от общего, у тебя же есть общие признаки для всех сотрудников.
>Почему потребление кофе - не является свойством сотрудника ?
Потому что оно из базового абстрактного класса считается. Свойство сотрудника - это его вид - руководитель, профессия, ранг.
>Является ли свойством количество производимых страниц ? Если нет - почему ?
Оно тоже считается из класса, основываясь на виде сотрудника. Ты не задаешь каждому сотруднику напрямую его страницы.
>Где должны создаваться и добавляться в департамент сотрудники ?
В контроллере. Для департамента пишешь сеттер сотрудников, контроллер добавляет их туда через сеттер. Можно без сеттера сразу при создании департамента через Dependency Injection, тогда тебе придется сначала создать сотрудников.
> Это нужно делать в отдельном классе или прямо в классе департамент ?
Это делается извне, в контроллере. В департаменте никакие сотрудники не создаются, они сами туда приходят (в нашем случае контроллер их туда добавляет).
> Расписал абстрактные классы компания, департамент и сотрудник.
А почему они абстрактные? Абстрактный класс - это класс, объект которого нельзя создать и который обычно предназначен как основа для наследования.
> Нужно создавать класс для каждой профессии, или сделать профессию свойством сотрудника ?
Можно и так, и так. Обычно отдельные классы делают, если у разных профессий разное поведение, разные методы расчета чего-нибудь.
> Почему потребление кофе - не является свойством сотрудника ?
Потому, что это производная величина, которая зависит от: профессии, ранга, статуса босса. Хранить надо исходные данные, а производные величины просто вычислять из них (если это не требует больших затрат процессорного времени конечно). Иначе ты замучаешься их обновлять при изменении исходных данных (например, ранга).
Ты случайно не путаешь базовое потребление кофе, одинаковое для всей профессии, и итоговое потребление, которое зависит от разных факторов?
> Является ли свойством количество производимых страниц ? Если нет - почему ?
Как я понимаю, это производная величина, вычисляющаяся через другие.
> Где должны создаваться и добавляться в департамент сотрудники ? Это нужно делать в отдельном классе или прямо в классе департамент ?
Тут есть 2 подхода:
- если сотрудники не могут существовать вне департамента, то может быть логично их в нем и создавать и использовать.
- если сотрудники могут существовать вне департамента, их можно добавлять в него и убирать, то очевидно, что это уже не задача департамента их создавать.
Что еще непонятно?
Решение задачи с помощью ООП по сути и сводится к тому, что надо понять, какие у тебя будут классы, что в них будет свойствами, как они взаимодействуют и тд. И надо искать решение, которое быстро пишется, делает код более понятным другим людям, в котором проще разобраться, которое легко изменять, в котором трудно сделать ошибку.
Если ты из тех, кто критикует ООП, считает что он переусложнен и не нужен, то можешь попробовать решить задачу без ООП для сравнения. И посмотреть, насколько более простым будет решение.
> Расписал абстрактные классы компания, департамент и сотрудник.
А почему они абстрактные? Абстрактный класс - это класс, объект которого нельзя создать и который обычно предназначен как основа для наследования.
> Нужно создавать класс для каждой профессии, или сделать профессию свойством сотрудника ?
Можно и так, и так. Обычно отдельные классы делают, если у разных профессий разное поведение, разные методы расчета чего-нибудь.
> Почему потребление кофе - не является свойством сотрудника ?
Потому, что это производная величина, которая зависит от: профессии, ранга, статуса босса. Хранить надо исходные данные, а производные величины просто вычислять из них (если это не требует больших затрат процессорного времени конечно). Иначе ты замучаешься их обновлять при изменении исходных данных (например, ранга).
Ты случайно не путаешь базовое потребление кофе, одинаковое для всей профессии, и итоговое потребление, которое зависит от разных факторов?
> Является ли свойством количество производимых страниц ? Если нет - почему ?
Как я понимаю, это производная величина, вычисляющаяся через другие.
> Где должны создаваться и добавляться в департамент сотрудники ? Это нужно делать в отдельном классе или прямо в классе департамент ?
Тут есть 2 подхода:
- если сотрудники не могут существовать вне департамента, то может быть логично их в нем и создавать и использовать.
- если сотрудники могут существовать вне департамента, их можно добавлять в него и убирать, то очевидно, что это уже не задача департамента их создавать.
Что еще непонятно?
Решение задачи с помощью ООП по сути и сводится к тому, что надо понять, какие у тебя будут классы, что в них будет свойствами, как они взаимодействуют и тд. И надо искать решение, которое быстро пишется, делает код более понятным другим людям, в котором проще разобраться, которое легко изменять, в котором трудно сделать ошибку.
Если ты из тех, кто критикует ООП, считает что он переусложнен и не нужен, то можешь попробовать решить задачу без ООП для сравнения. И посмотреть, насколько более простым будет решение.
Если ты написал все классы, то дальше надо использовать их для решения задачи. А именно:
- создать объект компании
- создать и добавить в нее все департаменты
- создать всех работников и рассовать по департаментам
- сделать цикл по всем департаментам, для каждого департамента получить данные по зарплате, кофе, вывести их в виде таблицы
>>929132
Контроллеры - это понятие из архитектуры MVC, у нас тут не MVC ведь, а по сути просто программа для командной строки.
>>929105
Вот ты говоришь, что нигде не нужно, а как ты собрался изучать фреймворки, если они все написаны с применением ООП?
>>929102
Ты данные выводишь, указав браузеру неправильную кодировку.
>>929077
С гайдами, да, не очень, но у нас есть задача про студентов и в ней очень много комментариев. Со ссылками на статьи про паттерны работы с БД, внедрение зависимостей, MVC и прочие умные вещи.
Я мельком глянул проект на гитхабе - https://github.com/victor-zinchenko/shop.php-start.com , пару классов - там много неправильного, на мой взгляд.
Ну например там есть папка components, а что такое "компоненты", ты можешь сформулировать?
Или например есть класс Cart, который судя по названию, представляет одну корзину, но почему у него все методы статические? Как создать например 2 корзины? На самом деле у него неправильное навание и правильно его называть SessionCartManager или как-то так.
То есть у меня ощущение, что там в коде использованы классы, но автор толком не понимает ООП. Зачем он тогда их использует? Чтобы были? Чтобы было что показать на собеседовании проверяющему, который тоже не знает ООП и не видит ошибок? Пусть пишет на функциях тогда.
Или вот класс DB. Можно подумать, это класс для работы с БД, но у него почему-то всего один метод getConnection. Может правильнее его было назвать ConnectionFactory? и опять же, везде статические методы.
ООП значит "объектно-ориентированное программирование". Но объектов-то в коде нет. Классы используются только для группировки функций.
Класс Product опять же назван неправильно, его объект не представляет собой один товар.
И так далее.
У нас в задаче про студентов изучаются примерно те же вещи, построение приложения. Только без злоупотребления статическими методами.
Если ты написал все классы, то дальше надо использовать их для решения задачи. А именно:
- создать объект компании
- создать и добавить в нее все департаменты
- создать всех работников и рассовать по департаментам
- сделать цикл по всем департаментам, для каждого департамента получить данные по зарплате, кофе, вывести их в виде таблицы
>>929132
Контроллеры - это понятие из архитектуры MVC, у нас тут не MVC ведь, а по сути просто программа для командной строки.
>>929105
Вот ты говоришь, что нигде не нужно, а как ты собрался изучать фреймворки, если они все написаны с применением ООП?
>>929102
Ты данные выводишь, указав браузеру неправильную кодировку.
>>929077
С гайдами, да, не очень, но у нас есть задача про студентов и в ней очень много комментариев. Со ссылками на статьи про паттерны работы с БД, внедрение зависимостей, MVC и прочие умные вещи.
Я мельком глянул проект на гитхабе - https://github.com/victor-zinchenko/shop.php-start.com , пару классов - там много неправильного, на мой взгляд.
Ну например там есть папка components, а что такое "компоненты", ты можешь сформулировать?
Или например есть класс Cart, который судя по названию, представляет одну корзину, но почему у него все методы статические? Как создать например 2 корзины? На самом деле у него неправильное навание и правильно его называть SessionCartManager или как-то так.
То есть у меня ощущение, что там в коде использованы классы, но автор толком не понимает ООП. Зачем он тогда их использует? Чтобы были? Чтобы было что показать на собеседовании проверяющему, который тоже не знает ООП и не видит ошибок? Пусть пишет на функциях тогда.
Или вот класс DB. Можно подумать, это класс для работы с БД, но у него почему-то всего один метод getConnection. Может правильнее его было назвать ConnectionFactory? и опять же, везде статические методы.
ООП значит "объектно-ориентированное программирование". Но объектов-то в коде нет. Классы используются только для группировки функций.
Класс Product опять же назван неправильно, его объект не представляет собой один товар.
И так далее.
У нас в задаче про студентов изучаются примерно те же вещи, построение приложения. Только без злоупотребления статическими методами.
>Потому, что это производная величина, которая зависит от: профессии, ранга, статуса босса. Хранить надо исходные данные, а производные величины просто вычислять из них (если это не требует больших затрат процессорного времени конечно). Иначе ты замучаешься их обновлять при изменении исходных данных (например, ранга).
>
>Ты случайно не путаешь базовое потребление кофе, одинаковое для всей профессии, и итоговое потребление, которое зависит от разных факторов?
Проиграл с задроченного ноулайфера, который не видит людей и строит абстрактные модели их предпочтений.
Статья на гитхабе про кодировки: https://github.com/codedokode/pasta/blob/master/cs/strings.md
>>929138
Можно сделать внешние классы-мапперы. Например, UserMapper умеет сохранять и загружать объекты User из таблицы users. В мапперах прописать название таблицы и соответствие между колонками таблицы и полями объектов, может даже правила преобразования (например поле DATETIME в PHP объект DateTime).
Паттерн так и называется Data Mapper, подробнее у Фаулера и у меня в уроке https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Также, вместо своего маппера можно взять библиотеку Doctrine. Она такое умеет.
>>929072
В динамических сайтах в БД хранятся исходные данные (в нормализованном виде), а шаблон страницы хранится в виде файла. Соответственно, при генерации страницы данные берутся из БД и подставляются в шаблон.
Что значит "сколько запросов"? А сколько у тебя данных выводится на странице? Может ты просто запросы делаешь неэффективные, и все тормозит? Или используешь бесплатный хотинг? Ты обязан хорошо знать SQL, чтобы писать динамические сайты с базой данных.
Бывает, делают кеширование, то есть сохраняют на будущее сгенерированный кусок HTML, но у этого подхода очень много недостатков (пример: мы можем кешировать список комментариев, но как только нам надо подсвечивать комментарии текущего пользователя, а также показыать автору пока не одобренные комментарии, этот кеш становится бесполезен).
Но даже в случае кеширования, кеш хранится отдельно от исходных данных.
Как можно в другом методе класса, который вызывается позже, получать доступ к $_SESSION? Почему-то к нему нету доступа, пишет
>Undefined variable: _SESSION
Нужно писать session_start()?
Сессия будет работать, если открыть браузер в анонимном режиме?
Сколько "держится" сессия?
О какой разметке речь? Если ты хочешь сохранить набранный в HTML редакторе код статьи с форматированием - пожалуйста, сохраняй. Если ты хочешь сохранить верстку/дизайн сайта в БД, то это плохая идея. Ну хотя бы потому, что верстальщику неудобно будет с ней работать. Ведь все редакторы кода работают с файлами на диске, а не с данными в базе.
Файлы легко поместить в систему контроля версий вроде git, а данные в базе - нет.
Бывают CMS, где верстка хранится в БД, но это очень неудобно.
Шаблоны дизайна все же должны лежать в виде файлов на диске.
> Например, у сайта сложный дизайн, не хранить же каждый блок текста в отдельной таблице и не выводить его потом??
кто сказал, что весь текст должен быть в базе? Если этот текст никогда не меняется, его проще просто прописать в сам шаблон. Если же его должен править редактор через админку то да, надо поместить его в базу.
> Но тогда получается, что одна страница будет лежать в нескольких колонках базы данных
И пусть.
> ведь под каждый кусок шаблона должен выводиться определенный кусок из одной статьи.
Ты что-то путаешь. Статья должна храниться в БД как одна сущность, целиком.
Что не так с MySQL?
>>929038
В комментариях к задаче про студентов в конце это написано. Прочитай их.
>>928964
Изучить комментарии которые даны к этой задаче. Там даны примеры, также есть ссылка на статью про MVC.
>>928942
Я соглашусь с тем, что сначала надо изучать более базовые вещи (ООП, MVC, базы данных), а потом уже делать сложные приложения на них.
>>928922
У него наверно классов не много, не используются сторонние библиотеки и риска конфликтов нет.
>>928916
Прописывать абсолютные пути, конечно, глупо. Не получится например запустить 2 копии проекта в 2 папках.
>>928914
Не понимаешь - значит, надо разобраться. Поставь echo/var_dump, смотри что хранится в переменных, сравни с тем, что в них должно быть.
Видеокурсы - это не значит, что можно не понимать, как работает код. Ты должен понимать каждую строчку в нем.
>>928910
Он пишет, что в foreach передан не массив. Разберись, что туда передается, почему это не массив, откуда приходят эти данные.
Готовых ответов не будет.
Не прописывай абсолютные пути в include, пиши относительно __DIR__ лучше.
>>928887
Значит не было операции INSERT в ходе которой сгенерировался новый id.
>>928746
1 класс = 1 файл. Почитай мой урок по PSR-4 https://github.com/codedokode/pasta/blob/master/php/autoload.md
Паттерны тебе рановато изучать, чтобы их изучать надо ковырять исходный код Симфони и Доктрины.
>>928702
- Изучи PSR-4 и автозагрузку (ссылка выше)
- Изучи MVC, ты вообще не понимаешь, что такое контроллер: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
У меня нет времени все разбирать сейчас, могу посоветовать почитать комментарии к задаче про студентов, там все подробно расписано: https://github.com/codedokode/pasta/blob/master/student-list.md
Что не так с MySQL?
>>929038
В комментариях к задаче про студентов в конце это написано. Прочитай их.
>>928964
Изучить комментарии которые даны к этой задаче. Там даны примеры, также есть ссылка на статью про MVC.
>>928942
Я соглашусь с тем, что сначала надо изучать более базовые вещи (ООП, MVC, базы данных), а потом уже делать сложные приложения на них.
>>928922
У него наверно классов не много, не используются сторонние библиотеки и риска конфликтов нет.
>>928916
Прописывать абсолютные пути, конечно, глупо. Не получится например запустить 2 копии проекта в 2 папках.
>>928914
Не понимаешь - значит, надо разобраться. Поставь echo/var_dump, смотри что хранится в переменных, сравни с тем, что в них должно быть.
Видеокурсы - это не значит, что можно не понимать, как работает код. Ты должен понимать каждую строчку в нем.
>>928910
Он пишет, что в foreach передан не массив. Разберись, что туда передается, почему это не массив, откуда приходят эти данные.
Готовых ответов не будет.
Не прописывай абсолютные пути в include, пиши относительно __DIR__ лучше.
>>928887
Значит не было операции INSERT в ходе которой сгенерировался новый id.
>>928746
1 класс = 1 файл. Почитай мой урок по PSR-4 https://github.com/codedokode/pasta/blob/master/php/autoload.md
Паттерны тебе рановато изучать, чтобы их изучать надо ковырять исходный код Симфони и Доктрины.
>>928702
- Изучи PSR-4 и автозагрузку (ссылка выше)
- Изучи MVC, ты вообще не понимаешь, что такое контроллер: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
У меня нет времени все разбирать сейчас, могу посоветовать почитать комментарии к задаче про студентов, там все подробно расписано: https://github.com/codedokode/pasta/blob/master/student-list.md
>Паттерн так и называется Data Mapper, подробнее у Фаулера и у меня в уроке
О, как раз что искал. Буду писать мапперы. Про доктрину почитал, но все пишут тормознутая, а мне надо что-то побыстрее. К тому же там как я понял надо в самих классах комментарии писать, чтобы доктрина их понимала. Свой маппер не должен сильно затормозить, это же всего +1 класс к каждому объекту, либо можно вообще общий для всех написать.
Вообще, у тебя все смешано в кучу. Вот посмотри:
list_selector($table, $column, 'list_id', 'list_name');
У тебя тут заложены ограничения:
- данные могут браться только из 2 колонок таблицы
- нельзя отфильровать данные перед выводом в список
- нельзя преобразовать данные
- нельзя брать данные не из таблицы
Гораздо удобнее сделать отдельно функцию получения данных, и отдельно функцию генерации списка.
По поводу аякса, тут есть варианты:
- можно возвращать в ответе новый список, и явакриптом перегенерировать список опций
- можно возвращать кусок HTML и заменять его
- можно просто добавлять/удалять одну опцию из списка (изучи DOM, такая возможность есть). Это наверно самый оптимальный вариант.
То есть тебе надо подучить DOM, и DOM-объекты соответствующие тегам SELECT и OPTION.
>>928615
- method_exists
- function_exists
- рефлекшен
>>928561
Уголовный кодекс изучи, школьник. А так, вирусы мало кто пишет, их либо покупают, либо берут выложенные в паблик. Но вот например если ты интересуешься этой темой, ты должен знать историю ботнета Mirai, Citadel и тд.
Могу посоветовать интересный блог с свежей и актуальной информацией про ботнеты и прочий скам: https://krebsonsecurity.com/ Правда он на английском, но без английского в сфере кибербезопасности делать нечего.
Антивирусы (ядро) обычно пишут на Си++ ради производительности, но чтобы их писать, нужны очень широкие знания - начиная с машинных кодов и работы процессора, продолжая WinAPI, заканчивая форматами файлов и виртуальными машинами. То есть надо очень много изучать разных вещей, и низкоуровневых, и высооуровыневых, и этому на сомнительных форумах вряд ли научат. Там тебя только научат как скачать готовую программу, взломать кривой непатченный годами магазин на PHP и попасть в отдел К.
Вообще, у тебя все смешано в кучу. Вот посмотри:
list_selector($table, $column, 'list_id', 'list_name');
У тебя тут заложены ограничения:
- данные могут браться только из 2 колонок таблицы
- нельзя отфильровать данные перед выводом в список
- нельзя преобразовать данные
- нельзя брать данные не из таблицы
Гораздо удобнее сделать отдельно функцию получения данных, и отдельно функцию генерации списка.
По поводу аякса, тут есть варианты:
- можно возвращать в ответе новый список, и явакриптом перегенерировать список опций
- можно возвращать кусок HTML и заменять его
- можно просто добавлять/удалять одну опцию из списка (изучи DOM, такая возможность есть). Это наверно самый оптимальный вариант.
То есть тебе надо подучить DOM, и DOM-объекты соответствующие тегам SELECT и OPTION.
>>928615
- method_exists
- function_exists
- рефлекшен
>>928561
Уголовный кодекс изучи, школьник. А так, вирусы мало кто пишет, их либо покупают, либо берут выложенные в паблик. Но вот например если ты интересуешься этой темой, ты должен знать историю ботнета Mirai, Citadel и тд.
Могу посоветовать интересный блог с свежей и актуальной информацией про ботнеты и прочий скам: https://krebsonsecurity.com/ Правда он на английском, но без английского в сфере кибербезопасности делать нечего.
Антивирусы (ядро) обычно пишут на Си++ ради производительности, но чтобы их писать, нужны очень широкие знания - начиная с машинных кодов и работы процессора, продолжая WinAPI, заканчивая форматами файлов и виртуальными машинами. То есть надо очень много изучать разных вещей, и низкоуровневых, и высооуровыневых, и этому на сомнительных форумах вряд ли научат. Там тебя только научат как скачать готовую программу, взломать кривой непатченный годами магазин на PHP и попасть в отдел К.
Это потому что у тебя либо много совбодного времени, либо очень маленький проект. На практике такие логи читать будет некому, и там много будет ложных срабатываний (вроде всяких ботов, заполняющих все формы подряд и тд). Проще просто не пропускать плохие запросы и все.
>>928541
Он принес нам интересную инсайдерскую информацию про курсы битрикса. Может кому пригодится.
>>928514
Давай рассуждать логически.
- как в сеттере могут оказаться неправильные данные?
- что мы хотим в этом случае делать?
Вот первое, что мне приходит в голову, что левые данные там могут оказаться из-за кривых рук программиста. Раз так. надо выбрасывать исключение с пояснением причин ошибки, и пусть оно завершит скрипт.
Но может быть, у нас класс специально сделан так, чтобы в него можно было пихать любые данные, а он возьмет что ему нужно. Если так, то может быть стоит сделать как-то по-другому.
А может у нас куча кривого кода, и надо срочно что-то сделать, и разбирать его некогда, сделаем его еще кривее, и пусть он игнорирует все ошибки, а разгребать это потом будет кто-то другой.
>>928529
У него другая ситуация.
>>928531
Валидация и пользователь тут вообще не при чем. Класс должен заниматься только своим делом, если валидация не его задача, то он не обязан искать и исправлять чужие ошибки. А может сразу выбросить исключение и отказаться работать.
>>928509
Я тут решил глянуть урок по ООП, и мне кажется, это не очень удачное начало: http://php720.com./lesson/50
Но это что касается ООП, другие темы я не смотрел там.
>>928496
Там написано в задании, надо чтобы условие не сработало и вывелся текст про false. Пункт 03 слева.
>>928462
Либо написать свой Data Mapper, либо прикрутить Доктрину. Урок про маппер: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
>>928375
Логично капчу выводить до попытки скачивания. Логика может быть такая:
На странице просмотра файла:
- если пользователь не прошел капчу, показать ему капчу
- иначе не показать
Обработчик формы с кодом капчи можно сделать обычный (POST-форма), можно аяксовый, на выбор. В случае обычной формы, можно после успшного решения капчи редиректить на скаичвание файла.
В обработчике скачивания файла:
- если пользователь прошел капчу. отдать файл
- иначе, перенаправить его на страницу просмотра файла
Хранить информацию о решении капчи можно в куках или сессии.
То есть тебе бы стоило начать с понимания, как работает HTTP, что такое GET/POST запросы.
Это потому что у тебя либо много совбодного времени, либо очень маленький проект. На практике такие логи читать будет некому, и там много будет ложных срабатываний (вроде всяких ботов, заполняющих все формы подряд и тд). Проще просто не пропускать плохие запросы и все.
>>928541
Он принес нам интересную инсайдерскую информацию про курсы битрикса. Может кому пригодится.
>>928514
Давай рассуждать логически.
- как в сеттере могут оказаться неправильные данные?
- что мы хотим в этом случае делать?
Вот первое, что мне приходит в голову, что левые данные там могут оказаться из-за кривых рук программиста. Раз так. надо выбрасывать исключение с пояснением причин ошибки, и пусть оно завершит скрипт.
Но может быть, у нас класс специально сделан так, чтобы в него можно было пихать любые данные, а он возьмет что ему нужно. Если так, то может быть стоит сделать как-то по-другому.
А может у нас куча кривого кода, и надо срочно что-то сделать, и разбирать его некогда, сделаем его еще кривее, и пусть он игнорирует все ошибки, а разгребать это потом будет кто-то другой.
>>928529
У него другая ситуация.
>>928531
Валидация и пользователь тут вообще не при чем. Класс должен заниматься только своим делом, если валидация не его задача, то он не обязан искать и исправлять чужие ошибки. А может сразу выбросить исключение и отказаться работать.
>>928509
Я тут решил глянуть урок по ООП, и мне кажется, это не очень удачное начало: http://php720.com./lesson/50
Но это что касается ООП, другие темы я не смотрел там.
>>928496
Там написано в задании, надо чтобы условие не сработало и вывелся текст про false. Пункт 03 слева.
>>928462
Либо написать свой Data Mapper, либо прикрутить Доктрину. Урок про маппер: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
>>928375
Логично капчу выводить до попытки скачивания. Логика может быть такая:
На странице просмотра файла:
- если пользователь не прошел капчу, показать ему капчу
- иначе не показать
Обработчик формы с кодом капчи можно сделать обычный (POST-форма), можно аяксовый, на выбор. В случае обычной формы, можно после успшного решения капчи редиректить на скаичвание файла.
В обработчике скачивания файла:
- если пользователь прошел капчу. отдать файл
- иначе, перенаправить его на страницу просмотра файла
Хранить информацию о решении капчи можно в куках или сессии.
То есть тебе бы стоило начать с понимания, как работает HTTP, что такое GET/POST запросы.
Зачем прятать? Если save.php нормально написан, то ничего прятать не требуется, он любые параметры принимает, валидирует и выдает сообщения об ошибке в случае левых данных. Надо не прятать, а нормально инпуты проверять.
В коде аякса секретный ключ + текущее время. В htaccess редирект например /save/ на save.php. Потом из аякса делаем запрос на /save?secretkey, где secretkey md5 от секретного ключа + текущее время. Сервак сравнивает и показывает 404 not found со всеми заголовками, если не совпало.
НИКАК
С помощью WEB-консоли браузера ЭКСПЕРИМЕНТАТОР может отследить параметры запроса и по какому адресу отправлены данные, а потом на питончике набросать скрипт, который отправляет тот же запрос, представляется браузером и сохраняет печеньки.
Можно настроить роутинг с помощью Alto Router или mod_rewrite, и тогда прописывать не action="save.php", а action="/backend/"; некоторые фреймворки позволяют повесить обработчики серверных событий, типа onRequestURL, например Wordpress мне кажется. Это не остановит заинтересованных лиц от попыток обхода механизмов. Так что для особо критических мест решает все более детальная проверка данных.
Ну, не буду спорить, конечно, но названия классов и переменных - это уже нутакое, не влияющее на функционал. А так да, у хохла много лишнего, типа папки КОНФИХ и компоненцс. С другой стороны, такой подход удобен, чтоб скинуть клиенту готовый архив с сайтом и послать нахуй, а он сам сможет разобраться уже в коде на базовом уровне.
Я вот сейчас тот же тутор хохла прохожу, но многое переделываю под себя и свою логику. убрал дб_конфих и внес в database.php, где к базе подключается и ведется всякое нехорошее
да, кстати, а зачем корзине быть нестатической? Ну, то есть, зачем создавать еще корзин богу корзин? Мне в любом шопе одной хватало, когда чот покупал, лол.
>>929077-кун
вообще приятно, что в пхп-тредике ответят и пояснят, а не обоссут за любой вопрос, как в жс-треде, раковнике
кстати, можешь что нибудь сказать о ноде? У меня лежат две книги по ней и чешуться руки, но я только начал осваивать пхп, а еще в инетах пишут, что нода нинужна
хотел бы работать фуллстек жс макакой, в идеале еще и на удаленке
Допустимо, но тут есть подвохи
- ссылку не надо формировать в шаблоне, а праивльно сделать метод, который ее генериурет
- нельзя давать прямую ссылку на файл, так как он может быть сохранен не под исходным именем или расширением
Почитай комментарии к задаче про файлообменнник, там же это расписано вроде.
Защиту от роботов делать не требуется, но если очень хочется, то можешь добавить.
>>928166
Вообще, лучше просто сделать функцию, проверяющую авторизацию. Если она вернула false, редиректить на страницу логина и завершать скрипт.
>>928135
У меня работает: http://ideone.com/Ppm71Y
Может у тебя какой-нибудь адблок стоит? Или может это были баги на ideone? Или браузер устарел?
>>928125
Думаю, да. До того, как у тебя будет большая аудитория, пройдет немало времени.
Википедия на PHP, фейсбук и вконтакте наичнались на PHP. Badoo на PHP.
>>928097
Разбей задачу на более мелкие:
- проверить введенный логин и пароль
- залогинить пользователя с данным id
Первое я разбирать не будут, а второе обычно реализуется через куки (либо через сесиии, которые используют те же куки).
Чтобы понять, кто перед нами, мы должны выдать куку с каким-то идентификатором. Например, id пользователя, email, логин.
Но этого мало. Ведь злоумышленник может поставить себе любую куку и выдать себя за легитимного пользователя. Потому дополнительно мы должны выдать какой-то код, ключ, токен, который знает только пользователь и не знает посторонний. Например: случайны длинный код, хеш пароля.
Вместо 2 кук можно использовать и одну. Например, длинный код может и идентифицировать пользователя, и подтверждать его личность.
Авторизация на основе сессий устроена примерно так же, только там нет риска подделки и достаточно хранить только идентификатор. Но сессия - вещь временная и быстро умирает.
Я тебе советую вместо чтения статьи просто понять, как работает авторизация и написать функции залогинивания/разлогинивания/проверки самому.
Допустимо, но тут есть подвохи
- ссылку не надо формировать в шаблоне, а праивльно сделать метод, который ее генериурет
- нельзя давать прямую ссылку на файл, так как он может быть сохранен не под исходным именем или расширением
Почитай комментарии к задаче про файлообменнник, там же это расписано вроде.
Защиту от роботов делать не требуется, но если очень хочется, то можешь добавить.
>>928166
Вообще, лучше просто сделать функцию, проверяющую авторизацию. Если она вернула false, редиректить на страницу логина и завершать скрипт.
>>928135
У меня работает: http://ideone.com/Ppm71Y
Может у тебя какой-нибудь адблок стоит? Или может это были баги на ideone? Или браузер устарел?
>>928125
Думаю, да. До того, как у тебя будет большая аудитория, пройдет немало времени.
Википедия на PHP, фейсбук и вконтакте наичнались на PHP. Badoo на PHP.
>>928097
Разбей задачу на более мелкие:
- проверить введенный логин и пароль
- залогинить пользователя с данным id
Первое я разбирать не будут, а второе обычно реализуется через куки (либо через сесиии, которые используют те же куки).
Чтобы понять, кто перед нами, мы должны выдать куку с каким-то идентификатором. Например, id пользователя, email, логин.
Но этого мало. Ведь злоумышленник может поставить себе любую куку и выдать себя за легитимного пользователя. Потому дополнительно мы должны выдать какой-то код, ключ, токен, который знает только пользователь и не знает посторонний. Например: случайны длинный код, хеш пароля.
Вместо 2 кук можно использовать и одну. Например, длинный код может и идентифицировать пользователя, и подтверждать его личность.
Авторизация на основе сессий устроена примерно так же, только там нет риска подделки и достаточно хранить только идентификатор. Но сессия - вещь временная и быстро умирает.
Я тебе советую вместо чтения статьи просто понять, как работает авторизация и написать функции залогинивания/разлогинивания/проверки самому.
Это тестовое задание? Можно через циклы, можно через array-walk_recursive или рекурсивные итераторы по массиву.
>>928063
Потому что лишнее усложнение и ограничение (с синглтоном не может быть больше 1 соединения).
Можно просто написать:
$pdo = new PDO(...);
зачем к этому добавлять синглтон? Чтобы было?
А вообще, чем плох в некотоых случаях синглтон, можно попробовать понять из урока по DI https://github.com/codedokode/pasta/blob/master/arch/di.md
>>928006
Инъекции там нет, но стиль плохой. Есть же плейсхолдеры, используй их, а не склеивай запрос из кусков.
Вместо Db::connect лучше использовать DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
SQL в контроллере это большой толстый контроллер, а не MVC. Код в контроллере не повторно используемый, его неудобно тестировать, и лучше его писать там поменьше.
> В идеале выделить работу с sql в прослойку - маппер, чтобы она не была в model. Но если нет, то где ей больше место - в model или controller?
Ты по моему не понимаешь, что такое модель и как работает MVC. У меня есть урок https://github.com/codedokode/pasta/blob/master/arch/mvc.md
>>927998
> SQL запросы прямо в контроллере, это не MVC:
Файл называется model/News, это разве контроллер?
По поводу синглтона - лучше дать ссылку на урок про DI. Это ведь к DI относится, как получить объект PDO.
>>927852
Это не книга, но в ОП посте есть учебник и в нем глава про ООП с парой задач. А так, не знаю.
Это тестовое задание? Можно через циклы, можно через array-walk_recursive или рекурсивные итераторы по массиву.
>>928063
Потому что лишнее усложнение и ограничение (с синглтоном не может быть больше 1 соединения).
Можно просто написать:
$pdo = new PDO(...);
зачем к этому добавлять синглтон? Чтобы было?
А вообще, чем плох в некотоых случаях синглтон, можно попробовать понять из урока по DI https://github.com/codedokode/pasta/blob/master/arch/di.md
>>928006
Инъекции там нет, но стиль плохой. Есть же плейсхолдеры, используй их, а не склеивай запрос из кусков.
Вместо Db::connect лучше использовать DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
SQL в контроллере это большой толстый контроллер, а не MVC. Код в контроллере не повторно используемый, его неудобно тестировать, и лучше его писать там поменьше.
> В идеале выделить работу с sql в прослойку - маппер, чтобы она не была в model. Но если нет, то где ей больше место - в model или controller?
Ты по моему не понимаешь, что такое модель и как работает MVC. У меня есть урок https://github.com/codedokode/pasta/blob/master/arch/mvc.md
>>927998
> SQL запросы прямо в контроллере, это не MVC:
Файл называется model/News, это разве контроллер?
По поводу синглтона - лучше дать ссылку на урок про DI. Это ведь к DI относится, как получить объект PDO.
>>927852
Это не книга, но в ОП посте есть учебник и в нем глава про ООП с парой задач. А так, не знаю.
Попробуй погуглить, что значит ORM
ORM = Object to Relational (DB) Mapper
Маппер (сопоставитель) объектов с строками таблицы в БД.
Вот и ответ. ORM нужен, если ты хочешь загружать или сохранять данные из БД в виде объектов.
Урок https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
> Но описывать более сложные SQL-запросы (с подзапросами, джойнами..) через квери-билдеры в ORM
Query Builder перпендикулярен ORM. Можно делать ORM без Query Bulder, можно использовать Query Builder без ORM. Как следует из названия, он предназначен для сборки запросов по частям, например, когда вид запроса зависит от каких-то условий. В моем уроке это упомянуто.
То есть ты не разобрался, используешь QB не там, где надо, и жалуешься, что это неудобно.
> Структура базы, связи между таблицами описывается внутри doc-комментов!
А как ты бы хотел их описывать? В доктрине есть другие варианты + можно написать свой парсер.
В общем читай мой урок.
А, всё нашёл уже, извиняюсь
Удобнее наверно сделать хороший редактор, где все вопросы на одной странице. Сам подумай, в тесте может быть 10-50 вопросов. И может потребоваться их переставлять как-то.
У меня там есть наброски интерфейса (на mockinbird), и там вроде показано, как это может выглядеть.
С другой стороны, если ты можешь сделать удобный многостраничный редактор (например страница со списком вопросов и страница редактирования), это приемлемо. Но ты тогда не научишься работать со сложными динамическими формами, что плохо.
>>927549
Якорь не отправляется на сервер.
> Или все постом делать, что бы урл не трогать?
Отправка POST запроса вроде меняет URL.
>>927449
Если на чем-то застрял, напиши на чем именно, покажи код, попроси совет или подсказку.
>>927389
Нельзя использовать составные выражения при инициализации поля. Но их можно поставить в конструкторе.
>>927382
У тебя в объекте не заполнено поле answers. Потому там лежит по умолчанию null. И при попытке сделать по нему цикл foreach происходит ошибка.
Также, у тебя очень странный код, у тебя объекты Questions создаются в 2 местах кода, причем в одном из них они осле этого никак не используются.
Удобнее наверно сделать хороший редактор, где все вопросы на одной странице. Сам подумай, в тесте может быть 10-50 вопросов. И может потребоваться их переставлять как-то.
У меня там есть наброски интерфейса (на mockinbird), и там вроде показано, как это может выглядеть.
С другой стороны, если ты можешь сделать удобный многостраничный редактор (например страница со списком вопросов и страница редактирования), это приемлемо. Но ты тогда не научишься работать со сложными динамическими формами, что плохо.
>>927549
Якорь не отправляется на сервер.
> Или все постом делать, что бы урл не трогать?
Отправка POST запроса вроде меняет URL.
>>927449
Если на чем-то застрял, напиши на чем именно, покажи код, попроси совет или подсказку.
>>927389
Нельзя использовать составные выражения при инициализации поля. Но их можно поставить в конструкторе.
>>927382
У тебя в объекте не заполнено поле answers. Потому там лежит по умолчанию null. И при попытке сделать по нему цикл foreach происходит ошибка.
Также, у тебя очень странный код, у тебя объекты Questions создаются в 2 местах кода, причем в одном из них они осле этого никак не используются.
О, это уже второй кот с борд, про которого я знаю (первый - Джаз, который с блинами).
>>927086
Язык, DOM и тд. У нас в ОП посте есть задачки.
>>926913
Удобнее всего отправлять как обычную форму с приложенными файлами наверно. Ну либо каждую картинку отдельным запросом, если соединение плохое и надо переотправлять запросы.
Библиотек не знаю, но наверно можно нагуглить при желании.
>>926847
нужно выделять из строк нужные элементы. Либо самому проанализировать и составить шаблоны, либо искать готовую библиотеку, которая умеет это делать.
>>926829
Обновление страницы не меняет URL. надо делать редирект.
>>926760
В Yii вообще немало любителей делать "не как все". Они вроде табы до сих пор используют, и кучу вещей переизобрели.
>>926725
Недостаточно информации. Опиши, что ты делал, какую команду ввел, что вывелось, что ожидал, что получилось.
>>926675
Да, лучше отказаться.
>>926674
Это наследие прошлых версий. Делать так неправильно.
>>926669
Что значит "на чистом PHP"? Какие ограничения это добавляет? Я не могу понять, как отличить чистый PHP от нечистого.
>>926597
Сначала стоит освоить основы: ООП, MVC, формы, таблицы. У нас есть задача про студентов, помогающая это изучить.
А потом браться за фреймворки. Иначе ты их плохо будешь понимать.
В твоем коде есть проблема: ты обращаешься к элементу массива $_GET[amount'], но даже не проверил, есть ли такой элемент в нем вообще.
Число 5000 не надо копипастить, а надо вынести в переменную.
О, это уже второй кот с борд, про которого я знаю (первый - Джаз, который с блинами).
>>927086
Язык, DOM и тд. У нас в ОП посте есть задачки.
>>926913
Удобнее всего отправлять как обычную форму с приложенными файлами наверно. Ну либо каждую картинку отдельным запросом, если соединение плохое и надо переотправлять запросы.
Библиотек не знаю, но наверно можно нагуглить при желании.
>>926847
нужно выделять из строк нужные элементы. Либо самому проанализировать и составить шаблоны, либо искать готовую библиотеку, которая умеет это делать.
>>926829
Обновление страницы не меняет URL. надо делать редирект.
>>926760
В Yii вообще немало любителей делать "не как все". Они вроде табы до сих пор используют, и кучу вещей переизобрели.
>>926725
Недостаточно информации. Опиши, что ты делал, какую команду ввел, что вывелось, что ожидал, что получилось.
>>926675
Да, лучше отказаться.
>>926674
Это наследие прошлых версий. Делать так неправильно.
>>926669
Что значит "на чистом PHP"? Какие ограничения это добавляет? Я не могу понять, как отличить чистый PHP от нечистого.
>>926597
Сначала стоит освоить основы: ООП, MVC, формы, таблицы. У нас есть задача про студентов, помогающая это изучить.
А потом браться за фреймворки. Иначе ты их плохо будешь понимать.
В твоем коде есть проблема: ты обращаешься к элементу массива $_GET[amount'], но даже не проверил, есть ли такой элемент в нем вообще.
Число 5000 не надо копипастить, а надо вынести в переменную.
>>929156
Ответь сначала на такие вопросы (если знаешь ответ, если нет, так и пиши):
- что такое сессия?
- где хранятся помещенные в нее данные?
- если сессий на сервере много, как понять, какая из них соответствует какому пользователю? Ну то есть пришел HTTP-запрос от браузера, как понять. какая сессия соответствует этому пользователю?
- что делает sesion_start()?
- кто и когда заполняет массив $_SESSION? Что происходит с ним после завершения скрипта?
- как удаляются данные сессий, если они долго не используются?
Можешь также погуглить перед тем, как писать, что не знаешь.
>>929171
Все пишут, а ты померяй. Ну и настрой правильно.
>>929177
Единых официальных нет, но есть рекомендации PSR-1 и PSR-2 которые поддерживают крупные фреймворки и многие проекты. Смотри второй пост треда.
>>929186
Никак.
>>929197
не проще этот ключ явно передавать как GET- или POST-параметр?
Время на клиенте и на сервере может не совпадать. У меня сегодня телефон почему-то на 2 часа отстал.
>>929199
А если этот "экспериментатор" освоит браузерные расширения, то его вообще будет крайне тяжело отличить от человека. Можно показать капчу, но есть сервисы по ее распознаванию, хотя говорят, гугловскую трудно пройти.
Проще наверно вводить какие-то лимиты и искать подозрительных пользователей которые методично загружают страницу за страницей.
>>929156
Ответь сначала на такие вопросы (если знаешь ответ, если нет, так и пиши):
- что такое сессия?
- где хранятся помещенные в нее данные?
- если сессий на сервере много, как понять, какая из них соответствует какому пользователю? Ну то есть пришел HTTP-запрос от браузера, как понять. какая сессия соответствует этому пользователю?
- что делает sesion_start()?
- кто и когда заполняет массив $_SESSION? Что происходит с ним после завершения скрипта?
- как удаляются данные сессий, если они долго не используются?
Можешь также погуглить перед тем, как писать, что не знаешь.
>>929171
Все пишут, а ты померяй. Ну и настрой правильно.
>>929177
Единых официальных нет, но есть рекомендации PSR-1 и PSR-2 которые поддерживают крупные фреймворки и многие проекты. Смотри второй пост треда.
>>929186
Никак.
>>929197
не проще этот ключ явно передавать как GET- или POST-параметр?
Время на клиенте и на сервере может не совпадать. У меня сегодня телефон почему-то на 2 часа отстал.
>>929199
А если этот "экспериментатор" освоит браузерные расширения, то его вообще будет крайне тяжело отличить от человека. Можно показать капчу, но есть сервисы по ее распознаванию, хотя говорят, гугловскую трудно пройти.
Проще наверно вводить какие-то лимиты и искать подозрительных пользователей которые методично загружают страницу за страницей.
В данном случае названия классов показывают непонимание их назначения и вообще непонимание ООП. Ну или может это такой альтернативный взгляд на вещи. Только вот будут ли рады ему например на собеседовании?
> да, кстати, а зачем корзине быть нестатической?
Ну например, надо зачем-нибудь сравнить корзины 2 пользователей. Или еще что-нибудь с ними сделать. Или в тесте мы создаем корзину, делаем что-нибудь с ней и выкидываем.
Ну или например мы хотим поработать с корзиной в CLI-скрипте. Этот класс ведь без сессии вообще работать не будет. Потому что это не корзина. А редактор данных в сессии, хранящих информацию о корзине.
В коде в принципе нет объекта, представляющего корзину. Есть только массив данных в сессии, описывающий ее содержимое.
По ноде я не могу сказать. Мне кажется, она нужна только в определенных нишах.
Если ты хочешь писать сложные JS приложения, тебе все равно надо понимать ООП. У нас кстати есть задачи по JS самого разного уровня в ОП посте. В том числе и на сложное приложение.
Я всегда стараюсь объяснить, почему кто-то не прав, чтобы из этого была польза и люди учились сами мыслить аналогично. А не заучивали догмы, не понимая, почему так, а не иначе.
>Это тестовое задание? Можно через циклы, можно через array-walk_recursive или рекурсивные итераторы по массиву.
Не тестовое, аутирую с вордпрессом. Мне требовалось убрать пробелы у значений перед отправкой в бд. Сделал через вложенный foreach, но думаю, что сделал какое-то говно и так никто не пишет.
>list_selector($table, $column, 'list_id', 'list_name');
Там как раз и есть две функции. Одна работает с таблицей, а list_selector только формирует список, используя столбец $column в таблице $table и формируя div с параметрами id и name, list_id и list_name, в который его и помещает.
DOM пока для меня сложновата, но я посмотрю.
С тех пор ничего не изменилось же. Новые фичи в php7 и фреймворки появились, но основы от этого не меняются. Что в шапке, вполне актуально, а для новых фич качни любую книгу, где php7 разбирается.
Необходимо создать таблицу из 2 столбцов
Жанр - Количество
То есть выбрать столбец жанр, выбрать второй столбец, в котором будут сгруппированы книги по жанрам.
Всего у меня 4 таблицы:
Книги:
id - название - имя автора
Авторы:
id - Имя - Фамилия
Жанры:
id - название
Связка жанров и книг:
idКниги - idЖанра
(тк одна книга может быть связана с несколькими жанрами).
Оформить запрос не получается. Прошу помощи.
http://pastebin.com/6C2W5zZ1 - структура бд
Необходимо создать таблицу из 2 столбцов
Жанр - Количество
То есть выбрать столбец жанр, выбрать второй столбец, в котором будут сгруппированы книги по жанрам.
Всего у меня 4 таблицы:
Книги:
id - название - имя автора
Авторы:
id - Имя - Фамилия
Жанры:
id - название
Связка жанров и книг:
idКниги - idЖанра
(тк одна книга может быть связана с несколькими жанрами).
Оформить запрос не получается. Прошу помощи.
http://pastebin.com/6C2W5zZ1 - структура бд
Я бы очень рад тебе помочь, но я не понял самого вопроса, в чем у тебя проблема то? Это тестовая/учебная задача, или у тебя траблы с реальным проектом?
Тебе нужно селект написать? В котором будет посчитано сколько книг в каждом жанре?
Наверное как-то так
SELECT genres, COUNT(*) FROM books GROUP BY genres;
Пробема в том что не знаю как организовать проверку кук и запись в сессии на всех страницах.
Сперва нужно по дефолту (при заходе на любую страницу сайта) повесить куку с дефолтным языком и записать дефолтную сессию, а при выборе другого языка - поменять.
Очевидно, мне не хочется проверять существование кук и сессий в каждом экшене, как это сделать в одном месте?
Может есть варик через конфигурацию компонентов?
>при заходе на любую страницу сайта
то есть при вызове любого контроллера?
Тогда в конструкторе родительского контроллера или что там у вас используется.
по сути мультиязычность нужна только на стороне клиента. так что на основном контроллере в папку frontend. Там сейчас 10 страниц, очень не хочется вешать на каждую проверку
Отвечая конкретно на вопрос, при вызове контроллера SiteController (я и не парился его переназывать ) проводить проверку сессий и кук
Для себя делаю, учусь.
да, нужен селект. Списки жанров и книг в разных таблицах.
Они связаны между собой проходной таблицей.
То есть вместо того, чтобы в одну ячейку писать id жанров, я сделал табличку.
Прости бро, но я в глаза ниразу не видел yii, просто основываясь на опыте пытаюсь тебе дать совет.
> Там сейчас 10 страниц, очень не хочется вешать на каждую проверку
Что такое страницы? Страница собой представляет контроллер? Или ты методы одного контроллера имеешь ввиду?
Если у тебя все "страницы" в пределах одного контроллера, то напиши ему конструктор, в котором это будет проверяться.
http://ideone.com/fz6Jg3
спасибо, я и не подумал
пыха
Правильно будет похапе
Что не так?
>Из функций значения уже возвращать не нужно?
Как это? Т.е. выводить результат функций командой echo нельзя?
a new car
Хотя я в артиклях довольно слаб, но предложения без артиклей сразу выдают иностранца.
Можно, но ты ниже используешь эхо на переменной, которая не существует в этой зоне видимости. И вообще это пиздарики. Куча каких-то эхо, наркоманские названия переменных, $bd++ вместо $db++.
это тяжелый случай, у тебя функция ничего не ретернит, нигде не вызывается и ты к внутренним переменным функции обращаешься в глобальном коде.
Попробуй понять вот этот пример:
http://ideone.com/VnlelH
https://www.codewars.com/kata/5839edaa6754d6fec10000a2/train/php
На примере моей задачи. Вот, я её так решил, в общем.
Кроме того, что я неправильно вызвал функцию, я решил её правильно?
Почему ты так любишь лепить везде эхо? Сделай return в функции, потом вызови ее в переменную и когда все уже будет готова можешь принтить.
Нет, ты можешь и внутри функции эхо написать, но ты ВЫЗОВИ СУКА ЕЁ ГДЕ-НИБУДЬ В КОДЕ
то что у тебя там написано
function hueta($huitka) {
....hueta code
}
- это только ОПИСАНИЕ ФУНКЦИИ, понимаешь? Ты тут лишь показываешь языку как она должна работать.
Потом ты в своём коде где тебе нужно пишешь
hueta($var);
и она выполняется
Блядь пиздец, пойду лучше в овервотч поиграю.
Ну и зачем ты все разжевал? А? А? А? А? А? А? А? А?
Давай удочку, а не рыбу, еба. Когда сам роешь инфу по проблеме, то в конце ты чувствуешь, что заебись ваще чотко все порешал. А если тебя тычут носом в решение, то это хуйня.
Я и дал ему удочку, я же не за него его говнокод переписал. А так тоже самое блядь описал в 10 строках, что и в любом учебнике.
Тебе нужны транслейты слов для Yii:t или переводы из базы?
>>929132
Спасибо за ответы ! Началось хоть какое-то движение.
Сейчас имею вот такой недокод: http://ideone.com/fLnxX3
Я все равно не могу понять ситуацию с кофе и страницами.
В моем нынешнем решении я копирую несколько раз код функции getDrunkCofee, меняя только одно значение. Этого ведь можно избежать, сделав кофе и страницы свойствами сотрудника, разве не так ?
Может я не правильно понял условия, ведь потребление кофе и количество страниц зависит только от того, является ли сотрудник боссом ? Или при повышении ранка повышается потребление кофе и производство страниц ?
>>>929134
>>>929132
И еще, кто-то может подсказать, в какую сторону читать: >>929612
В голове возникла мысль !
А может быть в общем классе "сотрудник" полностью описать функцию просчета, с использованием instanceOf, и выдавать результаты в зависимости от профессии(типа, класса) ?
Функцию еще иногда называют "подпрограмма". То есть это как бы отдельная маленькая программа. Мы вызываем ее, даем ей аргументы, и она что-то с ними делает и возвращает результат.
Конструкция такого вида
function somehing($x) {
.....
return $y;
}
создает новую функцию (но код в ней не выполняется, просто PHP теперь знает, что такая функция есть). В данном случае создается функция с названием something, которая принимает один аргумент, обозначенный переменной $x и возвращает что-то в конце своего выполнения с помощью return. return это специальная команда, которая выходит из функции, и возвращает указанное после нее значение.
Чтобы вызвать функцию (после того, как мы ее создали конечно), мы пишем ее имя и скобки, в скобках указываем значения аргументов:
something(10);
Вот эта строчка вызвает функцию и выолняет записанный в ней код. При этом в функции будет доступна переменная $x, равная 10. Если мы хотим сохранить результат, который вернет функция с помощью return, мы должны использовать переменную:
$result = something(10);
Вот конкретный пример. Напишем функцию, принимающую на вход 2 числа и возвращающую сумму их квадратов:
function getSquareSum($a, $b) {
return $a * $a + $b *$b;
}
Видно, что это функция, которая принимает 2 значения, возводит их в квадрат, складывает и возвращает сумму с помощью return.
Теперь посчитаем с ее помощью сумму квадратов чисел 3 и 4:
$sum = getSquareSum(3, 4);
echo $sum; // 25
Можно обойтись без промежуточной переменной:
echo getSquareSum(5, 6); // 61
Функция может быть без аргументов (ничего не принимать на вход) и может ничего не возвращать (то есть в ней нет команды return).
И да, функция не видит внешние переменные. Все, что ей нужно, ты должен явно передать через аргументы. Это делает код более логичным, сразу по заголовку видно, что нужно передать функции для ее работы.
Функцию еще иногда называют "подпрограмма". То есть это как бы отдельная маленькая программа. Мы вызываем ее, даем ей аргументы, и она что-то с ними делает и возвращает результат.
Конструкция такого вида
function somehing($x) {
.....
return $y;
}
создает новую функцию (но код в ней не выполняется, просто PHP теперь знает, что такая функция есть). В данном случае создается функция с названием something, которая принимает один аргумент, обозначенный переменной $x и возвращает что-то в конце своего выполнения с помощью return. return это специальная команда, которая выходит из функции, и возвращает указанное после нее значение.
Чтобы вызвать функцию (после того, как мы ее создали конечно), мы пишем ее имя и скобки, в скобках указываем значения аргументов:
something(10);
Вот эта строчка вызвает функцию и выолняет записанный в ней код. При этом в функции будет доступна переменная $x, равная 10. Если мы хотим сохранить результат, который вернет функция с помощью return, мы должны использовать переменную:
$result = something(10);
Вот конкретный пример. Напишем функцию, принимающую на вход 2 числа и возвращающую сумму их квадратов:
function getSquareSum($a, $b) {
return $a * $a + $b *$b;
}
Видно, что это функция, которая принимает 2 значения, возводит их в квадрат, складывает и возвращает сумму с помощью return.
Теперь посчитаем с ее помощью сумму квадратов чисел 3 и 4:
$sum = getSquareSum(3, 4);
echo $sum; // 25
Можно обойтись без промежуточной переменной:
echo getSquareSum(5, 6); // 61
Функция может быть без аргументов (ничего не принимать на вход) и может ничего не возвращать (то есть в ней нет команды return).
И да, функция не видит внешние переменные. Все, что ей нужно, ты должен явно передать через аргументы. Это делает код более логичным, сразу по заголовку видно, что нужно передать функции для ее работы.
Копировать код функции 4 раза - плохая идея. Это затрудняет изменение программы и говорит о неправильном подходе.
Более того, тот, кто будет добавлять новые професссии, должен будет копипастить этот код.
Давай подумаем: в 4 классах-профессиях логика расчета потребления кофе - разная или одинаковая? Она одинаковая, единственное различие - это один коэффициент.
Следовательно, нам нужно не 4, а одна функция. Мы объявляем ее в базовом классе, и она потому будет доступна во всех потомках.
Но как прописать, что в каждом классе свой коэффициент? Логично сделать поле либо функцию, которая его возвращает. Вот такого вида:
protected function getBasicCoffeeConsumption()
{
return 10;
}
Но тут встает другая проблема:
- во-первых, мы не можем из базового класса вызвать функцию, которая есть только в его потомках. Базовый класс не должен ничего знать о своих потомках, так как они могут добавляться в будущем
- во-вторых, как объяснить людям, которые будут делать новые профессии, что они обязаны сделать такую функцию?
Обе этих проблемы решают абстрактные методы. В базовом классе мы делаем абстрактный метод:
abstract protected function getBasicCoffeeConsumption();
Так как мы его объявили, то теперь мы можем его вызывать. А тот, кто наследует наш класс, обязан будет этот метод реализовать, иначе произойдет ошибка и класс не создастся.
То есть абстрактный метод - это метод, который не описан в базовом классе, но который обязаны реализовать все его потомки.
Иногда используют другой подход: делают метод по умолчанию в базовом классе, а часть потомков его переопределяет, если хочет. Но у нас не та ситуация, у нас нет коэффициента по умолчанию, у каждой профессии он свой.
Есть ли в моем подходе недостаток: да, есть. Он предполагает, что во всех профессиях будет использоваться один и тот же метод, формула расчета кофе. Если это не так, то этот мой подход будет плохо работать (попробуй подумать, почему).
Копировать код функции 4 раза - плохая идея. Это затрудняет изменение программы и говорит о неправильном подходе.
Более того, тот, кто будет добавлять новые професссии, должен будет копипастить этот код.
Давай подумаем: в 4 классах-профессиях логика расчета потребления кофе - разная или одинаковая? Она одинаковая, единственное различие - это один коэффициент.
Следовательно, нам нужно не 4, а одна функция. Мы объявляем ее в базовом классе, и она потому будет доступна во всех потомках.
Но как прописать, что в каждом классе свой коэффициент? Логично сделать поле либо функцию, которая его возвращает. Вот такого вида:
protected function getBasicCoffeeConsumption()
{
return 10;
}
Но тут встает другая проблема:
- во-первых, мы не можем из базового класса вызвать функцию, которая есть только в его потомках. Базовый класс не должен ничего знать о своих потомках, так как они могут добавляться в будущем
- во-вторых, как объяснить людям, которые будут делать новые профессии, что они обязаны сделать такую функцию?
Обе этих проблемы решают абстрактные методы. В базовом классе мы делаем абстрактный метод:
abstract protected function getBasicCoffeeConsumption();
Так как мы его объявили, то теперь мы можем его вызывать. А тот, кто наследует наш класс, обязан будет этот метод реализовать, иначе произойдет ошибка и класс не создастся.
То есть абстрактный метод - это метод, который не описан в базовом классе, но который обязаны реализовать все его потомки.
Иногда используют другой подход: делают метод по умолчанию в базовом классе, а часть потомков его переопределяет, если хочет. Но у нас не та ситуация, у нас нет коэффициента по умолчанию, у каждой профессии он свой.
Есть ли в моем подходе недостаток: да, есть. Он предполагает, что во всех профессиях будет использоваться один и тот же метод, формула расчета кофе. Если это не так, то этот мой подход будет плохо работать (попробуй подумать, почему).
> А может быть в общем классе "сотрудник" полностью описать функцию просчета, с использованием instanceOf, и выдавать результаты в зависимости от профессии(типа, класса) ?
Это полная противоположность ООП-подходу. В ООП, если какая-то информация или логика относится только к одному классу, то она должна быть в нем.
В таком случае мы можем добавлять новые профессии, не трогая старый код (не всегда конечно, но иногда это получается).
Ты предлагаешь вынести знание о том, как считать потребление, из класса профессии наружу.
В твоем случае для добавления профессии придется менять код в нескольких местах. Это как миниумм неудобно. Если ты используешь сторонний код, который править нельзя, то получается ты вообще не можешь расширять.
То есть код вида:
если класс == Менеджер то...
если класс == Инженер то...
противоречит идеям ООП о разделении кода на классы. Ты выносишь код, относящийся к классу, из класса наружу.
> public function getDrunkCofee()
>{
> return 'nothing'; //заглушка
Это неправильно. Таких "заглушек" быть не должно. Так как кто-то может вызвать функцию, и эти данные куда-то дальше пойдут. Тут надо использовать абстрактный метод без реализации.
Твоя ошибка в том, что ты сделал бесполезный класс Employee (зачем он сделан? за что он отвечает? за общий для профессий функционал? для этого у нас уже есть AbstractEmployee), в то время как надо было наследовать профессии напрямую от AbstractEmployee.
> abstract class AbstractEmployee
> private $baseRate;
Перечитай в чем разница между protected и private.
> protected $baseRate = 500;
Нет гарантии, что все потомки укажут значения поля. Тут надо использовать абстрактный метод для получения этого числа.
- Есть один сайт на php, который возможно мне придется админить/переписывать и тд.
- в php ноль, но знаю питон, не слишком хорошо, но 99% проблем могу решить сам, не умоляя слезно на всех форумах сразу.
Вопрос в том как мне поступить, учить php и дербанить существующий сайт или писать на питоне с нуля? И сколько нужно просить за такую работу (сайт на питоне), учитывая что я живу мухсранске, нищеброд без гроша и фирма мелкая.
Извиняюсь за сумбурность
пи-а-пи
Можно просто в style у элементов же прописывать.
<div style="background-image: url(../images/vasya.gif);background-repeat:no-repeat; background-position:50% 50%;"> background-position:50% 50% </div>
пост запросы не передаются аяксом на сервер в хроме, только геты. я и спрашиваю почему?
Я знаю, но они именно ебашут <style> в середине html и это меня ужас как бесит. Оправдывается разраб тем, что ну стили же легче будет найти потом)) т.е. ему легче искать стили к странице, в которой он их прям в середине заебашил. ппц
Тогда не надо.
Нахуй так сложно, вот так надо:
<body bgcolor="green" background="fon.bmp">
<center>
<u>
<font color="red">Надпись, охуеть</font>
</u>
</center>
</body>
И никакие cssы в середине страницы не нужны.
Я так и сделал.
Даже пастбин с моей базой сделал.
Жанры и книги связаны между собой через промежуточную таблицу.
Нужно придумать, как оформить Селект, чтобы было
Жанр - количество книг в жанре.
Дай пизды ему, епта
Помогите оценить время и стоимость следующей работы:
Нужен сайт - обменник криптовалюты
Предполагается:
1. Сверстать сайт под популярные браузеры из небольшого количества страниц без излишеств, никаких WebGL, Unity и прочих монстросити, но при этом не совсем голый html, допустим используется какой нибудь bootstrap
2. Настроить взаимодействие с некоторыми банками для транзакций (у некоторых, вполне возможно, есть API для php, у некоторых может и нет)
3. Настроить взаимодействие кошельков с криптовалютой для транзакций.
4. Настройка БД для логгирования различных действий (транзакции-шманзакции)
5. Смазать всё это дело защищенным соединением (HTTPS все дела)
При условии:
Разрабатывается в одно ебало, нет возможности работать в фул-тайм, где то половина-3/5 рабочего дня.
Хочу услышать большое количество времени, сколько при таких условиях ушло бы времени на разработку именно у ТЕБЯ и сколько бы денег в качестве оплаты запросил именно ТЫ.
Ну вот, у меня не получается это сделать.
Ну что у тебя за хостинг.
Например обычные веб-хостинги предоставляют панель управления. Такой же phpmyadmin.
Делаешь бекап своей базы, заливаешь его на хостинг.
Прописываешь пути подключения и все.
Смотря сколько секунд потратил на работу. Стандартный прайс для фрилансера 300к в секунду.
А какого ты ответа ждешь? Чтобы я тебе сказал сколько ты должен брать за джобу? Ну пиздец, чо. Требуй с него 1 дошик, если долго будешь делать, а есть быстро, то два.
>Чтобы я тебе сказал сколько ты должен брать за джобу?
Я попросил просто примерную стоимость описанной работы. Я же не прошу тут бухгалтерские расчёты вести и проводить серьёзную аналитику. Просто примерная цена или диапазон ну без фантазий конечно, типа 300 мильёновтыщ в секунду/доширак и свистнуть в хуй исходя из вышеописанных условий.
Пиздос, ты знаешь всю ситуацию, представляешь себе заказчика и не выкладывая никакой инфы по своим скилам, платежеспособности заказчика и примерном времени выполнения тобой джобы, спрашиваешь у анона сколько тебе заломить с заказчика.
Если ты считаешь, что заказчик может соскочить, и он из страны, где о рублях не слыхали, то стартуй из расчета 15 баксов в час.
Но это совет в стиле: 'Секс переоценен, я на дваче читал'. Только ты владеешь инфой и ты можешь трезво оценить всю хуйню. Короче я заебался писать, проси у него 3 дошика.
Отлично, так и запишем. "Три дошика".
>>Doctrine
>Даже после прочтения сложных определений в гугле не до конца понимают что это такое. Оно заменяет Mysql или любую другу БД?
На этот вопрос отвечать не нужно - ознакомился чуть подробней.
Вроде бы основную логику сделал, сейчас с выводом информации на экран мучаюсь
http://ideone.com/3uQv9G - вот код
Вопросы:
1) Какого хера мне выдает ошибку "PHP Fatal error: Uncaught Error: Call to undefined function mb_strlen() in /home/z65BF4/prog.php:200" ??? Делал ведь предыдущую задачу про эту компанию, с этими же функциями и всё работало...
2) Как бы так красиво, в ООП стиле оформить вывод информации на экран и сам экшон (заполнение компании департаментами, тех сотрудниками и подсчет всего, что необходимо по условиям задачи) ?
1) mb_internal_encoding('utf-8');
Но ideone выебывается и всеравно выдает какую-то хуйню. Проверил на сервере - все работает.
2)Попробуй выводить рамку из черточек и слешей. Реализация примерно такая:
function padRight($text, $col)
{
return $text.str_repeat(' ',$col-mb_strlen($text));
}
function padLeft($text, $col)
{
return str_repeat(' ',$col-mb_strlen($text)).$text;
}
function printData($company, $headline)
{
$col1 = 20;
$col2 = 8;
$col3 = 12;
$col4 = 12;
$col5 = 12;
$col6 = 12;
$col7 = 50;
if($headline == 0){
echo padRight("Департамент", $col1) .
padLeft("сотр.", $col2) .
padLeft("тугр.", $col3) .
padLeft("кофе", $col4) .
padLeft("стр.", $col5) .
padLeft("тугр./стр.", $col6) . "\n" .
str_repeat("==", 40) . "\n";
}
И далее через foreach
padRight(имя департамента, $col1)
padLeft(количество чего-то, $col2-7)
1) mb_internal_encoding('utf-8');
Но ideone выебывается и всеравно выдает какую-то хуйню. Проверил на сервере - все работает.
2)Попробуй выводить рамку из черточек и слешей. Реализация примерно такая:
function padRight($text, $col)
{
return $text.str_repeat(' ',$col-mb_strlen($text));
}
function padLeft($text, $col)
{
return str_repeat(' ',$col-mb_strlen($text)).$text;
}
function printData($company, $headline)
{
$col1 = 20;
$col2 = 8;
$col3 = 12;
$col4 = 12;
$col5 = 12;
$col6 = 12;
$col7 = 50;
if($headline == 0){
echo padRight("Департамент", $col1) .
padLeft("сотр.", $col2) .
padLeft("тугр.", $col3) .
padLeft("кофе", $col4) .
padLeft("стр.", $col5) .
padLeft("тугр./стр.", $col6) . "\n" .
str_repeat("==", 40) . "\n";
}
И далее через foreach
padRight(имя департамента, $col1)
padLeft(количество чего-то, $col2-7)
https://jsfiddle.net/akua0tr6/
Переделал 4ую js задачу с селекторами, ушел дальше ковырять MVC сапера. Вот твой предыдущий ответ из прошлого треда, если последний утонет: http://pastebin.ru/a1sx1IT2 , предыдущее решение: https://jsfiddle.net/82228qnq/
Доброго времени суток, я тут ебусь с задачей про айфон в кредит и не могу понять какого хуя оно что-то плюсует к долгу, вместо того, чтобы отнимать от него месячную стоимость.
Это происходит с разными данными. Например, с оригинальными данными 40к кредит, 5% процент и 1к комиссия, 5к месячная плата считает как надо, пробовал числа по-меньше - считает то нормально, то опять плюсует неизвестные числа. Сейчас в ней поставлены другие значения, а не оригинальные, дабы было понятно что за хуйня.
Может кто знает в чём ошибка и где я обосрался, помогите.
for (months=0;credit>0;months++)
{
credit = credit*percent+service-payment;
if (credit<payment) {
spent += credit;
months++;
break
} else { spent+= payment}
так же проще. если я не проебался нигде, еду с универа только
Я б сделал примерно за 2-3 месяца, может меньше. Но я не начинающий. Если хуефрилансера с сайта нанять, то может и на год растянуться и забаговано все будет. Самая сложная часть криптовалюты и как с ними взаимодействовать, остальное достаточно просто и стандартно. Запросил бы 10к баксов.
Умоляю, помогите
Кто-то работал с ним?
sudo apt install git
Шелл для спермы можешь нагуглить, их много. Главное GUI не используй, хотя бы первое время. Учись работать с консолью.
Заодно напомню о скидках: https://ideone.com/IscxDU
>Windows: C:\Users\{LoginName} - Открываем контектное меню и выбираем пункт Git Bash
Что за LoginName?можешь дать почту или линк вк?то я тупой
По принципу Лискова ты не можешь менять методы в классах-детях, которые были определены в родителе. Иначе ребенок уже не соответствует родителю, и ты не можешь заменить им родителя. Весь смысл наследования теряется. Конструктор к примеру если определен в родителе с параметрами, то он такой же и в детях, нельзя его переопределять под другие параметры.
К тому же это фукнциональности касается, да. Если к примеру был метод turnPage($number), который менял страницу, то если в ребенке у тебя turnPage не меняет страницу, а делает что-то другое, то это нарушение по Лискову.
> Иначе ребенок уже не соответствует родителю, и ты не можешь заменить им родителя. Весь смысл наследования теряется.
И правда.
> Конструктор к примеру если определен в родителе с параметрами, то он такой же и в детях, нельзя его переопределять под другие параметры.
Выходит, что parent::__construct() это плохо? Набросал простой пример, когда, вроде бы, принцип Лисков не нарушается, даже невзирая на то, что конструктор в наследнике переопределён: https://ideone.com/JBf2VC
Нет, почему? Если у тебя __construct в ребенке принимает все те же параметры, что и parent::construct, то можно расширять. Это соответствует open-closed principle - ты не меняешь parent, а расширяешь его функциональность в ребенке. При этом функциональность parent остается нетронутой, ведь его __construct работает так же как и прежде.
Сорь, непонятно написал - функциональность parent:__construct остается той же в ребенке, значит лисков и open-closed principle выполняются.
Имеется в виду имя пользователя, под которым ты пользуешься виндой. Ну то есть это просто папка, где хранятся разные файлы с настройками.
Если ты не используешь экран входа при загрузке, то скорее всего это что-то вроде c:\Users\Администратор
Если не знаешь точно, зайди в c:\Users и посмотри, что там есть.
>>930554
Не обязан. Разве что в конструкторе почти всегда приходится это делать (например, потому что он инициализирует private поля, которые по-другому не инициализировать).
>>930593
Методы можно менять, но изменения должны быть совместимыми. Например, можно добавить новый аргумент, но у него должно быть значение по умолчанию. А убирать, или менять тайп-хинт на более узкий, естественно, нельзя.
К конструктору это не относится, конструктор можно менять как угодно. На него правило Лисков не распространяется, так как оно относится к уже созданным объектам, а конструктор мы вызываем когда объект еще не создан.
Раз уж ты интересуешься наследованием, то почитай заодно про проблему прямоугольника и квадрата:
- http://al-zatv.livejournal.com/52115.html
- http://sergeyteplyakov.blogspot.ru/2014/09/liskov-substitution-principle.html
>>930625
К конструктору требование совместимости не относится. Так как конструктор никто не вызывает на уже созданных объектах. Принцип Лисков говорит о совместимости созданных объектов, а не классов.
Имеется в виду имя пользователя, под которым ты пользуешься виндой. Ну то есть это просто папка, где хранятся разные файлы с настройками.
Если ты не используешь экран входа при загрузке, то скорее всего это что-то вроде c:\Users\Администратор
Если не знаешь точно, зайди в c:\Users и посмотри, что там есть.
>>930554
Не обязан. Разве что в конструкторе почти всегда приходится это делать (например, потому что он инициализирует private поля, которые по-другому не инициализировать).
>>930593
Методы можно менять, но изменения должны быть совместимыми. Например, можно добавить новый аргумент, но у него должно быть значение по умолчанию. А убирать, или менять тайп-хинт на более узкий, естественно, нельзя.
К конструктору это не относится, конструктор можно менять как угодно. На него правило Лисков не распространяется, так как оно относится к уже созданным объектам, а конструктор мы вызываем когда объект еще не создан.
Раз уж ты интересуешься наследованием, то почитай заодно про проблему прямоугольника и квадрата:
- http://al-zatv.livejournal.com/52115.html
- http://sergeyteplyakov.blogspot.ru/2014/09/liskov-substitution-principle.html
>>930625
К конструктору требование совместимости не относится. Так как конструктор никто не вызывает на уже созданных объектах. Принцип Лисков говорит о совместимости созданных объектов, а не классов.
В твоем примере лисков как раз нарушен - ты в ребенке принимаешь другое число параметров чем в родителе. Теперь ситуация - ты где-то юзаешь ребенка, а понадобилось заменить родителем. Ты этого сделать больше не сможешь - вызовет ошибку из-за несоответствия количества параметров.
> ..если для каждого объекта o1 типа S существует объект o2 типа T такой, что для всех программ P, определенных в терминах T, поведение P не изменяется при замене o2 на o1, то S является подтипом (subtype) для T.
> Говорят, что экземпляр наследника также ЯВЛЯЕТСЯ экземпляром базового класса, что выражается в возможности использования экземпляров наследника везде, где ожидается использование базового класса.
Видите, речь идет об объектах. Условно говоря, если у вас есть функция
function doSmth(A $a) { ... }
Что это значит? Значит, что эта функция должна обращаться только к полям и методам, которые есть в классе A.
Но вы в нее можете передать объект класса B, наследующегося от A, и все должно работать. То, что у него другой конструктор - это не проблема, так как вызываете вы его за пределами функции. Сама функция констурктор у $a не вызовет, так как это уже созданный объект.
>К конструктору требование совместимости не относится. Так как конструктор никто не вызывает на уже созданных объектах. Принцип Лисков говорит о совместимости созданных объектов, а не классов.
Если создаешь объекты в цикле, и попал родитель и ребенок - будет ошибка. Тебе в каждом цикле придется смотреть, чтобы не создавался объект с 3 параметрами, а это уже кривой код. Код должен быть защищен от ошибок на уровне интерфейса.
Ты не так понял. "Можно использовать вместо родительского класса" не значит, что ты можешь пойти и в коды программы поменять имена классов. Смысл замены в том, что ты можешь в функцию передать объект наследник вместо предка, как тут >>930633
Пожалуйста перечитай внимательно определения. Речь о том, что можно использовать объект наследника вместо предка, а не менять имена классов в тексте программы.
- https://ru.wikipedia.org/wiki/Принцип_подстановки_Барбары_Лисков
- http://sergeyteplyakov.blogspot.ru/2014/09/liskov-substitution-principle.html
>Саттер и Александреску в своём руководстве по использованию C++ для выражения этого принципа также используют фразу «подкласс не должен требовать от вызывающего кода больше, чем базовый класс, и не должен предоставлять вызывающему коду меньше, чем базовый класс».
В случае конструктора с расширеными параметрами подкласс как раз требует больше, чем базовый класс. Прямое нарушение.
Возможно это из-за того, что цитата вырвана из контекста либо неточно сформулирована. Принцип Лисков не распространяется на конструкторы.
Если почитать исходное определение, то там речь идет о созданных объектах.
>>930634
Если ты имеешь в виду код вида $x = new $class, то это уже твоя проблема, что ты используешь такую конструкцию. Надо использовать if/else или switch ($class) ... и проблемы не будет. Или делать еще и конструкторы совместимыми.
>>930609
Принцип Лисков у тебя никак не нарушен. Объекты совместимы, а конструкторы не обязаны быть совместимыми.
в чем прикол консоли? Гуи = тот же функционал, но быстрее и нагляднее же.
Чсв повышать? Симулировать линукс?
>Принцип Лисков не распространяется на конструкторы.
В исходном определении речь идет о types и subtypes, которые по сути и есть классы и подклассы.
>We add to a type’s
specification a constraint clause that captures exactly those history properties
of a type that must be preserved by any of its subtypes, and we prove that each
of the type’s methods preserves the constraint.
Плюс с группой только то что английский есть
>http://sergeyteplyakov.blogspot.ru/2014/09/liskov-substitution-principle.html
На этой странице тоже пишут:
>Если посмотреть на исходное описание принципа подстановки в трудах Барбары Лисков, то можно с удивлением обнаружить, что оно полностью основано таких понятиях, как предусловия, постусловия и инварианты. Другими словами, описание этого принципа полностью основано на принципах проектирования по контракту:
>Производные классы не должны усиливать предусловия (не должны требовать большего от своих клиентов).
>Производные классы не должны ослаблять постусловия (должны гарантировать, как минимум тоже, что и базовый класс).
>Производные классы не должны нарушать инварианты базового класса (инварианты базового класса и наследников суммируются)
>Производные классы не должны генерировать исключения, не описанные базовым классом.
Изменения конструктора нарушает эти правила, в частности первое.
> $matchedProducts = $discountResult->getMatchedProducts();
> $matchedPrice = $matchedProducts->reduce($pricesSum, 0);
> $totalPrice += $matchedPrice - $matchedPrice $discountResult->getPercent();
Вот тут ты накладываешь много ограничений на расчет скидки, вместо того, чтобы отдать это объекту скидки. Ну например, ты требуешь, чтобы все скидки были в процентах, и не даешь возможности написать произвольный алгоритм расчета скидки. Мне кажется, если бы вместо этого мы могли передать набор товаров в скидку и получить абсолютную скидку либо итоговую сумму, было бы гибче.
С точки зрения задачи твой подход верный, но интуиция говорит мне, что на практике его может быть, придется потом менять.
Метод getDiscountResult я бы переименовал в calculateDiscount/applyDiscount, так точнее получается.
> class CombinationDiscount implements DiscountInterface
> public function getDiscountResult(ProductCollection $notUsedProducts) {
...
> $notUsedProducts->removeProduct($matchedProduct);
Вот это неожиданно. Я думал, что функция getDiscountResult не изменяет исходные данные, а она изменяет, причем это нигде никак не написано. Это может привести к ошибкам, например, если мы вызовем ее 2 раза с одними и теми же данными (например чтобы сделать вывод отладочной информации), то второй раз она вернет другие значения.
Тем более название начинается с get...
Тут наверно лучше было спроектировать функцию так, чтобы она не меняла исходные данные. Это ведь не сильно усложнит программу, а от ошибок защитит.
А, и еще. А что, если в цикле тут название первого товара есть в списке, а второго нет?
> foreach ($this->names as $name) {
...
> if ($matchedProduct) {
> $notUsedProducts->removeProduct($matchedProduct);
Тогда получается, скидка не будет применена, но товар будет удален из исходного списка. Это же ошибка. И допустил ее ты именно из-за того, что модифицируешь исходные данные. Ну и причина, почему я ее заметил, в этой строке:
> return new DiscountResult(new ProductCollection(), $this->percent);
Подозрительно, что ты не используешь тут переменную $productsUsedInThisDiscount, я решил выяснить, почему так, и нашел ошибку.
----
Я хочу еще немного отвлечься и рассказать про такую вещь, как exception safety, или "безопасность" исключений (корректность работы программы в случае выброса и перехвата исключения в любом месте кода). Эта тема важна только в программах, которые часто перехватывают и обрабатывают исключения (имеются в виду исключения, не говорящие об ошибке в коде, а например, об ошибке соединения с удаленным сервером), что редко встречается на практике, но тем не менее интересна.
Предположим, что у тебя строка $productsUsedInThisDiscount->addProduct($matchedProduct); может выбросить исключение и выполнение методы getDiscountResult прервется. При этом переданный список будет уже изменен, и удаленные продукты при повторном вызове метода не будут участвовать в расчете скидок. То есть безопасность программы при возникновении исключения нарушена.
В случае выброса исключения, код, который не меняет переданные данные, работает обычно корректнее, так как он не оставит исходные данные в наполовину измененном виде.
В этой статье https://habrahabr.ru/post/126374/ упоминается "строгая гарантия". Если бы метод не менял исходные данные, он бы реализовал "строгую" гарантию безопасности исключений.
Особо конечно с этим заморачиваться не нужно, но знать про такие вещи полезно.
----
> return new DiscountResult(new ProductCollection(), 0);
Если это часто встречается, стоило добавить конструктор DiscountResult::makeEmpty();
Насчет связи скидок через интерфейсы - думаю, это правильное решение, так как тут у скидок нет общего кода и особого смысла в наследовании нет, хотя допустимо сделать связь и через абстрактный класс.
> $answer = (200 - 200 0.1) + (200 - 200 0.05) + (200 - 200 0.05) + (400 - 400 * 0.01) + 100;
> assert($answer == $c->calculateTotalPrice($discountCollection, $productCollection));
Вот тут есть момент, на который стоит обратить внимание. Дело в том, что в PHP дробные числа (float) хранятся приближенно (да еще и в двоичном виде, что требует преобразований в/из десятичной системы счисления), и операции с ними неточные. Ну например:
> var_dump(0.1 + 0.2); var_dump(0.1 + 0.2 === 0.3);
> double(0.3)
> bool(false)
o_0 как так?
Дело в том, что в двоичной форме число 0.3 - это бесконечная дробь. Там на самом деле 0.1 + 0.2 дают что-то вроде 0.29999999, просто при выводе оно округляется до красивого числа. А вот при сравнении видимо где-то в последнем разряде получается разница.
Потому дробные числа нельзя сравнивать через ===. Надо проверять, что их разница меньше некоторого значения. Подробнее
- https://habrahabr.ru/post/112953/
- http://floating-point-gui.de/errors/comparison/ (англ)
- тут еще интересный случай https://habrahabr.ru/post/247015/ (в PHP такое может возникнуть при сортировке по произвольному условию)
В общем, задача у тебя решена неплохо (кроме проблемы с измненением исходных данных), надеюсь, ты стал чуть лучше разбираться в ООП.
> $matchedProducts = $discountResult->getMatchedProducts();
> $matchedPrice = $matchedProducts->reduce($pricesSum, 0);
> $totalPrice += $matchedPrice - $matchedPrice $discountResult->getPercent();
Вот тут ты накладываешь много ограничений на расчет скидки, вместо того, чтобы отдать это объекту скидки. Ну например, ты требуешь, чтобы все скидки были в процентах, и не даешь возможности написать произвольный алгоритм расчета скидки. Мне кажется, если бы вместо этого мы могли передать набор товаров в скидку и получить абсолютную скидку либо итоговую сумму, было бы гибче.
С точки зрения задачи твой подход верный, но интуиция говорит мне, что на практике его может быть, придется потом менять.
Метод getDiscountResult я бы переименовал в calculateDiscount/applyDiscount, так точнее получается.
> class CombinationDiscount implements DiscountInterface
> public function getDiscountResult(ProductCollection $notUsedProducts) {
...
> $notUsedProducts->removeProduct($matchedProduct);
Вот это неожиданно. Я думал, что функция getDiscountResult не изменяет исходные данные, а она изменяет, причем это нигде никак не написано. Это может привести к ошибкам, например, если мы вызовем ее 2 раза с одними и теми же данными (например чтобы сделать вывод отладочной информации), то второй раз она вернет другие значения.
Тем более название начинается с get...
Тут наверно лучше было спроектировать функцию так, чтобы она не меняла исходные данные. Это ведь не сильно усложнит программу, а от ошибок защитит.
А, и еще. А что, если в цикле тут название первого товара есть в списке, а второго нет?
> foreach ($this->names as $name) {
...
> if ($matchedProduct) {
> $notUsedProducts->removeProduct($matchedProduct);
Тогда получается, скидка не будет применена, но товар будет удален из исходного списка. Это же ошибка. И допустил ее ты именно из-за того, что модифицируешь исходные данные. Ну и причина, почему я ее заметил, в этой строке:
> return new DiscountResult(new ProductCollection(), $this->percent);
Подозрительно, что ты не используешь тут переменную $productsUsedInThisDiscount, я решил выяснить, почему так, и нашел ошибку.
----
Я хочу еще немного отвлечься и рассказать про такую вещь, как exception safety, или "безопасность" исключений (корректность работы программы в случае выброса и перехвата исключения в любом месте кода). Эта тема важна только в программах, которые часто перехватывают и обрабатывают исключения (имеются в виду исключения, не говорящие об ошибке в коде, а например, об ошибке соединения с удаленным сервером), что редко встречается на практике, но тем не менее интересна.
Предположим, что у тебя строка $productsUsedInThisDiscount->addProduct($matchedProduct); может выбросить исключение и выполнение методы getDiscountResult прервется. При этом переданный список будет уже изменен, и удаленные продукты при повторном вызове метода не будут участвовать в расчете скидок. То есть безопасность программы при возникновении исключения нарушена.
В случае выброса исключения, код, который не меняет переданные данные, работает обычно корректнее, так как он не оставит исходные данные в наполовину измененном виде.
В этой статье https://habrahabr.ru/post/126374/ упоминается "строгая гарантия". Если бы метод не менял исходные данные, он бы реализовал "строгую" гарантию безопасности исключений.
Особо конечно с этим заморачиваться не нужно, но знать про такие вещи полезно.
----
> return new DiscountResult(new ProductCollection(), 0);
Если это часто встречается, стоило добавить конструктор DiscountResult::makeEmpty();
Насчет связи скидок через интерфейсы - думаю, это правильное решение, так как тут у скидок нет общего кода и особого смысла в наследовании нет, хотя допустимо сделать связь и через абстрактный класс.
> $answer = (200 - 200 0.1) + (200 - 200 0.05) + (200 - 200 0.05) + (400 - 400 * 0.01) + 100;
> assert($answer == $c->calculateTotalPrice($discountCollection, $productCollection));
Вот тут есть момент, на который стоит обратить внимание. Дело в том, что в PHP дробные числа (float) хранятся приближенно (да еще и в двоичном виде, что требует преобразований в/из десятичной системы счисления), и операции с ними неточные. Ну например:
> var_dump(0.1 + 0.2); var_dump(0.1 + 0.2 === 0.3);
> double(0.3)
> bool(false)
o_0 как так?
Дело в том, что в двоичной форме число 0.3 - это бесконечная дробь. Там на самом деле 0.1 + 0.2 дают что-то вроде 0.29999999, просто при выводе оно округляется до красивого числа. А вот при сравнении видимо где-то в последнем разряде получается разница.
Потому дробные числа нельзя сравнивать через ===. Надо проверять, что их разница меньше некоторого значения. Подробнее
- https://habrahabr.ru/post/112953/
- http://floating-point-gui.de/errors/comparison/ (англ)
- тут еще интересный случай https://habrahabr.ru/post/247015/ (в PHP такое может возникнуть при сортировке по произвольному условию)
В общем, задача у тебя решена неплохо (кроме проблемы с измненением исходных данных), надеюсь, ты стал чуть лучше разбираться в ООП.
В случае гита в консоли ты напрямую отдаешь команды на получение или изменение данных. В случае ГУИ клиента это от тебя спрятано и ты хуже понимаешь, что происходит. Потому изучать лучше в консоли, а потом можешь пользоваться чем удобнее. Ну и ГУИ клиенты могут в каких-то сложных ситуациях работать не так и сбивать тебя с толку.
Вот простая книжка по git: https://git-scm.com/book/ru/v1
Не бойся командной строки, в ОП посте есть гайд по ней для начинающих.
>>930645
Можешь процитировать определение целиком? Я нахожу только такое:
https://en.wikipedia.org/wiki/Liskov_substitution_principle
> Subtype Requirement: Let \phi (x) be a property provable about objects x of type T. Then \phi (y) should be true for objects y of type S where S is a subtype of T.
Тут упомянуты объекты.
>>930646
Ну я вижу папку "Администратор". Думаю, надо зати в нее, щелкнуть правой кнопкой на пустом месте и выбрать "Git bash here " или как-то так.
Также git bash можно запустить через меню Пуск.
Если что, в ОП посте есть гайд по использованию консоли, советую изучить.
И в след. раз пиши больше подробностей:
- ссылку на гайд
- что ты сделал
- что не получилось
А то угадывать приходится.
В случае гита в консоли ты напрямую отдаешь команды на получение или изменение данных. В случае ГУИ клиента это от тебя спрятано и ты хуже понимаешь, что происходит. Потому изучать лучше в консоли, а потом можешь пользоваться чем удобнее. Ну и ГУИ клиенты могут в каких-то сложных ситуациях работать не так и сбивать тебя с толку.
Вот простая книжка по git: https://git-scm.com/book/ru/v1
Не бойся командной строки, в ОП посте есть гайд по ней для начинающих.
>>930645
Можешь процитировать определение целиком? Я нахожу только такое:
https://en.wikipedia.org/wiki/Liskov_substitution_principle
> Subtype Requirement: Let \phi (x) be a property provable about objects x of type T. Then \phi (y) should be true for objects y of type S where S is a subtype of T.
Тут упомянуты объекты.
>>930646
Ну я вижу папку "Администратор". Думаю, надо зати в нее, щелкнуть правой кнопкой на пустом месте и выбрать "Git bash here " или как-то так.
Также git bash можно запустить через меню Пуск.
Если что, в ОП посте есть гайд по использованию консоли, советую изучить.
И в след. раз пиши больше подробностей:
- ссылку на гайд
- что ты сделал
- что не получилось
А то угадывать приходится.
И можно еще простой пример кода, где было бы видно нарушение принципа подстановки Лисков при различии в конструкторах?
$db = chr($db - 1);
Это смотрится очень странно. Почему ты в одной переменной хранишь то число, то символ?
И названия $db, $bd неудачные, легко перепутать. Сам же перепутаешь и будешь спрашивать, что не так.
$findMissingLetter -> $letters
>>930360
Так неправильно, ты в последний месяц выплатишь больше 5000 в сумме за раз.
>>930337
А где ошибка? У тебя по моему просто за месяц набегает больше процентов чем анон может выплатить. Потому он никогда этот кредит не вернет.
Алсо видел новость, что микрофинансовая организация сначала выдала кредит под 290% годовых, а потом, когда должник не смог выплатить, передоговорилась на 2300% годовых. https://rg.ru/2017/02/08/reg-pfo/mikrofinansovaia-organizaciia-potrebovala-2379-procentov-godovyh.html
2300 годовых - значит долг через год будет в 24 раза больше исходной суммы.
Так что в моей задаче все еще по-божески.
>>930322
> function collectionToArray(collection) {
> return Array.prototype.slice.call(collection);
Кстати, если хочется извернуться с функциональным программированием, то можно написать так:
var collectionToArray = Array.prototype.slice... (дальше подумай сам)
> function getElemByIdWithContext(selector, context) {
> return getElementsByProperty("id", selector, context);
Тут неточность, при поиске по id надо сравнивать строку целиком, а не искать в ней частичное совпадение. Решить проблему можно функциональным программированием (которое тебе уже возможно успело надоесть в первых задачах) и отделением функции обхода дерева от функции проверки элемента:
var id = 'lalala';
findElementsRecursively(context, function (el) {
return el.id == id;
});
Или
findElementsRecursively(context, 'id', id, function (prop, value) {
return prop === value;
});
Второй вариант кажется неудобнее. Используя функцию partialAny, мы можем принести больше функционального программирования богу функционального программирования:
var findById = partialAny(findElementsRecursively, undefined, 'id', undefined, function (prop, value) ...);
findById(context, id)
Так, в общем, задачу можно считать решенной.
$db = chr($db - 1);
Это смотрится очень странно. Почему ты в одной переменной хранишь то число, то символ?
И названия $db, $bd неудачные, легко перепутать. Сам же перепутаешь и будешь спрашивать, что не так.
$findMissingLetter -> $letters
>>930360
Так неправильно, ты в последний месяц выплатишь больше 5000 в сумме за раз.
>>930337
А где ошибка? У тебя по моему просто за месяц набегает больше процентов чем анон может выплатить. Потому он никогда этот кредит не вернет.
Алсо видел новость, что микрофинансовая организация сначала выдала кредит под 290% годовых, а потом, когда должник не смог выплатить, передоговорилась на 2300% годовых. https://rg.ru/2017/02/08/reg-pfo/mikrofinansovaia-organizaciia-potrebovala-2379-procentov-godovyh.html
2300 годовых - значит долг через год будет в 24 раза больше исходной суммы.
Так что в моей задаче все еще по-божески.
>>930322
> function collectionToArray(collection) {
> return Array.prototype.slice.call(collection);
Кстати, если хочется извернуться с функциональным программированием, то можно написать так:
var collectionToArray = Array.prototype.slice... (дальше подумай сам)
> function getElemByIdWithContext(selector, context) {
> return getElementsByProperty("id", selector, context);
Тут неточность, при поиске по id надо сравнивать строку целиком, а не искать в ней частичное совпадение. Решить проблему можно функциональным программированием (которое тебе уже возможно успело надоесть в первых задачах) и отделением функции обхода дерева от функции проверки элемента:
var id = 'lalala';
findElementsRecursively(context, function (el) {
return el.id == id;
});
Или
findElementsRecursively(context, 'id', id, function (prop, value) {
return prop === value;
});
Второй вариант кажется неудобнее. Используя функцию partialAny, мы можем принести больше функционального программирования богу функционального программирования:
var findById = partialAny(findElementsRecursively, undefined, 'id', undefined, function (prop, value) ...);
findById(context, id)
Так, в общем, задачу можно считать решенной.
http://ideone.com/U1QFQk
>Fatal error: Uncaught ArgumentCountError: Too few arguments to function ChildClass::__construct(), 0 passed in /home/lpoSIT/prog.php on line 28 and exactly 2 expected in /home/lpoSIT/prog.php:20
Суть задачи тут все же в определении классов, как ты сделаешь вывод - не так принципиально. Можно просто функцию сделать, класс, или так написать.
Если тебе хочется ООП подход, то обычно это делается через классы для построения отчетов. Я бы сделал класс например StatReporter такого вида:
$reporter = new StatReporter;
$reporter->printReport($company);
Создание сотрудников можно просто сделать кодом, можно сделать отдельный класс, если хочется, который например получает на вход массив данных о сотрудниках, на выходе дает объекты.
> PHP Fatal error: Uncaught Error: Call to undefined function mb_strlen()
На сайте не включено расширение mb_string. Видимо что-то сломали. Попробуй другой сайт.
Например http://sandbox.onlinephpfunctions.com/code/481086d900d41eac5344b4194eb32dcf63926e95
Я бы советовал вместо setDepartments сделать addDepartment, так удобнее будет.
> $production = $this->getBaseProduction();
> if($this->isBoss) {
> $production = 0;
> }
> return $production;
Можно чуть сократить (без фанатизма):
return $this->isBoss? 0 : $this->getBaseProduction();
Создавать сотрудников удобно, сделав массив вида
[['Engineer', 1, 3], ...]
или
['3en1', '2ma3', ...]
Суть задачи тут все же в определении классов, как ты сделаешь вывод - не так принципиально. Можно просто функцию сделать, класс, или так написать.
Если тебе хочется ООП подход, то обычно это делается через классы для построения отчетов. Я бы сделал класс например StatReporter такого вида:
$reporter = new StatReporter;
$reporter->printReport($company);
Создание сотрудников можно просто сделать кодом, можно сделать отдельный класс, если хочется, который например получает на вход массив данных о сотрудниках, на выходе дает объекты.
> PHP Fatal error: Uncaught Error: Call to undefined function mb_strlen()
На сайте не включено расширение mb_string. Видимо что-то сломали. Попробуй другой сайт.
Например http://sandbox.onlinephpfunctions.com/code/481086d900d41eac5344b4194eb32dcf63926e95
Я бы советовал вместо setDepartments сделать addDepartment, так удобнее будет.
> $production = $this->getBaseProduction();
> if($this->isBoss) {
> $production = 0;
> }
> return $production;
Можно чуть сократить (без фанатизма):
return $this->isBoss? 0 : $this->getBaseProduction();
Создавать сотрудников удобно, сделав массив вида
[['Engineer', 1, 3], ...]
или
['3en1', '2ma3', ...]
> Так неправильно, ты в последний месяц выплатишь больше 5000 в сумме за раз.
чойта? Код прекращается до Завершения этого цикла.
ну, не знаю. Я пробовал и с консолью пол года, и на ГУИ. Гуи удобнее и нагляднее, а с консолью иногда казусы. Мне кажется, гуи дает полный необходимый для работы функционал, потому что большей частью фич гита никто не юзает, да и мало там прям таких извращенных штук.
Ага, я вчера тоже заметил такую хрень, когда просто менял саму сумму долга, а не долго+проценты и комиссия. Если меня только её - всё норм считает, так что можно считать что оно вроде бы правильно работает, хех. Но спасибо таки за ответ.
Почитай еще мой урок https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
>>930134
Не совсем то, но могу дать хорошую статью про ценообразование: http://russian.joelonsoftware.com/Articles/CamelsandRubberDuckies.html
А так, это определяется рынком. Заказчик хочет заплатить как можно меньше и точно не больше, чем он получит прибыли от проекта. Исполнитель хочет получить как можно больше (те самые 300 к в секунду), но чтобы выиграть, он должен попросить меньше других. Но при этом нет смысла брать меньше, чем можно получить за другую работу.
Ну то есть если условно тебе надо неделю, и ты можешь вместо написания программы разгружать коробки в ближайшей пятерочке за 5000 р, то меньше просить наверно смысла нет.
А если например ты по какой-то причине не можешь разгружать коробки, других заказов нет, и живешь ты за бесплатно, то даже подвинувшись по цене ты остаешься в плюсе. Даже работая за 1 рубль ты можешь остаться в плюсе, если у тебя нет никаких альтернатив.
В общем, учись считать.
Смысла спрашивать, сколько бы мы взяли, мало, так как у нас другие обстоятельства: другие расходы, другой уровень знаний, другой набор заказчиков и альтернатив.
Что именно входит в задачу - не важно, важно, сколько у тебя на нее уйдет времени.
Поясни, куда ты его ввел?
На твоем скриншоте я вижу, что ты запустил программу ssh-keygen и сгенерировал с ее помощью пару ключей. Публичный и приватный ключи были сохранены в виде файлов на диск (там написаны имена этих файлов). На экране выведен хеш публичного ключа по алгоритму SHA256 (и комментарий к нему). Ты эту строчку зачем-то выделил.
Хеши нужны, чтобы на глаз сравнить 2 ключа и понять, они одинаковые или нет.
Не очень понятно, что ты делал дальше и что не получилось.
Алсо непонятно, зачем ты генерировал этот ключ вообще. Ты не можешь дать ссылку на инструкции, которым ты следуешь?
Ну и конечно плохо, что ты сам не понимаешь, что делаешь.
Твой код выдает ошибку, так как ты используешь динамические возможности PHP. В классическом ООП нельзя создать объект по имени класса в переменной.
Твой код должен быть переписан так:
if ($class == 'ParentClass') {
$obj = new ParentClass(...);
} elseif (...) {
...
}
Или так:
$createParent = function () { return new ParentClass(...); };
$createChild = function () { ... };
$constructors = [$createParent, $createChild, ...];
Принцип Лисков тут вообще не при чем. Ошибка именно в твоем коде, так как когда ты пишешь
$object = new $class();
Ты должен гарантировать что данный класс не требует аргументов при создании. А не Барбара Лисков.
http://ideone.com/hrLgHQ
Вот еще одно нарушение, уже без имени класса из переменной. Конструктор с другими параметрами ломает метод create() в классе-наследнике. Клиентский код ожидает, что метод будет работать, как и должен в родителе - по принципу Лискова. Но он не работает, тот же самый Fatal error.
Да, верно, получается что при использовании new static в предке наследники обязаны сохранять совместимость конструктора. То есть не всегда, а только в таких отдельных случаях.
Причем, разумеется, сам PHP это не проверят и предупреждений не дает. И по заголовку класса это никак не очевидно. То есть использовать new static - это закладывать мину замедленного действия в код.
Добавлю еще, что new static - это изобретение разработчиков PHP (которые по моим ощущениям ООП вообще плохо понимают), мне оно не нравится, в других языках вроде Java такого бардака нет.
Только вот пример коду у тебя опять неудачный. зачем ты там нагородил статических методов? LSP он про объекты, а не про вызовы статических методов. И он не гарантирует что статические методы потомка совместимы с предком.
Ты сам похоже пока плохо LSP понял.
Метод create должен быть не-статический. Должно быть так:
class Parent
{
public fnction __construct() {}
public function test()
{
return new static;
}
}
class Child extens Parent
{
public function __construct($a, $b, $c) {}
}
function doSomething(Parent $p)
{
$p->test();
}
$o = new Parent();
doSomething($o); // ок
$o = new Child(1, 2, 3);
doSomething($o); // ошибка
Вот этот код иллюстрирует вредность использования new static.
В общем, вся проблем в нестандартных особенностях PHP, помогающих стрелять себе в ногу.
Только вот пример коду у тебя опять неудачный. зачем ты там нагородил статических методов? LSP он про объекты, а не про вызовы статических методов. И он не гарантирует что статические методы потомка совместимы с предком.
Ты сам похоже пока плохо LSP понял.
Метод create должен быть не-статический. Должно быть так:
class Parent
{
public fnction __construct() {}
public function test()
{
return new static;
}
}
class Child extens Parent
{
public function __construct($a, $b, $c) {}
}
function doSomething(Parent $p)
{
$p->test();
}
$o = new Parent();
doSomething($o); // ок
$o = new Child(1, 2, 3);
doSomething($o); // ошибка
Вот этот код иллюстрирует вредность использования new static.
В общем, вся проблем в нестандартных особенностях PHP, помогающих стрелять себе в ногу.
Хм, да, должна быть функция, которая объект принимает, чтобы было где тип родителя указать. Тогда вот так:
http://ideone.com/zC5nTV
Статические методы в PHP к объекту тоже применяются, значит принимающая объект функция тут же ломается, Лисков нарушен.
Ты опять полагаешься на странности PHP. Вот в этом коде: $object::method()
Это уже не ООП, а какое-то издевательство над ним. Статические методы и поля относятся к классу, а не к объекту, и вызываются на нем.
Ты должен писать ParentClass::method() для вызова статического метода.
А в твоем случае нужен обычный, не статический метод. Тем более, если ты хочешь, чтобы в разных классах он работал по-разному.
Про моему это просто код, написанный в плохом стиле.
Но раз уж ты так пишешь то да, получается, что в твоем конкретном случае наследники должны правильно переопределять статические методы. Но я считаю, это просто кривая реализация ООП в PHP, что так можно делать, из-за этого больше проблем, чем пользы. В частности, все эти тонкости неочевидны и легко сделать ошибку при наследовании. Потому такой код писать не следует.
Например, в Яве сделано по-другому. Там вызвается тот метод, который объявлен в тайп-хинте. То еть в функции:
public void doSomething(Parent p)
{
p.staticMethod();
}
Всегда будет вызван Parent::staticMethod() независимо от того, какой мы объект передадим. И правильно, нечего использовать статические методы не по назначению.
Пополняю серию. Правильно ли я установил события? http://ideone.com/GY8kav Ориентировался на твои слова:
> Также, в представлении могут быть методы установки обработчиков событий (например "задать обработчик клика по клетке"). Это позволит контроллеру не работать с DOM напрямую, а устанавливать обработчики событий через View.
> Чтобы узнавать о кликах по полю, контроллеру надо как-то подписаться на события. Это достаточно сделать один раз, перед игрой. Он может ставить обработчики событий напрямую, но наверно лучше делать это через представление, чтобы за работу с DOM, поиск нужного элемента отвечало оно.
>>921309
>>904635
К сожалению, совсем забыл проверить этого анона, исправляюсь.
https://github.com/masssn/students.loc/blob/master/db config.ini
В конфиг имеет смысл помещать только то, что может менять пользователь. Опция driver тут явно лишняя, так как твой код скорее всего заточен под опредленную базу данных и с другой работать не будет, значит не надо давать ее выбирать.
https://github.com/masssn/students.loc/blob/master/students.sql#L31
Для имени TEXT - это слишком много. Также, на поля TEXT нельзя ставить индексы. Тут надо сделать VARCHAR и указать макс. длину.
> `gender` enum('Мужской','Женский')
Тут принято использовать латинские идентификаторы. Для них также стоит сделать константы в коде.
> `group_number` text NOT NULL,
Тут надо ограничить длину. Вообще, почитай про типы полей в MySQL.
> email` varchar(20)
20 символов это недостаточно для email
> `password` varchar(8) NOT NULL
8 символов для токена это маловато. Также, надо добавить с помощью слова COMMENT комментарий, что хранится в этом поле, чтобы другим людям было проще разобраться в твоей базе. Вот урок про комментарии: https://github.com/codedokode/pasta/blob/master/db/comments.md
Также, у тебя все файлы, включая конфиг БД, лежат в публичной папке. Если кто-то откроет URL http://example.com/db config.ini то он увидит пароли базы данных. Лучше сделать отдельную публичную папку, а все остальное разместить снаружи, почитай, это описано в комментариях к задаче.
https://github.com/masssn/students.loc/blob/master/app/autoload.php
Я не советую полагаться на getcwd(). Он же может быть разный. Лучше использовать константу __DIR__.
Функция автозагрузки должна проверять, существует ли такой файл, перед тем как его инклудить, иначе такая команда вызовет ошибку:
class_exists('Test');
У меня есть урок по автозагрузке: https://github.com/codedokode/pasta/blob/master/php/autoload.md
https://github.com/masssn/students.loc/blob/master/app/controllers/Controller.php#L29
тут не очень хорошо сделано. Если ты хочешь требовать регистрацию, то надо не выводить форму на любой странице, а делать редирект на страницу регистрации. Если ты хочешь потом вернуться назад, то надо еще передавать URL исходной страницы (а перед редиректом назад проверять, что он на твоем сайте, иначе это будет открытый редирект).
Сама проверка тоже сделана неточно - ты проверяешь наличие куки, но не проверяешь, что она соответствует какому-то пользователю.
В методе action лучше было проверять весь URL, а не только первую часть. У тебя получается для одной страницы множество URL, что в общем-то плохо: /index/1, /index/2 и так далее. Лучше когда у страницы ровно один URL.
https://github.com/masssn/students.loc/blob/master/app/controllers/Controller.php#L152
Ошибка 404 сделана не совсем правильно. Ты должен отдавать HTTP код ответа 404, чтобы роботы и программы видели, что там ошибка.
- https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP
В PHP код ответа можно задать с помощью функции header().
Также, ты при выводе данных из $_SERVER не используешь htmlspecialchars. Из-за этого может быть XSS уязвимость.
В форме регистрации у тебя при ошибке не сохраняются введенные данные. В комментариях к задаче есть советы по работе с формами, и есть урок https://github.com/codedokode/pasta/blob/master/forms.md
Также, это вообще неправильно:
> $params = \app\DataHelper::makeSafeDatas($params);
Данные надо экранировать по-разному в разных ситуациях. Для вывода на странице - одним способом, для вставки в базу данных - другим. Как ты это сделаешь одной функцией? Никак. На каждый случай должна быть своя функция. А ты просто налепил кучу функций, в надежде, что они тебя защитят, не пытаясь разобраться, что каждая из них делает. Что, если у нас в форме есть поле комментария и в него можно вводить любые символы? Твои кривые функции вырежут половину из них.
Перечитай мои уроки по XSS и SQL и сделай правильную защиту:
- https://github.com/codedokode/pasta/blob/master/security/xss.md
- https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
https://github.com/masssn/students.loc/blob/master/app/views/View.php
Тут не очень понятно, для чего предназначен массив $attributes. Ну например в контроллере ты часть данных передаешь через добавленные динамически свойства, а часть напрямую:
> $this->view->pages = $navDatas["pages"];
> $this->view->setAtributes($params);
Не очень понятно, почему так сделано.
Удобнее наверно просто передавать значения для подстановки в шаблон в виде массива в метод display(), а там извлекать их с помощью extract().
Названия шаблонов лучше выбрать так, чтобы они соответствовали названиям методов контроллера.
https://github.com/masssn/students.loc/blob/master/app/DataHelper.php#L11
В методе validate ты не проверяешь, что в массиве есть такие поля. Может быть ошибка если часть полей не будет передана в POST.
> if (!filter_var($datas['birth_year'], FILTER_VALIDATE_INT) && mb_strlen($datas['birth_year']) > 5 || mb_strlen($datas['birth_year']) < 4 || preg_match("^[A-z]^", $datas['birth_year'])) {
Слишком много условий. Проще либо написать одно рег. выражение либо проверить что число в диапазоне между X и Y.
Аналогично с суммой баллов, лучше проверить что это число и что оно в определенном диапазоне.
Не очень понятно, почему у тебя везде передаются массивы. Не лучше ли сделать объект-модель студента и везде передавать его? В функцию валидации, в форму, в функцию сохранения в БД. Массив плох тем, что непонятно, какие в нем есть поля, а каких нет.
Класс DataHelper надо переименовать, например в StudentValidator.
Имена неймспейсов принято писать с большой буквы. app можно было и не писать вообще.
https://github.com/masssn/students.loc/blob/master/app/Pager.php
Плохо что этот класс лезет в GET. Ты жестко его привязал к внешним данным, и не позволяешь просто передать номер страницы как переменную.
Неправильно писать datas, так как data это уже множественное число. Вообще, у тебя неудачно выбрано название функции: getNavDatas. Слово data не дает никакой полезной информации, так как любая функция работает с данными. Надо лучше выбирать названия для функций, чтобы они соответствовали предназначению функции.
В данном случае функция рассчитыает параметры пагинации, так и можно ее назвать.
Также, ты старательно избегаешь использования объектов и ООП вообще. Ты не сделал модель для студента, а передаешь везде массивы. В пагинаторе то же самое- ты возвращаешь массив, но лучше было сделать просто объект и у него методы getPagesCount(), getLink() и так далее. Попробуй переделать класс расчета пагинации на использование полей и методов вместо возврата массива.
Контроллер поиска и вывода списка студентов можно объединить в один. Аналогично можно объединить регистрацию и редактирование.
https://github.com/masssn/students.loc/blob/master/app/Singleton.php
Зачем это? Не надо использовать паттерны там, где это не нужно. Есди ты хочешь изучить паттерны, изучи код библиотек вроде Symfony Forms или Doctrine, почитай книгу Фаулера (шаблоны разработки корпоративных приложений) и посмотри как их надо использовать на реальных примерах, а не читай статьи от тех, кто дальше синглтона ничего не понял.
>>921309
>>904635
К сожалению, совсем забыл проверить этого анона, исправляюсь.
https://github.com/masssn/students.loc/blob/master/db config.ini
В конфиг имеет смысл помещать только то, что может менять пользователь. Опция driver тут явно лишняя, так как твой код скорее всего заточен под опредленную базу данных и с другой работать не будет, значит не надо давать ее выбирать.
https://github.com/masssn/students.loc/blob/master/students.sql#L31
Для имени TEXT - это слишком много. Также, на поля TEXT нельзя ставить индексы. Тут надо сделать VARCHAR и указать макс. длину.
> `gender` enum('Мужской','Женский')
Тут принято использовать латинские идентификаторы. Для них также стоит сделать константы в коде.
> `group_number` text NOT NULL,
Тут надо ограничить длину. Вообще, почитай про типы полей в MySQL.
> email` varchar(20)
20 символов это недостаточно для email
> `password` varchar(8) NOT NULL
8 символов для токена это маловато. Также, надо добавить с помощью слова COMMENT комментарий, что хранится в этом поле, чтобы другим людям было проще разобраться в твоей базе. Вот урок про комментарии: https://github.com/codedokode/pasta/blob/master/db/comments.md
Также, у тебя все файлы, включая конфиг БД, лежат в публичной папке. Если кто-то откроет URL http://example.com/db config.ini то он увидит пароли базы данных. Лучше сделать отдельную публичную папку, а все остальное разместить снаружи, почитай, это описано в комментариях к задаче.
https://github.com/masssn/students.loc/blob/master/app/autoload.php
Я не советую полагаться на getcwd(). Он же может быть разный. Лучше использовать константу __DIR__.
Функция автозагрузки должна проверять, существует ли такой файл, перед тем как его инклудить, иначе такая команда вызовет ошибку:
class_exists('Test');
У меня есть урок по автозагрузке: https://github.com/codedokode/pasta/blob/master/php/autoload.md
https://github.com/masssn/students.loc/blob/master/app/controllers/Controller.php#L29
тут не очень хорошо сделано. Если ты хочешь требовать регистрацию, то надо не выводить форму на любой странице, а делать редирект на страницу регистрации. Если ты хочешь потом вернуться назад, то надо еще передавать URL исходной страницы (а перед редиректом назад проверять, что он на твоем сайте, иначе это будет открытый редирект).
Сама проверка тоже сделана неточно - ты проверяешь наличие куки, но не проверяешь, что она соответствует какому-то пользователю.
В методе action лучше было проверять весь URL, а не только первую часть. У тебя получается для одной страницы множество URL, что в общем-то плохо: /index/1, /index/2 и так далее. Лучше когда у страницы ровно один URL.
https://github.com/masssn/students.loc/blob/master/app/controllers/Controller.php#L152
Ошибка 404 сделана не совсем правильно. Ты должен отдавать HTTP код ответа 404, чтобы роботы и программы видели, что там ошибка.
- https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP
В PHP код ответа можно задать с помощью функции header().
Также, ты при выводе данных из $_SERVER не используешь htmlspecialchars. Из-за этого может быть XSS уязвимость.
В форме регистрации у тебя при ошибке не сохраняются введенные данные. В комментариях к задаче есть советы по работе с формами, и есть урок https://github.com/codedokode/pasta/blob/master/forms.md
Также, это вообще неправильно:
> $params = \app\DataHelper::makeSafeDatas($params);
Данные надо экранировать по-разному в разных ситуациях. Для вывода на странице - одним способом, для вставки в базу данных - другим. Как ты это сделаешь одной функцией? Никак. На каждый случай должна быть своя функция. А ты просто налепил кучу функций, в надежде, что они тебя защитят, не пытаясь разобраться, что каждая из них делает. Что, если у нас в форме есть поле комментария и в него можно вводить любые символы? Твои кривые функции вырежут половину из них.
Перечитай мои уроки по XSS и SQL и сделай правильную защиту:
- https://github.com/codedokode/pasta/blob/master/security/xss.md
- https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
https://github.com/masssn/students.loc/blob/master/app/views/View.php
Тут не очень понятно, для чего предназначен массив $attributes. Ну например в контроллере ты часть данных передаешь через добавленные динамически свойства, а часть напрямую:
> $this->view->pages = $navDatas["pages"];
> $this->view->setAtributes($params);
Не очень понятно, почему так сделано.
Удобнее наверно просто передавать значения для подстановки в шаблон в виде массива в метод display(), а там извлекать их с помощью extract().
Названия шаблонов лучше выбрать так, чтобы они соответствовали названиям методов контроллера.
https://github.com/masssn/students.loc/blob/master/app/DataHelper.php#L11
В методе validate ты не проверяешь, что в массиве есть такие поля. Может быть ошибка если часть полей не будет передана в POST.
> if (!filter_var($datas['birth_year'], FILTER_VALIDATE_INT) && mb_strlen($datas['birth_year']) > 5 || mb_strlen($datas['birth_year']) < 4 || preg_match("^[A-z]^", $datas['birth_year'])) {
Слишком много условий. Проще либо написать одно рег. выражение либо проверить что число в диапазоне между X и Y.
Аналогично с суммой баллов, лучше проверить что это число и что оно в определенном диапазоне.
Не очень понятно, почему у тебя везде передаются массивы. Не лучше ли сделать объект-модель студента и везде передавать его? В функцию валидации, в форму, в функцию сохранения в БД. Массив плох тем, что непонятно, какие в нем есть поля, а каких нет.
Класс DataHelper надо переименовать, например в StudentValidator.
Имена неймспейсов принято писать с большой буквы. app можно было и не писать вообще.
https://github.com/masssn/students.loc/blob/master/app/Pager.php
Плохо что этот класс лезет в GET. Ты жестко его привязал к внешним данным, и не позволяешь просто передать номер страницы как переменную.
Неправильно писать datas, так как data это уже множественное число. Вообще, у тебя неудачно выбрано название функции: getNavDatas. Слово data не дает никакой полезной информации, так как любая функция работает с данными. Надо лучше выбирать названия для функций, чтобы они соответствовали предназначению функции.
В данном случае функция рассчитыает параметры пагинации, так и можно ее назвать.
Также, ты старательно избегаешь использования объектов и ООП вообще. Ты не сделал модель для студента, а передаешь везде массивы. В пагинаторе то же самое- ты возвращаешь массив, но лучше было сделать просто объект и у него методы getPagesCount(), getLink() и так далее. Попробуй переделать класс расчета пагинации на использование полей и методов вместо возврата массива.
Контроллер поиска и вывода списка студентов можно объединить в один. Аналогично можно объединить регистрацию и редактирование.
https://github.com/masssn/students.loc/blob/master/app/Singleton.php
Зачем это? Не надо использовать паттерны там, где это не нужно. Есди ты хочешь изучить паттерны, изучи код библиотек вроде Symfony Forms или Doctrine, почитай книгу Фаулера (шаблоны разработки корпоративных приложений) и посмотри как их надо использовать на реальных примерах, а не читай статьи от тех, кто дальше синглтона ничего не понял.
>>921309
>>904635
https://github.com/masssn/students.loc/blob/master/app/DB.php
Зачем нужен этот класс? Почитай про DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
Также почитай про использование исключений, ты бездумно скопировал try/catch из какой-то статьи, не понимая, как он работает: https://github.com/codedokode/pasta/blob/master/php/exceptions.md
https://github.com/masssn/students.loc/blob/master/app/models/StudentsGateaway.php#L40
public function addStudent($params)
Эта функция написана неправильно. Она подразумевает, что ей будет передан массив содержащий строго определенные поля в определенном порядке. Причем этот порядок нигде не документирован. Стоит в форме переставить 2 поля местами, как все сломается.
Я советую передавать не массив, а объект студента с строго опредленным набором полей.
> setcookie('user_pass', $params["password"], time() + 3600000);
Есть принцип единой ответственности, каждый класс или функция должна выполнять свою задачу. Как установка куки связана с вставкой студента в БД? Никак, ее тут быть не должно вообще.
> public function getLastId()
Этот метод плох тем, что его можно вызывать только в опредленный момент, и это нигде не описано. Его тут быть не должно, если надо, то пусть функция вставки студента сохраняет куда-нибудь его id.
https://github.com/masssn/students.loc/blob/master/app/models/StudentsGateaway.php#L78
> public function getAll($sort, $start, $limit)
тут нет проверки, что в параметре sort разрешенные значения, а не какой-то вредоносный код. Перечитай урок про SQL инъекции.
> " LIMIT " . $start . "," . $limit;
Это надо было делать через плейсхолдеры.
> public function search($search, $sort, $start, $limit)
> :x IN(name, second_name, group_number, summary)
Для поиска надо использовать LIKE или что-то такое, так как твой вариант не позволяет искать по части фамилии.
> public function getCountBySearch($search)
> WHERE
> " . $search . "
Надо использовать плейсхолдеры.
https://github.com/masssn/students.loc/tree/master/app/views
Для шаблонов и классов надо сделать разные папки.
https://github.com/masssn/students.loc/blob/master/app/views/students_list_template.php#L10
> <td> <a href="/index/?sort=name&page=<?php echo $this->link; ?>">Имя</a></td>
Знак & нельзя просто так писать в HTML коде, это спецсимвол. Почитай про мнемоники (entities) в HTML.
> <a href="/index/?sort=<?php echo $this->sort; ?>&page=<?php echo $i;?>">[<?php echo $i?>]</a>
Не надо собирать ссылку по кускам. Лучше сделать функцию, которая ее генерирует.
>>921309
>>904635
https://github.com/masssn/students.loc/blob/master/app/DB.php
Зачем нужен этот класс? Почитай про DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
Также почитай про использование исключений, ты бездумно скопировал try/catch из какой-то статьи, не понимая, как он работает: https://github.com/codedokode/pasta/blob/master/php/exceptions.md
https://github.com/masssn/students.loc/blob/master/app/models/StudentsGateaway.php#L40
public function addStudent($params)
Эта функция написана неправильно. Она подразумевает, что ей будет передан массив содержащий строго определенные поля в определенном порядке. Причем этот порядок нигде не документирован. Стоит в форме переставить 2 поля местами, как все сломается.
Я советую передавать не массив, а объект студента с строго опредленным набором полей.
> setcookie('user_pass', $params["password"], time() + 3600000);
Есть принцип единой ответственности, каждый класс или функция должна выполнять свою задачу. Как установка куки связана с вставкой студента в БД? Никак, ее тут быть не должно вообще.
> public function getLastId()
Этот метод плох тем, что его можно вызывать только в опредленный момент, и это нигде не описано. Его тут быть не должно, если надо, то пусть функция вставки студента сохраняет куда-нибудь его id.
https://github.com/masssn/students.loc/blob/master/app/models/StudentsGateaway.php#L78
> public function getAll($sort, $start, $limit)
тут нет проверки, что в параметре sort разрешенные значения, а не какой-то вредоносный код. Перечитай урок про SQL инъекции.
> " LIMIT " . $start . "," . $limit;
Это надо было делать через плейсхолдеры.
> public function search($search, $sort, $start, $limit)
> :x IN(name, second_name, group_number, summary)
Для поиска надо использовать LIKE или что-то такое, так как твой вариант не позволяет искать по части фамилии.
> public function getCountBySearch($search)
> WHERE
> " . $search . "
Надо использовать плейсхолдеры.
https://github.com/masssn/students.loc/tree/master/app/views
Для шаблонов и классов надо сделать разные папки.
https://github.com/masssn/students.loc/blob/master/app/views/students_list_template.php#L10
> <td> <a href="/index/?sort=name&page=<?php echo $this->link; ?>">Имя</a></td>
Знак & нельзя просто так писать в HTML коде, это спецсимвол. Почитай про мнемоники (entities) в HTML.
> <a href="/index/?sort=<?php echo $this->sort; ?>&page=<?php echo $i;?>">[<?php echo $i?>]</a>
Не надо собирать ссылку по кускам. Лучше сделать функцию, которая ее генерирует.
По поводу сапера и словарей:
> MinesweeperGame.prototype._addCellToDict = function(x, y, dict) {
> MinesweeperGame.prototype._isCellInDict = function(x, y, dict) {
Тебе не кажется, что тут есть смысл сделать отдельный объект "двухмерный словарь" и писать dict.addCell(x, y) или dict.hasCell(x, y)?
Потому что сейчас у тебя там перемешаны игровая логика (как открыть и проверить соседние клеточки) и низкоуровневая работа со словарем (поиск в словаре, и тд). Это надо разнести хотя бы по функциям, а еще лучше в отдельный класс.
>>925484
> И еще, стоит ли писать вот такую обертку вокруг метода реализации чтобы не кидать каждый раз свойства объекта ему в аргументы
Если сделать отдельный объект, будет красивее и исчезнет обертка:
MinesweeperGame.prototype.countFlags = function() {
return this._flags.count();
};
>>925772
> ...ты имел ввиду действительно двумерный массив ([ [true, false], [false, false], [false, true], [true, false]) или словарь ( {1: {1: true, 2:false}, 2: {1: false, 2: false}, 3: {1:false, 2: true})?
Если там могут отсутствовать клеточки, то словарь логичнее так как массив подразумевает что в нем все индексы есть, пропусков нет.
> Мне кажется, ты имел ввиду первое, а я делаю через второе и в некоторых местах трудности (например мучаюсь как создать мины при такой структуре чтобы не было вечного рандома. Когда у меня был массив объектов Cell я просто делал на него shuffle и превращал в мины n-ое количество равное числу мин).
Можно ограничить число попыток, либо можно сделать отдельный массив координат, перемешать его и выставить мины в словаре.
>>931322
> Правильно ли я установил события?
У меня была идея убрать всю работу с DOM во вью, чтобы контроллер про него ничего не знал. И работал не с натиными событиями браузера, а с искуственными событиями, сгенерированными вью. Это например дает такое преимущество, что изменения в верстке затрагивают только вью, а не контроллер.
> this._domView.setLeftMouseDownEventToCell(function(event) {
В общем да, нормально, только может функцию стоит как-то еще переименовать. Вроде addCellLeftClickListener, как-нибудь так.
> that._domView.setFaceType("danger-face");
Вот мне кажется, это должно называться по другому. Что обозначает значок? Что идет обработка события? Тогда логично назвать функцию как setIsBusy(true) или как-то так. И может быть, даже стоит сделать смену значка внутри view.
То есть мне кажется, лучше бы если вью просто выдавал событие клика по клеточке, а не отдельно mousedown/up. И чтобы контроллер не проверял e.which. И чтобы вью передавал в контроллер не event.target (который относится к DOM), а координаты. То есть я предлагаю всю работу с DOM и нативными событиями засунуть в вью, а из контроллера убрать. Разделить зоны ответственности.
По поводу сапера и словарей:
> MinesweeperGame.prototype._addCellToDict = function(x, y, dict) {
> MinesweeperGame.prototype._isCellInDict = function(x, y, dict) {
Тебе не кажется, что тут есть смысл сделать отдельный объект "двухмерный словарь" и писать dict.addCell(x, y) или dict.hasCell(x, y)?
Потому что сейчас у тебя там перемешаны игровая логика (как открыть и проверить соседние клеточки) и низкоуровневая работа со словарем (поиск в словаре, и тд). Это надо разнести хотя бы по функциям, а еще лучше в отдельный класс.
>>925484
> И еще, стоит ли писать вот такую обертку вокруг метода реализации чтобы не кидать каждый раз свойства объекта ему в аргументы
Если сделать отдельный объект, будет красивее и исчезнет обертка:
MinesweeperGame.prototype.countFlags = function() {
return this._flags.count();
};
>>925772
> ...ты имел ввиду действительно двумерный массив ([ [true, false], [false, false], [false, true], [true, false]) или словарь ( {1: {1: true, 2:false}, 2: {1: false, 2: false}, 3: {1:false, 2: true})?
Если там могут отсутствовать клеточки, то словарь логичнее так как массив подразумевает что в нем все индексы есть, пропусков нет.
> Мне кажется, ты имел ввиду первое, а я делаю через второе и в некоторых местах трудности (например мучаюсь как создать мины при такой структуре чтобы не было вечного рандома. Когда у меня был массив объектов Cell я просто делал на него shuffle и превращал в мины n-ое количество равное числу мин).
Можно ограничить число попыток, либо можно сделать отдельный массив координат, перемешать его и выставить мины в словаре.
>>931322
> Правильно ли я установил события?
У меня была идея убрать всю работу с DOM во вью, чтобы контроллер про него ничего не знал. И работал не с натиными событиями браузера, а с искуственными событиями, сгенерированными вью. Это например дает такое преимущество, что изменения в верстке затрагивают только вью, а не контроллер.
> this._domView.setLeftMouseDownEventToCell(function(event) {
В общем да, нормально, только может функцию стоит как-то еще переименовать. Вроде addCellLeftClickListener, как-нибудь так.
> that._domView.setFaceType("danger-face");
Вот мне кажется, это должно называться по другому. Что обозначает значок? Что идет обработка события? Тогда логично назвать функцию как setIsBusy(true) или как-то так. И может быть, даже стоит сделать смену значка внутри view.
То есть мне кажется, лучше бы если вью просто выдавал событие клика по клеточке, а не отдельно mousedown/up. И чтобы контроллер не проверял e.which. И чтобы вью передавал в контроллер не event.target (который относится к DOM), а координаты. То есть я предлагаю всю работу с DOM и нативными событиями засунуть в вью, а из контроллера убрать. Разделить зоны ответственности.
> Все еще не ясно, что ты имеешь ввиду под использованием сервисов. В 83 треде ты писал, что:
>> $app['validator'] = function() use($app){return new App\Helper\Validator($app);};
>>Это по моему нарушение принципа DI, получается не DI, а Service Locator
> Нужно регистрировать сервис провайдеры или что?
Там проблема в том, что ты в сервис Validator передаешь весь $app целиком вместо указания конкретных зависимостей. Это паттерн ServiceLocator, и это описано в моем уроке по DI. В данном случае это плохо, никакого смысла так делать нет.
Надо так: new Validator($app->something, $app->em, $app->helper)
> 1 - я не храню пароль в базе, а в форме он должен быть
В Симфони сделано так: там можно добавлять в форму элементы, которые не соответствуют полям в entity, и потом их значения можно брать из этих элементов, по моему так:
$form = ...
$form->submit();
$password = $form->password->getValue();
$auth->setUserPassword($user, $password);
...
То есть часть полей идут напрямую в entity, часть остаются в форме.
> 2 - для проверки полей на UniqueValue() нужно установить doctrine bridge и doctrine bundle
Doctrine Bundle в силекс, я думаю, не поставить, так как это кусок фреймворка Симфони.
>>924780
> в официальной документации приводятся примеры использования yaml конфига, как альтернатива аннотациям, но проблема в том, что я использую силекс, а не симфони и тут все не так, как в симфони.
А тебе не особо и нужна инструкция. Ты можешь посмотреть, как сделано в Симфони, и нагородить похожую схему. Там просто нужно создать и настроить объект, который будет читать аннотации из файлов.
> Чем больше модулей симфони прикручивается к силексу, тем больше мне кажется, что он изначально не предназначался для этого.
Компоненты Симфони как раз сделаны так, чтобы их можно было использовать где угодно. Но вот часть функционала, которая не в компонентах, а в бандлах, ее придется делать самому. Но там часто просто нужно создать какие-то объекты и задать им нужные настройки.
> Если мы используем модель пользователя для регистрации, то зачем создавать отдельную модель для логина
Ну логин все же немного другое, на мой взгляд. Форма логина - это не заполнение информации о пользователе. Это что-то вроде формы поиска.
>>>Я все же думаю, что это уже не задача формы - проверять логин.
> Но это же не форма. Это валидатор, в который я передаю форму с констрантой.
Не, это валидатор, который ты встроил в форму и который по сути становится ее частью. Мне кажется, проверка логина/пароля должна быть не в ней. А в коде, который использует эту форму. Ты же не встраиваешь сохранение в базу данных в форму. Надо разделять прием данных от пользователя и дальнейшее использование этих данных.
> Этот класс создан специально для того, чтобы проверить есть ли такая связка логин+пароль в базе, и если этот класс не может этого сделать, то зачем он тогда нужен?
Он нужен, но должен вызываться в другом месте. Не ставиться как валидатор для формы.
> Что значит использовать сервисы? У тебя есть уроки использования или примеры таких сервисов?
Сервис - это класс, который не представляет какую-то сущность, но содержит методы. Это довольно широкое определение получается, вот тут есть еще что-то по теме, но не очень понятно: http://design-pattern.ru/patterns/service-layer.html
Ну например мы можем сделать сервис для работы с БД:
class UserGateway {
public function insertUser(User $user);
...
}
У тебя в коде контроллера часть кода можно вынести в отдельные функции, а эти функции - в сервисы. Но ты вместо этого пишешь код стеной, не разбивая его на отдельные действия.
Давай посмотрим сюда: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Controller/CommentController.php и параллельно вспомним чем должен заниматься контроллер в MVC.
У тебя он занимается:
- выводом данных (printComments, очень странная функция) - а должен заниматься view
- сборкой дерева комментариев (makeCommentTree) - а должна model (кстати используется неэффективный алгоритм, почитай потом урок https://github.com/codedokode/pasta/blob/master/db/trees.md )
- определением id текущего пользователя ($hash = $_COOKIE['fileshare'];) - а лучше сделать отдельную функцию для этого в отдельном сервисе авторизации
- генерацией ссылок (return $app->redirect("/download/$fileId");) - а лучше сделать для этого функцию
Или посмотрим на https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Controller/RegisterController.php . Он занимается:
- генерацией паролей и хешей, что относится к модели ( $user->setSalt(uniqid());, $user->setHash(md5($user->getEmail().$user->getSalt().$data->password));). Очевидно, в модели должна быть функция вроде setUserPassword, ты вместо этого просто пишешь код стеной
- установкой авторизационных кук вместо сервиса авторизации
То есть тот код, который логично поместить в другой компонент или вынести в функцию, ты просто вписал в контроллер, даже не вынеся в отдельный метод.
Это и называется толстый контроллер (fat ugly controller).
Это затрудняет понимание и правку кода, мешает повторному использованию. ну например, нам нужно в другом месте получить дерево комментариев. Как это сделать, если код для этого жестко вклеен в контроллер и его нельзя вызвать из другого места? То же касается операций смены пароля, авторизации, генерации ссылок.
> Все еще не ясно, что ты имеешь ввиду под использованием сервисов. В 83 треде ты писал, что:
>> $app['validator'] = function() use($app){return new App\Helper\Validator($app);};
>>Это по моему нарушение принципа DI, получается не DI, а Service Locator
> Нужно регистрировать сервис провайдеры или что?
Там проблема в том, что ты в сервис Validator передаешь весь $app целиком вместо указания конкретных зависимостей. Это паттерн ServiceLocator, и это описано в моем уроке по DI. В данном случае это плохо, никакого смысла так делать нет.
Надо так: new Validator($app->something, $app->em, $app->helper)
> 1 - я не храню пароль в базе, а в форме он должен быть
В Симфони сделано так: там можно добавлять в форму элементы, которые не соответствуют полям в entity, и потом их значения можно брать из этих элементов, по моему так:
$form = ...
$form->submit();
$password = $form->password->getValue();
$auth->setUserPassword($user, $password);
...
То есть часть полей идут напрямую в entity, часть остаются в форме.
> 2 - для проверки полей на UniqueValue() нужно установить doctrine bridge и doctrine bundle
Doctrine Bundle в силекс, я думаю, не поставить, так как это кусок фреймворка Симфони.
>>924780
> в официальной документации приводятся примеры использования yaml конфига, как альтернатива аннотациям, но проблема в том, что я использую силекс, а не симфони и тут все не так, как в симфони.
А тебе не особо и нужна инструкция. Ты можешь посмотреть, как сделано в Симфони, и нагородить похожую схему. Там просто нужно создать и настроить объект, который будет читать аннотации из файлов.
> Чем больше модулей симфони прикручивается к силексу, тем больше мне кажется, что он изначально не предназначался для этого.
Компоненты Симфони как раз сделаны так, чтобы их можно было использовать где угодно. Но вот часть функционала, которая не в компонентах, а в бандлах, ее придется делать самому. Но там часто просто нужно создать какие-то объекты и задать им нужные настройки.
> Если мы используем модель пользователя для регистрации, то зачем создавать отдельную модель для логина
Ну логин все же немного другое, на мой взгляд. Форма логина - это не заполнение информации о пользователе. Это что-то вроде формы поиска.
>>>Я все же думаю, что это уже не задача формы - проверять логин.
> Но это же не форма. Это валидатор, в который я передаю форму с констрантой.
Не, это валидатор, который ты встроил в форму и который по сути становится ее частью. Мне кажется, проверка логина/пароля должна быть не в ней. А в коде, который использует эту форму. Ты же не встраиваешь сохранение в базу данных в форму. Надо разделять прием данных от пользователя и дальнейшее использование этих данных.
> Этот класс создан специально для того, чтобы проверить есть ли такая связка логин+пароль в базе, и если этот класс не может этого сделать, то зачем он тогда нужен?
Он нужен, но должен вызываться в другом месте. Не ставиться как валидатор для формы.
> Что значит использовать сервисы? У тебя есть уроки использования или примеры таких сервисов?
Сервис - это класс, который не представляет какую-то сущность, но содержит методы. Это довольно широкое определение получается, вот тут есть еще что-то по теме, но не очень понятно: http://design-pattern.ru/patterns/service-layer.html
Ну например мы можем сделать сервис для работы с БД:
class UserGateway {
public function insertUser(User $user);
...
}
У тебя в коде контроллера часть кода можно вынести в отдельные функции, а эти функции - в сервисы. Но ты вместо этого пишешь код стеной, не разбивая его на отдельные действия.
Давай посмотрим сюда: https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Controller/CommentController.php и параллельно вспомним чем должен заниматься контроллер в MVC.
У тебя он занимается:
- выводом данных (printComments, очень странная функция) - а должен заниматься view
- сборкой дерева комментариев (makeCommentTree) - а должна model (кстати используется неэффективный алгоритм, почитай потом урок https://github.com/codedokode/pasta/blob/master/db/trees.md )
- определением id текущего пользователя ($hash = $_COOKIE['fileshare'];) - а лучше сделать отдельную функцию для этого в отдельном сервисе авторизации
- генерацией ссылок (return $app->redirect("/download/$fileId");) - а лучше сделать для этого функцию
Или посмотрим на https://github.com/anotherCodeMunkey/fileshare/blob/master/App/Controller/RegisterController.php . Он занимается:
- генерацией паролей и хешей, что относится к модели ( $user->setSalt(uniqid());, $user->setHash(md5($user->getEmail().$user->getSalt().$data->password));). Очевидно, в модели должна быть функция вроде setUserPassword, ты вместо этого просто пишешь код стеной
- установкой авторизационных кук вместо сервиса авторизации
То есть тот код, который логично поместить в другой компонент или вынести в функцию, ты просто вписал в контроллер, даже не вынеся в отдельный метод.
Это и называется толстый контроллер (fat ugly controller).
Это затрудняет понимание и правку кода, мешает повторному использованию. ну например, нам нужно в другом месте получить дерево комментариев. Как это сделать, если код для этого жестко вклеен в контроллер и его нельзя вызвать из другого места? То же касается операций смены пароля, авторизации, генерации ссылок.
И еще. Если ты встроил проверку логина/пароля в форму, то как их проверить без формы? Тот же вопрос по поводу валидации комментариев, пользователей. Можем ли мы достать комментарий из формы или еще откуда-нибудь и проверить на правильность?
То есть, у нас должны быть какие-то классы (сервисы), часть модели, которые содержат операции вроде "сменить пароль", "залогиниться под пользователем X", "проверить правильнсоть комментария", "добавить комментарий" и тд.
Тебе надо изучить теорию про классы и объекты, ну например в учебнике из ОП поста или еще откуда-то. Ты явно какую-то тему проскочил и не понимаешь что такое объект, что такое поле.
$this->first_name это поле объекта
$first_name это переменная, аргумент конструктора
>>927224
> Почему нельзя написать вместо $this->first_name просто $first_name?
Потому что в методе say_name нет такой переменной, и если ты не пропустил изучение функций, то должен знать что внутри функции видны только созданные в ней или переданные ей перменные, и $first_name там нет.
Тебе это кажется сложным, потому что ты изучаешь язык не последовательно, а пропустил более ранние темы. Или учебник плохо объясняет.
Ну ты говоришь объекты, это сложно. А реши задачу про Вектор или кошки-мышки из моего учебника без объектов. Будет еще сложнее. ООП как раз придумано чтобы позволить в больших программах разбивать код на отдельные изолированные классы чтобы с ним было проще работать.
Ну и если не использовать объекты, как в программе хранить информацию о каких-то сущностях? Вот у тебя есть гостиница, в ней номера, у каждого номера есть свойства вроде этажа, стоимости и тд. Как ты это будешь хранить и обрабатывать без обектов?
> http://ideone.com/WeGENR - Вопросы с абстрактными классами.
В функции checkAnswer лучше было сделать только проверку, без вывода на экран. То есть пусть функция просто проверяет ответ, возврашает 0 или 1 (ну или true/false), или число баллов, а код, который ее вызвал, уже решает, что с этим делать.
Из-за этого у тебя еще и копипаста получилась.
Поле $tip логичнее было поместить в AbstractQustion так как подсказка может быть у вопроса любого типа.
В остальном верно.
> http://ideone.com/qRGcXB - Компания Вектор.
Тут все верно.
>Есть где-нибудь пример полного процесса создания сайта, от вёрстки макета и до натягивания его на КМС?
>КМС
$user->password = password_hash($data['password'], PASSWORD_DEFAULT);
Пароль должен хешироваться в базе, но ниху не получается. Прописывается в таблице обычными символами, т.е. то что ввел юзер при регистрации.
Чому так? Где я мог обосраться?
ОпенСервер, PHP 5.5, Апач 2.4, MySQL 5.6, регистрацию через подключенную RedBean
>Ты пароль шифруешь?
Да, шифрую пароль, введенный юзером при регистрации на сайте.
>С помощью чего?
С помощью функции password_hash(). Алгоритм хеширования BCrypt.
с помощью var_dump посмотри значение поля
$user->password
Либо передай в другую форму и посмотри значения
Да. Появилось множество методологий.
Пре-процессоры. Если тебя технологии интересуют.
В разве что появилось множество интереснейших вещей.
>>>Задача 2: сделать функцию addProperty(object, name, initialValue) для создания приватных свойств с геттерами и сеттерами на объекте или прототипе объекта.
>Тут опечатка в коде...
Исправил, теперь всё работает: https://jsfiddle.net/sh21j4p1/1/
Я пытался задать метод через прототип и обратиться к нему в "конструкторе" функции, но мне выдалась ошибка о том что этот метод не является функцией: https://jsfiddle.net/3sjvm5gp/
Почему так получилось? Ведь если функция не находиться в объекте она ищется по цепочке в его прототипе.
>>908071
>Задача 3: сделать функцию для добавления в объект или прототип нового метода addMethod(object, name, fn).
Что-то у меня не получилось понять подвоха этой задачи: https://jsfiddle.net/zmLjh7ur/
Более того, я не смог понять как работает функция super(): https://jsfiddle.net/b073aLb5/1/
В чем моя ошибка? Здесь же должна быть такая же ситуация, super должен искать метод в прототипе.
>>921312
>Задача про фастфуд
>> Почему-то не получается https://jsfiddle.net/y28h2o2b/3/
>this.menu не определено
Определил https://jsfiddle.net/y28h2o2b/3/
Привычка что переменные сами определяются в php
>Электросеть
https://jsfiddle.net/6591a2sL/5/
>>>Для ЛЭП проще всего указать нули и учитывать их вклад отдельно (сделать у них методы для получения информации, сколько мощности доступно и по какой цене).
>> Лучше исключить её из перебора потому, что ЛЭП тоже может вернуть какое-то количество мощности.
>Мне кажется, надежнее прописать там ноль, так как мы не знаем, какая у нее мощность. Ну или вообще не делать в ней метода getPower(), чтобы его не пытались даже вызывать.
Но мы можем и даже должны иметь возможность узнать её мощность.
>>>> ElectricalNetwork.prototype.countPrice = function() {
>>>> price += this.elements.countPrice(balance);
>>>> balance = this.elements.countPower(balance);
>>>Вот здесь нехорошо, что код расчета закупок/продаж размазан по 2 классам - ElectricalNetwork и PowerLine. Логичнее его оставить только в ElectricalNetwork. Ну подумай сам: кто принимает решение о закупке: электросеть или начальник на конкретной ЛЭП (или руководство удаленной сети, к которой подключена ЛЭП)? Логично, что решение принимают в центре, а на ЛЭП только сообщают, сколько нужно принять или передать, запрашивают цены и тд. Так как сама ЛЭП не знает про баланс энергии в сети.
>>С другой стороны электросеть не знает о ценах и о внутреннем устройстве ЛЭП. Моя идея в том, баланс можно просто передать в условный счетчик. Ведь это для ЛЭП свойственно иметь и считать цену.
>Электросеть не знает цены и пропускной способности, но она всегда может ее у ЛЭП спросить. ЛЭП сама не может ничего посчитать, так как не знает баланс энергии во всей сети, и не может решить, сколько надо закупать. Потому расчет, сколько энергии купить, должен быть в электросети.
А почему ЛЭП не может спросить сколько энергии нужно?
Тут дело даже не в этом, ЛЭП сама по себе ничего не спрашивает - мы просто передаем энергию и пользуемся её функцией счетчиком, который работает с её же свойством цена.
>> function PowerLine(power, price) {
>> this.power = power;
>> this.price = price;
>> }
>Мне кажется, тут неправильно используется свойство power, так как у ЛЭП это не фактически переданная мощность, а пропускная способность (сколько максимум можно купить/продать), и логично для нее использовать другое название, чтобы не было путаницы.
В реальной жизни тоже некоторые свойства могут называться одинакового, не смотря на то что выполняют разные функции.
>> PowerLine.prototype.countPower = function(power) {
>> var thispower = this.power;
>> for (var i = 0; i < thispower; thispower--) {
>> if (power == 0) {
>> break;
>> }
>> power += power / -Math.abs(power);
>что-то я не могу толком понять, что тут происходит. Зачем мы power делим саму на себя? Получится ведь либо 1, либо -1. Тут явно ошибка.
Наверно я сбил столку неправильно назвав функцию - эта функция считает сколько энергии останется после передачи\получения её через конкретную ЛЭП.
power мы делим саму на себя чтобы узнать прибавлять или отнимать энергию каждый раз, чтобы достичь баланса, т.е. узнать обратный знак своего значения. То есть, мы каждый раз либо прибавляем или отнимаем по одному, до тех пор пока значение не станет нулём.
>> if (this.elements instanceof PowerLine) {
>> price += this.elements.countPrice(balance);
>> balance = this.elements.countPower(balance);
>Тут нет расчета, сколько именно нужно купить. Нужно покупать не всю доступную энергию, а только ту, что не удалось произвести.
Так баланс это и есть вся энергия которую не удалось произвести \ удалось чрезмерно произвести.
Счетчики работают до тех пор пока он не станет нулевым или у ЛЭП не хватит больше мощности.
>Определение типа переменной
>>>Недостаточно, надо бы проверить что там есть свойства от 0 до length - 1.
>>>от 0 до length - 1
>> А как это выразить в условии
>Написать цикл, проверяющий наличие свойств с помощью оператора in.
А что с этим циклом делать? Нельзя же вернуть значение только по окончанию цикла, если in всегда были истины.
>15. Напиши функцию неглубокого копирования объектов и массивов
https://jsfiddle.net/uyey3at1/3/
>>>> for (property in object) {
>>>тут есть подвох, for in перебирает не только свойства объекта, но и его прототипов. Если кто-то расширит стандартный Object.prototype, это свойство или метод попадет в цикл.
>> Опять же, не могу понять что с этим не так: Если это клон объекта, то этот клон должен иметь тот же прототип что и "донор"(?).
>Не, до такой степени скопировать объект мы вряд ли сможем. Достаточно копировать только свойства самого объекта, копировать прототип не требуется.
А понял. Подвох в том что копируется не сам прототип а только его свойства, верно же?
>Тут в коде, я вижу, есть поддержка копирования объектов Date и обычных объектов, а что с массивами? Для них ведь надо изначально создавать пустой массив и копировать элементы.
А других встроенных объектов это замечание касается?
>Глубокое копирование
>> var clone = new object.constructor;
>Неприавльно вызывать конструктор. Мы ведь не знаем, какие аргументы у него есть и что в них надо передать. Надо делать так:
>
>- если источник - это Date, то создать новый Date, можно сразу передать правильное значение даты
>- если источник - массив, создать новый массив и скопировать элементы
>- если источник - другой объект создать пустой объект и скопировать все его свойства, не относящиеся к прототипам
>- иначе, если источник - не объект (а число, строка, null и тд), можно просто вернуть его
Переписал всё: https://jsfiddle.net/b0a7tk75/
>>>Задача 2: сделать функцию addProperty(object, name, initialValue) для создания приватных свойств с геттерами и сеттерами на объекте или прототипе объекта.
>Тут опечатка в коде...
Исправил, теперь всё работает: https://jsfiddle.net/sh21j4p1/1/
Я пытался задать метод через прототип и обратиться к нему в "конструкторе" функции, но мне выдалась ошибка о том что этот метод не является функцией: https://jsfiddle.net/3sjvm5gp/
Почему так получилось? Ведь если функция не находиться в объекте она ищется по цепочке в его прототипе.
>>908071
>Задача 3: сделать функцию для добавления в объект или прототип нового метода addMethod(object, name, fn).
Что-то у меня не получилось понять подвоха этой задачи: https://jsfiddle.net/zmLjh7ur/
Более того, я не смог понять как работает функция super(): https://jsfiddle.net/b073aLb5/1/
В чем моя ошибка? Здесь же должна быть такая же ситуация, super должен искать метод в прототипе.
>>921312
>Задача про фастфуд
>> Почему-то не получается https://jsfiddle.net/y28h2o2b/3/
>this.menu не определено
Определил https://jsfiddle.net/y28h2o2b/3/
Привычка что переменные сами определяются в php
>Электросеть
https://jsfiddle.net/6591a2sL/5/
>>>Для ЛЭП проще всего указать нули и учитывать их вклад отдельно (сделать у них методы для получения информации, сколько мощности доступно и по какой цене).
>> Лучше исключить её из перебора потому, что ЛЭП тоже может вернуть какое-то количество мощности.
>Мне кажется, надежнее прописать там ноль, так как мы не знаем, какая у нее мощность. Ну или вообще не делать в ней метода getPower(), чтобы его не пытались даже вызывать.
Но мы можем и даже должны иметь возможность узнать её мощность.
>>>> ElectricalNetwork.prototype.countPrice = function() {
>>>> price += this.elements.countPrice(balance);
>>>> balance = this.elements.countPower(balance);
>>>Вот здесь нехорошо, что код расчета закупок/продаж размазан по 2 классам - ElectricalNetwork и PowerLine. Логичнее его оставить только в ElectricalNetwork. Ну подумай сам: кто принимает решение о закупке: электросеть или начальник на конкретной ЛЭП (или руководство удаленной сети, к которой подключена ЛЭП)? Логично, что решение принимают в центре, а на ЛЭП только сообщают, сколько нужно принять или передать, запрашивают цены и тд. Так как сама ЛЭП не знает про баланс энергии в сети.
>>С другой стороны электросеть не знает о ценах и о внутреннем устройстве ЛЭП. Моя идея в том, баланс можно просто передать в условный счетчик. Ведь это для ЛЭП свойственно иметь и считать цену.
>Электросеть не знает цены и пропускной способности, но она всегда может ее у ЛЭП спросить. ЛЭП сама не может ничего посчитать, так как не знает баланс энергии во всей сети, и не может решить, сколько надо закупать. Потому расчет, сколько энергии купить, должен быть в электросети.
А почему ЛЭП не может спросить сколько энергии нужно?
Тут дело даже не в этом, ЛЭП сама по себе ничего не спрашивает - мы просто передаем энергию и пользуемся её функцией счетчиком, который работает с её же свойством цена.
>> function PowerLine(power, price) {
>> this.power = power;
>> this.price = price;
>> }
>Мне кажется, тут неправильно используется свойство power, так как у ЛЭП это не фактически переданная мощность, а пропускная способность (сколько максимум можно купить/продать), и логично для нее использовать другое название, чтобы не было путаницы.
В реальной жизни тоже некоторые свойства могут называться одинакового, не смотря на то что выполняют разные функции.
>> PowerLine.prototype.countPower = function(power) {
>> var thispower = this.power;
>> for (var i = 0; i < thispower; thispower--) {
>> if (power == 0) {
>> break;
>> }
>> power += power / -Math.abs(power);
>что-то я не могу толком понять, что тут происходит. Зачем мы power делим саму на себя? Получится ведь либо 1, либо -1. Тут явно ошибка.
Наверно я сбил столку неправильно назвав функцию - эта функция считает сколько энергии останется после передачи\получения её через конкретную ЛЭП.
power мы делим саму на себя чтобы узнать прибавлять или отнимать энергию каждый раз, чтобы достичь баланса, т.е. узнать обратный знак своего значения. То есть, мы каждый раз либо прибавляем или отнимаем по одному, до тех пор пока значение не станет нулём.
>> if (this.elements instanceof PowerLine) {
>> price += this.elements.countPrice(balance);
>> balance = this.elements.countPower(balance);
>Тут нет расчета, сколько именно нужно купить. Нужно покупать не всю доступную энергию, а только ту, что не удалось произвести.
Так баланс это и есть вся энергия которую не удалось произвести \ удалось чрезмерно произвести.
Счетчики работают до тех пор пока он не станет нулевым или у ЛЭП не хватит больше мощности.
>Определение типа переменной
>>>Недостаточно, надо бы проверить что там есть свойства от 0 до length - 1.
>>>от 0 до length - 1
>> А как это выразить в условии
>Написать цикл, проверяющий наличие свойств с помощью оператора in.
А что с этим циклом делать? Нельзя же вернуть значение только по окончанию цикла, если in всегда были истины.
>15. Напиши функцию неглубокого копирования объектов и массивов
https://jsfiddle.net/uyey3at1/3/
>>>> for (property in object) {
>>>тут есть подвох, for in перебирает не только свойства объекта, но и его прототипов. Если кто-то расширит стандартный Object.prototype, это свойство или метод попадет в цикл.
>> Опять же, не могу понять что с этим не так: Если это клон объекта, то этот клон должен иметь тот же прототип что и "донор"(?).
>Не, до такой степени скопировать объект мы вряд ли сможем. Достаточно копировать только свойства самого объекта, копировать прототип не требуется.
А понял. Подвох в том что копируется не сам прототип а только его свойства, верно же?
>Тут в коде, я вижу, есть поддержка копирования объектов Date и обычных объектов, а что с массивами? Для них ведь надо изначально создавать пустой массив и копировать элементы.
А других встроенных объектов это замечание касается?
>Глубокое копирование
>> var clone = new object.constructor;
>Неприавльно вызывать конструктор. Мы ведь не знаем, какие аргументы у него есть и что в них надо передать. Надо делать так:
>
>- если источник - это Date, то создать новый Date, можно сразу передать правильное значение даты
>- если источник - массив, создать новый массив и скопировать элементы
>- если источник - другой объект создать пустой объект и скопировать все его свойства, не относящиеся к прототипам
>- иначе, если источник - не объект (а число, строка, null и тд), можно просто вернуть его
Переписал всё: https://jsfiddle.net/b0a7tk75/
>Тут из-за кучи цитат становится уже трудно понимать некоторые ответы, если что-то непонятно, можно просто задать вопрос отдельно.
Простите, я просто обычно делаю всё разом.
А ОП-пост прочитать слабо?
Чтобы делать эти задания, тебе надо установить Апач + PHP (можно заодно сразу и MySQL) на компьютер. Вот полезные инструкции:
https://github.com/codedokode/pasta/blob/master/soft/php-install.md
https://github.com/codedokode/pasta/blob/master/soft/apache-install.md
Ты ОП? Подскажи пожалуйста, я вообще хотел язык программирования - кое-какие операции на компе сделать самым простым способом. Я года 3 назад в этом треде прошёл курс молодого бойца, так что помню что в ПХП есть всякие операции с папками и язык лёгкий.
Или есть что получше?
(я уже раньше писал, просто скопирую сюда)
Давай я тебе дам задачу. Есть какая-то сущность и коллекция этих сущностей (Работник и список работников, Ученик и классный журнал, Товар и Корзина, Товар и Склад). Можешь представить их как объекты или массивы, если не знаешь ООП.
Напиши функцию, определяющую сколько сущностей в коллекции соответствуют какому-то критерию. Критерий может быть любой. Ну например, сколько учеников выше 160 см или имеют среднюю оценку или чья фамилия начинается на гласную.
Напиши функцию, отбирающую массив сущностей, соответствующих определенному критерию.
Напиши функцию, находящую лучшую сущность по произвольному критерию (ученик с самой большой суммой оценок, с самой короткой фамилией, самый тяжелый товар, товар с самой большой скидкой, итд)
Напиши функцию, принимающую на вход массив сущностей и возвращающую массив вычисленных из них по произвольной формуле значений (например: на вход подаем товары с ценой и скидкой, на выходе - массив цен с учетом скидки, на вход - ученики, на выходе - массив средних баллов).
Как ты представишь в своем коде "критерий" или "формула"? Как передать в функцию этот критерий?
Суть примерно в том, что в результирующем массиве должен был затираться элемент "6ab" и вместо него повторяться "5ab" еще раз.
Мб версия пыхи старая, мб еще что - не знаю, в общем кто поможет мб понять в чем обсер?
http://ideone.com/LMl108
Для начала изучи внимательно предупреждение про ссылки и foreach тут http://php.net/manual/ru/control-structures.foreach.php
Не стоит использовать ссылки если ты не понимаешь до конца как они работают и не прочел целиком раздел мануала про них.
для начала установи апач\нгинкс, пхп и базу данных. Настрой все это, чтобы работало. Потом можешь начать с создания страницы с прикрученным к ней бутстрапом. Дальше расширяй и наполняй контентом. Аноны кидают тут свои гитхабы, можешь посмотреть как они делают.
У меня какая-то попобава. Если вставляю в html скрипт, например
<input type="text" id="input">
<script>
input.onblur = function() {
console.log("ок");
};
</script>
то он работает. А если вынести его в отдельный файл, подключив его в шапке
<script type="text/javascript" src="core.js"></script>
то не работает. В консоль браузера сразу при загрузки страницы падает
ReferenceError: input is not defined
В другом месте вот такая функция не работает с той же ошибкой:
$(function(){
$('td, .table-cell').click(function(e){//ловим элемент, по которому кликнули
//....
});
});
Перечитал, как нужно js-скрипты в отдельный файл выносить - никаких особых камней там нет. Подключай сверху, да выноси на здоровье. Не понимаю.
Да, добавлю, что на соседнем сайте точно такие же скрипты работают нормально.
Там есть таки подводный камень, в общем у тебя js подключен в шапке, то там должны быть проверки на то что js ждет построения DOM дерева, иначе он будет пытаться выполниться до того как у тебя собственно html отрисовался и ссылаться на несуществующие id или class'ы на html странице.
А вообще ты возможно с путями проебался, попробуй в корень сайта кинуть а не в ту же папку в которой html лежит, ладно я хуевый советчик, простите :(
https://github.com/grigoryMovchan/zuihitsu
> Подскажите гайдов по парсингу html-страниц. >Хочу сделать страницу с ценами конкурентов.
А смысл? Все будет ломаться при малейших изменениях в верстке.
Ну так починить не долго, тем более там сайты работают годами на одних и тех же cms и с одной и той же версткой. Просто реально заебывает руками перебирать всех и смотреть кто начал демпинговать.
Если знаком с DOM и CSS-селекторами, то просто ставишь Goutte: https://github.com/FriendsOfPHP/Goutte или аналог, и парсишь без задней мысли что нужно.
Разобрался, какой плагин поставить на саблайн, чтоб пхп юзать.
Файлы (HTML, JS) не могут загрузиться мгновенно. Они загружаются с какой-то скоростью.
Браузер парсит (разбирает) HTML и картинки поточно, по мере поступления (а вот JS и CSS файлы он сначала загружает целиком, а только потом применяет их).
Соответственно, когда у тебя скрипт в шапке получается такая ситуация:
- браузер запрашивает с сервера HTML страницу
- браузер разбирает приходящий с сервера HTML файл по мере поступления
- приходит шапка (head), браузер ее разбирает и создает соответствующие DOM элементы
- затем браузер видит в HTML коде тег script. Он ставит разбор HTML на паузу и начинает загружать скрипт
- когда скрипт загружен целиком, браузер выполняет записанный в нем код. В этот момент созданы DOM элементы только для шапки страницы, которые идут до скрипта и твой инпут видимо еще не создан
- после выполнения JS кода браузер продолжает разбирать HTML файл дальше
Почитай статьи про то как работает браузер, например эти
- https://learn.javascript.ru/onload-ondomcontentloaded
- http://javascript.ru/tutorial/events/timing#zagruzka-stranicy
- https://learn.javascript.ru/external-script
- http://webknowledge.ru/kak-rabotaet-brauzer-chto-proishodit-za-kulisami-sovremennyh-brauzerov/
Тебе тут советуют подключать скрипт в конце файла. Действительно, во многих статьях так советуют, но никто не объясняет плюсы и минусы разных подходов. Тут тоже есть недостаток:
- обработчик ставится на инпут не сразу, а только когда загрузится весь HTML файл. Какое-то время страница не будет реагировать на клики, и тд (я очень часто такое вижу). Еще хуже, кстати, когда JS скрипт при инициализации изменяет верстку и она прыгает в момент его срабатывания. Это указывает на уровень профессионализма фронтенд-специалиста.
- если в коде ты ссылаешься на функции из файла, ну например <button onclick="something()"> то до загрузки скрипта клик по кнопке будет вызывать ошибку
Некоторые подключают скрипт в начале, но делают инициализацию по событию document.ready (оно срабатывает когда браузер разобрал HTML файл до конца и создал все дерево ДОМ). Этот подход имеет такой же недостаток.
Мне в голову пришел такой способ:
- сделать JS файл, содержащий только функции, но не ставящий никаких обработчиков никуда
- подключить его в шапке
- сразу после инпута поставить вызов функции инициализации внутри тега script
Также, можно использовать классические обработчики в HTML атрибутах: <button onclick="doSomething()">. Этот метод не всегда подходит (плохо стыкуется с ООП кодом).
Если не понимаешь, что я написал, то тебе надо изучить DOM получше и почитать статью по ссылке выше. И тогда станет понятно.
И что интересно, мало где описывается то, что я написал. Такая вот индустрия, такие вот люди в ней работают. Прыгающая верстка и не сразу работающие кнопки это норма для них. Вот киви кошелек например, на нем кнопки начинают работать только после загрузки страницы целиком. Зато у них используется ангулар и все на аяксе. А твиттер это вообще один большой сборник антипаттернов по построению интерфейсов. Кнопка "назад" в нем работает как генератор случайных чисел и никогда не знаешь, куда она тебя приведет. Алсо, меня периодически перебрасывает на мобильную версию по непонятной причине.
Файлы (HTML, JS) не могут загрузиться мгновенно. Они загружаются с какой-то скоростью.
Браузер парсит (разбирает) HTML и картинки поточно, по мере поступления (а вот JS и CSS файлы он сначала загружает целиком, а только потом применяет их).
Соответственно, когда у тебя скрипт в шапке получается такая ситуация:
- браузер запрашивает с сервера HTML страницу
- браузер разбирает приходящий с сервера HTML файл по мере поступления
- приходит шапка (head), браузер ее разбирает и создает соответствующие DOM элементы
- затем браузер видит в HTML коде тег script. Он ставит разбор HTML на паузу и начинает загружать скрипт
- когда скрипт загружен целиком, браузер выполняет записанный в нем код. В этот момент созданы DOM элементы только для шапки страницы, которые идут до скрипта и твой инпут видимо еще не создан
- после выполнения JS кода браузер продолжает разбирать HTML файл дальше
Почитай статьи про то как работает браузер, например эти
- https://learn.javascript.ru/onload-ondomcontentloaded
- http://javascript.ru/tutorial/events/timing#zagruzka-stranicy
- https://learn.javascript.ru/external-script
- http://webknowledge.ru/kak-rabotaet-brauzer-chto-proishodit-za-kulisami-sovremennyh-brauzerov/
Тебе тут советуют подключать скрипт в конце файла. Действительно, во многих статьях так советуют, но никто не объясняет плюсы и минусы разных подходов. Тут тоже есть недостаток:
- обработчик ставится на инпут не сразу, а только когда загрузится весь HTML файл. Какое-то время страница не будет реагировать на клики, и тд (я очень часто такое вижу). Еще хуже, кстати, когда JS скрипт при инициализации изменяет верстку и она прыгает в момент его срабатывания. Это указывает на уровень профессионализма фронтенд-специалиста.
- если в коде ты ссылаешься на функции из файла, ну например <button onclick="something()"> то до загрузки скрипта клик по кнопке будет вызывать ошибку
Некоторые подключают скрипт в начале, но делают инициализацию по событию document.ready (оно срабатывает когда браузер разобрал HTML файл до конца и создал все дерево ДОМ). Этот подход имеет такой же недостаток.
Мне в голову пришел такой способ:
- сделать JS файл, содержащий только функции, но не ставящий никаких обработчиков никуда
- подключить его в шапке
- сразу после инпута поставить вызов функции инициализации внутри тега script
Также, можно использовать классические обработчики в HTML атрибутах: <button onclick="doSomething()">. Этот метод не всегда подходит (плохо стыкуется с ООП кодом).
Если не понимаешь, что я написал, то тебе надо изучить DOM получше и почитать статью по ссылке выше. И тогда станет понятно.
И что интересно, мало где описывается то, что я написал. Такая вот индустрия, такие вот люди в ней работают. Прыгающая верстка и не сразу работающие кнопки это норма для них. Вот киви кошелек например, на нем кнопки начинают работать только после загрузки страницы целиком. Зато у них используется ангулар и все на аяксе. А твиттер это вообще один большой сборник антипаттернов по построению интерфейсов. Кнопка "назад" в нем работает как генератор случайных чисел и никогда не знаешь, куда она тебя приведет. Алсо, меня периодически перебрасывает на мобильную версию по непонятной причине.
имею кусок кода:
$text = preg_replace_callback('/(?<=^|>)[^><]+?(?=<|$)/', [????], $text);
function replace($text, $pattern, $tag) {
return preg_replace('/'. $pattern .'/', $tag, $text);
}
как мне написать параметр [????], чтобы передать в callback функцию необходимые параметры?
http://ideone.com/oDVbum можно ли делать вот такой return false как у меня в 14 и 16 строке? Не выбрасывать же исключение, если пользователь мисскликнул флагом по открытой клетке. На счет значка не совсем тебя понял что с ним делать. Это пикрелейтед 2 когда идет mousedown, и пик1 когда mouseup.
http://ideone.com/B91hZ0 перенес dom-заботы во View. Это нормально, что я всегда кидаю координаты из event.dataset к вызову любой функции func? Не знаю еще каким образом их можно передать из контроллера в представление.
> можно ли делать вот такой return false
Смутно догадываюсь что такая проверка и дальнейшее ничего-не-делание должна быть в контроллере..
Про значок вопрос относится ко второй ссылке на ideone. Извиняюсь за флуд, вначале пишу в тред, потом думаю.
> можно ли делать вот такой return false как у меня в 14 и 16 строке?
Можно. Выбрасывать исключение или нет определяется требованиями к функции, как ты ее спроектировал. Но желательно в комментарии к функции писать об этом. В твоем случае этот return false ни на что не влияет, но в других случаях, если не проверить результат функции, может появиться какая-то ошибка.
> this._flags.addCell(x, y);
> this._flagsCounter--;
А мы не можем вычислять число оставшихся флагов через разницу между числом мин и числом в this._flags? Можно было бы избавиться от лишнего поля и и риска его рассинхронизации с другими полями.
> На счет значка не совсем тебя понял что с ним делать. Это пикрелейтед 2 когда идет mousedown, и пик1 когда mouseup.
Я предлагал рассмотреть вариант, когда переключение значка делается во View, и контроллер о нем даже не знает. Если он переключается только при клике и никак не связан с игровой логикой.
> this._domView.preventContextMenuOnField();
Вот это явно ведь должно быть во view. Ведь это особенность браузера, что правый клик открывает меню, и пусть вью борется с этим явлением сам.
Я рассуждал так: можно сделать всю работу с DOM во вью, чтобы он наружу выставлял только методы и события, которые никак с DOM не связаны (в общем, инкапсулировать работу с DOM внутри вью). Этакое абстрактное игровое поле, и внешний код не знает ничего о том, как оно там внутри реализовано, и вообще используется ли там DOM.
Вот, если что, про инкапсуляцию статья: http://learn.javascript.ru/internal-external-interface (я не призываю копировать стиль кода и скрытие переменных в замыкании, а просто почитать про суть инкапсуляции)
Условно, если мы завтра поменяем полностью верстку или вообще сделаем вывод поля на другой, не-DOM технологии (например, будем рисовать на канвасе), контроллер останется неизменным. Но даже если мы ничего не будем переделывать, разделение ответственности делает код организованнее и понятнее.
Соответственно, сообщать отдельно о нажатии и отпускании кнопки мыши в контроллер не требуется (если я правильно все понял) - ему достаточно лишь знать о завершившемся клике по клеточке.
> Это нормально, что я всегда кидаю координаты из event.dataset к вызову любой функции func?
> каким образом их можно передать из контроллера в представление.
Из представления в котроллер, ты имел в виду? Да, можно через аргументы, можно сделать объект события, если хочется усложнить.
> можно ли делать вот такой return false как у меня в 14 и 16 строке?
Можно. Выбрасывать исключение или нет определяется требованиями к функции, как ты ее спроектировал. Но желательно в комментарии к функции писать об этом. В твоем случае этот return false ни на что не влияет, но в других случаях, если не проверить результат функции, может появиться какая-то ошибка.
> this._flags.addCell(x, y);
> this._flagsCounter--;
А мы не можем вычислять число оставшихся флагов через разницу между числом мин и числом в this._flags? Можно было бы избавиться от лишнего поля и и риска его рассинхронизации с другими полями.
> На счет значка не совсем тебя понял что с ним делать. Это пикрелейтед 2 когда идет mousedown, и пик1 когда mouseup.
Я предлагал рассмотреть вариант, когда переключение значка делается во View, и контроллер о нем даже не знает. Если он переключается только при клике и никак не связан с игровой логикой.
> this._domView.preventContextMenuOnField();
Вот это явно ведь должно быть во view. Ведь это особенность браузера, что правый клик открывает меню, и пусть вью борется с этим явлением сам.
Я рассуждал так: можно сделать всю работу с DOM во вью, чтобы он наружу выставлял только методы и события, которые никак с DOM не связаны (в общем, инкапсулировать работу с DOM внутри вью). Этакое абстрактное игровое поле, и внешний код не знает ничего о том, как оно там внутри реализовано, и вообще используется ли там DOM.
Вот, если что, про инкапсуляцию статья: http://learn.javascript.ru/internal-external-interface (я не призываю копировать стиль кода и скрытие переменных в замыкании, а просто почитать про суть инкапсуляции)
Условно, если мы завтра поменяем полностью верстку или вообще сделаем вывод поля на другой, не-DOM технологии (например, будем рисовать на канвасе), контроллер останется неизменным. Но даже если мы ничего не будем переделывать, разделение ответственности делает код организованнее и понятнее.
Соответственно, сообщать отдельно о нажатии и отпускании кнопки мыши в контроллер не требуется (если я правильно все понял) - ему достаточно лишь знать о завершившемся клике по клеточке.
> Это нормально, что я всегда кидаю координаты из event.dataset к вызову любой функции func?
> каким образом их можно передать из контроллера в представление.
Из представления в котроллер, ты имел в виду? Да, можно через аргументы, можно сделать объект события, если хочется усложнить.
Кстати, а ты не хочешь автоматизированные тесты позже к своему саперу попробовать написать, чтобы они проверяли например игровую логику? У меня есть обзорный урок по тестам, и я это всем предлагаю, кто дошел до каких-то абстракций, паттернов, архитектур и тд.
Тестировать естественно в первую очередь модель, это и проще, и полезнее.
Я проверил на Хромиуме, по ширине пустое окно ограничено 337 пикс, по высоте можно хоть 0 пикселей сделать. Фаерфокс - то же самое.
Я думаю, HTML тут не при чем, это особенности браузера, ну например, кнопки и поля на панели инструментов меньше не сделать, или может там просто в коде прописано такое ограничение.
Ты можешь 1) увеличить масштаю, что уменьшит ширину 2) использовать режим тестирования адаптивной верстки, в Хроме это Ctrl + SHift + I -> иконка смартфона, в Фаерфоксе то ли пункт в меню, то ли кнопка в отладчике.
Еще ты можешь сделать iframe любого размера и в нем открыть свою страницу.
Под 1 пиксель проверять верстку не требуется. Проверяй на реалистичных разрешениях, http://mydevice.io/devices/
>>932704
Возможно тип файла не тот. Попробуй на html-файле. Или что-то в настройках не включено.
>>932566
Я сталкивался с таким, чинить придется постоянно, если много сайтов.
А яндекс-маркет не годится для этого? Я всегда там цены сравниваю.
И я помню, был какой-то сервис который давал API для извлечения цен.
>>932556
Изучи DOM, может быть Xpath, и возьми какую-нибудь библиотеку вроде phpQuery или как нижу упомянули, Goutte.
>>932297
Это прошел? https://github.com/codedokode/pasta/blob/master/soft/web-server.md
Комментарии и пояснения к студентам все прочел?
Туториал из php мануала прошел? http://php.net/manual/ru/tutorial.php
>>931986
В БД надо хранить как DATE, DATETIME, TIMESTAMP.
>>931930
PHP может не самый идеальный, но если ты его уже знаешь то его взять будет легче.
А так, да, bat/bash/powershell/python - что-то из этого.
>>931876
не зная в чем проблема - вряд ли.
>>931843
CSS3 это дополнения к CSS2.1. То есть тени или скругления это новое, а расчет ширины/высоты элемента идет по тем же принципам.
>>931777
Скорее всего просто ты не понял логику работы скрипта или что-то не заметил и ты думаешь, что берется одна переменная, а там берется другая, с исходным паролем. Или может сразу из POST он берется.
Если ты думаешь, что проблема в password_hash то просто протестируй ее, дай ей что-нибудь и проверь что будет на выходе.
>>931369
Есть, я видел такие курсы, в том числе бесплатные. Ссылок не помню, погугли.
Я проверил на Хромиуме, по ширине пустое окно ограничено 337 пикс, по высоте можно хоть 0 пикселей сделать. Фаерфокс - то же самое.
Я думаю, HTML тут не при чем, это особенности браузера, ну например, кнопки и поля на панели инструментов меньше не сделать, или может там просто в коде прописано такое ограничение.
Ты можешь 1) увеличить масштаю, что уменьшит ширину 2) использовать режим тестирования адаптивной верстки, в Хроме это Ctrl + SHift + I -> иконка смартфона, в Фаерфоксе то ли пункт в меню, то ли кнопка в отладчике.
Еще ты можешь сделать iframe любого размера и в нем открыть свою страницу.
Под 1 пиксель проверять верстку не требуется. Проверяй на реалистичных разрешениях, http://mydevice.io/devices/
>>932704
Возможно тип файла не тот. Попробуй на html-файле. Или что-то в настройках не включено.
>>932566
Я сталкивался с таким, чинить придется постоянно, если много сайтов.
А яндекс-маркет не годится для этого? Я всегда там цены сравниваю.
И я помню, был какой-то сервис который давал API для извлечения цен.
>>932556
Изучи DOM, может быть Xpath, и возьми какую-нибудь библиотеку вроде phpQuery или как нижу упомянули, Goutte.
>>932297
Это прошел? https://github.com/codedokode/pasta/blob/master/soft/web-server.md
Комментарии и пояснения к студентам все прочел?
Туториал из php мануала прошел? http://php.net/manual/ru/tutorial.php
>>931986
В БД надо хранить как DATE, DATETIME, TIMESTAMP.
>>931930
PHP может не самый идеальный, но если ты его уже знаешь то его взять будет легче.
А так, да, bat/bash/powershell/python - что-то из этого.
>>931876
не зная в чем проблема - вряд ли.
>>931843
CSS3 это дополнения к CSS2.1. То есть тени или скругления это новое, а расчет ширины/высоты элемента идет по тем же принципам.
>>931777
Скорее всего просто ты не понял логику работы скрипта или что-то не заметил и ты думаешь, что берется одна переменная, а там берется другая, с исходным паролем. Или может сразу из POST он берется.
Если ты думаешь, что проблема в password_hash то просто протестируй ее, дай ей что-нибудь и проверь что будет на выходе.
>>931369
Есть, я видел такие курсы, в том числе бесплатные. Ссылок не помню, погугли.
Как я понял, у них мок это объект, ожидающий, что на другом объекте (который передался в конструктор мока при создании) будут вызваны определённые методы:
var dep = {foo: function() {}};
var depMock = sinon.mock(dep);
dpeMock.expects("foo").once();
// передаём объект dep как зависимость тестируемого класса
...
// убеждаемся в том, что на объекте dep был вызван метод foo один раз:
depMock.verify()
А что делать если тестируемый объект проверяет переданные аргументы через instanceof? По ссылке на stackoverflow предлагают вместо этого преверять наличие только нужных методов, но это же неудобно, что если их много?
Делаю сейчас так:
var dep = {foo: function () {}, __proto__: FooClass.prototype}
// дальше то же, что и вверху
Нужна другая (нормальная) библиотека для моков, которая бы производила объекты с правильным прототипом (я могу путать, но по моему instanceof просто проверяет наличие прототипа конструктора в цепочке прототипов объекта).
Стандарт, который довольно трудно понять, говорит примерно то же:
https://www.ecma-international.org/ecma-262/5.1/#sec-11.8.6
https://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.3
> var dep = {foo: function () {}, __proto__: FooClass.prototype}
Это неправильно. __ proto __ не определена стандартами и не обязана работать везде и всегда.
>иначе он будет пытаться выполниться до того как у тебя собственно html отрисовался и ссылаться на несуществующие id или class'ы на html странице
Билять. Вот оно что.
А пути правильные, проверял уже (они и так в корне).
>>932408
Попробую.
>>932461
Хех, читал эту статью. Всё это хрень. Может на собеседованиях и спрашивают, а реально нет смысла гнаться за трендом. Делай так, как работает, и хрен с ним. Заказчику глубоко наплевать. Да и как по мне, самый простой код - самый лучший. Если можно обойтись без усложнения, то лучше это сделать.
>>932749
>- сделать JS файл, содержащий только функции, но не ставящий никаких обработчиков никуда
>- подключить его в шапке
>- сразу после инпута поставить вызов функции инициализации внутри тега script
Пожалуй, этот способ самый оптимальный, так и сделаю.
И ведь читал про выполнение кода со страницы, а про вызовы с конкретными id не дошло. Решил, что раз функция срабатывает после некоего события, то до события этот кусок кода просто не используется. А когда используется, все id уже поименованы. Окей, мне стало понятнее.
>Создайте класс, принимающий в конструкторе JSON-строку и позволяющий получить элемент через текучий интерфейс.
Конструктор, понятное дело, запилил, это херня. А вот как быть с методом get -- не представляю. Аноны, подскажите как реализовать подозреваю, что тут что-то связано с замыканиями.
$o = new JsonFluent('{"first":"apple","second":"banana","third":{"sub1":1,"sub2":2,"sub3":3}}');
$o->get('first'); // Вернёт 'apple'
$o->get('third')->get('sub2'); // Вернёт 2
Не, JS это не декларативный язык вроде CSS и конструкция
$('#abc').click(...);
это именно команда: найти все элементы с id = abc, которые есть в данный момент, и повесить на них обработчик. Она не повесит обработчики на добавленные в DOM позже элементы.
>>933026
Текучий интерфейс это когда все методы возвращают this. Мне это не нравится, бессмысленное увеличение кода.
>>933058
А ты попробуй сделать тест, например создать 10000 строк в одинарных кавычках и 10000 в двойных и сравнить разницу по времени при создании одной строки.
Код с одинарными кавычками требует больше времени на написание и хуже читается:
echo 'a=' . $a . ', b = $b'. ...
И это ухудшение не стоит нескольких нано- или микросекунд.
Это называется "микрооптимизация", когда время разработчиков (и деньги) тратится на такие бесполезные улучшения, которые дают сопоставимый с погрешностью измерения выигрыш. Если бы они хотели ускорить код, они бы занялись более полезными вещами: поменяли алгоритм, оптимизировали запросы к базе данных итд.
> в двойных кавычках пхп проверяет наличие переменных, а в одинарных не ожидает их встретить
А это важно только на этапе компиляции. При использовании кеширования опкодов (opcache) компиляция длеается только один раз, а дальше скомпилированный скрипт берется из кеша и разницы не будет (как мне кажется).
Давай проверим, во что скомпилируется строка в одинарных и двойных кавычках. для этого нужно расширение vld, которое показывает опкоды, в которые превратилась твоя программа. Оно легко ставится под линуксом, под виндой не пробовал использовать.
> php -dvld.active=1 -r 'echo "Hello world";'
line # E I O op fetch ext return operands
-------------------------------------------------------------------------------------
1 0 E > ECHO 'Hello+world'
1 > RETURN null
Как видим, наш код скомпилировался в 2 инструкции, echo и return.
Теперь поменяем вид кавычек:
line # E I O op fetch ext return operands
-------------------------------------------------------------------------------------
1 0 E > ECHO 'Hello+world'
1 > RETURN null
Как видим, коды после компиляции получились точно такие же.
А вот как выглядит результат компиляции программы с переменной внутри двойных кавычек:
> php -dvld.active=1 -r 'echo "Hello $x world";'
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
1 0 E > ADD_STRING ~0 'Hello+'
1 ADD_VAR ~0 ~0, !0
2 ADD_STRING ~0 ~0, '+world'
3 ECHO ~0
4 > RETURN null
Вот тут действительно видно как строка собирается по кусочкам.
Поскольку форматирование могла сбиться, скопирую пост на pastebin: http://pastebin.ru/NhNjneDP
Не, JS это не декларативный язык вроде CSS и конструкция
$('#abc').click(...);
это именно команда: найти все элементы с id = abc, которые есть в данный момент, и повесить на них обработчик. Она не повесит обработчики на добавленные в DOM позже элементы.
>>933026
Текучий интерфейс это когда все методы возвращают this. Мне это не нравится, бессмысленное увеличение кода.
>>933058
А ты попробуй сделать тест, например создать 10000 строк в одинарных кавычках и 10000 в двойных и сравнить разницу по времени при создании одной строки.
Код с одинарными кавычками требует больше времени на написание и хуже читается:
echo 'a=' . $a . ', b = $b'. ...
И это ухудшение не стоит нескольких нано- или микросекунд.
Это называется "микрооптимизация", когда время разработчиков (и деньги) тратится на такие бесполезные улучшения, которые дают сопоставимый с погрешностью измерения выигрыш. Если бы они хотели ускорить код, они бы занялись более полезными вещами: поменяли алгоритм, оптимизировали запросы к базе данных итд.
> в двойных кавычках пхп проверяет наличие переменных, а в одинарных не ожидает их встретить
А это важно только на этапе компиляции. При использовании кеширования опкодов (opcache) компиляция длеается только один раз, а дальше скомпилированный скрипт берется из кеша и разницы не будет (как мне кажется).
Давай проверим, во что скомпилируется строка в одинарных и двойных кавычках. для этого нужно расширение vld, которое показывает опкоды, в которые превратилась твоя программа. Оно легко ставится под линуксом, под виндой не пробовал использовать.
> php -dvld.active=1 -r 'echo "Hello world";'
line # E I O op fetch ext return operands
-------------------------------------------------------------------------------------
1 0 E > ECHO 'Hello+world'
1 > RETURN null
Как видим, наш код скомпилировался в 2 инструкции, echo и return.
Теперь поменяем вид кавычек:
line # E I O op fetch ext return operands
-------------------------------------------------------------------------------------
1 0 E > ECHO 'Hello+world'
1 > RETURN null
Как видим, коды после компиляции получились точно такие же.
А вот как выглядит результат компиляции программы с переменной внутри двойных кавычек:
> php -dvld.active=1 -r 'echo "Hello $x world";'
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
1 0 E > ADD_STRING ~0 'Hello+'
1 ADD_VAR ~0 ~0, !0
2 ADD_STRING ~0 ~0, '+world'
3 ECHO ~0
4 > RETURN null
Вот тут действительно видно как строка собирается по кусочкам.
Поскольку форматирование могла сбиться, скопирую пост на pastebin: http://pastebin.ru/NhNjneDP
А, хотя в твоем случае надо писать не return $this. Подумай сам, что надо возвращать. Или поищи готовые аналогичные библиоетки, например, phpQuery какой-нибудь.
Сгенерил миллион строк в php 7.1:
3 подстановки vs 6 конкатенаций (оно вообще употребляется во множественном числе?).
Подстановки отрабатывают на 15-20% быстрее.
Да, мне уже подсказали. Возвращать нужно new JsonFluent($data).
Допустим я хочу написать сервис по обмену сообщениями, и при этом хочу чтобы эти сообщения ни кто не мог прочитать кроме собеседников. Никогда! Вообще!
Я ознакомился что для шифрование применяется при помощи ключа. Но где хранить этот ключ? Если сохранить его в БД, то доступ будет не только у собеседников, но и у тех у кого есть доступ к БД.
Нужно сделать так чтобы ключ оставался только у собеседников. Может для этого выступить сам пароль от сервиса? Сомневаюсь что это будет надежно, потому что пользователи вряд ли будут делать пароли длиннее, к примеру, 12 символов.
Попробуй почитать для начала про такие вещи:
- симметричное и асимметричное шифрование
- открытый и закрытый ключ
- протокол Диффи-Хеллмана
Почитай про то, как устроен протокол SSL.
Боюсь себе даже представить зачем такое кому-то может понадобится.
>- протокол Диффи-Хеллмана
https://ru.wikipedia.org/wiki/Протокол_Диффи_—_Хеллмана#.D0.9E.D0.BF.D0.B8.D1.81.D0.B0.D0.BD.D0.B8.D0.B5_.D0.B0.D0.BB.D0.B3.D0.BE.D1.80.D0.B8.D1.82.D0.BC.D0.B0.5B2.5D
Что-то у меня не получилось получить одно и тоже число https://ideone.com/I0LCYo
1) ^ это операция XOR, а не возведение в степень. Не перепутал?
2) при возведении в степени вроде 10 000 получаются гигантские числа. PHP такие числа хранит в float формате, который сохраняет 8-16 знаков из числа, а нужно точное представление. Потому надо брать расширения для работы с большими целыми числами:
http://php.net/manual/ru/book.gmp.php
http://php.net/manual/ru/book.bc.php
Также, ты используешь для генерации случайных чисел rand(). Она использует линейный генератор ( https://ru.wikipedia.org/wiki/Линейный_конгруэнтный_метод ) который довольно предсказуем. Нам нужно что-нибудь получше, а именно:
http://php.net/manual/ru/function.openssl-random-pseudo-bytes.php
http://php.net/manual/ru/function.random-bytes.php
Также, потом советую глянуть расширение openssl, содержащее готовые функции шифрования (но сначала попробуй написать свою): http://php.net/manual/ru/book.openssl.php
Насчет кода - должны получиться именно одинаковые числа. Потому что (x ^ y) ^ z = (x ^ z) ^ y. Остаток от деления нужен для того, чтобы неприятель не мог догадаться, какие были исходные числа, проведя обратную операцию.
Просто ради просвещения: что, по Вашему мнению, можно хотя бы с натяжкой отделить от говна в таком случае?
https://habrahabr.ru/company/tuturu/blog/321820/
В index.php стоят две одинаковые кнопки:
<div><a href="javascript:void(0)" onclick="show_flights_table()" id="a_save_object" class="a_demo_three b_green" title="Показать таблицу">Показать таблицу</a></div>
и
<div><a href="javascript:void(0)" onclick="add_line_in_flights_table()" id="a_save_object" class="a_demo_three b_green" title="Добавить строку">Добавить строку</a></div>
Там же в хидере подключён скрипт
<script type="text/javascript" src="core.js">
В core.js стоят функции
function show_flights_table()
{
...
}
и такая же
function add_line_in_flights_table()
{
...
}
Нажимаем первую кнопку - всё ок, никаких варнингов, браузер показывает таблицу. Нажимаем вторую - хром ничего не делает, выдаёт
Uncaught ReferenceError: add_line_in_flights_table is not defined at HTMLAnchorElement.onclick (index.php:54)
А если запустить через файерфокс, то вторая кнопка срабатывает. Правда, там дальше начинает ругаться на $(div).html(data); (ReferenceError: $ is not defined[Подробнее] core.js:262:5), но это уже другой вопрос.
Я даже пробовал стирать из второй функции вообще всё между { }. Всё равно not defined. Да как так-то?
Я думаю, из-за 2 одинаковых id подряд.
> onclick="show_flights_table()" id="a_save_object"
>onclick="add_line_in_flights_table()" id="a_save_object"
Скорее всего ты намешал там где то джиквери.
когда ты вызываешь "onclick='function()'", она должна быть в глобальной области видимости (принадлежать объекту window), возможно твои функции обернуты во что то, хз, покажи код хоть.
Ну и еще:
>a href="javascript:void(0)"
Если нет ссылки, почему бы просто не опустить параметр href?
>Если нет ссылки, почему бы просто не опустить параметр href?
А так можно? Мне казалось, что это что-то вроде стандартной заглушки для большего фен-шуя.
>Я думаю, из-за 2 одинаковых id подряд.
Да, есть такое. Исправил. Но ошибка та же.
>Скорее всего ты намешал там где то джиквери.
Чего нет, того нет. По нажатии на кнопку запускается js-скрипт, оттуда аяксом запускается php-скртипт и выполняет что-то там с базой. Раньше так делал, всё работало нормально.
Код примерно такой (я убрал лишние блоки и функции для удобочитаемости): https://jsfiddle.net/jou5Ljyj/
ef2e3b878bea166e1a349599475821d6b94b13cb609c8db980144ff065535613a:2:{i:0;s:5:"_csrf";i:1;s:32:"Q7hju019PJ61cOMbR1Y4Bm9V4stpWCpU";}
я не ебу что это такое ( ну только догадываюсь)
что-то связанное с безопасностью.
кто знает как с этим бороться, что делать?
Вообще, я тоже для всяких интерактивных элементов использую тег "<a>" без href (НО ТАК НЕ НАДО ДЕЛАТЬ, лол). Например для кнопок есть "<button>" и т.п.
У тебя там жесть какая то в примере. Вот сравни я упростил: https://jsfiddle.net/Lwv50ps3/ - так работает. Вопрос, почему у тебя функции не попадают в глобальную видимость. Если ты там прямо из проекта скопипастил кусок кода, то у меня для тебя плохие новости: ты ничего не понимаешь в хтмл, no offence
Это пока не проект, а просто разрозненные куски кода.
Не понял, зачем после функций вставлять такие строки?
window.add_line = add_line;
>Вопрос, почему у тебя функции не попадают в глобальную видимость.
Скорее, почему одна попадает, а вторая (точно такая же) нет.
Ну как бы я эту функцию прикрепил к глобальной видимости (window). А иначе она бы не запустилась на jsfiddle, с той же ошибкой что у тебя.
>Скорее, почему одна попадает, а вторая (точно такая же) нет.
Да, вот это. Может кто поумнее объяснит нам позже.
Ящитаю, это какой-то глюх Хрома. Может быть даже конкретно моего. ФФ даже не морщится.
>прикрепил к глобальной видимости (window)
И ничего страшного, что прикрепление происходит после самой функции? При чтении, браузер читает сначала всё, что вне функций, а только потом их, так вроде?
Почему программа некорректно считает среднее значение тугриков/страниц ? 296 строка
http://sandbox.onlinephpfunctions.com/code/e7ac3081e037c3c3e6b0064d3feb1589d3da0721
>И ничего страшного, что прикрепление происходит после самой функции? При чтении, браузер читает сначала всё, что вне функций, а только потом их, так вроде?
Первый раз слышу, если честно. Никогда никаких проблем не было с таким подходом.
> в разных методах с точки зрения MVC?
В разных методах класса-обработчика.
>Если приходят по гет.
То обрабатываем обработчиком и контроллеру отдаем решать что с ним делать.
>а если пост
то обрабатываем обработчиком и отдаем контроллеру решать что с ним делать.
>gmp_powm
Эта функция была специально придумана для этого алгоритма?
Теперь всё получилось: https://ideone.com/I0LCYo
Завтра постараюсь задать вопросы, после того как осознаю алгоритм.
Спасибо!!!
>класса-обработчика.
Что под этим подразумевается? Обычно роутинг настроен так, что запрос от пользователя попадает в метод контроллера (экшн). Меня интересует в разных ли экшенах обрабатывать гет и пост запрос для одной страницы, учитывая, то если запрос гет, то там нужно только вид отрендерить.
В форме удобнее обрабатывать и ГЕТ и ПОСТ одной функцией. Почитай например https://github.com/codedokode/pasta/blob/master/forms.md
Нет принципиальной разницы: можно в одном экшне, можно в разных. Если у тебя там мало кода, то, как по мне, проще читать один экшн, да и разделение на 2 экшна вынудит копипастить код.
После case должна быть не точка с запятой, а двоеточие. И нахуя ты используешь альтернативный синтаксис, если у тебя сплошной код без разрывов?
Как по-твоему работают сессии? Откуда берутся и куда сохраняются данные из массива $_SESSION в начале и после завершения скрипта?
Это суперглобальная переменная, которая хранится вне зависимости от домена. Главное, чтобы на одном сервере. Кстати, проблему решил буквально только что одной строчкой кода:
$some_name = session_name("some_name");
Сука, 5 часов проводил раскопки на StackOverflow
Нет, тебе сначала надо изучить механизм работы сессий (прежде чем его вообще использовать) так как ты его явно не понимаешь. Сессия это не переменая. $_SESSION это лишь механизм для доступа к одной сессии из хранилища.
На каком уровне нужно знать пхп, чтоб устроиться на стажировку?что обычно спрашивают на собеседовании, что нужно делать стажировке.
Я слышал только про Робокассу, не знаю, есть ли там нужный тебе функционал.
читать со словарем, плюс смотреть ютуб и т.д. практика короч. если есть желание фундаментально прокачаться, еще нужно добавить какой-нить видеокурс по произношеию типа american english и учебник по грамматике, допустим English Grammar by Raymond Murphy. я английский знаю норм (не считая литературного), но моей главной проблемой является лень читать на английском, если есть страница на русском, потому что для обработки английского текста требуется больше усилий, даже если все слова известны. но перебарываю себя, надеюсь, однажды эта разница сгладится и мне будет похуй, на каком языке читать.
по поводу собеседований, я был пока на одном, в моем случае были тесты на php (с вопросами на уверенность знания мануала, типа как отработает данная функция с переменной которая внутри указана как global, что вернет функция со ссылками, как отсортировать с изменением индекса в обратном порядке и тд) и в данных тестах было также много вопросов по sql и js, что подпортило мои результаты. в интервью классических вопросов, которые я ожидал (что такое синглтон, чем абстрактный класс отличается от интерфейса и т.д.) не было, зато были вопросы чем апач отличается от нджинкса, что происходит между отправкой запроса и выводом результата. такие вопросы на общую грамотность. возможно это была больше работа из серии "веб-мастер", поэтому там все было с перекосом в эту сторону.
>> На каком уровне нужно знать пхп, чтоб устроиться на стажировку?
я для себя понял так, что все, что написано в мануале, нужно знать уверенно, а не на уровне "ээ, я помню где про это написано, нужно залезть освежить".
плюс учебные проекты, которые у тебя есть, нужно максимально причесать, чтобы не говорить "вот тут такой-то функционал не работает, потому что это учебный проект"
Если просят, чтоб был опыт в коммерческой деятельности, можно ли обойти это на собеседовании ?
С каким редактором лучше работать?(пхпшторм не вариант)
Почему пхпшторм не вариант? Единственная нормальная IDE. У меня как-то коллега в саблайме писал, так я подходил к нему и просто охуевал с того, как он каждый класс в глобальном поиске ищет.
Sublime 3 умеет переходить к классу и функции по имени. А к файлам по имени умеет переходить Sublime 2. Не понимаю, зачем там поиск. Наверно чтобы найти все случаи использования класса.
если просят чтоб был опыт, то нужно либо соврать, что он был (что вполне реально, допустим найти левый сайт вебстудии и сказать, что клепал там визитки и интернет магазины), но могут заебать вопросами и спалить, либо сказать, что опыта нет, но я вот умею то и это, вот мои готовые проекты. я бы выбрал второй вариант.
если религия найти ключик к шторму в торрентах, то наверное netbeans. я начинал с нетбинс и в принципе было нормально, но потом перешел на шторм и понял, что потерял время. для меня самый большой плюс шторма это документация и туториалы в ютубе.
кстати на собеседовании также могут спросить, каким ide пользуешься, явно ожидая, что ты скажешь "штормом". у меня так и было.
Я только что понял что значит этот алгоритм. Я думал что среди аргументов есть само сообщение которое нужно зашифровать, и алгоритм конкретно предназначен именно для этого, но оказалось что он только генерирует ключ по которому сообщение будет шифроваться.
Хорошо, на вопрос как одним из способов шифруются сообщения мы ответили, но что если помимо обмена сообщений, я хочу, чтобы сообщения навсегда сохранялись в БД. Если сессия(?) прекратит свое существование, то все параметры который нужны для алгоритма будут утрачены.
Я вижу только один вариант с хранением этих параметров или самого ключа в БД. Но я, опять же, тут наступаю на свой хвост: Если ключ храниться в БД, то возможность расшифровать сообщения есть не только у собеседников, но и у тех у кого есть доступ к этой самой БД.
Есть ли какие-нибудь решения этой проблемы или я хочу невозможного?
Есть разные варианты:
- хранить закрытый ключ в браузере (например в localstorage), если пользователь очистит данные в браузере, то ключ надо создавать новый, старые сообщения не расшифровать
- тут есть риск, что злоумышленник, захвативший компьютер пользователя, может извлечь его закрытый ключ. Для борьбы с этим можно зашифровать закрытый ключ паролем
- также, остается риск, что злоумышленник захватит сервер и запустит в браузере пользователя свой код, например, извлекающий закрытый ключ и отправляющий его злоумышленнику. Для борьбы с этим надо вместо сайта использовать отдельный клиент, например, расширение к браузеру или вообще отдельное приложение (Electron позволяет создавать приложения на HTML)
- можно вообще не хранить историю ни на сервере, ни в браузере
В общем, подумай, что тебе нужно: нужно ли хранить историю, кто должен иметь доступ к сообщениям и тд.
Также ты можешь почитать про то как реализуют шифрование другие мессенджеры:
- telegram (англ) https://core.telegram.org/api/end-to-end
- https://core.telegram.org/mtproto
- протокол приложения signal (англ) https://en.wikipedia.org/wiki/Signal_Protocol
Также, прочитай про SSL (это слой шифрования, который например используется в HTTPS).
Если ты хочешь хранить сообщения на сервере (для истории и синхронизации), то надо хранить их зашифрованными. А ключ хранить где-то в другом месте. Но может быть лучше вооще их не хранить? Или хотя бы спрашивать пользователя, хочет ли он вести запись истории или нет. Программа, которая уважает приватность пользователя, не должна ничего записывать без его согласия.
Хм, сейчас чекну.
О!! Бля, как я заебался. С десяти вечера сижу. Добра, анон.
Реквестирую гуру книжного php. обмазался за много лет пиком, но так и не начинал читать. сейчас думаю с чего начать, может быть анон мне в этом поможет? хотел бы услышал порядок прочтения этих многобукав
Из всего этого списка говном не отдает только орейли
я когда спрашивал про книги у своего препода по этому делу (его квалификация у меня сомнений не вызывает, работает тимлидом), он говорит все переведенные книги по php - неактуальное говно, которым надо топить печь. думаю он имел в виду примерно такую литературу, как на картинке.
а рекомендовал он например такие книги:
роберт мартин "чистый код"
роберт мартин "идеальный программист"
чет фаулер "программист-фанатик"
Орайли с фотки выше, да и в шапке инфы полно. Начать учить язык очень легко, хоть на codecademy курсы проходи, тяжело будет дальше.
Какай то гайд использовал? ПХП до апача ставил? Зачем тебе pma?
подскажите кто знает, а то уже заебался. не верю, что тут нет этого, в netbeans же был netbeans connector, который именно эту полезную функцию выполнял.
Я не знаю, есть ли такая функция, но советую отвыкать от такого режима. Лучше сесть, все сделать, и потом проверять, чем обновлять страницу каждые 10 секунд. Если тебе надо отследить, как происходит выполнение кода по шагам, попробуй использовать отладчик.
В пхп треде?
Система именования классов мне там нравится. Компоненты БЭМ у меня использовать повода и необходимости не было.
> Ну например, ты требуешь, чтобы все скидки были в процентах, и не даешь возможности написать произвольный алгоритм расчета скидки.
Тогда предлагаю ввести дополнительную абстракцию. Например, нужно добавить такую скидку: если среди товаров есть A и B, то снизить их суммарную стоимость на 20 единиц (не процентов). То есть раньше у нас было "снижение стоимости" основанное на процентах, теперь добавляется "снижение стоимости" в виде фиксированного значения. Код в классе Calculator поменялся с
$totalPrice += $matchedPrice - $matchedPrice ✱ $discountResult->getPercent();
на
$totalPrice += $matchedPrice - $discountResult->getDepreciation();
Не мог подобрать нормального названия для "снижения стоимомсти". Ну и подход с новой абстракцией получился многословным.
https://bitbucket.org/learning_acc/discounts/src
> Потому дробные числа нельзя сравнивать через ===. Надо проверять, что их разница меньше некоторого значения
А мне нужно это реализовывать?
https://2ch.hk/pr/res/898502.html#919067 (М)
> Что касается комбинирования скидок - вот я не уверен, что тут его стоило делать. Я такие штуки видел во фреймворках, кажется,что они позволяют строить произвольные комбинации объектов, но на практике это не особо нужно и проще в коде прописать нужное условие.
Так по условию требуется их комбинировать, там ещё написано "Скидки применяются последовательно в порядке описанном выше."
http://www.cyberforum.ru/php-oop/thread1459985.html
>>934654
Phpstorm никто вам на работах ставить не будет, дорого. Учите netbeans или eclipse, он везде. Как вариант notepad++, но это неудобно.
Я бы на твоем месте не читал все это говно, а читал напрямую документацию на http://php.net/manual/ru/
Обязательный раздел справочник языка. Дальше читай книги в шапке (Шлосснгейл, Зандстра). Еще http://www.phptherightway.com/ хорош. PSR стандарты почитай на http://www.php-fig.org/psr/, научат код нормально оформлять и велосипеды не изобретать. Ну и пасты опа конечно все перечитать. Остальные твои книги на потом оставь, будешь читать в последнюю очередь, там все равно обычно ничему хорошему не научат. Зато если их в конце читать будешь, то сразу уже видно будет, когда тебе какое-нибудь говно навязывают, вместо общепринятых практик.
ООП освоить надо сначала на примере PHP. Зандстру читай из шапки, очень доступно объясняет. После него уже любая книга по паттернам нормально идет, он там в конце еще и упомянает, какие дальше прочесть можно.
https://habrahabr.ru/company/everydaytools/blog/322068/
https://github.com/AndreyAzimov/whentosurf/
http://www.phptherightway.com/#building_and_deploying_your_application
Ознакомься, в нормальных книгах все давно расписано.
У тебя работа какая-то не очень правильная, мне с первого дня дали мак (на выбор еще убунта была), два здоровских монитора и лиценщионный шторм. Как можно вообще работать в эклипсе и небинс? Шторм на голову выше всех этих поделок
можешь сделать обновление страницы самостоятельное.
Можно через плагин для браузера.
Можно в редакторе Atom.
Я как-то пытался в пекла поставить джёрмана на седьмую версию пхп, трахался где-то пару часов
Очевидно, проблема в каком-то автолоадере, который не обучен заменять подчёркивания на слеши. Интересно, в каком. И почему это не работает из-коробки? Или, возможно, использовать PHPUnit_Framework_TestCase это устаревший подход?
Послать нахуй препода. Можешь попросить у анонов ну или спиздить тех же студентов и выдать их за свой диплом.
Периодически все равно тред пересоздается же, новые люди приходят. А кто изучать взялся, все равно тут по многу тредов сидит уже. Вот реклама в /b не помешала бы, там часто народ создает треды о программировании и не знает с чего начать, надо бы им сюда ссылку кидать.
>>932781
Я все это читал и делал, пришлось всякие уроки искать для понимания что мне делать и как, сделал вот так пока что
https://github.com/siqel/abiturientlist
Со старыми еще не разобрались
>>936205
Нужно больше подробностей. Ты уверен, что проблема не на твоей стороне? И ты случайно не из од винды там что-то собрать пытаешься?
>>936084
А ты разобрался с возможностями PhpStorm? Он немного умеет деплоить, а именно: копировать файлы в другую папку, копировать их по FTP итд. Все нажатием одной кнопки. Как именно у тебя организован деплой? Как настроен сервер, какие папки итд?
>>936090
Вопрос был больше по особенностям PhpStorm. Я не думаю, что ему нужны сторонние инструменты. Сам я кстати в большинстве случаев использовал самописные bash скрипты. Которые умеют например, копировать только измнившиеся файлы, чем экономят время.
>>936046
> работаю удаленно в Railsware.
Интересно, конечно, чем он там занимается с таким уровнем знаний
> Учитесь писать код двумя методами: практика и поиск в Google
Не советую брать с него пример.
Из /b сюда приходят в основном чтобы вставить копипасту с лурочки про PHP, которая уже лет 11 как неактуальна.
Если куда-то после школы поступишь, то можешь воспользоваться студенческой лицензией для PHPStorm, сам на такой сижу: https://www.jetbrains.com/student/
>>936752
> надо бы им сюда ссылку кидать.
Ты потом будешь лично код каждого из этих случайных вкатывальщиков из /b проверять? Я так-то тоже случайный, но хотя бы в состоянии без сторонней помощи зайти в раздел по программированию.
>>936773
Не понимаю зачем в студентах роутер, там ведь 2-3 страницы - для формы и для студентов. Кстати этот роутер в треде был уже десятки раз, с одними и теми же ошибками (никак не обрабатываются отсутствующие контроллеры/экшны, не используется preg_quote). DataGateway не должен брать данные из $_POST, работать с HTTP - задача контроллера.
Пагинатор обычно считает limit и offset, а не лезет в базу и суперглобальные переменные: https://github.com/siqel/abiturientlist/blob/master/src/PageLinker.php
Тут ведь есть пример с объяснениями: https://github.com/codedokode/pasta/blob/master/student-list.md#Постраничная-навигация/
Повсюду статические вызовы, классы используются, но код по-прежнему процедурный: https://github.com/codedokode/pasta/blob/master/arch/di.md
Почитай про форматирование кода: >>919075
спасибо, я постараюсь переделать, хотел спросить еще почему при выводе студентов первая запись не отображается, а начинает выводить только со второй, а первую запись выводит уже на 4 странице
В php.ini прописал(раскоментил):
extension_dir = "ext"
extension=php_mbstring.dll
Апач перезагружал, все равно выдает ошибку 500, если загружать в браузере, в логах
PHP Fatal error: Uncaught Error: Call to undefined function mb_strlen
Подскажите, в чем проблема ?
К примеру:
$massive = array (
'massive' => array (
'massive1' => 'govno',
'massive2' => 'ne rabochee'
)
);
Вот как мне вывести отдельно "говно" и "не рабочее"? Пишу echo "тут должно быть {$massive['massive']}" - пишет просто "Array", а если что-то уровня echo "тут должно быть {$massive['massive'['massive1']]} и {{$massive['massive'['massive2']]}" - вообще ничего, просто пустота, но не крашится.
Может я как-то не так это делаю? Гуглить пытался, но чёт внятного объяснения не нашёл.
В 2 шага:
// Получаем внутренний массив
$inner = $massive['massive'];
// Получаем элемент из внутреннего массива
echo $inner['massive1'];
Или, если убрать промежуточную переменную:
echo $massive['massive']['massive1'];
> вообще ничего, просто пустота, но не крашится.
У тебя просто скрыт вывод ошибок. Я запустил у себя- выводит:
PHP Parse error: syntax error, unexpected '[', expecting ']' in D:\2.php on line 1
Проверь значение параметра display_errors в php.ini
Спасибо большое!
Я аутист-ньюфаг в пхп и пишу текстовую "игру" да, я ебанутый и пишу игру на пхп, которая будет в основном зависить от рандома и геймплея как такого нет, кроме как читанины отчёта происходящего на момент запуска кода игры. Почти все события и ивенты будут происходить с помощь циклов, рандомизации и условий, ну и других танцев с бубном. Пока что я сделал основные статы и основной эквип для обычного тестовой пробы работоспособности. Вот этот пиздец: http://ideone.com/UhZSsG
Так что вот какой вопрос: есть ли способ сделать более-менее приемлемый и не вырвиглазный простой интерфейс для текста с помощью хтмл и чтобы это было реально для ньюфага по сложности? Ну и желательно с возможностью смены данных статов героя и имени, ну или хотя бы пустое окошко для ввода имени, лол.
Хоть бы на классах это попробовал сделать, лол.
Ошибки в рассуждениях автора каждый может поискать самостоятельно.
Конечно, фреймворк еще надо установить. Несколько команд в консоли надо выполнить.
Вот так вот, по мнению автора статьи, фреймворки "усложняют" жизнь разработчика. Или он какие-то фреймворки неправильные использовал?
Последний момент, где я клянусь богом - нигде не нашел.
http://php720.com/lesson/42
function square($num)
$num - что это. На примере урока, это квадрат числа, который задается в $num.
Но сама по себе. Вот эта вот переменная в скобках, она должна существовать до создания функции, или её можно указать впервые в этой скобки.
Каким образом при вызове этой функции, и написания в скобках число, переменная понимает, что это число её?
square($num)
square(7);
Как это происходит? Вот мы вызвали функцию, написали в скобках число. Функция просчитала все и выдала ответ. Но как она поняла, что число именно приписывается к $num? Точнее так. Смотрите. До создании функции, мне надо создать переменную $num?
Помогите пожалуйста, у меня сейчас голова взорвется.
Я вижу часы, вижу время, но не понимаю как они работают.
MVC сапер.
https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md
> Если игрок попал на мину и игра проиграна, представление должно отобразить соответствующее сообщение с кнопкой перезапуска игры (нажатие на которую тоже обработает контроллер).
А как контроллер обработает попап, если я создам его (попап) только к концу игры? Т.е. для кликов по полю (которое создано до игры) он биндит функции до начала игры, а тут вот такой случай. Создавать попап заранее и держать его где-то чтоли.
$num - аргумент этой функции. Это лишь условная переменная которой функция будет оперировать.
Когда будешь эту функцию вызывать то можешь класть любое число в качестве аргумента
А что такое аргумент с точки зрения функции?
Если там, в этих скобках - нужно будет подставить не число, а пример, или слово? Как это работает?
Я бы хотел узнать об этих вот. Словах. АРгумент. Вот. Что это значит?
Я все эти учебники смотрю. Там мне дают рыбу, а не удочку. Везде нужно следовать инструкции. И не дают внутрь глянуть прибора.
Я понимаю, все и не сразу. Но хоть жаргонные слова знать, что-бы вот сейчас ты написал мне аргумент, а я даже не знаю что это. А если загуглю - охуею
Функция - это подпрограмма (отдельная маленькая программа), которую мы используем так:
1) объявляем (определяем, создаем) функцию с помощью слова function, например function square($n) { return $n * $n ; }. Мы указали название функции square, список аргументов в круглых скобках, тело функции в фигурных скобках.
2) далее мы вызываем функцию с помощью указания имени и круглых скобок, например echo square(10);
Функция может принимать на вход от 0 до бесконечности аргументов. Они указываются при определении функции в скобках, и выглядят как переменные, например:
function square($n) { ... - один аргумент $n
function add4($a, $b, $c, $d) { return $a + $b + $c + $d; } - 4 аргумента
При вызове ты обязан в скобках указать столько значений (цифр, переменных, выражений), сколько у функции аргументов, ни больше, ни меньше. Например:
square(100) - вызываем функцию и передаем ей 100 как значение $n
add4(10, $x, 100, 500 + 1) - вызваем функцию и передаем ей 4 значения как $a = 10, $b = $x, $c = 100 и $d = 501
При вызове начинает выполняться тело функции, а переменные-аргументы получают указанные в скобках значения. То есть если ты вызвал функцию как square(100), то внутри нее переменная $n будет иметь значение 100.
Функция не видит переменные, созданные снаружи нее. Передать значения в функцию можно только через аргументы. И наоборот, код снаружи функции не видит созданные внутри нее переменные. Вернуть что-то из функции можно только командой return.
Функция может, если хочет, при завершении вернуть какое-то значение с помощью return. Этот значение вызывающий код может сохранить в переменную или как-то использовать, например, вывести:
echo square(10); // 100
$x = add4(1, 2, 3, 4);
echo $x; // 10
Если в функции нет return, то она вернет значение null. даже если функция что-то вернула, ты не обязан сохранять или использовать это значение. То есть можно написать просто
square(100);
Хотя смысла в этом нет.
У меня в учебнике есть урок по функциям http://archive-ipq-co.narod.ru/l1/functions.html и задача к нему. Можешь почитать.
> $num - что это.
Это переменная-аргумент функции.
> На примере урока, это квадрат числа, который задается в $num.
Это не квадрат числа. Это переменная-аргумент, в которую будет помещено число, указанное при вызове функции.
> Но сама по себе. Вот эта вот переменная в скобках, она должна существовать до создания функции, или её можно указать впервые в этой скобки.
Она будет создана при вызове функции автоматически. Она видна только внутри функции. Даже если ты создаешь снаружи переменную с таким же именем, функция не увидит ее, это будет по сути 2 независимых переменных (внешнаяя и внутренняя).
> Каким образом при вызове этой функции, и написания в скобках число, переменная понимает, что это число её?
Что значит понимает? Когда ты вызваешь функцию, то значения из скобок присваиваются аргументам в том же порядке, в котором они описаны.
> До создании функции, мне надо создать переменную $num?
Если ты не передаешь ее в функцию, то не надо. Я не понял из твоего кода, square($num) - это кусок описания функции или это вызов? Если вызов то такая переменная должна существовать.
Что еще непонятно?
Функция - это подпрограмма (отдельная маленькая программа), которую мы используем так:
1) объявляем (определяем, создаем) функцию с помощью слова function, например function square($n) { return $n * $n ; }. Мы указали название функции square, список аргументов в круглых скобках, тело функции в фигурных скобках.
2) далее мы вызываем функцию с помощью указания имени и круглых скобок, например echo square(10);
Функция может принимать на вход от 0 до бесконечности аргументов. Они указываются при определении функции в скобках, и выглядят как переменные, например:
function square($n) { ... - один аргумент $n
function add4($a, $b, $c, $d) { return $a + $b + $c + $d; } - 4 аргумента
При вызове ты обязан в скобках указать столько значений (цифр, переменных, выражений), сколько у функции аргументов, ни больше, ни меньше. Например:
square(100) - вызываем функцию и передаем ей 100 как значение $n
add4(10, $x, 100, 500 + 1) - вызваем функцию и передаем ей 4 значения как $a = 10, $b = $x, $c = 100 и $d = 501
При вызове начинает выполняться тело функции, а переменные-аргументы получают указанные в скобках значения. То есть если ты вызвал функцию как square(100), то внутри нее переменная $n будет иметь значение 100.
Функция не видит переменные, созданные снаружи нее. Передать значения в функцию можно только через аргументы. И наоборот, код снаружи функции не видит созданные внутри нее переменные. Вернуть что-то из функции можно только командой return.
Функция может, если хочет, при завершении вернуть какое-то значение с помощью return. Этот значение вызывающий код может сохранить в переменную или как-то использовать, например, вывести:
echo square(10); // 100
$x = add4(1, 2, 3, 4);
echo $x; // 10
Если в функции нет return, то она вернет значение null. даже если функция что-то вернула, ты не обязан сохранять или использовать это значение. То есть можно написать просто
square(100);
Хотя смысла в этом нет.
У меня в учебнике есть урок по функциям http://archive-ipq-co.narod.ru/l1/functions.html и задача к нему. Можешь почитать.
> $num - что это.
Это переменная-аргумент функции.
> На примере урока, это квадрат числа, который задается в $num.
Это не квадрат числа. Это переменная-аргумент, в которую будет помещено число, указанное при вызове функции.
> Но сама по себе. Вот эта вот переменная в скобках, она должна существовать до создания функции, или её можно указать впервые в этой скобки.
Она будет создана при вызове функции автоматически. Она видна только внутри функции. Даже если ты создаешь снаружи переменную с таким же именем, функция не увидит ее, это будет по сути 2 независимых переменных (внешнаяя и внутренняя).
> Каким образом при вызове этой функции, и написания в скобках число, переменная понимает, что это число её?
Что значит понимает? Когда ты вызваешь функцию, то значения из скобок присваиваются аргументам в том же порядке, в котором они описаны.
> До создании функции, мне надо создать переменную $num?
Если ты не передаешь ее в функцию, то не надо. Я не понял из твоего кода, square($num) - это кусок описания функции или это вызов? Если вызов то такая переменная должна существовать.
Что еще непонятно?
> АРгумент. Вот. Что это значит?
Мне нравится этот вопрос, его можно использовать на собеседовании, чтобы избавиться от большинства кандидатов. Попробую сформулировать.
Аргумент - это переменная, которая указывается при объявлении функции. Она принимает значение, которое было передано в скобках в момент вызова функции.
Давай я еще раз повторю:
function name($arg) { body; }
Эта команда значит "создать функцию с указанным именем, аргументами и телом, но пока не запускать".
name(1) значит "вызвать функцию name, передав в качестве значения $arg значение 1".
$x = name(1); значит "вызвать функцию и сохранить значение, которое она вернет (через return), в $x".
"вызвать функцию" значит выполнить записанные в ее теле команды.
Со строками все работает так же как и с числами: name("Hello")
Для попапа логично сделать отдельный объект. Ну например, отдельную пару View/Controller или просто отдельный вью или отдельный контроллер (совмещающий обе функции). Так как по идее это отдельное окно и логично его сделать в виде независимой сущности, чтобы не загрязнять код контроллера/вью игрового поля (хотя конечно ты бы мог просто создать невидимый попап в DOM и делать его видимым позже, но это не способствует разделению кода).
Можно даже сделать чуть универсальнее и сделать виджет попапа, которому при создании указывается произвольный текст и заголовок. Тогда один попап можно будет использовать в нескольких местах.
Вот как это может выглядеть:
var popupCtl = new OkCancelPopupController('Повторить попытку?', parentView);
popupCtrl.show();
popupCtrl.onEvent(EVENT_CLOSE, function (answer) {
// вызывается при нажатии кнопки или закрытии окна
});
setTimeout(function () { popupCtrl.close() }, 5000);
Теперь мы можем открывать сколько угодно попапов и мучать пользователя дурацкими вопросами.
Заметь, что я указываю parentView - родительское вью. Это создает связь между родительским и дочерним вью, и позволяет попапу например позиционироваться по его центру и блокировать его. Если у тебя несколько игровых полей на экране, то попап будет знать, к какому именно полю он относится. Также это позволяет попапу уничтожаться при уничтожении родительского вью, перемещаться вместе с ним итд. Хотя можно и без этого, сделать просто попап, перекрывающий весь экран, но в случае с несколькими полями это плохо будет работать.
Иногда функции контроллера и вью объединяют в одном классе. Но это работает только для очень простых виджетов.
Если тебе надо создать сложный диалог (например, диалог настроек с выбором числа мин, сложности и тд), то у этого диалога вполне может быть своя пара View/Controller и может даже отдельная модель для хранения настроек.
Вообще, принято выносить отдельные элементы интерфейса (виджеты) в отдельные классы, чтобы было разделение кода и чтобы их можно было повторно использовать. Ну к примеру, ты можешь сделать виджет-ползунок, который можно двигать, и благодаря вынесению его в отдельные классы ты можешь его использовать повторно на разных экранах.
Современные графические интерфейсы так и сделаны - там есть куча стандартных объектов-виджетов вроде кнопок, выпадающих списков, галочек, диалоговых окон, ползунков, полос прокрутки, выезжающих окон уведомлений, и тд. Мы создаем окно диалога, создаем кнопку и добавляем ее как дочерний виджет на это окно.
Вот примеры такой программы на Ява, может что-то поймешь: https://ru.wikibooks.org/wiki/Java/Первое_окно
Там есть контроллеры или вью виджетов (JFrame, JButton), я не знаю, есть ли у них отдельные M, V и С, или там все объединено в одном классе в силу примитивности кода.
То есть каждое окно/вкладка/виджет в теории может иметь свои V и С. И при желании, M. Контроллер может отстутствовать, если с элементом нельзя взаимодействовать, или может быть встроен в View, если он очень примитивный.
Эти виджеты используются в языках вроде Java, С# при построении интерфейса, и в библиотеках вроде angular, react тоже предусмотрено разбиение интерфейса на отдельные компоненты, хотя и не совсем так. Ну например вместо создания виджетов в коде через new, они описываются в виде похожей на HTML разметки:
<Dialog>
<Button label="ОК"/>
</Dialog>
То есть если у тебя приложение большое и состоит из множества окон, вкладок, виджетов, то моделей/контроллеров/вьюшек может быть тоже много. И не обязательно одинаковое количество.
Для попапа логично сделать отдельный объект. Ну например, отдельную пару View/Controller или просто отдельный вью или отдельный контроллер (совмещающий обе функции). Так как по идее это отдельное окно и логично его сделать в виде независимой сущности, чтобы не загрязнять код контроллера/вью игрового поля (хотя конечно ты бы мог просто создать невидимый попап в DOM и делать его видимым позже, но это не способствует разделению кода).
Можно даже сделать чуть универсальнее и сделать виджет попапа, которому при создании указывается произвольный текст и заголовок. Тогда один попап можно будет использовать в нескольких местах.
Вот как это может выглядеть:
var popupCtl = new OkCancelPopupController('Повторить попытку?', parentView);
popupCtrl.show();
popupCtrl.onEvent(EVENT_CLOSE, function (answer) {
// вызывается при нажатии кнопки или закрытии окна
});
setTimeout(function () { popupCtrl.close() }, 5000);
Теперь мы можем открывать сколько угодно попапов и мучать пользователя дурацкими вопросами.
Заметь, что я указываю parentView - родительское вью. Это создает связь между родительским и дочерним вью, и позволяет попапу например позиционироваться по его центру и блокировать его. Если у тебя несколько игровых полей на экране, то попап будет знать, к какому именно полю он относится. Также это позволяет попапу уничтожаться при уничтожении родительского вью, перемещаться вместе с ним итд. Хотя можно и без этого, сделать просто попап, перекрывающий весь экран, но в случае с несколькими полями это плохо будет работать.
Иногда функции контроллера и вью объединяют в одном классе. Но это работает только для очень простых виджетов.
Если тебе надо создать сложный диалог (например, диалог настроек с выбором числа мин, сложности и тд), то у этого диалога вполне может быть своя пара View/Controller и может даже отдельная модель для хранения настроек.
Вообще, принято выносить отдельные элементы интерфейса (виджеты) в отдельные классы, чтобы было разделение кода и чтобы их можно было повторно использовать. Ну к примеру, ты можешь сделать виджет-ползунок, который можно двигать, и благодаря вынесению его в отдельные классы ты можешь его использовать повторно на разных экранах.
Современные графические интерфейсы так и сделаны - там есть куча стандартных объектов-виджетов вроде кнопок, выпадающих списков, галочек, диалоговых окон, ползунков, полос прокрутки, выезжающих окон уведомлений, и тд. Мы создаем окно диалога, создаем кнопку и добавляем ее как дочерний виджет на это окно.
Вот примеры такой программы на Ява, может что-то поймешь: https://ru.wikibooks.org/wiki/Java/Первое_окно
Там есть контроллеры или вью виджетов (JFrame, JButton), я не знаю, есть ли у них отдельные M, V и С, или там все объединено в одном классе в силу примитивности кода.
То есть каждое окно/вкладка/виджет в теории может иметь свои V и С. И при желании, M. Контроллер может отстутствовать, если с элементом нельзя взаимодействовать, или может быть встроен в View, если он очень примитивный.
Эти виджеты используются в языках вроде Java, С# при построении интерфейса, и в библиотеках вроде angular, react тоже предусмотрено разбиение интерфейса на отдельные компоненты, хотя и не совсем так. Ну например вместо создания виджетов в коде через new, они описываются в виде похожей на HTML разметки:
<Dialog>
<Button label="ОК"/>
</Dialog>
То есть если у тебя приложение большое и состоит из множества окон, вкладок, виджетов, то моделей/контроллеров/вьюшек может быть тоже много. И не обязательно одинаковое количество.
Если подумать, то форма регистрации и форма редактирования личных данных - это одна и та же форма. Для нее можно использовать один общий контроллер и один общий шаблон для view. Просто в нужных местах добавить ветвление в зависимости от того, редактируем мы или создаем пользователя.
Во-первых, у тебя тут конечно направшивается использование объектов вместо массивов с характеристиками персонажей. Ты можешь глянуть учебник из ОП поста (я бы конечно его советовал вообще целиком прочесть), там есть глава про ООП и там описано, как использовать объекты.
Насчет вывода данных с помощью HTML. Не вижу никакой проблемы. Тебе надо 2 вещи:
- сделать HTML-шаблоны для вывода данных. По окончании вычислений PHP скрипт будет подготавливать переменные с результатами боя, параметрами героев и передавать их в шаблон, который их выведет. Читай http://www.phpinfo.su/articles/practice/shablony_v_php.html
- сделать запуск скрипта через браузер и веб-сервер (если ты их еще не используешь). Чтобы ты заходил на определенный адрес в браузере, запускался бы скрит и ты видел в окне результат боя. Читай https://github.com/codedokode/pasta/blob/master/soft/web-server.md
Насчет ввода данных от пользователя - почитай про HTML-формы. Ты можешь добавить на страницу поля так, что после их заполнения и нажатия кнопки будет отправляться запрос с этими данными на веб-сервер, там будет запускаться скрипт и использовать указанные в форме данные.
Но тут есть проблема, что скрипт запускается каждый раз с нуля, все переменные очищаются. То есть нельзя например делать битву из нескольких ходов, и после каждого давать игроку выбор вариантов, так как каждый раз запускается отдельная копия скрипта. И даже имя игрока скрипт будет забывать после одного запуска.
Для решения этой проблемы надо где-то сохранять состояние игры при завершении скрипта и загружать их оттуда при повторном запуске. Вот где можно хранить состояние:
- в самой HTML-странице в форме можно сделать скрытое поле/поля, хранящее информацию о состоянии игры. Наш скрипт сохраняет информацию в это поле и после отправки формы игроком это сохраненное состояние отправится на веб-сервер вместе с введенными игроком данными
- в куках/сессии. Минус - куки/сессия общие для всех вкладок и нельзя вести 2 игры в 2 вкладках параллельно
- в файлах. Надо решить проблему, как различать игроков. Это почти то же, что и сессии.
- в базе данных.
Во-первых, у тебя тут конечно направшивается использование объектов вместо массивов с характеристиками персонажей. Ты можешь глянуть учебник из ОП поста (я бы конечно его советовал вообще целиком прочесть), там есть глава про ООП и там описано, как использовать объекты.
Насчет вывода данных с помощью HTML. Не вижу никакой проблемы. Тебе надо 2 вещи:
- сделать HTML-шаблоны для вывода данных. По окончании вычислений PHP скрипт будет подготавливать переменные с результатами боя, параметрами героев и передавать их в шаблон, который их выведет. Читай http://www.phpinfo.su/articles/practice/shablony_v_php.html
- сделать запуск скрипта через браузер и веб-сервер (если ты их еще не используешь). Чтобы ты заходил на определенный адрес в браузере, запускался бы скрит и ты видел в окне результат боя. Читай https://github.com/codedokode/pasta/blob/master/soft/web-server.md
Насчет ввода данных от пользователя - почитай про HTML-формы. Ты можешь добавить на страницу поля так, что после их заполнения и нажатия кнопки будет отправляться запрос с этими данными на веб-сервер, там будет запускаться скрипт и использовать указанные в форме данные.
Но тут есть проблема, что скрипт запускается каждый раз с нуля, все переменные очищаются. То есть нельзя например делать битву из нескольких ходов, и после каждого давать игроку выбор вариантов, так как каждый раз запускается отдельная копия скрипта. И даже имя игрока скрипт будет забывать после одного запуска.
Для решения этой проблемы надо где-то сохранять состояние игры при завершении скрипта и загружать их оттуда при повторном запуске. Вот где можно хранить состояние:
- в самой HTML-странице в форме можно сделать скрытое поле/поля, хранящее информацию о состоянии игры. Наш скрипт сохраняет информацию в это поле и после отправки формы игроком это сохраненное состояние отправится на веб-сервер вместе с введенными игроком данными
- в куках/сессии. Минус - куки/сессия общие для всех вкладок и нельзя вести 2 игры в 2 вкладках параллельно
- в файлах. Надо решить проблему, как различать игроков. Это почти то же, что и сессии.
- в базе данных.
Проще всего указать ширину наример 65% + margin-right 5%.
Также, я советую убрать фиксированную высоту контента и сайдбара, так как она на практике неизвестна.
Ты задал всего один вопрос, значит все остальное тебе понятно? Ну хорошо, а то тут люди жалуются, что для них это равносильно рисованию совы.
Также, сразу посоветую внимательно прочитать комментарии к задаче о студентах, там очень много полезной информации.
Я вижу, что структура твоего кода напоминает фреймворк Yii. Не уверен, что она тут хорошо подходит. Вот, что мне не нравится:
- нет отдельной публичной папки, все файлы по сути доступны извне. Это небезопасно, читай комментарии к студентам.
- папки templates и views - непонятно их различие. Обычно в веб-приложении HTML/PHP-шаблон это и есть вью
- components - непонятно, что это такое? Что ты называешь "компонент"? Контроллер - это не компонент?
- есть папка src (исходный код), но controllers и components почему-то не в ней.
Далее, я не вижу четкого разделения на PHP-код и шаблоны (Controller/View). Вот этот файл: https://github.com/siqel/abiturientlist/blob/master/index.php - он выполняет и функции фронт контроллера (вызвает роутер), и занимается выводом кусков HTML кода. Плохо. Лучше сделать так:
- подготовить все данные
- вывести их с помощью шаблона
У тебя подготовка и вывод данных идет вперемешку.
Также, у тебя почему-то в index.php идет какая-то работа с базой данных, если POST не пуст. Разве это не задача контроллера формы регистрации? Идея как раз в том, что каждый контроллер отвечает за свою страницу, а index.php это как бы общая точка входа (фронт контроллер) и она не должна выполнять обязанности одного конкретного контроллера вместо него.
То есть у тебя пока плохо с разделением кода на части, распределением отвественности, кто чем занимается. Ты должен на 100% понимать, какой код за что отвечает, а не вписывать код куда попало.
> https://github.com/siqel/abiturientlist/blob/master/components/Db.php
Тут опять же все вперемешку. В файле с классом должно быть только описание этого класса. А ты после него еще какой-то объект там создаешь. А перед ним добавил подключение автозагрузчика.
Я не уверен, что класс Db нужен. Объясни, зачем он нужен, если мы можем просто писать $pdo = new PDO...
Ты ведь его просто скопировал из какой-то статьи, не думая. Не надо так. Ты должен писать каждую строчку кода сам, понимая, зачем она нужна. Не понимаешь - спроси, или перечитай комментарии к задаче.
Иногда нам надо делать какие-то действия в самом начале (настроить автозагрузчик, обработку ошибок, создать какие-то объекты). Для этого можно использовать bootstrap-файл, читай комментарии.
В роутере мне не нравится, что в нем жестко прописан путь к конфигу и идет обращение к _SERVER. Лучше вынести это из него, и передавать список роутов и текущий URL снаружи.
Код плохо выровнен: https://github.com/siqel/abiturientlist/blob/master/components/Router.php#L28
Настрой редактор, чтобы он вставлял пробелы вместо табов. Отформатируй код. Читай второй пост треда.
В роутере у тебя какой-то гигантский цикл foreach и гигантский if. Переделай код, чтобы они были небольшими.
Если URL не соответствует ни одной странице, надо выдавать ошибку 404.
> To change this license header, choose License Headers
Удали эту ерунду из кода.
Если ты используешь автозагрузчик, то отдельные require нужно убрать.
> https://github.com/siqel/abiturientlist/blob/master/src/AbiturientDataGateway.php#L9
Тут опять нарушение разделения ответственности. Data Gateway - это класс для взаимодействия с БД, он ничего не должен знать про формы и $_POST. Перечитай мой урок по MVC и комментарии к задаче. Это нарушение принципа MVC.
> $result = $db->query('SELECT* from abiturients WHERE id='.$id);
Используй плейсхолдеры вместо склеивания строк.
Фукнции чтения данных из БД - не лучше ли, если они будут возвращать объекты Student, а не массивы?
> https://github.com/siqel/abiturientlist/blob/master/src/AbiturientDataGateway.php#L111
> function pdoSet($allowed, &$values, $source = array()) {
Что делает функция в файле с описанием класса? Почему она обращается к POST? У тебя реально все перемешано. Смотри, как должно быть:
- создаем объект Student
- отдельной функцией принимаем данные POST и записываем их в объект
- отдельной функцией проверяем на правильнсоть данные в объекте
- отдельной функцией сохраняем их в базу данных из объекта
У тебя все перемешано в кучу. Надо разделить.
> https://github.com/siqel/abiturientlist/blob/master/src/Init.php
Тут тоже все смешано. Тут и инициализация, и вывод HTML кода.
> https://github.com/siqel/abiturientlist/blob/master/src/PageLinker.php
и тут все смешано в кучу. Сам посчитай, сколько задач выполняет одна функция тут.
> https://github.com/siqel/abiturientlist/blob/master/controllers/AbiturientsController.php#L7
Тут непонятно, зачем у действия index сделан параметр id. Зачем передавать туда id? Если ты хотел сделать постраничный вывод, надо передавать номер страницы (и назвать переменную правильно), и из него рассчитывать offset/limit. И их уже передавать в DataGateway.
> while ($row = $result->fetch()) {
Вообще-то там можо получить все строки одной командой. Погугли.
В общем, пока сделано на двойку с плюсом. Исправляйся.
Ты задал всего один вопрос, значит все остальное тебе понятно? Ну хорошо, а то тут люди жалуются, что для них это равносильно рисованию совы.
Также, сразу посоветую внимательно прочитать комментарии к задаче о студентах, там очень много полезной информации.
Я вижу, что структура твоего кода напоминает фреймворк Yii. Не уверен, что она тут хорошо подходит. Вот, что мне не нравится:
- нет отдельной публичной папки, все файлы по сути доступны извне. Это небезопасно, читай комментарии к студентам.
- папки templates и views - непонятно их различие. Обычно в веб-приложении HTML/PHP-шаблон это и есть вью
- components - непонятно, что это такое? Что ты называешь "компонент"? Контроллер - это не компонент?
- есть папка src (исходный код), но controllers и components почему-то не в ней.
Далее, я не вижу четкого разделения на PHP-код и шаблоны (Controller/View). Вот этот файл: https://github.com/siqel/abiturientlist/blob/master/index.php - он выполняет и функции фронт контроллера (вызвает роутер), и занимается выводом кусков HTML кода. Плохо. Лучше сделать так:
- подготовить все данные
- вывести их с помощью шаблона
У тебя подготовка и вывод данных идет вперемешку.
Также, у тебя почему-то в index.php идет какая-то работа с базой данных, если POST не пуст. Разве это не задача контроллера формы регистрации? Идея как раз в том, что каждый контроллер отвечает за свою страницу, а index.php это как бы общая точка входа (фронт контроллер) и она не должна выполнять обязанности одного конкретного контроллера вместо него.
То есть у тебя пока плохо с разделением кода на части, распределением отвественности, кто чем занимается. Ты должен на 100% понимать, какой код за что отвечает, а не вписывать код куда попало.
> https://github.com/siqel/abiturientlist/blob/master/components/Db.php
Тут опять же все вперемешку. В файле с классом должно быть только описание этого класса. А ты после него еще какой-то объект там создаешь. А перед ним добавил подключение автозагрузчика.
Я не уверен, что класс Db нужен. Объясни, зачем он нужен, если мы можем просто писать $pdo = new PDO...
Ты ведь его просто скопировал из какой-то статьи, не думая. Не надо так. Ты должен писать каждую строчку кода сам, понимая, зачем она нужна. Не понимаешь - спроси, или перечитай комментарии к задаче.
Иногда нам надо делать какие-то действия в самом начале (настроить автозагрузчик, обработку ошибок, создать какие-то объекты). Для этого можно использовать bootstrap-файл, читай комментарии.
В роутере мне не нравится, что в нем жестко прописан путь к конфигу и идет обращение к _SERVER. Лучше вынести это из него, и передавать список роутов и текущий URL снаружи.
Код плохо выровнен: https://github.com/siqel/abiturientlist/blob/master/components/Router.php#L28
Настрой редактор, чтобы он вставлял пробелы вместо табов. Отформатируй код. Читай второй пост треда.
В роутере у тебя какой-то гигантский цикл foreach и гигантский if. Переделай код, чтобы они были небольшими.
Если URL не соответствует ни одной странице, надо выдавать ошибку 404.
> To change this license header, choose License Headers
Удали эту ерунду из кода.
Если ты используешь автозагрузчик, то отдельные require нужно убрать.
> https://github.com/siqel/abiturientlist/blob/master/src/AbiturientDataGateway.php#L9
Тут опять нарушение разделения ответственности. Data Gateway - это класс для взаимодействия с БД, он ничего не должен знать про формы и $_POST. Перечитай мой урок по MVC и комментарии к задаче. Это нарушение принципа MVC.
> $result = $db->query('SELECT* from abiturients WHERE id='.$id);
Используй плейсхолдеры вместо склеивания строк.
Фукнции чтения данных из БД - не лучше ли, если они будут возвращать объекты Student, а не массивы?
> https://github.com/siqel/abiturientlist/blob/master/src/AbiturientDataGateway.php#L111
> function pdoSet($allowed, &$values, $source = array()) {
Что делает функция в файле с описанием класса? Почему она обращается к POST? У тебя реально все перемешано. Смотри, как должно быть:
- создаем объект Student
- отдельной функцией принимаем данные POST и записываем их в объект
- отдельной функцией проверяем на правильнсоть данные в объекте
- отдельной функцией сохраняем их в базу данных из объекта
У тебя все перемешано в кучу. Надо разделить.
> https://github.com/siqel/abiturientlist/blob/master/src/Init.php
Тут тоже все смешано. Тут и инициализация, и вывод HTML кода.
> https://github.com/siqel/abiturientlist/blob/master/src/PageLinker.php
и тут все смешано в кучу. Сам посчитай, сколько задач выполняет одна функция тут.
> https://github.com/siqel/abiturientlist/blob/master/controllers/AbiturientsController.php#L7
Тут непонятно, зачем у действия index сделан параметр id. Зачем передавать туда id? Если ты хотел сделать постраничный вывод, надо передавать номер страницы (и назвать переменную правильно), и из него рассчитывать offset/limit. И их уже передавать в DataGateway.
> while ($row = $result->fetch()) {
Вообще-то там можо получить все строки одной командой. Погугли.
В общем, пока сделано на двойку с плюсом. Исправляйся.
А ты поменял именно тот файл php.ini, который используется? И ты перезапустил веб-сервер Апач после изменения настроек PHP (php ведь работает как модуль внутри Апача)?
Сделай php-файл с кодом <?php phpinfo(); , запусти его через браузер и посмотри там какой именно файл php.ini использовался. Отредактируй его, и перезапусти Апач.
>>937123
Вообще, если у тебя нет ORDER BY, то это значит, что база данных может возвращать данные как хочет. Без ORDER вообще нельзя сказать, какая запись первая, а какая последняя. Так что начни с его добавления.
>>936255
А я за пару вечеров смог скомпилировать PHP7 под Windows XP. Пока только ядро, без большинства расширений. Тесты почти все проходят. Надо найти время, доделать все и выложить на гитхаб. Скажем нет навязыванию принудительных обновлений софта, не несущих выгоды пользователям.
>>936205
Может с интернетом проблемы или роскомнадзор подсуетился?
У меня тут есть несколько идей, которые не требуются по условиям задачи, но которые, как мне кажется, могли бы пригодиться при разработке новых скидок. Хотя конечно на практике скидки бывают непредсказуемымми. Например, скидка (50 рублей) при предъявлении купона и превышении определенной суммы чека, в опредленные дни. Можно использовать до 5 купонов за раз. В нынешнюю модель она не укладывается.
> Тогда предлагаю ввести дополнительную абстракцию. Например, нужно добавить такую скидку: если среди товаров есть A и B, то снизить их суммарную стоимость на 20 единиц (не процентов). То есть раньше у нас было "снижение стоимости" основанное на процентах...
У меня была идея в том, что объект скидки сам вычисляет цену (на товары, к которым применена скидка) как хочет. То есть хоть вычитает проценты, хоть рубли, хоть применяет произвольную формулу. Максимум гибкости без закладывания каких-то алгоритмов (в задаче это не требуется, но вдруг пригодится). Код выглядел бы так:
> $totalPrice += $discountResult->getPriceWithDiscount();
Ну и вторая идея была, дать скидке возможность видеть товары, на которые применена скидка ранее:
$discountResult = $discount->applyDiscount($notUsedProducts, $usedProducts);
Это опять же дает нам больше гибкости, хотя и не требуется по условиям задачи. Например, позволяет делать условия "скидка на товар A, если есть товар B, даже если он уже использовался в скидке". И может даже позволит делать скидки при определенной сумме чека.
И еще, насчет уменьшения цены, я бы может поменял вот это:
> $totalPrice += $matchedPrice - $discountResult->getDepreciation();
на это:
$totalPrice += $discountResult->getDepreciation()->decrementPrice($matchedPrice);
Это дает возможность объекту Depreciation делать произвольные операции с ценой, не только уменьшать ее на какую-то величину. А его интерфейс выглядел бы так: public function decrementPrice($originalPrice); Хотя так он не будет знать, с какими именно товарами работает.
Сам класс Calculator мне нравится тем, что он получился очень простым.
> То есть раньше у нас было "снижение стоимости" основанное на процентах, теперь добавляется "снижение стоимости" в виде фиксированного значения.
По моему удобнее возвращать не на сколько рублей меньше стали стоить товары (попавшие под скидку), а итоговую их цену. Так наверно код проще будет. Или нет?
>> Потому дробные числа нельзя сравнивать через ===. Надо проверять, что их разница меньше некоторого значения
> А мне нужно это реализовывать?
Вообще, да. Иначе твои тесты работают неверно. В твоем случае можно просто вручную указать epsilon, меньше которого должна быть разница 2 чисел.
> Так по условию требуется их комбинировать, там ещё написано "Скидки применяются последовательно в порядке описанном выше."
Нет, я имел в виду комбинирование, когда объект скидки создается из примитивных блоков вроде такого:
$discont = new CombinedDiscount([
new DiscountIfProductExists('A'),
new DiscountIfAnyProductExists(['B', 'C', 'D'])
]);
Это на первый взгляд дает возможность строить произвольные конструкции, но на практике усложняет код и часто просто не требуется. Лучше избегать такого, если не видна выгода от этого подхода.
Я вижу, ты сделал расчет скидки отдельным объектом (PercentDepreciation). Но нужно ли нам до такой степени разделять скидку на составляющие, раздувая код? Не проще ли просто прописать расчет величины в самом классе скидки? Тут надо взвесить выгоды и недостатки этого разделения. Мы должны разбивать код на части, но где-то мы должны остановиться.
Интуиция мне подсказвает, что на практике такой гибкости скорее всего не потребуется и она будет только мешать. Увы, тут обосновать я это не могу, просто интуиция и опыт. Так что к твоему подходу претензий нет.
Вот тебе кстати статья по теме: https://habrahabr.ru/post/153225/
И еще, если ты поймешь Java (он похож на PHP): https://m.habrahabr.ru/company/abbyy/blog/173885/
Кстати, класс ProductCollection можно было реализовать поверх стандартного SpjObjectStorage - там есть эффективные по скорости методы для склеивания/нахождения разницы/удаления.
> private function isCountDepreciationMapValid
Тут я поймал себя на мысли, что в языках вроде Ява эта проверка делается одним тайп-хинтом вроде Map<int, DepreciationInterface>. Может и в PHP когда-нибудь такое завезут (generic types). RFC с идеей уже есть: https://wiki.php.net/rfc/generics
Тут не исправлена проблема модификации переданной коллекции: https://bitbucket.org/learning_acc/discounts/src/91a4b811186aeef2d2041cca52b685c8f17a9a4b/src/Discount/CombinationDiscount.php?at=master&fileviewer=file-view-default#CombinationDiscount.php-31
Не очевидно, что метод может удалять товары из переданной на вход коллекции, и надо бы обойтись без этого. Это приведет к труднообнаружимым ошибкам в коде. Самое простое - клонировать коллекцию, но может есть более оптимальное решение.
Также, там вроде надо давать скидку на каждую найденную группу товаров, но у тебя дается скидка только на первую. Ну то есть для набора A, A, B, B и условия, что должны присуствовать A + B надо выдать 2 скидки, а ты выдаешь только одну. Вот я скопирую условие:
> Если одновременно выбраны А и B, то их суммарная стоимость уменьшается на 10% (для каждой пары А и B)
И вот тут еще я нашел подвох:
> Если пользователь выбрал одновременно 3 продукта, он получает скидку 5% от суммы заказа
Эта скидка применяется к итоговой сумме заказа, или только к этим 3 продуктам? У тебя второе, я в принципе не против, так как сама задача неточно сформулирована.
Так в общем, решение хорошее (кроме CombinationDiscount), а все идеи, что я написал - это лишь мысли по улучшению, так-то твой код уже соответствует условиям задачи. Надеюсь что понимание ООП у тебя улучшилось в итоге. Я даже думаю, ты уже можешь читать исходный код Symfony и понимать, что там написано.
Код у тебя довольно легко читается (хотя может это потому, что мы с этой задачей уже не первый день возимся).
На мой взгляд, задачи на ООП должны быть с меняющимися со временем требованиями (имитирующие реальную разработку). Я у себя в задаче про Вектор так и постарался сделать, чтобы проверить, насколько гибкий получается код в итоге.
У меня тут есть несколько идей, которые не требуются по условиям задачи, но которые, как мне кажется, могли бы пригодиться при разработке новых скидок. Хотя конечно на практике скидки бывают непредсказуемымми. Например, скидка (50 рублей) при предъявлении купона и превышении определенной суммы чека, в опредленные дни. Можно использовать до 5 купонов за раз. В нынешнюю модель она не укладывается.
> Тогда предлагаю ввести дополнительную абстракцию. Например, нужно добавить такую скидку: если среди товаров есть A и B, то снизить их суммарную стоимость на 20 единиц (не процентов). То есть раньше у нас было "снижение стоимости" основанное на процентах...
У меня была идея в том, что объект скидки сам вычисляет цену (на товары, к которым применена скидка) как хочет. То есть хоть вычитает проценты, хоть рубли, хоть применяет произвольную формулу. Максимум гибкости без закладывания каких-то алгоритмов (в задаче это не требуется, но вдруг пригодится). Код выглядел бы так:
> $totalPrice += $discountResult->getPriceWithDiscount();
Ну и вторая идея была, дать скидке возможность видеть товары, на которые применена скидка ранее:
$discountResult = $discount->applyDiscount($notUsedProducts, $usedProducts);
Это опять же дает нам больше гибкости, хотя и не требуется по условиям задачи. Например, позволяет делать условия "скидка на товар A, если есть товар B, даже если он уже использовался в скидке". И может даже позволит делать скидки при определенной сумме чека.
И еще, насчет уменьшения цены, я бы может поменял вот это:
> $totalPrice += $matchedPrice - $discountResult->getDepreciation();
на это:
$totalPrice += $discountResult->getDepreciation()->decrementPrice($matchedPrice);
Это дает возможность объекту Depreciation делать произвольные операции с ценой, не только уменьшать ее на какую-то величину. А его интерфейс выглядел бы так: public function decrementPrice($originalPrice); Хотя так он не будет знать, с какими именно товарами работает.
Сам класс Calculator мне нравится тем, что он получился очень простым.
> То есть раньше у нас было "снижение стоимости" основанное на процентах, теперь добавляется "снижение стоимости" в виде фиксированного значения.
По моему удобнее возвращать не на сколько рублей меньше стали стоить товары (попавшие под скидку), а итоговую их цену. Так наверно код проще будет. Или нет?
>> Потому дробные числа нельзя сравнивать через ===. Надо проверять, что их разница меньше некоторого значения
> А мне нужно это реализовывать?
Вообще, да. Иначе твои тесты работают неверно. В твоем случае можно просто вручную указать epsilon, меньше которого должна быть разница 2 чисел.
> Так по условию требуется их комбинировать, там ещё написано "Скидки применяются последовательно в порядке описанном выше."
Нет, я имел в виду комбинирование, когда объект скидки создается из примитивных блоков вроде такого:
$discont = new CombinedDiscount([
new DiscountIfProductExists('A'),
new DiscountIfAnyProductExists(['B', 'C', 'D'])
]);
Это на первый взгляд дает возможность строить произвольные конструкции, но на практике усложняет код и часто просто не требуется. Лучше избегать такого, если не видна выгода от этого подхода.
Я вижу, ты сделал расчет скидки отдельным объектом (PercentDepreciation). Но нужно ли нам до такой степени разделять скидку на составляющие, раздувая код? Не проще ли просто прописать расчет величины в самом классе скидки? Тут надо взвесить выгоды и недостатки этого разделения. Мы должны разбивать код на части, но где-то мы должны остановиться.
Интуиция мне подсказвает, что на практике такой гибкости скорее всего не потребуется и она будет только мешать. Увы, тут обосновать я это не могу, просто интуиция и опыт. Так что к твоему подходу претензий нет.
Вот тебе кстати статья по теме: https://habrahabr.ru/post/153225/
И еще, если ты поймешь Java (он похож на PHP): https://m.habrahabr.ru/company/abbyy/blog/173885/
Кстати, класс ProductCollection можно было реализовать поверх стандартного SpjObjectStorage - там есть эффективные по скорости методы для склеивания/нахождения разницы/удаления.
> private function isCountDepreciationMapValid
Тут я поймал себя на мысли, что в языках вроде Ява эта проверка делается одним тайп-хинтом вроде Map<int, DepreciationInterface>. Может и в PHP когда-нибудь такое завезут (generic types). RFC с идеей уже есть: https://wiki.php.net/rfc/generics
Тут не исправлена проблема модификации переданной коллекции: https://bitbucket.org/learning_acc/discounts/src/91a4b811186aeef2d2041cca52b685c8f17a9a4b/src/Discount/CombinationDiscount.php?at=master&fileviewer=file-view-default#CombinationDiscount.php-31
Не очевидно, что метод может удалять товары из переданной на вход коллекции, и надо бы обойтись без этого. Это приведет к труднообнаружимым ошибкам в коде. Самое простое - клонировать коллекцию, но может есть более оптимальное решение.
Также, там вроде надо давать скидку на каждую найденную группу товаров, но у тебя дается скидка только на первую. Ну то есть для набора A, A, B, B и условия, что должны присуствовать A + B надо выдать 2 скидки, а ты выдаешь только одну. Вот я скопирую условие:
> Если одновременно выбраны А и B, то их суммарная стоимость уменьшается на 10% (для каждой пары А и B)
И вот тут еще я нашел подвох:
> Если пользователь выбрал одновременно 3 продукта, он получает скидку 5% от суммы заказа
Эта скидка применяется к итоговой сумме заказа, или только к этим 3 продуктам? У тебя второе, я в принципе не против, так как сама задача неточно сформулирована.
Так в общем, решение хорошее (кроме CombinationDiscount), а все идеи, что я написал - это лишь мысли по улучшению, так-то твой код уже соответствует условиям задачи. Надеюсь что понимание ООП у тебя улучшилось в итоге. Я даже думаю, ты уже можешь читать исходный код Symfony и понимать, что там написано.
Код у тебя довольно легко читается (хотя может это потому, что мы с этой задачей уже не первый день возимся).
На мой взгляд, задачи на ООП должны быть с меняющимися со временем требованиями (имитирующие реальную разработку). Я у себя в задаче про Вектор так и постарался сделать, чтобы проверить, насколько гибкий получается код в итоге.
У тебя там есть проблема. Там есть условие
if ($creditBalance <= 4000)
и я не вижу, откуда взялось число 4000. Его нет в условии задачи. Что, елси мы поменяем исходные данные, программа будет работать корреткно?
Ты наверно считал 4000 как 5000 - 1000. Но это неправильно, так как ты не учел проценты, добавляемые на остаток долга.
Задачу вообще можно решить так:
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
«Платим» здесь значит уменьшаем долг и увеличиваем общую сумму выплаченного.
>>935535
Скорее всего у тебя не настроено в Апаче выполнение php-скриптов. То есть Апач просто отдает php скрипт, вместо того чтобы выполнить его с помощью mod_php.
Ты может как-то не так установил эти программы? Вроде по умолчанию в дебиане установка PHP добавляет нужные параметры в конфиг Апача.
>>934750
Это вряд ли связано с самой сессией, скорее с тем как ты их выводишь.
>>936752
Кидай, только тогда сразу стоит объяснить, чем мы занимаемся, что надо знать, и что за месяц "вкатиться" и выучить все нереально.
У тебя там есть проблема. Там есть условие
if ($creditBalance <= 4000)
и я не вижу, откуда взялось число 4000. Его нет в условии задачи. Что, елси мы поменяем исходные данные, программа будет работать корреткно?
Ты наверно считал 4000 как 5000 - 1000. Но это неправильно, так как ты не учел проценты, добавляемые на остаток долга.
Задачу вообще можно решить так:
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
«Платим» здесь значит уменьшаем долг и увеличиваем общую сумму выплаченного.
>>935535
Скорее всего у тебя не настроено в Апаче выполнение php-скриптов. То есть Апач просто отдает php скрипт, вместо того чтобы выполнить его с помощью mod_php.
Ты может как-то не так установил эти программы? Вроде по умолчанию в дебиане установка PHP добавляет нужные параметры в конфиг Апача.
>>934750
Это вряд ли связано с самой сессией, скорее с тем как ты их выводишь.
>>936752
Кидай, только тогда сразу стоит объяснить, чем мы занимаемся, что надо знать, и что за месяц "вкатиться" и выучить все нереально.
Да, когда сфинкс индексирует таблицу, он кладет данные в обычные индексы. Они лучше оптимизированы для хранения больших объемов данных, но их нельзя обновить, только пересоздать с нуля целиком. Их обычно переиндексируют нечасто - раз в сутки, раз в час.
Информация в них устаревает. Вот, что может произойти между индексированиями:
- может быть добавлена новая запись в БД
- может быть изменена запись в БД
- может быть удалена запись из БД
При этом в индексе остаются старые неактуальные данные.
А реалтайм-индексы менее оптимальные для хранения и поиска, но зато их можно менять в реальном времени, с помощью запросов INSERT, UPDATE, DELETE к SQL-интерфейсу сфинкса.
Реалтйм-индексы по моему хранятся в памяти, но бекапятся на диск на случай падения/перезапуска сфинкса и не теряют данные в этой ситуации.
Идея такая: большинство данных хранится в обычных индексах, но объекты, созданные/измененные/удаленные после индексации, сохраняются в реалйтайм индекс до следующей индексации (где они попадут в основной индекс). Важно настроить все так, чтобы данные не терялись ни в каком случае.
Мануал (англ) http://sphinxsearch.com/docs/current/rt-indexes.html
Логика работы с ними такая:
- описываем реалтайм-индекс в конфиге
- при переиндексировании очищаем реалтайм индекс
- в приложении, при создании|удалении|изменении объекта, вставляем его данные в реалтайм-индекс
- при поиске ищем и по обычному, и по реалтайм индексу. Исключаем из основного индекса записи, которые есть в основном индексе, но помечены как удаленные или измененные в реалтайм-индексе (то есть данные по которым в основом индексе устаревшие).
Без реалтайм индексов поиск не будет видеть новые объекты, созданные после индексирования.
> То есть, применительно к задаче на файлообменник, для того чтобы эти индексы заполнялись при добавлении файла, дополнительно к запросу на заполнение полей таблицы нужен еще и запрос для заполнения реалтайм-индекса?
Да.
> Далее, при использовании реалтайм-индекса невозможно сразу получить все поля строки, т.е. если у нас проиндексировано только имя файла, то сфинкс отдаст в виде результата поиска только айдишник(и)
Сфинкс это не база данных, он и не обязан ничего хранить, только искать id записей. Хранение данных только усложнило бы все, например появилась бы проблема их расхождения с данными в БД.
> Или нужно все поля в индекс загонять?
Только те, по которым ведется поиск.
Да, когда сфинкс индексирует таблицу, он кладет данные в обычные индексы. Они лучше оптимизированы для хранения больших объемов данных, но их нельзя обновить, только пересоздать с нуля целиком. Их обычно переиндексируют нечасто - раз в сутки, раз в час.
Информация в них устаревает. Вот, что может произойти между индексированиями:
- может быть добавлена новая запись в БД
- может быть изменена запись в БД
- может быть удалена запись из БД
При этом в индексе остаются старые неактуальные данные.
А реалтайм-индексы менее оптимальные для хранения и поиска, но зато их можно менять в реальном времени, с помощью запросов INSERT, UPDATE, DELETE к SQL-интерфейсу сфинкса.
Реалтйм-индексы по моему хранятся в памяти, но бекапятся на диск на случай падения/перезапуска сфинкса и не теряют данные в этой ситуации.
Идея такая: большинство данных хранится в обычных индексах, но объекты, созданные/измененные/удаленные после индексации, сохраняются в реалйтайм индекс до следующей индексации (где они попадут в основной индекс). Важно настроить все так, чтобы данные не терялись ни в каком случае.
Мануал (англ) http://sphinxsearch.com/docs/current/rt-indexes.html
Логика работы с ними такая:
- описываем реалтайм-индекс в конфиге
- при переиндексировании очищаем реалтайм индекс
- в приложении, при создании|удалении|изменении объекта, вставляем его данные в реалтайм-индекс
- при поиске ищем и по обычному, и по реалтайм индексу. Исключаем из основного индекса записи, которые есть в основном индексе, но помечены как удаленные или измененные в реалтайм-индексе (то есть данные по которым в основом индексе устаревшие).
Без реалтайм индексов поиск не будет видеть новые объекты, созданные после индексирования.
> То есть, применительно к задаче на файлообменник, для того чтобы эти индексы заполнялись при добавлении файла, дополнительно к запросу на заполнение полей таблицы нужен еще и запрос для заполнения реалтайм-индекса?
Да.
> Далее, при использовании реалтайм-индекса невозможно сразу получить все поля строки, т.е. если у нас проиндексировано только имя файла, то сфинкс отдаст в виде результата поиска только айдишник(и)
Сфинкс это не база данных, он и не обязан ничего хранить, только искать id записей. Хранение данных только усложнило бы все, например появилась бы проблема их расхождения с данными в БД.
> Или нужно все поля в индекс загонять?
Только те, по которым ведется поиск.
Школа + сам читал учебники + сериалы + сайты + мануалы на англ. В общем, довольно много времени вложено.
>>934039
У тебя $p очень маленький. Попробуй поставить его повыше, например, 257 или 1001, а то там все единицы получились и непонятно, это все правильно, или ошибка.
>>934007
По моему контроллер и есть обработчик запросов, нет? Что-то ты путаешь.
>>933802
У тебя формула расчета странная. Ты по идее должен сделать одно из двух:
- взять число произведенных всеми страниц и потратить на общую сумму зарплат
- либо посчитать среднее из 4 выше рассчитанных значений
То есть формула неправильная. Не надо делить на число департаментов, ибо получается не то.
>>933707
Может там включена подпись кук?
>>933684
Кнопки надо делать тегом button, а не тегом <a>. Ссылка - это то, где есть href указывающий на какую-то страницу.
>>933673
Это что? Пакет PEAR? Тогда тебе надо разобраться, как исользовать PEAR, учти что под линуксом и виндой могут быть различия. В ОП посте есть гайд по командной строке.
Также, по моему, пакеты PEAR можно ставить через композер.
>>933639
MVC хоронят уже не первый раз, как и изобретают его альтернативы: MVVM, MVP и так далее.
>>933496
Проверь. Должен по идее.
Школа + сам читал учебники + сериалы + сайты + мануалы на англ. В общем, довольно много времени вложено.
>>934039
У тебя $p очень маленький. Попробуй поставить его повыше, например, 257 или 1001, а то там все единицы получились и непонятно, это все правильно, или ошибка.
>>934007
По моему контроллер и есть обработчик запросов, нет? Что-то ты путаешь.
>>933802
У тебя формула расчета странная. Ты по идее должен сделать одно из двух:
- взять число произведенных всеми страниц и потратить на общую сумму зарплат
- либо посчитать среднее из 4 выше рассчитанных значений
То есть формула неправильная. Не надо делить на число департаментов, ибо получается не то.
>>933707
Может там включена подпись кук?
>>933684
Кнопки надо делать тегом button, а не тегом <a>. Ссылка - это то, где есть href указывающий на какую-то страницу.
>>933673
Это что? Пакет PEAR? Тогда тебе надо разобраться, как исользовать PEAR, учти что под линуксом и виндой могут быть различия. В ОП посте есть гайд по командной строке.
Также, по моему, пакеты PEAR можно ставить через композер.
>>933639
MVC хоронят уже не первый раз, как и изобретают его альтернативы: MVVM, MVP и так далее.
>>933496
Проверь. Должен по идее.
Спасибо, осознал.
Изначально приложение делалось как тестовое задание на Yii2 для одной компании, но не получилось, было много ошибок. Поэтому я решил его поправить и оформить его как небольшой проект для портфолио и заодно натренироваться в php и yii. Прошу помощи от здешних профессионалов, что с их точки зрения неправильно в моем приложении. Это первое относительно серьезное приложение на yii и вообще на php, поэтому возможен говнокод.
Я получил список багов от тестировщиков компании. Проблема в том что часть из них я не смог отловить, у меня на локальном сервере все нормально работает, все проверятся, все процессы функционируют. Выкладываю список багов ниже, возможно, вы сможете объяснить мне, в чем я не прав, или указать на новые баги.
Проект - https://github.com/kiloman-76/TestMoneyApp
Список возможностей программы - http://pastebin.com/GRSiLBK5
Баги - http://pastebin.com/YEFZsrcd
Спасибо большое, няш.
Например тут: https://martinfowler.com/eaaCatalog/tableDataGateway.html
Ты можешь увидеть, что TDG не обязательно должен работать с объектами, допустимы методы вроде $tdg->update(1, 'Вася', 21);
Датамаппер работает с объектами: https://martinfowler.com/eaaCatalog/dataMapper.html
Там код выше будет выглядеть так:
$user->setName('Вася');
$user->setAge(21);
$mapper->save($user); // Если у $user есть id, то вставляем новые данные, иначе - обновляем старые.
Итого: TDG - более простой, предоставляет только шлюз к БД. Маппер оперирует объектами (маппит объекты на данные БД и наоборот). Почему объекты лучше массивов: объект может понадобиться нам позже в каком-нибудь сервисе (например аутентификации), с объектами проще работать, так как они имеют чёткую структуру (если мы не используем магические свойства и методы), в отличии от массивов.
return возвращает управление программой модулю, из которого была вызвана функция. Выполнение программы продолжается с инструкции, следующей за местом вызова.
Из мануала. Я вообще ничего не понял. И в твоём посте не понял. Я дурак. Умоляю, помоги на примере, как эта return работает. Ты уже показал пример, но я все равно ничего не понял.
>Я уже неделю не могу осилить функции.
А что не понятно. Функция - это подпрограмма в твоей программе. Т.е. вызвая ф-ю в своей программе ты как бы используешь еще одну программу.
>function square($num)
>$num - что это. На примере урока, это квадрат числа, который задается в $num.
Это то, с чем твоя ф-я будет работать. Вот у любом программы есть входные данные и данные которые она возвращает с помощью оператора return. Например функция суммы двух чисел:
function sum($x, $y) {
$sum = $x + $y;
return $sum;
}
Да, только запомни что $sum, например, существует только внутри ф-и.
В данном случае sum это программа, входные данные(аргументы) которой это х и у. А переменная $sum это то, что программа отдает на выходе.
Теперь в своей программе, допустим homework.php тебе понадобилось подсчитать два сложных числа. Соответственно, ты делaешь следующее:
$num1 = 100500;
$num2 = 4564564;
$summa_chisel = sum($num1, $num2);
Вуаля, у тебя в переменной summa_chisel будет сумма переменных нам1 и нам2, ты решил домашку и можешь идти гулять.
>Но сама по себе. Вот эта вот переменная в скобках, она должна существовать до создания функции, или её можно указать впервые в этой скобки.
Да как угодно. Хоть прямо во время вызова ф-и.
>Каким образом при вызове этой функции, и написания в скобках число, переменная понимает, что это число её?
>Как это происходит? Вот мы вызвали функцию, написали в скобках число. Функция просчитала все и выдала ответ. Но как она поняла, что число именно приписывается к $num? >Точнее так. Смотрите. До создании функции, мне надо создать переменную $num?
Это задается при создании ф-и, только это называется определением ф-и. При определении ф-и в данном случае мы указали, что вызываться она должна с двумя аргументами, иначе она вернет тебе ошибку, попробуй на практике.
>Из мануала. Я вообще ничего не понял. И в твоём посте не понял. Я дурак. Умоляю, помоги на примере, как эта return работает. Ты уже показал пример, но я все равно ничего не понял.
Есть у тебя скрипт.
Допустим
function sum($x, $y) {
$sum = $x + $y;
return $sum;
echo "какое-то очень важное сообщение, которое обязаны увидеть все кто использует эту ф-ю";
}
$num1 = 100500;
$num2 = 4564564;
$num1 = 100500;
$num2 = 4564564;
$summa_chisel = sum($num1, $num2);
Твой код читает такая штуковина, которая называется интерпритатор. В данном случае он берет твой пхп скрипт, читает @ выполняет его и возвращает html-код. Так вот. Идет он такой по скрипту, и видит вызов ф-и. Смотрит потом код еще раз, находит ее определение и вызывает. Т.е. как-бы когда он видит ф-ю он входит внутрь нее и выполняет в качестве программы, и когда он видит оператор return он понимает, что ему надо взять то на что оператор указывает(в данном случае он берет $sum), выходит из подпрограммы, (т.е. ф-и) и переходит в программу, в которой был до этого, т.е. идет дальше по скрипту. Соответственно, что там дальше в ф-и его не заботит и пользователь не увидит это важное сообщение. Можешь для практики подумать, что надо сделать что бы пользователь увидел сообщение.
Спасибо, начал изучение
Получается, с оператором return, интерпретатор игнорирует все, что не связано с той переменной, что указано при написании return?
Вот return $sum;. Значит он выполняет только то, где участвует переменная $sum, а остальное игнорирует?
Или он обрывает выполнение функции на команде return, а все, что ниже написано return - игнорируется?
Нет же. Return просто забирает результат каких-то действий и указывает интерпретатору что вот оно, то что требуется вернуть. А что там дальше интерпритатора уже не ебет, после строчки с ретерном он уже не будет читать и выполнять подпрограмму, а до нее ты можешь сколько угодно действий выполнять.
Благодарю. Спасибо. Примерно понял.
Но чтобы добить. На примере этого урока, http://php720.com/lesson/47
Цитирую:
> Т.е в нашем случае, мы посчитали нужное нам выражение, занесли его в переменную $res и вернули через оператор return. Если бы мы этого не сделали, то переменная $z была бы пустая.
А почему бы она была пустая? Исходя из этого, если я хочу получить какой-то результат при выполнении функции, я должен буду всегда в конце функции написать return с указанным аргументом в виде переменной, в которой я хочу получить результат?
>> Т.е в нашем случае, мы посчитали нужное нам выражение, занесли его в переменную $res и вернули через оператор return. Если бы мы этого не сделали, то переменная $z была бы пустая.
Пустая она была бы потому, что без ретерна ф-я бы ничего не возвращала. Т.е. отработала бы в холостую и все. Интерпритатор штука прямая как шпала, понимает всё буквально, соответственно так в коде и надо выражаться.
>А почему бы она была пустая? Исходя из этого, если я хочу получить какой-то результат при выполнении функции, я должен буду всегда в конце функции написать return с указанным аргументом в виде переменной, в которой я хочу получить результат?
Ну да. Только не обязательно в ретерне отдавать переменную, можно что угодно - массив, объект или прямо на месте делать вычисление, т.е. можно даже написать return $x + $y в данном случае. Вообще ф-я может и не возвращать ничего, попробуй вместо ретерна написать echo, вызывать не для присваивания а просто так. Такие вещи вообще лучше всего на практике изучать.
return это команда выхода из функции (завершить выполнение подпрограммы). Ее можно использовать в 2 формах:
return; - выйти из функции и вернуть null
return значение; - выйти из функции и вернуть указанное значение. Это может быть число, строка, переменная, выражение.
return прекращает выполнение кода в функциии и возвращает управление к месту, откуда была вызвана функция.
Если return не ставить, то код функции выполняется до конца.
нет. return завершает выполнение функции и потому код после него не выполняется. Переменная тут не при чем.
Ты по моему пропустил самый первый, и самый важный урок - что такое программа. Программа это последовательность команд. Интерпретатор PHP по умолчанию просто выполняет записанные в программе команды последовательно, сверху вниз.
Но некоторые команды меняют порядок выполнения программы. Например, if позволяет не выполнять команды внутри него при опредленном условии, for позволяет выполнить одни и те же команды несколько раз подряд, а функции позволяют создать подпрограмму и позже ее вызывать. Подпрограмму можно вызвать, и из нее можно выйти раньше времени командой return.
Соответственно переменная тут не при чем. Просто return завершает выполнение функиции и команды ниже уже не выполняются.
У меня ощущение, что ты куда-то спешишь и пропускаешь изучение основ. И из-за этого не понимаешь более сложные темы. Я бы советовал взять учебник в ОП посте и прорешать там все задачи подряд, чтобы лучше разобраться, как вообще работают прогаммы, какие есть команды итд.
Если не вернуть из функции какое-то значение явно командой return, то она вернет значение по умолчанию - null. И в переменную сохранится этот null. В том учебнике это называют "переменная будет пустая".
Это программа-клиент для MySQL для опытных программистов, которые в совершенстве знают SQL. Начинающему надо писать запросы руками в консоли, чтобы запомнить синтаксис и научиться получать нужные данные.
>для опытных программистов
Опытные юзают HeidiSQL или Workbench. Phpmyadmin как раз для нубов с вордпрессами, там тебе ни табов, ни сохранения запросов, ни биндинга, да, скорость черепашья и ошибки через одну валятся. Рекомендуют ее застрявшие в 90х старперы, не освоившие новых инструментов.
Битриксы и вордпрессы в жопу, они для криворуких, учи нормальные фреймворки - Symphony, Yii, Laravel. Из cms можно Cake Php, typo3, для магазинов PrestaShop. Если фреймворк освоил, то тебе в принципе CMS и не нужен, можешь на фреймворке все то же самое быстро и легко написать, возможностей и контроля больше.
А как же требования? Поддержка и написание функционала к магазинчикам по продаже дилдаков? Фреймворк однозначно стоит брать, но пока я его буду учить у меня кончатся все дошики и я помру с голода, а так устроиться хотябы куда нибудь
Я думаю, можно уже показать: https://github.com/greenTea242/MinesweeperMVC . Посмотри, пожалуйста. Пока что не стал всякие angular, knockout делать, попроще реализацию выбрал из задачи. Несколько вопросов:
Это нормально что я работаю со словарями в таком стиле: dict1.prototype.merge(dict2) изменит dict1 и ничего не возвращает. По другому будет Dict.merge(dict1, dict2) - вернется новый словарь, dict1 не изменится.
Что делать с символом единицы в верстке? Объясню. Если приглядеться, немножко кривые табло флагов и времени. Потому что в css файле я сдвинул span.digit через text-align вправо чтобы получился стиль калькулятора (ты поймешь о чем я, как увидишь). Может двигать только символ единицы?
Можно ли сделать dataset данные (которые цифры) в опциях зависимыми от моих констант? А то можно ошибиться.
Ну и конечно я хочу услышать твое мнение на счет Popup V/C. Не очень правильно реализовал, наверное, тяжеловато приходит понимание MVC. Для каждой менюшки, если по правильному, тоже нужно делать V/C?
А что код на github pages не выложишь, чтобы сразу смотреть можно было?
> dict1.prototype.merge(dict2)
наверно dict1.merge(dict2)?
> По другому будет Dict.merge(dict1, dict2)
Почему? Можно var d = d1.merge(d2);
Вообще, есть 2 подхода:
- создается и возвращается новый объект
- модифицируется существующий
В разных ситуациях они имеют свои преимущества и недостатки.
В Руби для различения таких методов тем, которые модифицируют объект, добавляют в имя символ !, например dict1.merge!(dict2)
> Для каждой менюшки, если по правильному, тоже нужно делать V/C?
Логичнее сделать 1 конфигурируемый виджет меню (V/C), и в него передавать структуру меню.
> https://github.com/greenTea242/MinesweeperMVC/blob/master/public/minesweeper.html#L60
> Object.assign(MinesweeperGame.prototype, EventDispatcher.prototype);
Почему это не в фйле, где описан Minesweeper?Ты может и методы класса по нескольким файлам разбросаешь?
Ну и в общем, мне не нравится такой миксин, так как тут нет никакой защиты от того, что в 2 классах могут оказаться поля с одинаковыми именами. В реальном большом проекте с несколькими разработчиками такие конфликты будут, и замучаешься их распутывать. Один человек добавил метод в EventDispatcher и где-то в другом месте что-то сломалось.
PopupController ты реализовал нехорошо, на мой взгляд. Я тебе предложил идею сделать независимый виджет попапа, который ничего не знает про игру, а умеет только показывать/скрывать попап и сообщать о нажатии кнопок в нем. Но ты зачем-то отказался от идея разделения ответственности и влепил в контроллер попапа обращение к модели игры:
> this._parentView.getMinesweeperGame().reset();
Вдобавок ты еще и нагрузил View лишними обязанностями. Задача View - отображать данные, а не предоставлять всем желающим ссылку на объект модели. Однозначно у View не должно быть публичного метода getMinesweeperGame.
То же касается метода DomGameController.prototype.getMinesweeperGame(). Предоставлять ссылку на модель не входит в задачи контроллера.
То, как сделан вывод тут ConsoleGameView.js показывает, что у модели не очень удобный интерфейс предоставления данных:
> var visibleMines = this._minesweeperGame.getVisibleMines();
...
По моему так лучше сделать либо метод, дающий информацию по конкретной клетке:
game.hasMine(x, y)
Либо дающий объект клетки:
game.getCell(x, y).hasMine();
Это внутреннее знание модели, как именно хранится информация о клеточках. А ты ее выставляешь наружу и предлагаешь всем самостоятельно копаться в каких-то внутренних хранилищах.
Тот интерфейс, что я предложил, как мне кажется, лучше скрывает информацию о хранении данных внутри модели и при рефакторинге меньше придется менять.
> var spacesCounter = 3 - Util.getNumberLength(y);
> var pad = Util.repeatString(spacesCounter, " ");
Можно объединить в функцию padLeft()
> this._minesweeperGame.addEventListener(GameEvent.CELL_CHANGED
> this._minesweeperGame.addEventListener(GameEvent.SEVERAL_CELLS_CHANGED,
Только второго события по моему достаточно. Первое избыточно.
> var template = document.querySelector(".template-popup");
> this._popupView = new PopupView(this, template.innerHTML, message);
Шаблон попапа наверно должен получаться в PopupView, а не снаружи. То есть это только PopupView знает, где взять шаблон.
removePopup тоже должен быть частью PopupView. Как иначе его повторно использовать, везде копировать этот код?
Попап выгоднее центрировать средствами CSS.
> Dictionary
Правильнее назвать CellDictionary или CellList как-то так, это ведь не словарь для любых объектов. А, вспомнил! CellSet. Возможно, что Set - это как раз та структура данных, которая тут нужна: https://tproger.ru/translations/sets-for-beginners/
Set (множество) - это такая структура, куда можно добавлять данные и потом проверять, есть они там или нет.
И кстати, в новом JS они есть (точнее будут): https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Set - просто, чтобы ты знал.
Это я мельком глянул, может потом напишу подробнее.
А что код на github pages не выложишь, чтобы сразу смотреть можно было?
> dict1.prototype.merge(dict2)
наверно dict1.merge(dict2)?
> По другому будет Dict.merge(dict1, dict2)
Почему? Можно var d = d1.merge(d2);
Вообще, есть 2 подхода:
- создается и возвращается новый объект
- модифицируется существующий
В разных ситуациях они имеют свои преимущества и недостатки.
В Руби для различения таких методов тем, которые модифицируют объект, добавляют в имя символ !, например dict1.merge!(dict2)
> Для каждой менюшки, если по правильному, тоже нужно делать V/C?
Логичнее сделать 1 конфигурируемый виджет меню (V/C), и в него передавать структуру меню.
> https://github.com/greenTea242/MinesweeperMVC/blob/master/public/minesweeper.html#L60
> Object.assign(MinesweeperGame.prototype, EventDispatcher.prototype);
Почему это не в фйле, где описан Minesweeper?Ты может и методы класса по нескольким файлам разбросаешь?
Ну и в общем, мне не нравится такой миксин, так как тут нет никакой защиты от того, что в 2 классах могут оказаться поля с одинаковыми именами. В реальном большом проекте с несколькими разработчиками такие конфликты будут, и замучаешься их распутывать. Один человек добавил метод в EventDispatcher и где-то в другом месте что-то сломалось.
PopupController ты реализовал нехорошо, на мой взгляд. Я тебе предложил идею сделать независимый виджет попапа, который ничего не знает про игру, а умеет только показывать/скрывать попап и сообщать о нажатии кнопок в нем. Но ты зачем-то отказался от идея разделения ответственности и влепил в контроллер попапа обращение к модели игры:
> this._parentView.getMinesweeperGame().reset();
Вдобавок ты еще и нагрузил View лишними обязанностями. Задача View - отображать данные, а не предоставлять всем желающим ссылку на объект модели. Однозначно у View не должно быть публичного метода getMinesweeperGame.
То же касается метода DomGameController.prototype.getMinesweeperGame(). Предоставлять ссылку на модель не входит в задачи контроллера.
То, как сделан вывод тут ConsoleGameView.js показывает, что у модели не очень удобный интерфейс предоставления данных:
> var visibleMines = this._minesweeperGame.getVisibleMines();
...
По моему так лучше сделать либо метод, дающий информацию по конкретной клетке:
game.hasMine(x, y)
Либо дающий объект клетки:
game.getCell(x, y).hasMine();
Это внутреннее знание модели, как именно хранится информация о клеточках. А ты ее выставляешь наружу и предлагаешь всем самостоятельно копаться в каких-то внутренних хранилищах.
Тот интерфейс, что я предложил, как мне кажется, лучше скрывает информацию о хранении данных внутри модели и при рефакторинге меньше придется менять.
> var spacesCounter = 3 - Util.getNumberLength(y);
> var pad = Util.repeatString(spacesCounter, " ");
Можно объединить в функцию padLeft()
> this._minesweeperGame.addEventListener(GameEvent.CELL_CHANGED
> this._minesweeperGame.addEventListener(GameEvent.SEVERAL_CELLS_CHANGED,
Только второго события по моему достаточно. Первое избыточно.
> var template = document.querySelector(".template-popup");
> this._popupView = new PopupView(this, template.innerHTML, message);
Шаблон попапа наверно должен получаться в PopupView, а не снаружи. То есть это только PopupView знает, где взять шаблон.
removePopup тоже должен быть частью PopupView. Как иначе его повторно использовать, везде копировать этот код?
Попап выгоднее центрировать средствами CSS.
> Dictionary
Правильнее назвать CellDictionary или CellList как-то так, это ведь не словарь для любых объектов. А, вспомнил! CellSet. Возможно, что Set - это как раз та структура данных, которая тут нужна: https://tproger.ru/translations/sets-for-beginners/
Set (множество) - это такая структура, куда можно добавлять данные и потом проверять, есть они там или нет.
И кстати, в новом JS они есть (точнее будут): https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Set - просто, чтобы ты знал.
Это я мельком глянул, может потом напишу подробнее.
В ideone и прочих онлайн компиляторах выводит табличку корректно.
работает, спасибо. А в чем фишка ?
И еще вопрос. Как подружить апач с расширением mbstring ?
Если юзаю встроенный пхп сервер - работают скрипты нормально. Когда запускаю на апаче - выдает Fatal error: Call to undefined function mb_strlen().
Content-type броузеру отрпавляешь? Если нет он вырезает лишние пробелы, а онлайн компиляторы транслируют тебе вывод консоли "как есть".
>работает, спасибо. А в чем фишка ?
Ну типа, обычные символ пробела может по разному интерпритироваться разными браузерами, или что-то там. А неразрывный проблем это везде неразрывный пробел.
ideone показывает тебе выведенный текст как есть.
Браузер по умолчанию воспринимает то, что выведет программа, как HTML. В языке HTML любое число пробелов равносильно 1 пробелу.
Чтобы переносы строк и пробелы нормально работали и в браузере и в ideone (и в консоли), можно в начале программы поставить
header("Content-Type: text/plain; charset=utf-8");
Это заставит браузер воспринимать то, что выводит твоя программа, как обычный текст, а не HTML, и уважать пробелы и переносы строк в нем.
Ты имеешь в виду REPL? Да, для PHP он есть встроенный, запускать через php -a в консоли. Если хочешь продвинутый автокомплит с историей, то погугли PsySH.
Господа, еле откопал тред. Кто-нибудь пытался прописывать куки в CodeIgniter?? Почему, если я ставлю куку, то она сохраняется только в пределах одного контроллера??
А еще, если я пишу проверку на куку в каждом методе контроллера типа
if(!isset($cookie)){
redirect('/index/', 'refresh');
}
$post;
то меня при обновлении страницы (например, методом post из формы) выкидывает на главную страницу, т.е. кука удаляется? втф? при этом если страницу не обновлять, то кука типа хранится, но как тогда обновлять страницу не удаляя куку?
То есть нотисы игнорируются. Если, к примеру, опечататься в имени переменной то PHP в конфигурации по умолчанию ничего не напишет. Ищи потом, где ошибка.
Потому я вынужден в первом уроке в первой программе ставить этот дурацкий error_reporting(-1), хотя без него программа бы смотрелась и короче и логичнее. И начинающим приходится отвлекаться на изучение каких-то магических заклинаний, которые они толком не поймут.
Почему нельзя поставить нормальные настройки по умолчанию? Почему нельзя выкинуть все эти варнинги и перейти на исключения?
В общем, пока убрать это не получится, придется потерпеть. Привыкайте с самого начала, что в работе PHP разработчика не все медом намазано.
Я вообще первым языком Си учил, там для хеллоуворлда от новичка требуется гораздо больше заклинаний, не говоря уже о том, что сишка очень низкоуровневая по сравнению с PHP. С PHP новичок может уже в первый день написать что-то интересное, сишка радушно встречает новичка конструкциями fscanf(stdin, "%d", &array_size) и новичку придётся разбираться с указателями, если он хочется продвинуться хотя бы дальше циклов.
Ну и сейчас таки в PHP варнинги понемногу заменяют на исключения. Вот если новичок первым языком JS учит, то это плохо, будет в голове каша из функций как объектов и объектов как ассоциативных массивов, не говоря уже о конвертации типов. Так что error_reporting, как по мне, мелочь. Я когда PHP изучал по учебнику с анимешными картинками, то не придал error_reporting'у никакого значения, просто добавил бездумно.
>>937429
В общем начал переделывать код.
Класс DB нужен именно для те писать того чтобы не писать консрукцию $pdo = new PDO вместе с [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION], но в общем можно убрать позже.
не совсем понятно как bootstrap-файл поможет (настроить автозагрузчик, обработку ошибок, создать какие-то объекты)
То есть если перефразировать то, что ты сказал, то получается, что класс DB нужен ради единственной функции в нем, которая создает объект PDO.
Но тогда напрашивается вопрос, во-первых, почему тогда его не назвать PDoFactory, во-вторых, а почему для других классов такого не сделано? То есть класс, создающий объекты Abiturient, класс создающий объекты AbiturientDataGAteway, объекты PageLinker и так далее?
Пока у меня ощущение, что ты просто скопировал эту идею из какого-то урока, не особо задумываясь.
Предлагаю почитать для начала, как другие люди решали проблемы зависимостей объектов и их создания: https://github.com/codedokode/pasta/blob/master/arch/di.md
Поставил PhpStorm и xampp, потом столкнулся с проблемой " interpreter not configured phpstorm
Все сделал в точь точь, но нихуя не работает код.
https://www.youtube.com/watch?v=J-jn6yi3x0c
Что делать?
без вааас не справлюсь((
Опять надо клещи доставать. Поясни, что значит "не работает"? Что ты делаешь, что ожидаешь увидеть, что видишь? Если ты что-то открываешь в браузере, напиши какой URL в адресной строке, если запускаешь код, запости куда-нибудь его.
Также, если ты используешь веб-сервер, посмотри его логи ошибок. Для Апача под виндой они хранятся где-то в его папке, в линуксе в /var/log.
Видео пересматривать целиком и угадывать, где именно ты ошибся, это не вариант.
Я просто хочу запустить код, чтоб код отображался на экране, а я вижу 502x
В настройках я поставил все по дефолту как на видео.
А окошко шторма с настройками веб-сервера?
У тебя показано окно CLI interpreters, интерпретатор для запуска в командной строке.
И что с логами? Для 502 ошибки в логе веб-сервера должна быть запись с пояснениями. Попробуй сам найти, где этот лог, если знаешь, если нет, я потом погуглю. Я пока что понятия не имею, какойв шторме использован веб-сервер и как он настроен.
Я у тебя там вижу упоминание xampp установленного на диск c:, поищи логи в его папке и посмотри есть ли там что-нибудь интересное.
Ну серьезно, мне и в винде неплохо. Может быть я не знаю чего-то?
$news = \App\Models\Article::getAll();
Соответственно Article - это модель, которая наследуется от базовой абстрактной модели, где эти общие для моделей методы написаны. Например там же можно сделать манипуляции с этими данными, заменить что-то в тексте и т.д. И добавляем обращение ко view.
Но читаю мануалы по Symfony, там рекомендуют в контроллере делать обращение к доктрине типа
$em = $this->getDoctrine()->getManager();
$news = $em->getRepository('AppBundle:Article')->findAll();
Все работает, но если я допустим хочу к с этой выборкой предварительно сделать что-то до выведения в шаблон, например закешировать текст новости, в котором существуют markdown теги, то мне это где правильнее сделать?
Я бы сам ответил что в Entity, предварительно сделав родительский класс. Так как контроллер вроде как не должен с данными работать. Но скачал проект от них же с типа рекомендованным подходом https://github.com/symfony/symfony-demo и там вообще не подобного кода в моделях и они ни от чего не наследуются. И я хз как делать в итоге.
Короче объясните, куда правильнее добавлять код для манипуляции с данными?
>Короче объясните, куда правильнее добавлять код для манипуляции с данными?
В репозиторий. Когда ты создаешь entity через консоль, она тебе автоматически генерит пустой repository в той же папке Entity. Туда ты записываешь все необходимые тебе методы работы с данными, для этого у доктрины есть прекрасный QueryBuilder. Потом в контроллере ты можешь получить репозиторий в отдельную переменную через ту же конструкцию $this->getDoctrine()->getRepository() и обращаться уже к тем методам, которым ты сам написал в нем. Встроенные методы типа findAll() и findBy() будут так же доступны.
В твоих сущностях в идеале не должно быть методов работы с данными, это все работа доктрины.
<VirtualHost *:80>
# Имя сервера которое обслуживает этот VirtualHost
ServerName example.com
# Корневая папка сервера
DocumentRoot /var/www/example.com/public
# ....
это же все мои сайты на локалке заданет.
Разобралдся, оказывается я что-то не так делал, раз сначала оно у меня на все сайты влияло. Однако теперь из-за этой хуиты отвалился .htacces. Прописал туда же AllowOverride All - сервер вообще перстал запускаться. ЧЯДНТ? Может мне лучше не париться, а просто через .htacces такое делать?
Гугли apache virtual hosts по своему дистру, в мануалах все подробно описано. Если ты обмазался шпермой, то ничем помочь не могу.
Да я то все понял. Осталось только разобраться почему .htacces не работает, хотя вроде как должно.
>Если ты обмазался шпермой, то ничем помочь не могу.
А какая разница? Ну кроме как того, что в шинде и красноглазиксах по разному директории пишутся.
<VirtualHost :80>
# Имя сервера которое обслуживает этот VirtualHost
ServerName students.local
# Корневая папка сервера
DocumentRoot путь\public
<Directory "путь">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.)$ index.php [QSA]
AddDefaultCharset UTF-8
</Directory>
</VirtualHost>
И работает. Однако все равно хотелось бы узнать, как заставить в данном случае исполнять директивы из .htacces, так по крайне мере удобней и не надо лезть в конфиги апача если что понадобится.
Спасибо!
ну в общем я его действительно скопировал и хотел в итоге убрать, но new PDO не принимает больше 4 параметров, то бишь не получалось записать new PDO(host,dbname,user,pass,[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION], я позже уберу этот метод.
еще хотел спросить про роутинг, как в мой метод run() в Router добавить 404 error, попробовал сделать такое если не существует вызываемого класса или метода, но выдает ошибку Cannot modify header information
function ErrorPage404()
{
$host = 'http://'.$_SERVER['HTTP_HOST'].'/';
header('HTTP/1.1 404 Not Found');
header("Status: 404 Not Found");
header('Location:'.$host.'404');
}
}
еще хотел спросить как мне сделать view для подготовки данных на вывод, может в гайде было, перечитаю еще раз
>А окошко шторма с настройками веб-сервера?
>У тебя показано окно CLI interpreters, интерпретатор для запуска в командной строке.
Я может слепой, но нигде не нашел окно CLI interpreters
Кодировку сменить?
Спасибо, не надо, я уже настрадался с этой хуйней. Принесешь курсач на этой херне преподу на флешке - либо все настройки разметки слетят и весь текст поедет, либо вообще не откроется файл.
И либре офис, и опенОфис. Пока пересилил лень и не поставил проксю, зашел на рутрекер и не купил там Мелкософт Офисе2013. Не нарадуюсь.
Пидор не пидор, а сдавать ему предмет с курсачем и дипломом. У них там везде 2007 седьмого года офис стоит на спермерках.
Я даже со своим 2013 испытываю проблемы на их 2007, иногда приходится у них вручную текст доводить. Пиздец в общем.
It hurts to be you. Попробуй нетбинс или эклипс что-ли, и воровать не надо.
Потому что ты, судя по всему, скачал версию для продакшена.
У меня, в ampps включены ошибки и различные предупреждения.
Вообще не разбираюсь в этом.
Да разобрался, то я каким-то образом умудрился нагадить в конфиги апача. Алсо, раз уж такие пляски, поясните - когда надо прописывать всякие директивы в .htacces, а когда лучше в конфиг сервера лезть?
Не выбирай там ничего, жми далее. Тут идешка предлагает тебе сразу строить проект на одном из фреймворков, тебе оно не надо.
Преимущество htaccess в том что он распространяется вместе с проектом и другому человеку не надо в настройки сервера что-то вставлять. При изменении настроек не надо перезапускать сервер.
Недостатки в том, что он поддерживается только Апачом, настройки в конфиге парсятся 1 раз, а не на каждый запрос, и их нельзя изменить без прав администратора (взломщикам чуть труднее навредить получается).
Отлично. Извини пожалуйста, но у меня тут проблема. Как запустить свою порнографию?
В первый раз при нажатии этой кнопки мне вылезло окошко с выбором php файла. Ну я и выбрал без задней мысли тот, на котором у меня все это написано. А теперь и найти это окно не могу.
А ты сделал проект? Какой у тебя софт в качестве локального сервера установлен? Открывать как обычный сайт по ссылке.
У меня просто строки кода с примерами выполнения функций, уроков, переменных.
В общем все то, что полные нубы тут пишут на Ideone. Я просто решил сохранять все, и вести одним большим файлом все то, что делаю.
Ну для исполнения пхп-скриптов тебе нужен сервер, который будет выполнять скрипты эмулируя веб-сервер, у оп-а в уроках это было. Можешь поставить какой-нибудь xamp, или вообще open server - готовая сборка, в которой все работает искаропки. Но для понимания наверное лучше сделать как у ОПа в уроках.
Это не ответ на мой ответ. Не срача ради, приведи аргументы для использования нгинкса вместо апача.
Понял, спасибо.
Опен сервер у меня уже стоит, xamp стоял, но удалил - сложна. Но поставлю обратно, вернусь к урокам опа.
Сам не знаю, но много народу использует и энгинкс.
>А Error: document root is not specified or invalid
Что у тебя там стоит?можешь скинуть или показать какие должны стоять дефолтные настройки там, уже жалею что перешел на пхпшторм
Там должен быть путь к исполняеямому файлу php(экзешнику, ёба). В прыщах это /usr/bin/php, в сперме надо задать путь к тому что ты там себе понаставил.
А стоп, я бухой и проебался. DocumentRoot это абсолютный путь к корню твоего сайта.
Красноглазый называет кого-то пердольщиком.
>>939638
Пиздос. У тебя в конфиге сервака прописан документ рут(нгинкс), как там в апаче я не знаю в котором серв ищет индексный файл и вообще считает эту папку рутом кто бы блять мог подумать. Укажи этот рут в шторме.
Ну так шперохлёбы пердольщики и есть. На линуксах всё подробно прописано в мануалах и вопросов что куда писать просто не возникает.
Может быть я чего-то не понимаю, но о каком пердолингк идет речь? Я являюсь почетным спермобоярином и обладаю виртуальной машиной с прыщами. Ни в первом, ни во втором случае вопросов по установке пыхи/нгинкса/мускла не возникало. Я просто не понимаю о чем идет речь, когда кто-то говорит, что у него проблемы с настройкой/установкой на винде.
Конечно не мог, ведь в интернетах кто-то неправ.
Пожалуйста, для начала прочитайте урок про то, что такое веб-сервер и зачем он нужен: https://github.com/codedokode/pasta/blob/master/soft/web-server.md
Если рассматривать PHP и Апач, то есть ровно 2 конфигурации:
- запускать PHP в режиме сервера, как описано в моем уроке. Информация об ошибках при этом пишется в консоль, где запущен сервер
- установить Апач, настроить в нем использование PHP, немного описано тут: https://github.com/codedokode/pasta/blob/master/soft/apache-install.md . Информация об ошибках пишется при этом в лог-файл Апача.
PhpStrorm не предлагает ничего нового, и не содержит в себе какого-то своего веб-сервера, он может делать лишь 2 вещи:
- запустить один из вариантов веб-сервера
- открыть в браузере страницу, которая будет загружена с запущенного веб-сервера
И настройки в PhpStorm как раз задают, какой вариант вы хотите использовать, где находится корневая папка сервера, где находится exe-файл PHP или Апача. То есть PhpStorm просто за вас может запустить сервер и открыть страницу в браузере. Если вы правильно пропишете настройки.
Обратите внимание, что задачи из учебника можно запускать также в консоли (командной строке). Сервер тут не требуется, так как мы запускаем интерпретатор PHP и наш скрипт напрямую, не используя браузер и веб-сервер. В PhpStrom встроена консоль, и вы можете настроить, чтобы по нажатию кнопки он запускал бы вашу программу и результат отображался в этой встроенной консоли. Так даже может быть проще для наичнающего.
Чтобы это сделать, вам надо сделать 2 вещи:
- указать путь к интерпретатору PHP
- настроить, чтобы phpstorm запускал ваш скрипт в режиме командной строки (CLI)
Как вручную запустить скрипт в командной строке, описано тут в конце: https://github.com/codedokode/pasta/blob/master/soft/php-install.md
Пожалуйста, ознакомьтесь с теорией, выберите вариант, который вам подходит, проверьте что у вас все установлено и настроено (например попробуйте запустить скрипт без phpstrom, руками) и если после этого вам что-то не понятно или не работает, опишиет вашу проблему подробно, что как настроено, что в логе ошибок и тд.
Пожалуйста, для начала прочитайте урок про то, что такое веб-сервер и зачем он нужен: https://github.com/codedokode/pasta/blob/master/soft/web-server.md
Если рассматривать PHP и Апач, то есть ровно 2 конфигурации:
- запускать PHP в режиме сервера, как описано в моем уроке. Информация об ошибках при этом пишется в консоль, где запущен сервер
- установить Апач, настроить в нем использование PHP, немного описано тут: https://github.com/codedokode/pasta/blob/master/soft/apache-install.md . Информация об ошибках пишется при этом в лог-файл Апача.
PhpStrorm не предлагает ничего нового, и не содержит в себе какого-то своего веб-сервера, он может делать лишь 2 вещи:
- запустить один из вариантов веб-сервера
- открыть в браузере страницу, которая будет загружена с запущенного веб-сервера
И настройки в PhpStorm как раз задают, какой вариант вы хотите использовать, где находится корневая папка сервера, где находится exe-файл PHP или Апача. То есть PhpStorm просто за вас может запустить сервер и открыть страницу в браузере. Если вы правильно пропишете настройки.
Обратите внимание, что задачи из учебника можно запускать также в консоли (командной строке). Сервер тут не требуется, так как мы запускаем интерпретатор PHP и наш скрипт напрямую, не используя браузер и веб-сервер. В PhpStrom встроена консоль, и вы можете настроить, чтобы по нажатию кнопки он запускал бы вашу программу и результат отображался в этой встроенной консоли. Так даже может быть проще для наичнающего.
Чтобы это сделать, вам надо сделать 2 вещи:
- указать путь к интерпретатору PHP
- настроить, чтобы phpstorm запускал ваш скрипт в режиме командной строки (CLI)
Как вручную запустить скрипт в командной строке, описано тут в конце: https://github.com/codedokode/pasta/blob/master/soft/php-install.md
Пожалуйста, ознакомьтесь с теорией, выберите вариант, который вам подходит, проверьте что у вас все установлено и настроено (например попробуйте запустить скрипт без phpstrom, руками) и если после этого вам что-то не понятно или не работает, опишиет вашу проблему подробно, что как настроено, что в логе ошибок и тд.
ОПчик, если у тебя когда будет кризис идей - знай, что есть как минимум один анон которому было бы интересно почитать уроки по настройке апача, нгинкса, про .htacces и подобное.
А уроки по ссылкам ты все прочел? По Апачу согласен, целостного урока нет, но если знаком с английским, есть объемная документация на оф. сайте.
По PHP - у меня есть же урок про установку. Остальное в оф мануале, в том числе на русском.
По нгинксу документация на русском потому что его делает наш соотечественник.
Документация на апач то обширная, но мне скорее хотелось бы понимать что надо знать и уметь обязательно, дабы не обосраться в неожиданной ситуации, мало ли когда оно вкатывальщику понадобится.
ОП, если ты конечно ОП. Зачем ты что-то объясняешь днищам, которые даже конфигурацию апача осилить не могут, в то время как незабаненные в гугле товарищи эти проблемы решают по щелчку пальцев буквально?
AllowOverride All добавь.
Туториалов по настройке апача и .htaccess море, доку можно не читать. Она правда неудобная и нужна только если что конкретное ищешь. Если в гугле не забанен, то за минуту штук 100 нормальных туториалов находится, я хз чего ты от ОПа еще нового хочешь, когда уже во всех подробностях расписано.
>Если в гугле не забанен, то за минуту штук 100 нормальных туториалов находится, я хз чего ты от ОПа еще нового хочешь, когда уже во всех подробностях расписано.
Читать не умеешь? Я хочу понимать что надо обязательно знать и уметь вкатывальщику, а что в процессе джуниорства можно освоить.
Все так делают.
Лучше прочесть туториал, а потом команды из него посмотреть в документации.
>>939788
С гуглом не все так просто. Не везде объяснят что такое веб-сервер, как он работает. Часто просто дают конфиг который надо бездумно скопипастить и пользы от таких статей ноль.
>>939638
DocumentRoot это путь к корню сайта, где хранятся скрипты и файлы. Прочти это >>939743
>>939628
Веб-сервер либо не запущен, либо запущен на каком-то нестандартном порту и он не указан в URL. Прочти это сначала >>939743
>>939622
Нгинкс для продакшен-сайтов, а на компьютере разработчика он только усложняет все. Удобнее обычно либо Апач, либо встроенный в PHP веб-сервер.
>>939618
Прочитай сначала >>939743
>>939610
Тебе надо открыть докуменацию по этому окну и посмтреть, что там какая опция обозначает, так как это неочевидно.
>>939591
Это диалог для запуска композера и установки через него примера приложения от фреймворка. Тебе это не нужно. Как минимум тебе надо сначала изучить композер и нужный фреймворк, прежде чем использовать эту опцию.
Лучше прочесть туториал, а потом команды из него посмотреть в документации.
>>939788
С гуглом не все так просто. Не везде объяснят что такое веб-сервер, как он работает. Часто просто дают конфиг который надо бездумно скопипастить и пользы от таких статей ноль.
>>939638
DocumentRoot это путь к корню сайта, где хранятся скрипты и файлы. Прочти это >>939743
>>939628
Веб-сервер либо не запущен, либо запущен на каком-то нестандартном порту и он не указан в URL. Прочти это сначала >>939743
>>939622
Нгинкс для продакшен-сайтов, а на компьютере разработчика он только усложняет все. Удобнее обычно либо Апач, либо встроенный в PHP веб-сервер.
>>939618
Прочитай сначала >>939743
>>939610
Тебе надо открыть докуменацию по этому окну и посмтреть, что там какая опция обозначает, так как это неочевидно.
>>939591
Это диалог для запуска композера и установки через него примера приложения от фреймворка. Тебе это не нужно. Как минимум тебе надо сначала изучить композер и нужный фреймворк, прежде чем использовать эту опцию.
Я говорю про настройку по умолчанию, заложенную в PHP. При чем тут сборка ampps, в которую входит PHP? Если люди мой код будут запускать на ideone, то там ведь будут применяться настройки по умолчанию скорее всего.
И у PHP нет отдельных версий для продакшена и для разработки.
>>939566
>>939563
Можно в PDF сохранить. Ну или сохранить в опеноффисе в формате какого-нибудь старого Word 2003 и скорее всего все откроется.
Как это надо форматировать текст, чтобы оформление слетело? Ты наверно используешь каике-то фичи нестандартные просто.
>>939550
Из твоего описания непонятно, в чем проблема. Где буквы выводятся как вопросительные знаки? Что ты сделал? Скорее всего PhpStorm тут вообще не при чем. Ну и на всякий случай глянь ссылки тут, может что есть: >>939743
Обычно надо настроить везде utf-8: кодировку исходного кода в редакторе, тег meta charset, если используется браузер.
Вообще, вопросы по PhpStorm логичнее было бы адресовать в их техподдержку.
>>939546
Вот на этом твоем скришоте в заголовке написано CLI: https://2ch.hk/pr/src/919074/14878266574851.png (М)
> еще хотел спросить про роутинг, как в мой метод run() в Router добавить 404 error, попробовал сделать такое если не существует вызываемого класса или метода, но выдает ошибку Cannot modify header information
Прочти про протокол HTTP тут например https://github.com/codedokode/pasta/blob/master/soft/web-server.md
Прочти мануал по функции header().
Там в ответе сначала идут заголовки, потом тело ответа. Если ты вывел хоть один символ, хоть один пробел, то заголовки отравлять уже нельзя. В мануале это написано.
Некоторые делают ошибку, сохраняя файлы в кодировке utf-8 с BOM. BOM это невидимый символ, который вставляется в начало документа до <?php, выводится интерпретатором PHP и не позволяет отправлять заголовки.
Проверь, нет ли у тебя файлов с BOM или файлов с пробелом или еще чем-то, что выводится до заголовков.
Также, у тебя неправильный код. Почитай про пртокол HTTP. Почитай, что такое редирект. Если ты используешь Location то PHP автоматически меняет код статуса ответа на 3xx вместо 404. Соответственно это уже получаеся не страница 404, а что-то другое.
Ты как-то пропускаешь темы. Ну как можно использовать редирект, не изучив, что такое HTTP, как это работает, как его обрабатывает браузер итд. Надо изучать теорию, что происходит при вызове той или иной функции.
> еще хотел спросить как мне сделать view для подготовки данных на вывод, может в гайде было, перечитаю еще раз
Почитай это: http://www.phpinfo.su/articles/practice/shablony_v_php.html
>>939498
AllowOverride должно быть достаточно. Может, там где-то стоит запрещающий AllowOverride, или ты не перезапустил Апач поле правки конфига, или ты опечатался в названии файла .htaccess
>>939480
Если не можешь помочь, то не надо. Почитай второй пост. У нас тут не тред для хвастовства, у кого глаза краснее.
Ну и мне тоже есть чем похвастаться. Я например, всего за 2 вечера смог пропатчить и скопилировать PHP7 под Windows XP (правда немного костыльно и пока без расширений). Уверен, что в линуксе, даже в какой-нибудь генте с такими задачами сталкиваться не приходится.
>>939236
> $news = \App\Models\Article::getAll();
В Доктрине используется паттерн Data Mapper, а не Active Record. Тебе надо сначала изучить теорию про паттерны работы с БД: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Затем изучить документацию Доктрины внимательно, так как без ее хорошего знания в Симфони ты не разберешься.
> Все работает, но если я допустим хочу к с этой выборкой предварительно сделать что-то до выведения в шаблон, например закешировать текст новости, в котором существуют markdown теги, то мне это где правильнее сделать?
Правильно сделать сервис получения новостей, в нем настроить кеширование, а в контроллере вызвать его:
$news = $newsService->getLatestNews();
Ну то есть сделать это в модели (сервисы очевидно часть модели в MVC). Я тебе советую сначала почитать урок про MVC: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Вообще, я советую зайти в мой гитхаб https://github.com/codedokode/pasta/ . Симфони это не фреймворк для чайников и для начинающих. Ты должен изучить процентов 90 из того, что у меня в гитхабе только для того, чтобы читать туториалы для начинающих по Симфони. надо знать MVC, ООП, DI. Ты видимо пропустил основы и взялся сразу за сложное, отсюда и непонимание.
И кстати. чтобы в ней разобраться, мало читать документацию, многие вещи можно узнать только из ее исходного кода. Надо его изучать и разбирать.
> бы сам ответил что в Entity, предварительно сделав родительский класс.
Неправильно. Entity просто представляет сущность и ни про какие кеши ничего знать не должна.
Ну и наследование там тоже не нужно.
> еще хотел спросить про роутинг, как в мой метод run() в Router добавить 404 error, попробовал сделать такое если не существует вызываемого класса или метода, но выдает ошибку Cannot modify header information
Прочти про протокол HTTP тут например https://github.com/codedokode/pasta/blob/master/soft/web-server.md
Прочти мануал по функции header().
Там в ответе сначала идут заголовки, потом тело ответа. Если ты вывел хоть один символ, хоть один пробел, то заголовки отравлять уже нельзя. В мануале это написано.
Некоторые делают ошибку, сохраняя файлы в кодировке utf-8 с BOM. BOM это невидимый символ, который вставляется в начало документа до <?php, выводится интерпретатором PHP и не позволяет отправлять заголовки.
Проверь, нет ли у тебя файлов с BOM или файлов с пробелом или еще чем-то, что выводится до заголовков.
Также, у тебя неправильный код. Почитай про пртокол HTTP. Почитай, что такое редирект. Если ты используешь Location то PHP автоматически меняет код статуса ответа на 3xx вместо 404. Соответственно это уже получаеся не страница 404, а что-то другое.
Ты как-то пропускаешь темы. Ну как можно использовать редирект, не изучив, что такое HTTP, как это работает, как его обрабатывает браузер итд. Надо изучать теорию, что происходит при вызове той или иной функции.
> еще хотел спросить как мне сделать view для подготовки данных на вывод, может в гайде было, перечитаю еще раз
Почитай это: http://www.phpinfo.su/articles/practice/shablony_v_php.html
>>939498
AllowOverride должно быть достаточно. Может, там где-то стоит запрещающий AllowOverride, или ты не перезапустил Апач поле правки конфига, или ты опечатался в названии файла .htaccess
>>939480
Если не можешь помочь, то не надо. Почитай второй пост. У нас тут не тред для хвастовства, у кого глаза краснее.
Ну и мне тоже есть чем похвастаться. Я например, всего за 2 вечера смог пропатчить и скопилировать PHP7 под Windows XP (правда немного костыльно и пока без расширений). Уверен, что в линуксе, даже в какой-нибудь генте с такими задачами сталкиваться не приходится.
>>939236
> $news = \App\Models\Article::getAll();
В Доктрине используется паттерн Data Mapper, а не Active Record. Тебе надо сначала изучить теорию про паттерны работы с БД: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Затем изучить документацию Доктрины внимательно, так как без ее хорошего знания в Симфони ты не разберешься.
> Все работает, но если я допустим хочу к с этой выборкой предварительно сделать что-то до выведения в шаблон, например закешировать текст новости, в котором существуют markdown теги, то мне это где правильнее сделать?
Правильно сделать сервис получения новостей, в нем настроить кеширование, а в контроллере вызвать его:
$news = $newsService->getLatestNews();
Ну то есть сделать это в модели (сервисы очевидно часть модели в MVC). Я тебе советую сначала почитать урок про MVC: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Вообще, я советую зайти в мой гитхаб https://github.com/codedokode/pasta/ . Симфони это не фреймворк для чайников и для начинающих. Ты должен изучить процентов 90 из того, что у меня в гитхабе только для того, чтобы читать туториалы для начинающих по Симфони. надо знать MVC, ООП, DI. Ты видимо пропустил основы и взялся сразу за сложное, отсюда и непонимание.
И кстати. чтобы в ней разобраться, мало читать документацию, многие вещи можно узнать только из ее исходного кода. Надо его изучать и разбирать.
> бы сам ответил что в Entity, предварительно сделав родительский класс.
Неправильно. Entity просто представляет сущность и ни про какие кеши ничего знать не должна.
Ну и наследование там тоже не нужно.
Репозиторий это плохое место для кеширования так как репозитории не создаются через DI контейнер и сервис кеша ты туда так просто не внедришь. Репозиторий предназначен только для каких-то операций с БД.
> Туда ты записываешь все необходимые тебе методы работы с данными, для этого у доктрины есть прекрасный QueryBuilder.
Ты не понимаешь, зачем нужен QB. Почитай мой урок про паттерны: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Код с QB намного хуже читается чем SQL или DQL. QB используют там, где SQL/DQL использовать не получается.
>>939380
Работа Доктрины - только загружать и сохранять сущности в БД.
>>939196
Может кому-то просто нравится его использовать?
Если тебе в винде удобно, то используй ее. Но линукс изучить все равно надо, так как он на серверах используется.
>>938988
Баги исправить, добавить на сайт какую-нибудь страницу, сделать рассылку писем и тд, зависит от компании. Обычно только какие-то сложные вещи, вроде выбора архитектуры, фреймворков и тд, поручают опытным разработчикам, а все остальное можно и джуниору, почему нет, если задачу правильно поставить.
>>938811
У кук есть параметр path. По умолчанию он совпадает с URL страницы и может у тебя кука ставится не на весь сайт, а на его подраздел.
> то меня при обновлении страницы (например, методом post из формы) выкидывает на главную страницу, т.е. кука удаляется?
Скорее всего у тебя просто ошибки в коде.
>>938694
Встроенный REPL есть (-a), но очень убогий. Я им не пользуюсь. Если надо запустить что-то в консоли, пишу php -r 'echo "Yes!\n";'
>>938097
> что без ретерна ф-я бы ничего не возвращала
Правильнее сказать, что в PHP функция по умолчанию возвращает значение null.
Репозиторий это плохое место для кеширования так как репозитории не создаются через DI контейнер и сервис кеша ты туда так просто не внедришь. Репозиторий предназначен только для каких-то операций с БД.
> Туда ты записываешь все необходимые тебе методы работы с данными, для этого у доктрины есть прекрасный QueryBuilder.
Ты не понимаешь, зачем нужен QB. Почитай мой урок про паттерны: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Код с QB намного хуже читается чем SQL или DQL. QB используют там, где SQL/DQL использовать не получается.
>>939380
Работа Доктрины - только загружать и сохранять сущности в БД.
>>939196
Может кому-то просто нравится его использовать?
Если тебе в винде удобно, то используй ее. Но линукс изучить все равно надо, так как он на серверах используется.
>>938988
Баги исправить, добавить на сайт какую-нибудь страницу, сделать рассылку писем и тд, зависит от компании. Обычно только какие-то сложные вещи, вроде выбора архитектуры, фреймворков и тд, поручают опытным разработчикам, а все остальное можно и джуниору, почему нет, если задачу правильно поставить.
>>938811
У кук есть параметр path. По умолчанию он совпадает с URL страницы и может у тебя кука ставится не на весь сайт, а на его подраздел.
> то меня при обновлении страницы (например, методом post из формы) выкидывает на главную страницу, т.е. кука удаляется?
Скорее всего у тебя просто ошибки в коде.
>>938694
Встроенный REPL есть (-a), но очень убогий. Я им не пользуюсь. Если надо запустить что-то в консоли, пишу php -r 'echo "Yes!\n";'
>>938097
> что без ретерна ф-я бы ничего не возвращала
Правильнее сказать, что в PHP функция по умолчанию возвращает значение null.
Там неправильно сформулировано. Если ты не вернешь значение явно командой return, то функция возвращает значение null. Неправильно говорить "переменная будет пустая".
> я должен буду всегда в конце функции написать return с указанным аргументом в виде переменной, в которой я хочу получить результат?
Невнимательно ты прочел мои объяснения.
Ты не можешь указать переменную, в которую ты хочешь получить результат, так как внутри функции внешние переменные не видны. Ты указываешь после return выражение, которое надо вычислить и значение которого надо вернуть из функции. Примеры:
return 1;
return 2 + 2;
$x = 1; return $x;
return "Yes";
return mt_rand(1, 100);
>>938071
Да, return завершает выполнение функции. После него идет возврат в то место, откуда была вызвана функция.
>>938041
Местами мануал не очень понятно написан. Давай разберем по словам.
> return возвращает управление программой модулю, из которого была вызвана функция....
Под "модулем" здесь имеется кусок кода, откуда была вызвана функция.
> Выполнение программы продолжается с инструкции, следующей за местом вызова.
Инструкция - это команда, из которых состоит программа и которые разделяются точкой с запятой. Вот примеры инструкций:
$x = 1;
echo "yes";
doSomething();
Имеется в виду то, что команда return завершает выполнение функции и выполнение программы продолжается с того места, откуда была вызвана функция.return значит "вернуться". Вернуться к месту, откуда была вызвана функция и продолжать выполнять код там.
Вот например код:
echo "1";
func(1);
echo "2";
func(2);
echo "3";
func(3);
Этот код выполняется так:
- выполняется echo "1"
- вызвается функция func с аругментом 1. Интерпретатор переходит к ней и выполняет записанные в ней команды
- затем, после того, как он выполнит все команды в функции или выйдет из нее по команде return, он возвращается к месту вызова (к строке func(1)) и продолжает выполнять код после нее.
- выполняется echo "2"
- снова вызвается функция func, но уже с аргументом 2. Выполняются записанные в ней команды
- выполняется echo "3"
- и так далее
Там неправильно сформулировано. Если ты не вернешь значение явно командой return, то функция возвращает значение null. Неправильно говорить "переменная будет пустая".
> я должен буду всегда в конце функции написать return с указанным аргументом в виде переменной, в которой я хочу получить результат?
Невнимательно ты прочел мои объяснения.
Ты не можешь указать переменную, в которую ты хочешь получить результат, так как внутри функции внешние переменные не видны. Ты указываешь после return выражение, которое надо вычислить и значение которого надо вернуть из функции. Примеры:
return 1;
return 2 + 2;
$x = 1; return $x;
return "Yes";
return mt_rand(1, 100);
>>938071
Да, return завершает выполнение функции. После него идет возврат в то место, откуда была вызвана функция.
>>938041
Местами мануал не очень понятно написан. Давай разберем по словам.
> return возвращает управление программой модулю, из которого была вызвана функция....
Под "модулем" здесь имеется кусок кода, откуда была вызвана функция.
> Выполнение программы продолжается с инструкции, следующей за местом вызова.
Инструкция - это команда, из которых состоит программа и которые разделяются точкой с запятой. Вот примеры инструкций:
$x = 1;
echo "yes";
doSomething();
Имеется в виду то, что команда return завершает выполнение функции и выполнение программы продолжается с того места, откуда была вызвана функция.return значит "вернуться". Вернуться к месту, откуда была вызвана функция и продолжать выполнять код там.
Вот например код:
echo "1";
func(1);
echo "2";
func(2);
echo "3";
func(3);
Этот код выполняется так:
- выполняется echo "1"
- вызвается функция func с аругментом 1. Интерпретатор переходит к ней и выполняет записанные в ней команды
- затем, после того, как он выполнит все команды в функции или выйдет из нее по команде return, он возвращается к месту вызова (к строке func(1)) и продолжает выполнять код после нее.
- выполняется echo "2"
- снова вызвается функция func, но уже с аргументом 2. Выполняются записанные в ней команды
- выполняется echo "3"
- и так далее
Чтобы изучать фреймворки нужны базовые знания - ООП, DI, MVC, работа с базой. У тебя их возможно нет, потому я советую изучить шапку треда и поизучать что-нибудь оттуда.
CMS предназначены для создания сайта с минимумом программирования. Они в разы проще, но их возможности могут быть ограничены.
Что востребовано, смотри на сайтах вакансий.
>>937678
Как я понимаю, Data Mapper предназначен в первую очередь для сохранения/загрузки объектов в БД и из нее (сопоставления/отображения объектов в памяти на строчки таблицы в базе данных).
TableDataGateway - это просто класс-сборник методов для работы с таблицей, не только загрузка/сохранение объектов туда, но и любые другие манипуляции. Удаление например или какая-нибудь хитрая обработка записей в базе.
С другой стороны, Data Mapper может быть более продвинутым. Ну например, он сам может находить изменившиеся объекты и записывать изменения в базу. Или например он может доставать не только объект из БД, но и позволять получить связанные с ним объекты.
Можно еще почитать подробные описания паттернов в книге Фаулера (Шаблоны разработки корпоративных приложений) и сравнить их.
>>937592
Может у тебя просто на локальном сервере отключен вывод ошибок (display_errors в php.ini), они идут в логи, но ты туда не смотришь и думаешь, что их нет? Проверь логи веб-сервера, логи Юи.
Я не вижу никакой ошибки на строке 12, но код по моему некорректный. Вот смотри:
> for ($i = 1; $i <= count($totalArray); $i++) {
> if ($i % $skip == 0) {
> unset($totalArray[$i]);
Эта штука просто удалит элементы, индекс которых делится на $skip. Ну то есть 5, 10, 15, 20, но остальные элементы она не тронет никогда. В PHP при удалении элемента через unset остальные индексы не перечитываются. Используй array_splice если хочешь, чтобы после удаления элементы заново нумеровались от 0 до N.
>>924207
Неудобно работать с гигантскими файлами из многих тысяч строк. Ну и вообще, библиотека в PHP это обычно не один файл, а много.
>>924834
Там задача про кредит сложная, на ней все спотыкаются. Твое решение считает верно, но я бы вынес из кода числа вроде 1000, 5000 и поместил бы их в переменные в начале программы, чтобы их можно было легко менять.
>>925011
> if (is_numeric($char)) $currNumber .= $char;
Не стоит экономить на фигурных скобках тут.
> exit();
Функция тут не должна делать exit, надо делать return, а то твою функцию 2 раза нельзя будет вызвать. Также, лучше, если функция не выводит ответ, а возвращает, чтобы вызывающий код мог делать с ним что хотел.
Само решение правильное.
>>925132
Вообще, лучше бы в первый раз было все самому ставить, чтобы разобраться как это все в связке работает.
>>925277
Наверно, баг, я не наблюдал.
>>925387
Да, ты описал все верно. Единственное, что $Line['text'][0] это скорее получение первого символа в строке (работает только с латиницей).
>>926076
http://www.phpinfo.su/articles/practice/shablony_v_php.html
Я не вижу никакой ошибки на строке 12, но код по моему некорректный. Вот смотри:
> for ($i = 1; $i <= count($totalArray); $i++) {
> if ($i % $skip == 0) {
> unset($totalArray[$i]);
Эта штука просто удалит элементы, индекс которых делится на $skip. Ну то есть 5, 10, 15, 20, но остальные элементы она не тронет никогда. В PHP при удалении элемента через unset остальные индексы не перечитываются. Используй array_splice если хочешь, чтобы после удаления элементы заново нумеровались от 0 до N.
>>924207
Неудобно работать с гигантскими файлами из многих тысяч строк. Ну и вообще, библиотека в PHP это обычно не один файл, а много.
>>924834
Там задача про кредит сложная, на ней все спотыкаются. Твое решение считает верно, но я бы вынес из кода числа вроде 1000, 5000 и поместил бы их в переменные в начале программы, чтобы их можно было легко менять.
>>925011
> if (is_numeric($char)) $currNumber .= $char;
Не стоит экономить на фигурных скобках тут.
> exit();
Функция тут не должна делать exit, надо делать return, а то твою функцию 2 раза нельзя будет вызвать. Также, лучше, если функция не выводит ответ, а возвращает, чтобы вызывающий код мог делать с ним что хотел.
Само решение правильное.
>>925132
Вообще, лучше бы в первый раз было все самому ставить, чтобы разобраться как это все в связке работает.
>>925277
Наверно, баг, я не наблюдал.
>>925387
Да, ты описал все верно. Единственное, что $Line['text'][0] это скорее получение первого символа в строке (работает только с латиницей).
>>926076
http://www.phpinfo.su/articles/practice/shablony_v_php.html
Прочёл твою заметку. Спасибо. Ответь тогда тому Анону на вопрос про кеширование, раз я ответил неправильно.
Выходит, что сервис загрузки файла должен делать такие вещи:
- загрузить файл на сервер, вернуть сущность File в случае успеха. Это сущность нужна контроллеру, чтобы он мог выполнить редирект на страницу с этой сущностью
- как-то обработать ошибки и если они есть, отдать их наверх. Получается, что метод upload может вернуть 2 разных объекта в зависимости от валидности файла? Это, наверное, неправильно: нужно делать проверки на instanceof в контроллере.
Не понимаю, как сюда вклинить обработку ошибок, набросок сервиса: https://ideone.com/ijUVZR
набросок контроллера: https://ideone.com/dgyVC5
Я использую такую абстракцию для файла: https://github.com/symfony/http-foundation/blob/master/File/File.php
Почему сразу не использую UploadedFIle: https://github.com/symfony/http-foundation/blob/master/File/UploadedFile.php
Его тоже можно переместить, даже если он получен не из HTTP (параметр $test в конструкторе), но, как я понял, параметр $test тут только для тестов.
>чтобы он мог выполнить редирект на страницу с этой сущностью
А зачем?
>Получается, что метод upload может вернуть 2 разных объекта в зависимости от валидности файла? Это, наверное, неправильно: нужно делать проверки на instanceof в контроллере.
Зачем? Разве не логичней в случае ошибки вернуть false? Тоже делаю фйлообменник, если что.
>> чтобы он мог выполнить редирект на страницу с этой сущностью
> А зачем?
Ну вот загрузил ты файл на файлообменник и хочешь поделиться ссылочкой. Логично сразу после загрузки произвести редирект. rghost тоже делает редирект на загруженный файл, можешь проверить.
>> Получается, что метод upload может вернуть 2 разных объекта в зависимости от валидности файла? Это, наверное, неправильно: нужно делать проверки на instanceof в контроллере.
> Зачем? Разве не логичней в случае ошибки вернуть false? Тоже делаю фйлообменник, если что.
А как ты донесёшь до пользователя причину ошибки? Фразой "Что-то пошло не так"? Я хочу давать внятные объяснения.
Если ты используешь силекс, то можешь воспользоваться уникальной возможностью прикрутить к нему симфониевские формы и валидатор. Если же нет, то советую сделать следующее:
Сделать контроллер страницы загрузки, куда попадает засубмиченая форма
Сразу валидировать форму с помощью сервиса валидации, который отправляет true/false в контроллер
Если форма валидна - сохранять файл и редиректить куда тебе надо
Если нет, то рендерить тот же шаблон с ошибкой
>А как ты донесёшь до пользователя причину ошибки? Фразой "Что-то пошло не так"? Я хочу давать внятные объяснения.
Вроде как такое должны делать исключения, не? Типа в контроллере траишь, и если всплыло исключение то вместо редиректа показываешь шаблон с ошибкой. Я бы как-то так делал.
Почему действие2 не выполняется?
$ears = 0;
for ($vklad = 10000; $vklad < 1000000; $vklad 1.1 ) {
$ears++;
}
$earsOld = 16 + $ears;
echo "Вкладчику будет $earsOld лет. В банке будет 1000000 через $ears лет.";
Если я его отдельно запилю, то все работает, а в самом цикле - нет.
$ears = 0;
for ($vklad = 10000; $vklad < 1000000; ) {
$ears++;
$vklad = $vklad 1.1;
}
$earsOld = 16 + $ears;
echo "Вкладчику будет $earsOld лет. В банке будет 1000000 через $ears лет.";
пропустилзнак умножения, но с ним все равно не работает
$ears = 0;
for ($vklad = 10000; $vklad < 1000000; $vklad * 1.1) {
$ears++;
}
$earsOld = 16 + $ears;
echo "Вкладчику будет $earsOld лет. В банке будет 1000000 через $ears лет.";
$x * 10 умножает $x на 10 но никуда не сохраняет результат, и $x остается неизменной
Надо писать $x = $x * 10 или $x *=10 чтобы сохранить результат.
Спасибо!
Ребят, а у вас своего скайп-чата нету? Раньше был, вроде...
Раньше был... ты про тот в котором я и ещё один анон который написал 4 сообщения и съебал?
И вот допустим я создаю тест и нужно туда создать вопросов и тут 2 варианта:
1 создать сначала пустой тест, а потом по ссылке перейти на форму создания вопросов
2 создавать сразу тест с вопросами
В первом варианте недостаток в том. что юзер может спамить пустыми тестами, создавая их до бесконечности, а во втором не понятно как объединить форму теста с формой вопроса, при том, что тест еще не создан, а значит id его не известно(автогенерация) и вопросы не могут быть привязаны к этому тесту.
> закрыт навсегда
К хренам собачьим?
>Можно создать новый если хочешь.
Хочу. Только было бы неплохо, если бы там помимо меня был ещё кто-то. Вообще, помню, там людей было немало - человек 10-15, вроде.
В телеграмме было 10-15 человек. Но там было по 1500 мужских членов в сутки, поэтому не взлетело.
Там было 3 дебила которых никто не хотел банить, потому-что ШВАБОДА ОБЩЕНИЯ!!!! ШВАБОДА МНЕНИЯ!!! В итоге все адекватные аноны поливали.
Тебе надо изучить мануал доктрины про отношения:
- (англ) http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html
- http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html
- перевод был на этом сайте http://archive.li/WreO6 но его отключили, а веб-архив не показывает страницы из-за robots.txt
Так как Доктрины пытается создать видимость, что все объекты находятся в памяти, то связи в них делают через поля с нужной аннотауцией. Если например у Теста есть Автор, то мы так и пишем:
class Test
{
// аннотация ManyToOne
protected $author;
}
И Доктрина сама подставит туда либо объект Пользователь, либо прокси-объект для ленивой загрузки.
Ну и ты можешь туда прописать объект Пользователя и при flush() соответствующие внешние ключи в базе может быть будут обновлены (там есть подвох, проверяется только один объект в отношении и надо понимать, какой).
Там надо изучить документацию, так как много особенностей, которые надо понимать. И про коллекции, и про ленивую загрузку и прокси-объекты.
Соответственно для установки связи тебе не нужны id и ты можешь создавать целые графы из объектов:
$author = new User;
$test = new Test;
$test->setAuthor($author);
$question = new Question;
$test->addQuestion($question);
$em->persist(...);
$em->flush();
> 1 создать сначала пустой тест, а потом по ссылке перейти на форму создания вопросов
> 2 создавать сразу тест с вопросами
А зачем создавать пустой тест? Что тебе мешает сразу переходить на форму создания теста, не создавая ничего в БД?
> а во втором не понятно как объединить форму теста с формой вопроса, при том, что тест еще не создан, а значит id его не известно(автогенерация) и вопросы не могут быть привязаны к этому тесту.
Если ты используешь аякс, то у тебя одна страница с которой ты никуда не уходишь, и проблемы нет (хотя даже в этом случае надо подумать, как сохранять данные на случай например закрытия браузера или неожиднного отключения питания). Если ты не используешь аякс, а используешь обычные формы то да, тебе надо где-то хранить пока еще не сохраненный тест. Тут много вариантов, у каждого свои недостатки:
- хранить все несохраненные данные в скрытых полях формы (например сериализованную версию графа объектов теста), и передавать с одной страницы на другую (сложновато, но зато соответствует идеям REST). В ASP по моему когда-то такая штука использовалась.
- хранить в БД, с пометкой, что это не сохраненный тест (чтобы не показывать его в списках). При желании можно написать сборщик мусора, удаляющий старые не сохраненные тесты, но я не уверен, что это хорошая идея. Может лучше их хранить под названием "черновики тестов"?
- сделать какое-то временное хранилище, вроде отдельной таблицы, и там хранить сериализованные данные. Что-то вроде сессий, только не на основе кук.
- сессии. Не нравится тем, что они удаляются через полчаса неиспользования, и тем что они общие для всех вкладок браузера, неудобно будет редактировать 2 теста одновременно.
Поясню про сериализацию. Доктрина позволяет связывать объекты без id, и ты можешь в конце скрипта сериализовать граф несохраненных в БД объектов теста, вопросов и тд и сохранять его например в скрытом поле формы, а в начале скрипта - восстанавливать его. Правда с восстановлением сохраненных в БД сущностей будут некоторые сложности, но их при желании можно решить.
Примерно так:
$serialized = serialize($test);
<input type="hidden" value="{{ serialized }}">
Тут правда есть проблема: десериализовывать данные, пришедшие от пользвоателя небезоасно, это может привести к уязвимости, были случаи. Так что хранить данные надо не в форме, а на сервере, либо надо их сериализовывать в какой-то безопасный формат вроде JSON и тщательно проверять при восстановлении.
> юзер может спамить пустыми тестами, создавая их до бесконечности,
А пустые можно не сохранять. А если юзер что-то ввел, то может он хочет чтобы эти данные сохранились и чтобы он мог продолжить редактирвать тест позже. Насчет места я бы не беспокоился - сейчас в ходу терабайтные диски. Фейсбук например сохраняет даже стертые и не отправленные посты и его это не беспокоит.
Тебе надо изучить мануал доктрины про отношения:
- (англ) http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html
- http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html
- перевод был на этом сайте http://archive.li/WreO6 но его отключили, а веб-архив не показывает страницы из-за robots.txt
Так как Доктрины пытается создать видимость, что все объекты находятся в памяти, то связи в них делают через поля с нужной аннотауцией. Если например у Теста есть Автор, то мы так и пишем:
class Test
{
// аннотация ManyToOne
protected $author;
}
И Доктрина сама подставит туда либо объект Пользователь, либо прокси-объект для ленивой загрузки.
Ну и ты можешь туда прописать объект Пользователя и при flush() соответствующие внешние ключи в базе может быть будут обновлены (там есть подвох, проверяется только один объект в отношении и надо понимать, какой).
Там надо изучить документацию, так как много особенностей, которые надо понимать. И про коллекции, и про ленивую загрузку и прокси-объекты.
Соответственно для установки связи тебе не нужны id и ты можешь создавать целые графы из объектов:
$author = new User;
$test = new Test;
$test->setAuthor($author);
$question = new Question;
$test->addQuestion($question);
$em->persist(...);
$em->flush();
> 1 создать сначала пустой тест, а потом по ссылке перейти на форму создания вопросов
> 2 создавать сразу тест с вопросами
А зачем создавать пустой тест? Что тебе мешает сразу переходить на форму создания теста, не создавая ничего в БД?
> а во втором не понятно как объединить форму теста с формой вопроса, при том, что тест еще не создан, а значит id его не известно(автогенерация) и вопросы не могут быть привязаны к этому тесту.
Если ты используешь аякс, то у тебя одна страница с которой ты никуда не уходишь, и проблемы нет (хотя даже в этом случае надо подумать, как сохранять данные на случай например закрытия браузера или неожиднного отключения питания). Если ты не используешь аякс, а используешь обычные формы то да, тебе надо где-то хранить пока еще не сохраненный тест. Тут много вариантов, у каждого свои недостатки:
- хранить все несохраненные данные в скрытых полях формы (например сериализованную версию графа объектов теста), и передавать с одной страницы на другую (сложновато, но зато соответствует идеям REST). В ASP по моему когда-то такая штука использовалась.
- хранить в БД, с пометкой, что это не сохраненный тест (чтобы не показывать его в списках). При желании можно написать сборщик мусора, удаляющий старые не сохраненные тесты, но я не уверен, что это хорошая идея. Может лучше их хранить под названием "черновики тестов"?
- сделать какое-то временное хранилище, вроде отдельной таблицы, и там хранить сериализованные данные. Что-то вроде сессий, только не на основе кук.
- сессии. Не нравится тем, что они удаляются через полчаса неиспользования, и тем что они общие для всех вкладок браузера, неудобно будет редактировать 2 теста одновременно.
Поясню про сериализацию. Доктрина позволяет связывать объекты без id, и ты можешь в конце скрипта сериализовать граф несохраненных в БД объектов теста, вопросов и тд и сохранять его например в скрытом поле формы, а в начале скрипта - восстанавливать его. Правда с восстановлением сохраненных в БД сущностей будут некоторые сложности, но их при желании можно решить.
Примерно так:
$serialized = serialize($test);
<input type="hidden" value="{{ serialized }}">
Тут правда есть проблема: десериализовывать данные, пришедшие от пользвоателя небезоасно, это может привести к уязвимости, были случаи. Так что хранить данные надо не в форме, а на сервере, либо надо их сериализовывать в какой-то безопасный формат вроде JSON и тщательно проверять при восстановлении.
> юзер может спамить пустыми тестами, создавая их до бесконечности,
А пустые можно не сохранять. А если юзер что-то ввел, то может он хочет чтобы эти данные сохранились и чтобы он мог продолжить редактирвать тест позже. Насчет места я бы не беспокоился - сейчас в ходу терабайтные диски. Фейсбук например сохраняет даже стертые и не отправленные посты и его это не беспокоит.
Никогда такого не делал. Как думаешь, где лучше сделать чат - в telegram или скайпе?каким файлообменником ты пользуешься?
Скайп блокирует 80 и 443 порт, а они нужны апачу, вмваре и опенсерверу. Так что лучше телеграмм. Ну или ты будешь обьяснять как настраивать скаеп каждому новому ньюфагету.
>Скайп блокирует
Погоди, разве он не так создается?: https://www.youtube.com/watch?v=yJfx_3m1Byg
Скажи членораздельно - скайп-чат же создается и настраивается так, как эта женщина показала?
Чтобы снова не взлетело? Нужно солидное что-то.
Вот, знаешь, любопытно, почему среди тестовых заданий опа ещё нет написания имиджборды, м?
Телеграммочатик с автоинвайтом.
Добавился, но что-то не могу никак отправлять сообщения...
Там только голосом можно, что ли?
Там голос опционально, только если на голосовой чат жмешь. Иначе текстом просто.
Можно подучить английский.
Программисты скоро не нужны будут, DeepCoder от Microsoft сам пишет код из требований заказчика. Будущее за самопищущимся кодом.
И сюда это притянули. Ты понимаешь, что даже человеку сложно облачить невнятные требования заказчика в формализованные задачи, что уже говорить о роботах.
Программистов если и заменят, то только в последнюю очередь.
Административная панель:
Вывод всех студентов
ФИ
группа
курс
кол-во тестов на 3, 4, 5, общее кол-во
средний балл (% правильных ответов)
средняя оценка
с фильтрами по группам, курсам, предметам
Сортировки при выводе по ФИ, результатам, дате написания последнего теста, %правильных, по среднему баллу.
Добавление, удаление, редактирование записей студентов и о студентах при нажатии на ФИ студента.
Статистическую информацию по каждому студенту:
Сколько раз решал каждый тест
Лучший результат
Подробная информация о попытках (сколько ошибок, сколько времени затратил, дата решения теста) по каждой теме предмета и по общему тесту
Форма создания и редактирования тестов(вопросов)+перемещение между темами
Форма создания и редактирования предметов
Форма создания и редактирования тем
Предусмотреть что в теме уже могут быть вопросы. для того, чтобы удалить тему, нужно либо удалить вопросы этой темы или переместить в другую тему
я вообще хз как это делать
PHP:
<?php
function shutdown() {
$error = error_get_last();
if ($error['type'] === E_ERROR) {
header("HTTP/1.1 503 Internal server error");
print json_encode([
'errors' => [
[
'code' => '503',
'message' => $error['message']
]
]
]);
}
}
--- остальной код
Пока использую нечто подобное.
JS:
Нет мыслей.
Тем более важно, что обычно при фатальных ошибках максимум что происходит - это ничего не происходит (кроме сообщения в консоль, но много ли вы видели людей которые лезут в инструменты разработчика, чтобы посмотреть, что же случилось?).
inb4: фатальные ошибки не могут произойти если всё протестировано отлажено... Возможно, могут? Довольно много лазаю по различным сайтам, иногда вижу php нотайсы, ошибки, бывает даже куски кода вылезают наружу. И это у дядек, которые зарабатывают программированием.
PHP:
<?php
function shutdown() {
$error = error_get_last();
if ($error['type'] === E_ERROR) {
header("HTTP/1.1 503 Internal server error");
print json_encode([
'errors' => [
[
'code' => '503',
'message' => $error['message']
]
]
]);
}
}
--- остальной код
Пока использую нечто подобное.
JS:
Нет мыслей.
Тем более важно, что обычно при фатальных ошибках максимум что происходит - это ничего не происходит (кроме сообщения в консоль, но много ли вы видели людей которые лезут в инструменты разработчика, чтобы посмотреть, что же случилось?).
inb4: фатальные ошибки не могут произойти если всё протестировано отлажено... Возможно, могут? Довольно много лазаю по различным сайтам, иногда вижу php нотайсы, ошибки, бывает даже куски кода вылезают наружу. И это у дядек, которые зарабатывают программированием.
Бессмысленный вопрос. Если хочешь вкатиться, должен быть подход "даже если все скажут, что нельзя, я все равно вкачусь и выебу этот стереотип" и т.д. и т.п.
Если ты придешь на собеседование и покажешь лучшее владение предметом, чем у всех остальных (а я так понимаю, на джуна большинство кандидатов околонулевого уровня), возьмут конечно тебя.
Добавляйтесь скорее :)
Ещё быстрее :)
Мы всё равно будем тебе рады.
А если я у тебя спрошу что-нибудь?
https://github.com/codedokode/pasta/blob/master/student-list.md#Постраничная-навигация/
> Я помню как с mysql я это делал, но в PDO как-то застрял.
А разница в чём? Запросы к базе ведь одинаковые в обоих случаях будут.
Платное обучение гораздо менее доступно.
P.S.: Добро пожаловать в наш чат!:
https://t.me/joinchat/AAAAAAlT02py0soe6xnbMw
Посмотрите, пожалуйста, как сделал задания. Заранее благодарю)
http://ideone.com/9wsu8g - Игра в кубики;
http://ideone.com/sRvu3e - мини "калькулятор";
http://ideone.com/8x1TeV - Игра в кубики с Пекой;
http://ideone.com/pJ4TO8 - Пхп без смс;
http://ideone.com/8GN4S3 - депозит;
http://ideone.com/O7tPEA - Айфон в кредит;
http://ideone.com/7SJzev - Задание со стихами;
http://ideone.com/MGrQkg - Регулярка на номера телефонов
Код без использования сторонних библиотек, фреймворков и т.п.
>Пхп без смс;
<?php
error_reporting(-1);
$answers = ['да','нет','не знаю','никогда','это зависит от тебя','спроси у анона'];
$question = 'выучил ли ты пхп без смс?';
$answer = $answers[array_rand($answers)];
echo "Вопрос: " . $question . PHP_EOL;
echo "Ответ: " . $answer . PHP_EOL;
#O_o
>Игра в кубики с Пекой;
<?php
error_reporting(-1);
$anonDice1 = rand(1,6);
$anonDice2 = rand(1,6);
$compDice1 = rand(1,6);
$compDice2 = rand(1,6);
echo "У анона выпало: $anonDice1 и $anonDice2." . PHP_EOL;
echo "У пеки выпало: $compDice1 и $compDice2." . PHP_EOL;
$anonSum = $anonDice1 + $anonDice2;
$compSum = $compDice1 + $compDice2;
if ($anonDice1 == $anonDice2 && $compDice1 == $compDice2) {
die("у вас выпала дабла" . PHP_EOL);
} elseif ($anonSum > $compSum) {
die("анон подебил" . PHP_EOL);
} elseif ( $anonSum < $compSum) {
die("пека подебил" . PHP_EOL);
}
die("дружба" . PHP_EOL);
#O_O
>Игра в кубики с Пекой;
<?php
error_reporting(-1);
$anonDice1 = rand(1,6);
$anonDice2 = rand(1,6);
$compDice1 = rand(1,6);
$compDice2 = rand(1,6);
echo "У анона выпало: $anonDice1 и $anonDice2." . PHP_EOL;
echo "У пеки выпало: $compDice1 и $compDice2." . PHP_EOL;
$anonSum = $anonDice1 + $anonDice2;
$compSum = $compDice1 + $compDice2;
if ($anonDice1 == $anonDice2 && $compDice1 == $compDice2) {
die("у вас выпала дабла" . PHP_EOL);
} elseif ($anonSum > $compSum) {
die("анон подебил" . PHP_EOL);
} elseif ( $anonSum < $compSum) {
die("пека подебил" . PHP_EOL);
}
die("дружба" . PHP_EOL);
#O_O
Он не один. Насколько я понял, по условию задачи дан массив разных ответов. Из них нужно выбрать один случайный.
И ещё вопрос. Вот на прикле я написал, он разве не должен выдать текст со всеми символами в нижнем регистре?
Не могу с палиндромом, постоянно ошибку выдает. Хотя предыдущие два задания решил без проблем.
у тебя на 14 строке точки с запятой нет.
Пробелы в начале и конце строки удаляются функцией trim(). Если нужно удалить вообще все пробелы, то str_replace() или preg_replace()
У меня так получилось (см. пик №1):
http://sandbox.onlinephpfunctions.com/code/37e9bafb75f09c766bd25fb9bd66435a24ac9d5a
> он разве не должен выдать текст со всеми символами в нижнем регистре?
Чтобы пользоваться функциями mb_*, нужно установить соответствующее расширение: http://php.net/manual/ru/book.mbstring.php . На ideone, как видишь (см. пик №2), оно не установлено.
P.S.: А у нас ещё чат есть :3: https://t.me/joinchat/AAAAAAlT02py0soe6xnbMw
> Возможно ли определить ip с помощью php и как это сделать?
Ты бы хоть погуглить пару минут для приличия:
http://stackoverflow.com/questions/15699101/get-the-client-ip-address-using-php
> Если оттуда происходит переход к скрипту settings.php
Переход это редирект? Если да - то переменная $n в том скрипте видна не будет, так как редирект - это новый HTTP-запрос, а PHP на каждый запрос заново инициализируется и умирает.
> как сделать, чтоб она была там видна.
- передавать как get-параметр: settings.php?n=10, либо хуже - писать $n в куки/сессию. В скрипте-получателе выковыривать.
*погуглил
В admin.php делаешь file_put_contents('n', $n);
В settings.php делаешь $n = file_get_contents('n');
Получаешь переменную в обоих скриптах.
Опытным путём выяснил что он ругается даже на <>
А смысл?
Я Insert делаю через админ например. Ошибка нигде не проходит, сайт отображает всё отлично. Всё читается, работает и вообще всё отлично. Но вот PHPmyAdmin упорно говорит что я неправ при попытке отредактировать запись или при попытке отредактировать всё что после этой записи. Пикрил например.
.
Короче, у кого новый оперсервер последней версии, можете смело на него накатывать Myadmin 4,6,6 и переносить конфиги. И будет вам счастье. Старый админ 4,5,7 глючит очень сильно.
Писал на форум их, но ответа не получил. Без понятия, как решить это.
Суть проблема такова: хочу установить расширение для Unyson, а именно Конструктор страниц.
В треде JS, не помогли
Заходишь в /web, там отдельный тред по WP.
Что конкретно тебе непонятно. По моему всё очевидно написано в описании ошибки. Не?
А как ее решить?я просто очень плохо разбираюсь в этом деле.
Не мог бы объяснить, пожалуйста ньюфагу. Спасибо.
Там говорится о том, что при попытке подключиться на локальный порт 1080 твоего компьютера запрос на соединение отклоняется. Значит, то, что должно принять этот запрос, не работает. Что именно? Веб-сервер, может? Может, что-то ещё?
Вообще, тут интересный случай. Порт 1080 используется для сервиса SOCKS (что-то вроде прокси для выхода в сеть через него). Не представляю, зачем расширению доступ к этгому порту.
Вот эти объектные паттерны, они применимы только к ПХП или они более абстарктны и их можно использовать в любом другом ОО языке, в котором ОО реализовано примерно как в ПХП?
Лучше всего, конечно, глянуть в исходники и узнать, при каких условиях он вываливает ошибку.
Слухай, а у тебя там URL правильный? Я нашел, вроде, файл, отвечающий за обновление. Если ошибку всё ещё не устранил, можно попробовать расставить там var_dump`ы и посмотреть на вывод.
> глянуть в исходники и узнать
Не понимаю о каком исходнике ты говоришь, ибо тупой:( Можешь название сказать этого исходника, если не затруднит.
>>942961
Я так понимаю эта папка отвечает за обнову, но какой файл ты имел ввиду?ведь там сотни их.
Матерь Божья, у тебя Windows... Да уж...
Ладно, если для тебе сложно, проверь, правильно ли указан URL по которому происходит запрос на обновление.
Примерно структура таблиц на пике. Так пойдет? Понимаю, что возможно не самая эффективная архитектура для борды, но 3НФ и с точки зрения моделей Eloquent правильно вроде.
Сейчас мне надо решить главные вопросы, как организовать номера постов, и порядок и смыв тредов. Если делать так как на пике, то порядковый номер треда будет храниться вместе с инфой о треде в колонке ordinal number. Соответственно при поднятии треда все остальные придется опускать и менять им ordinal_number. Можно вообще не хранить порядковые номера тредов, а например (если хранить в boolean сажу для поста) вычислять на ходу по последнему посту без сажи. По идее гораздо больше мороки, придется лопатить все посты и при чтении и при записи.
Та же история с номером последнего поста на доске. Можно хранить, можно вычислять.
Как кошернее будет? Впрочем любым советам рад буду.
У постов и тредов Первичным Ключом id'шник, а не составной потому что Eloquent (местный ORM) составных ПК не понимает. У борд вместо id'шника можно сразу линк использовать, но так проще борды переименовывать.
Можно через .htaccess, я же делал через настройки апача, или что у тебя там. Пишешь в конфиге
<VirtualHost :80>
# Имя сервера которое обслуживает этот VirtualHost
ServerName students.local
# Корневая папка сервера
DocumentRoot C:\OpenServer\domains\students.local\public
<Directory "C:\OpenServer\domains\students.local\public">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.)$ index.php [QSA]
AddDefaultCharset UTF-8
</Directory>
</VirtualHost>
В <directory> можешь скопипастить содержание .htaccess
И он уже считай тебе не нужен.
Почему далее запрос возвращает массив? Поля совпадают (пробовал ставить public), все равно не работает
Он должен вернуть массив объектов я так понял.
Покажи код, который идет дальше, где происходит получение данных.
>>943385
У веб-сервера есть такое понятие, как корневая директория (document root). Надо в конфиге сервера просто поставить корневой директорией нужную папку. Для Апача в конфиге надо найти нужный блок VirtualHost и внутри него поставить директиву
DocumentRoot "/var/www/example/public"
Там анон ниже привел пример конфига.
Если ты посмотришь документацию на DocumentRoot: https://httpd.apache.org/docs/2.4/mod/core.html#documentroot то там видно, где ее можно указывать:
Context: server config, virtual host
То есть в .htaccess нельзя, только в общем конфиге и в блоке VirtualHost.
>>943318
Это да, загрузка и запись в базу данных объектов. Если ты используешь ООП, то в твоем коде есть классы-сущности и надо как-то сохранять и загружать их из БД. Чтобы не писать все это с нуля и не мучаться, есть готовые библиотеки ORM вроде Доктрины. С нуля такое писать год можно, если не больше.
Вот тут в моем уроке это упомянуто: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Там конечно не все так просто, много подвохов, с производительностью могут быть проблемы, если тщательно не разобраться в используемой библиотеке.
Покажи код, который идет дальше, где происходит получение данных.
>>943385
У веб-сервера есть такое понятие, как корневая директория (document root). Надо в конфиге сервера просто поставить корневой директорией нужную папку. Для Апача в конфиге надо найти нужный блок VirtualHost и внутри него поставить директиву
DocumentRoot "/var/www/example/public"
Там анон ниже привел пример конфига.
Если ты посмотришь документацию на DocumentRoot: https://httpd.apache.org/docs/2.4/mod/core.html#documentroot то там видно, где ее можно указывать:
Context: server config, virtual host
То есть в .htaccess нельзя, только в общем конфиге и в блоке VirtualHost.
>>943318
Это да, загрузка и запись в базу данных объектов. Если ты используешь ООП, то в твоем коде есть классы-сущности и надо как-то сохранять и загружать их из БД. Чтобы не писать все это с нуля и не мучаться, есть готовые библиотеки ORM вроде Доктрины. С нуля такое писать год можно, если не больше.
Вот тут в моем уроке это упомянуто: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Там конечно не все так просто, много подвохов, с производительностью могут быть проблемы, если тщательно не разобраться в используемой библиотеке.
Я советую проектироват базу так: сделать полностью нормализованное решение, а потом добавлять избыточные поля для оптимизации действий с таблицами.
Вообще, ordinal_number у треда это довольно неудачное решение. Это избыточная информация (так как порядковый номер треда можно вычислить через дату добавления последнего поста и наличие сажи). В принципе такие поля можно добавлять ради оптимизации выборки, например, но в твоем случае это довольно неудачное решение, так как при добавлении каждого поста нам надо обновлять все номера тредов, а базы не очень любят большое число операций записи. То есть от него вреда больше, чем пользы получается.
Я бы предложил добавить другое лишнее поле: дату обновления треда с учетом сажи (назовем ее bumpDate - момент последнего бампа треда). Если мы добавляем пост без сажи, и не достигли бамплимита, то мы обновляем bumpDate у треда. Соответственно, после этого мы можем сортировать посты по ORDER BY fixed DESC, bumpDate DESC (индекс по (fixed, bumpDate) отлично оптимизирует сортировку).
Конечно, номер хорош тем, что позволяет оптимизировать постраничную навигацию - мы можем сделать запрос WHERE number BETWEEN 10 AND 20 ORDER BY number, который идеально ложится на индекс. Без номера мы вынуждены использовтаь LIMIT, который не очень оптимально работает дальше N-й страницы (подробнее о недостатках LIMIT ниже).
Также, надо решить проблему вывода 3 последних постов треда. Для эффективной их выборки, скорее всего, надо добавлять дополнительные данные - например, таблицу связи между тредами и постами, или колонку, в которой через запятую хранятся id последних постов (тут правда не получится использовать внешние ключи).
Часть колонок у тебя я не понял. Зачем например колонка timestamps у треда и борды? Из названия понять ее назначение невозможно. Зачем last_post_nubmer у борды?
Зачем post_number у постов? Если тебе надо ставить номера при выводе, то может проще их считать в момент выовда? Хотя, вреда от него особого нет, так как номера не требуется пересчитывать при вставке поста, и получение номера последнего поста хрошо оптимизируется индексом.
Добавлю немного про LIMIT. Он используется повсеместно для пагинации, но у него есть недостатки:
- при очень большом OFFSET запросы получаются неэффективные
- в обновляющихся лентах, если ты идешь от 1-й страницы к последней, и добавляются новые посты, ты можешь натыкаться на уже просмотренные посты
Для борьбы с этими проблемами есть особая техника, когда мы используем вместо номера страницы значение поля, по которому сортируются записи, примерно так: /threads?page=4&bumpDate=2017-01-02T12:00:00. Подробнее от этом тут: http://use-the-index-luke.com/no-offset
Такой подход исплоьзуется например на reddit.
И еще, подозреваю, что выборка 500 объектов-постов через ORM будет работать не очень быстро. На таких объемах наверно выгоднее выбирать данные напрямую из БД.
Вообще, борда это довольно тяжелый (но интересный) случай с точки зрения оптимизации, так как тут при чтении треда идет выборка большого объема информации (500 постов - это может быть около 2 Мб текста), и ее трудно сделать быстрой. Мы должны прочитать из БД 2 Мб информации, засунуть ее в шаблон, отформатировать текст, отдать пользователю. Трафик, который может отдать база, он понятное дело, ограничен, и это может стать узким местом. И не только база, канал связи между пользователем и сервером - он тоже ограничен и конечно пересылать маленькие объемы данных было бы выгоднее.
С другой стороны, пользователь, получив тред, будет его долго читать, и может быть, не станет дальше беспокоить сервер запросами. А может он прочтет 2 поста и перейдет к другому треду.
Ну допустим у нас соединение с БД идет по 1 Гбит/с каналу. 2 мегабайта это 16 Мбит, значит мы в принципе не можем в такой ситуации в секунду отдать больше 60 больших тредов в секунду. Это в идеальном случае, если база способна генерировать данные с такой скоростью.
Я знаю, что некоторые борды хранят кеш HTML-кода треда, например в виде файла на диске. При чтении треда мы просто отдаем пользователю файл с диска, а при добавлении поста мы либо перегенерируем файл, либо как-то по-умному вставляем пост в конец треда (что может быть эффективнее). Но тут мы получаем другие проблемы:
- добавление одного поста может требовать перезапись огромного файла. Скорость записи на диск ограничена. То есть мы быстро вставляем 1 пост в базу и не спеша обновляем файл с кешем страницы, и это ограничивает скорость постинга. Более того, диски лучше всего принимают последовательную запись, а когда несколько потоков параллельно пишут треды - общая скорость проседает.
- перегенерация кеша треда требует блокировки. Так как если 2 потока одновременно будут перегенерировать один тред, может выйти что-то кривое.
- с другой стороны, отдача статических файлов - это то, с чем веб-сервера вроде nginx справляются отлично. То есть мы можем даже не дергать PHP приложение при запросе треда, плюс в линуксе есть разные оптимизации, которые позволяют например нгинксу отдавать файл в сеть, не читая его (ядро само этим займется).
- статический файл можно сжать gzip при записи, что снизит нагрузку на диск (так как данных будет меньше, текст хорошо жмется, раз в 5-10) и ускорит отдачу, снизит нагрузку на интернет-канал. С другой стороны, непонятно что тогда отдавать пользователям, не поддерживающим gzip? распаковывать для них файл на лету? Также, в сжатый тред нельзя вставить один новый пост, его можно только переписать целиком (или нет? я не эксперт по gzip).
- с третьей стороны, мы можем для кеша вместо реального диска использовать ram-диск (tmpfs в линукс). То есть хранить файлы в оперативной памяти, лучше наверно в сжатом виде. Запись в память идет с максимальной скоростью, и отдача файлов из памяти nginx будет очень эффективна. Мне кажется, что быстрее варианта, чем отдавать нгинксом файлы из памяти, в принципе нет.
- с четвертой стороны, кеш имеет те недостатки, что файл получается одинаков для всех пользователей. Представь, что мы хотим написать в углу "здравствуйте, Иван" или подсвечивать посты пользователя. Вся наша схема перестанет работать, так как HTML-код теперь разный для разных пользователей. Мы должны либо задейтствовать PHP, и возможно, базу, либо добавлять нужные данные яваскриптом в браузере. На этой борде используется подсветка через яваскрипт
То есть в итоге, я бы поэкспериментировал с кешированием тредов в tmpfs, может в сжатом виде, и посмотрел бы, можно ли при добавлении поста не перегенерировать тред целиком. Хотелось бы конечно иметь и сжатие, и добавление одного поста.
Некоторые борды также генерировали HTML-файлы для страниц со списком постов (главная, 2-я старница, 3-я и тд), но это по моему неэффективно, так как при каждом добавлении поста мы должны перегенерировать все файлы страниц. Это узкое место. Мне кажется, что страницы выгоднее генерировать на лету. Единственное, что тут можно оптимизировать - можно сделать HTML-кеш в памяти для треда (ОП-пост + 3 последних поста + HTML-обвязка). То есть из базы выбираем id тредов на текущей странице, затем из кеша берем их представление и отдаем пользователю. При обновлении треда либо дропаем кеш, либо обновляем.
Зато тут есть возможности для распараллеливания:
- разные борды никак не связаны друг с другом, и мы можем помещать их на отдельные сервера. Причем в базу друг к другу они не лезут и каждый может использовать свою БД
- отдача тредов из tmpfs не параллелится на несколько серверов
- отдача списков тредов методом база + кеш параллелится
Вообще, тут конечно мало рассуждать теоретически, надо брать сервер и делать эксерименты, измерения.
Если хочешь, можешь тоже над этим подумать. Можешь кстати потом померять свою борду.
И еще, вдруг пригодится, у меня есть готовая верстка для борды: https://github.com/codedokode/board-markup
Я советую проектироват базу так: сделать полностью нормализованное решение, а потом добавлять избыточные поля для оптимизации действий с таблицами.
Вообще, ordinal_number у треда это довольно неудачное решение. Это избыточная информация (так как порядковый номер треда можно вычислить через дату добавления последнего поста и наличие сажи). В принципе такие поля можно добавлять ради оптимизации выборки, например, но в твоем случае это довольно неудачное решение, так как при добавлении каждого поста нам надо обновлять все номера тредов, а базы не очень любят большое число операций записи. То есть от него вреда больше, чем пользы получается.
Я бы предложил добавить другое лишнее поле: дату обновления треда с учетом сажи (назовем ее bumpDate - момент последнего бампа треда). Если мы добавляем пост без сажи, и не достигли бамплимита, то мы обновляем bumpDate у треда. Соответственно, после этого мы можем сортировать посты по ORDER BY fixed DESC, bumpDate DESC (индекс по (fixed, bumpDate) отлично оптимизирует сортировку).
Конечно, номер хорош тем, что позволяет оптимизировать постраничную навигацию - мы можем сделать запрос WHERE number BETWEEN 10 AND 20 ORDER BY number, который идеально ложится на индекс. Без номера мы вынуждены использовтаь LIMIT, который не очень оптимально работает дальше N-й страницы (подробнее о недостатках LIMIT ниже).
Также, надо решить проблему вывода 3 последних постов треда. Для эффективной их выборки, скорее всего, надо добавлять дополнительные данные - например, таблицу связи между тредами и постами, или колонку, в которой через запятую хранятся id последних постов (тут правда не получится использовать внешние ключи).
Часть колонок у тебя я не понял. Зачем например колонка timestamps у треда и борды? Из названия понять ее назначение невозможно. Зачем last_post_nubmer у борды?
Зачем post_number у постов? Если тебе надо ставить номера при выводе, то может проще их считать в момент выовда? Хотя, вреда от него особого нет, так как номера не требуется пересчитывать при вставке поста, и получение номера последнего поста хрошо оптимизируется индексом.
Добавлю немного про LIMIT. Он используется повсеместно для пагинации, но у него есть недостатки:
- при очень большом OFFSET запросы получаются неэффективные
- в обновляющихся лентах, если ты идешь от 1-й страницы к последней, и добавляются новые посты, ты можешь натыкаться на уже просмотренные посты
Для борьбы с этими проблемами есть особая техника, когда мы используем вместо номера страницы значение поля, по которому сортируются записи, примерно так: /threads?page=4&bumpDate=2017-01-02T12:00:00. Подробнее от этом тут: http://use-the-index-luke.com/no-offset
Такой подход исплоьзуется например на reddit.
И еще, подозреваю, что выборка 500 объектов-постов через ORM будет работать не очень быстро. На таких объемах наверно выгоднее выбирать данные напрямую из БД.
Вообще, борда это довольно тяжелый (но интересный) случай с точки зрения оптимизации, так как тут при чтении треда идет выборка большого объема информации (500 постов - это может быть около 2 Мб текста), и ее трудно сделать быстрой. Мы должны прочитать из БД 2 Мб информации, засунуть ее в шаблон, отформатировать текст, отдать пользователю. Трафик, который может отдать база, он понятное дело, ограничен, и это может стать узким местом. И не только база, канал связи между пользователем и сервером - он тоже ограничен и конечно пересылать маленькие объемы данных было бы выгоднее.
С другой стороны, пользователь, получив тред, будет его долго читать, и может быть, не станет дальше беспокоить сервер запросами. А может он прочтет 2 поста и перейдет к другому треду.
Ну допустим у нас соединение с БД идет по 1 Гбит/с каналу. 2 мегабайта это 16 Мбит, значит мы в принципе не можем в такой ситуации в секунду отдать больше 60 больших тредов в секунду. Это в идеальном случае, если база способна генерировать данные с такой скоростью.
Я знаю, что некоторые борды хранят кеш HTML-кода треда, например в виде файла на диске. При чтении треда мы просто отдаем пользователю файл с диска, а при добавлении поста мы либо перегенерируем файл, либо как-то по-умному вставляем пост в конец треда (что может быть эффективнее). Но тут мы получаем другие проблемы:
- добавление одного поста может требовать перезапись огромного файла. Скорость записи на диск ограничена. То есть мы быстро вставляем 1 пост в базу и не спеша обновляем файл с кешем страницы, и это ограничивает скорость постинга. Более того, диски лучше всего принимают последовательную запись, а когда несколько потоков параллельно пишут треды - общая скорость проседает.
- перегенерация кеша треда требует блокировки. Так как если 2 потока одновременно будут перегенерировать один тред, может выйти что-то кривое.
- с другой стороны, отдача статических файлов - это то, с чем веб-сервера вроде nginx справляются отлично. То есть мы можем даже не дергать PHP приложение при запросе треда, плюс в линуксе есть разные оптимизации, которые позволяют например нгинксу отдавать файл в сеть, не читая его (ядро само этим займется).
- статический файл можно сжать gzip при записи, что снизит нагрузку на диск (так как данных будет меньше, текст хорошо жмется, раз в 5-10) и ускорит отдачу, снизит нагрузку на интернет-канал. С другой стороны, непонятно что тогда отдавать пользователям, не поддерживающим gzip? распаковывать для них файл на лету? Также, в сжатый тред нельзя вставить один новый пост, его можно только переписать целиком (или нет? я не эксперт по gzip).
- с третьей стороны, мы можем для кеша вместо реального диска использовать ram-диск (tmpfs в линукс). То есть хранить файлы в оперативной памяти, лучше наверно в сжатом виде. Запись в память идет с максимальной скоростью, и отдача файлов из памяти nginx будет очень эффективна. Мне кажется, что быстрее варианта, чем отдавать нгинксом файлы из памяти, в принципе нет.
- с четвертой стороны, кеш имеет те недостатки, что файл получается одинаков для всех пользователей. Представь, что мы хотим написать в углу "здравствуйте, Иван" или подсвечивать посты пользователя. Вся наша схема перестанет работать, так как HTML-код теперь разный для разных пользователей. Мы должны либо задейтствовать PHP, и возможно, базу, либо добавлять нужные данные яваскриптом в браузере. На этой борде используется подсветка через яваскрипт
То есть в итоге, я бы поэкспериментировал с кешированием тредов в tmpfs, может в сжатом виде, и посмотрел бы, можно ли при добавлении поста не перегенерировать тред целиком. Хотелось бы конечно иметь и сжатие, и добавление одного поста.
Некоторые борды также генерировали HTML-файлы для страниц со списком постов (главная, 2-я старница, 3-я и тд), но это по моему неэффективно, так как при каждом добавлении поста мы должны перегенерировать все файлы страниц. Это узкое место. Мне кажется, что страницы выгоднее генерировать на лету. Единственное, что тут можно оптимизировать - можно сделать HTML-кеш в памяти для треда (ОП-пост + 3 последних поста + HTML-обвязка). То есть из базы выбираем id тредов на текущей странице, затем из кеша берем их представление и отдаем пользователю. При обновлении треда либо дропаем кеш, либо обновляем.
Зато тут есть возможности для распараллеливания:
- разные борды никак не связаны друг с другом, и мы можем помещать их на отдельные сервера. Причем в базу друг к другу они не лезут и каждый может использовать свою БД
- отдача тредов из tmpfs не параллелится на несколько серверов
- отдача списков тредов методом база + кеш параллелится
Вообще, тут конечно мало рассуждать теоретически, надо брать сервер и делать эксерименты, измерения.
Если хочешь, можешь тоже над этим подумать. Можешь кстати потом померять свою борду.
И еще, вдруг пригодится, у меня есть готовая верстка для борды: https://github.com/codedokode/board-markup
>>943530
public function getUserByEmail ($email) {
$pdoStatement = $this->pdo->prepare("SELECT * FROM users WHERE `email` = :email;");
$pdoStatement->bindValue(":email", $email);
$pdoStatement->execute();
$pdoStatement->setFetchMode(\PDO::FETCH_CLASS, 'User');
$user = $pdoStatement->fetch();
// var_dump($user);
return $user;
}
И еще насчет авторизации: сделал, что, авторизовавшись, пользователь получает код в куки, и он же (код) идет в БД.
И в суперкласс Контроллеров добавил свойство Юзер и метод, который по коду в куках возвращает Юзера; метод вызывается в констукторе, но это как-то криво, так как пришлось для Маппера и класса Авторазации добавить поля в супер класс Контроллера, но этого, навеное, можно избежать: лишние зависимости. Что еще можно придумать?
В чем проблема с виндоуз? PHP кроссплатформенн.
>>942916
Погугли HTTP код 400: это значит Bad Request. То есть гитхабу что-то не нравится.
Для диагностики надо посмотреть, какой запрос отправляет скрипт, правильный ли он, и отправить его вручную и посмотретьт ответ - может там есть намек.
А может конечно гитхаб просто не рад тому, что его пытаются использовать в качестве сервера обновлений. Трафик не бесплатный все таки.
>>942889
В любом языке. ООП в разных языках использует одни и те же принципы, и тщательно выучив ООП в PHP, решив мои задачки по ООП, а также задачу про скидки, ты сможешь легко разобрться с ООП кодом на других языках.
>>942850
Обратиться за поддержкой к разработчикам темы. Либо разобраться, что там происходит и почему ошибка.
>>942760
Да, запрос получился странный.
>>942691
Ты не учел что с сайтом может работать несколько пользователей и у каждого могут быть свои данные. Лучше передавать через GET/POST. сессии/куки имеют тот недостаток, что они общие для всех вкладок в браузере, а URL в каждой вкладке свои.
>>942597
Надо учесть что куки и сессии общие для всех вкладок браузера.
>>942391
Мне кстати не нравится телеграм тем, что надо сдать свою персональную информацию Дурову. Никакой надобности, кроме желания следить за пользователями, в этом нет. Он и вконтакте собирал все, что можно, и тут собирает. Уверен, у него где-нибудь есть бекап базы ВК.
>>942244
Неработающий mstring на ideone - скорее всего результат обновления на PHP7.
>>942059
У тебя в коде ошибка, там лишняя ошибочная строчка есть.
В чем проблема с виндоуз? PHP кроссплатформенн.
>>942916
Погугли HTTP код 400: это значит Bad Request. То есть гитхабу что-то не нравится.
Для диагностики надо посмотреть, какой запрос отправляет скрипт, правильный ли он, и отправить его вручную и посмотретьт ответ - может там есть намек.
А может конечно гитхаб просто не рад тому, что его пытаются использовать в качестве сервера обновлений. Трафик не бесплатный все таки.
>>942889
В любом языке. ООП в разных языках использует одни и те же принципы, и тщательно выучив ООП в PHP, решив мои задачки по ООП, а также задачу про скидки, ты сможешь легко разобрться с ООП кодом на других языках.
>>942850
Обратиться за поддержкой к разработчикам темы. Либо разобраться, что там происходит и почему ошибка.
>>942760
Да, запрос получился странный.
>>942691
Ты не учел что с сайтом может работать несколько пользователей и у каждого могут быть свои данные. Лучше передавать через GET/POST. сессии/куки имеют тот недостаток, что они общие для всех вкладок в браузере, а URL в каждой вкладке свои.
>>942597
Надо учесть что куки и сессии общие для всех вкладок браузера.
>>942391
Мне кстати не нравится телеграм тем, что надо сдать свою персональную информацию Дурову. Никакой надобности, кроме желания следить за пользователями, в этом нет. Он и вконтакте собирал все, что можно, и тут собирает. Уверен, у него где-нибудь есть бекап базы ВК.
>>942244
Неработающий mstring на ideone - скорее всего результат обновления на PHP7.
>>942059
У тебя в коде ошибка, там лишняя ошибочная строчка есть.
> http://ideone.com/9wsu8g - Игра в кубики;
Все верно.
> http://ideone.com/sRvu3e - мини "калькулятор";
Там внизу ошибка: ты забыл поставить скобки после error_reporting(-1);
В остальном все верно.
> http://ideone.com/8x1TeV - Игра в кубики с Пекой;
Тут вместо кучи exit лучше было сделать большой блок if/esleif/elseif/else. В таком блоке может выполниться только одно из условий и exit не нужен.
> http://ideone.com/pJ4TO8 - Пхп без смс;
Верно.
> http://ideone.com/8GN4S3 - депозит;
Нет скобок после error_reporting, также в задаче требовалось ответить, когда накопится миллион на счету. Видимо ты не очень внимательно прочел условие.
> http://ideone.com/O7tPEA - Айфон в кредит;
Верно.
> http://ideone.com/7SJzev - Задание со стихами;
Верно, хотя там какой-то отступ добавился у 2-й и 3-й строки.
> http://ideone.com/MGrQkg - Регулярка на номера телефонов
> (\\s|\\-|\\(|\\)
Тут наверно было бы удобнее использовать квадратные скобки, тем более что внутри них круглые скобки можно писать без бекслешей.
А так, решено правильно.
> http://ideone.com/9wsu8g - Игра в кубики;
Все верно.
> http://ideone.com/sRvu3e - мини "калькулятор";
Там внизу ошибка: ты забыл поставить скобки после error_reporting(-1);
В остальном все верно.
> http://ideone.com/8x1TeV - Игра в кубики с Пекой;
Тут вместо кучи exit лучше было сделать большой блок if/esleif/elseif/else. В таком блоке может выполниться только одно из условий и exit не нужен.
> http://ideone.com/pJ4TO8 - Пхп без смс;
Верно.
> http://ideone.com/8GN4S3 - депозит;
Нет скобок после error_reporting, также в задаче требовалось ответить, когда накопится миллион на счету. Видимо ты не очень внимательно прочел условие.
> http://ideone.com/O7tPEA - Айфон в кредит;
Верно.
> http://ideone.com/7SJzev - Задание со стихами;
Верно, хотя там какой-то отступ добавился у 2-й и 3-й строки.
> http://ideone.com/MGrQkg - Регулярка на номера телефонов
> (\\s|\\-|\\(|\\)
Тут наверно было бы удобнее использовать квадратные скобки, тем более что внутри них круглые скобки можно писать без бекслешей.
А так, решено правильно.
Попробуй поставить setFetchMode перед execute. Если это поможет, проверь что при перестановке обратно проблема вернулась, и напиши тут об этом. Я хочу в этом случае написать разработчикам PHP, чтобы они либо добавили предупреждение в код при неправильном использовании функции, либо хотя бы задокументировали это. Давайте поможем сделать PHP лучше.
> И в суперкласс Контроллеров добавил свойство Юзер и метод, который по коду в куках возвращает Юзера;
Вообще, это, мне кажется, неправильно. На каком основании ты решил этот код внедрить во все контроллеры? Не логичнее ли сделать отдельный класс, отвечающий за авторизацию?
Вот это плохая идея, сегодня ты добавил авторизацию, завтра еще что-нибудь и у тебя базовый контроллер превратится в god object.
Что касается зависимостей, в Симфони например контроллер получает DI контейнер (через метод setContainer) и может сам брать из него что нужно. Хотя никто не запрещает использовать DI и передавать зависимости контроллеру в конструктор.
Док-я англ:
- http://api.symfony.com/3.0/Symfony/Component/DependencyInjection/ContainerAwareInterface.html
-
Я сдаюсь, слишком мало скиллов, чтоб понять что от меня хотят.
Пошел Drupal пытаться осваивать, может там повезет больше, спасибо за ответы :p
Ой, чот тред утонул
Я читаю:
Русские: habr, geektimes
Западный: news.ycombinator.com
Еще есть украинский dou.ua, но там многовато нытья про зарплаты, про вам перезвонят, малолетних сеньоров итд.
Поставил апач, пхп. Пытаюсь создать "Hello World", сохраняю в дефолтную папку var/www/html - "Нет доступа". КАЕФ.
Решил поступить другим путем: поменять директорию. Открываю etc/apache2/apache2.conf, пытаюсь изменить <Directory /var/www/> Хм, странно. Индексная странница находиться в /var/www/html/, а прописано что в www Ха, нельзя сохранить - нет доступа.
Гуглю как получить доступ, затем пишу в терминале хаахаххахаха в терминале редактирую файл))00000))) sudoedit... Ничего не сработало, корневая директория по прежнему осталась стандартной.
Гуглю дальше, пишут что нужно поменять ещё в etc/apache2/sites-enabled/000-default.conf Очень странно.
Пытаюсь его отредактировать выдается ошибка:
sudoedit: etc/apache2/sites-enabled/000-default.conf: editing symbolic links is not permitted
В гугле не очень нашлось по этому. Попробовал написать иначе sudo nano... Хах, редактор открылся, причем тот же самый что и при sudoedit. Но ничего не поменялось.
Использовал вот эти гайды:
http://www.tecmint.com/change-root-directory-of-apache-web-server/
https://www.digitalocean.com/community/tutorials/how-to-move-an-apache-web-root-to-a-new-location-on-ubuntu-16-04 У меня в файле sites-enabled/000-default.conf нету даже строки с <Directory /var/www/html/>, она есть в apache2.conf
Так как поменять корневую директорию?) У меня стоит свежая версия убунту.
Ой всё, даже жить не хочется что такие проблемы возникают. Что я не так сделал бля?
Извиняюсь за кривой почерк, я просто ничего не понимаю как так может быть.
1) Убунта кусок говна. Ставь федорку
2) Если поменяешь бубунту на норм дистр, то сможешь использовать su и su - c " " вместо sudo
3) В sites-enabled лежат симлинки на конфиги из sites-available, правь их там
Чтобы сохранять нормально в папку дай себе на нее права таки.
chown -R user:group /path/to/folder (рекурсивно сменить владельца)
chmod -R 777 /path/to/folder (рекурсивно выставить максимальные права доступа для всех на все что лежит в папке)
А вообще правильнее так будет:
1) владельцем ставишь себя
2) группой владельца группу под которой запущен апач
3) себе ставишь полный доступ
4) остальным можешь вооще закрыть
6) группе апача чтение на все и запись на папку куда загружаешь файлы (uploads какой-нибудь)
Вроде так будет прально. Могу ошибаться впрочем.
> Убунта кусок говна. Ставь федорку
Это много говорит о твоем уровне компетентности. Ты пост-то читал?
Костыли. Как организовать работу (права) над сайтом, если надо дать доступ нескольким пользователям + запускать cli программы? И гарантировать, что из-за дефолтного umask 022 например не получатся защищенные от записи файлы и каталоги?
В гайде Симфони рекомендуют использовать ACL, так как стандартные юниксовые права там плохо работают.
Пост читал, следущие два ответа тоже мои.
Убунта реально глючный кусок говна. Fedora тоже не сахар, как-никак тестовый полигон красношапки, но бубунта умудряется быть даже более глючный чем тестовый полигон. К тому же дистр, который за меня решает можно ли мне логиниться под рутом или нельзя ненужон и место его в мусорном ведре.
И не костыли, а стандартная рабочая схема. Права на создаваемые файлы тоже редактируются если правильно помню.
Нескольким пользователям с такой моделью доступа никак.
Как будет правильнее? Вопрос всего касается, но на примере вывода списка тредов.
1)
тонкая модель с только Eloquent'овскими отношениями
тонкий контроллер, который берет все треды для доски и передает их во вьху
$board = \App\Board::where('link', $board_link)->firstOrFail();
$threads = $board->threads()->get();
return view('threads.index', ['threads' => $threads]);
И во вьюхе уже по отношению, мы вытаскиваем
$thread->posts()->where('op_post', true)->first()->topic
2) моделька пожирнее, где мы скажем довляем функцию op_post, а во вьюхе пользуем просто
$thread->op_post()->topic
3) В контроллере или модели собрать структуру данных не являющуюся отражением модели, но хранящую всю нужную информацию для построения вьюхи и передавать вьюхе уже ее, чтобы она сама БД не дергала
А ещё и кусок кода начал работать после перезагрузки.
Всем спасибо за помощь. Все советы были полезными.
Эм.. а ты как хотел? Почему пхп файлы должны были убираться из редиректа по твоему?
Я в этом новичок, думал -f уберет все статические файлы, а .php оставит. Конструкция !\.(css|js) работает как надо. Но тогда непонятно зачем -f и -d во все гайды добавляют. Разве из-за этого не может возникнуть дыра в безопасноти?
Вот такое гавно на сайте в компании, где кореш работает
Что за код, как получил? Вполне вероятно, что да, если переписать можешь. Но таки подробности нужны.
http://url.ru/uslugi.php?id=1
Не хочу все-таки палить ребят, а то налетят опытные ребята
если вместо 1, написать 2, то 404. Если 1+1, то страница ошибки, если 2-1, то 404. То есть обработка есть.
Если от 8 до 22, то будут выводиться услуги
>Попробуй поставить setFetchMode перед execute.
Не вышло, попробую сегодня студентов начать делать, может, увижу косяк, когда на ясную голову.
>Вообще, это, мне кажется, неправильно. На каком основании ты решил этот код внедрить во все контроллеры? Не логичнее ли сделать отдельный класс, отвечающий за авторизацию?
Я понимаю, что это неправильно, и класс у меня для авторизации есть, только так как вызывать мне нужно было его каждый раз при переходе на страницы, то я и сделал создание объекта класса Авторизация и его вызов в конструкторе Контроллера, чтобы всегда проверялось, авторизован ли пользователь.
>Что касается зависимостей, в Симфони например контроллер получает DI контейнер.
А разве это не становится тем самым ServiceLocator, то есть лишней зависимостью, от которой должен по идее избавлять DI Container?
Я так понимаю в файле инициализации создается DI Container (например, Pimple), которому задаются все схемы зависимостей (функции), а далее с ним как в идеале? Или констуктору нужного Контроллеру передавать или глобальный объект, что плохо? Просто опять-таки не вижу разницы между ServiceLocator, кроме удобного интерфейса, который и не относится к самой идее DI Container. Думаю, у меня в этом вопросе некоторое недопонимание.
И еще несколько вопросов:
Как эффективнее назначать класс active текущей странице из навигационного меню.
Можно в контроллере иметь поле name, а в шаблоне шапки сайта константы (можно ли такое в шаблоне хранить?), в самом меню условие:
<li <?=($name==HOME)?'class="active"':''?>><a href="/">Главная</a></li>
<li <?=($name==GOODS)?'class="active"':''?>><a href="/goods">Товары</a></li>
Когда-то тоже задавал вопрос по поводу внедрения DI-контейнера прямо в контроллер.
Да, это именно сервис-локатор, так как зависимости контроллера скрываются, непонятно что нужно класть в контейнер, чтобы контроллер работал. В Symfony/Slim есть такое понятие как controller-as-a-service: https://symfony.com/doc/current/controller/service.html
То есть в контейнер кладётся контроллер и в качестве его зависимостей указываются нужные сервисы. В Slim можно писать как-то так:
$container['UserController'] = function ($container) {
____return new \UserController($container->get('renderer'));
};
Тогда контроллер будет иметь прозрачные зависимости. Но в этом не очень много смысла так как хороший тонкий контроллер юнит-тестировать не нужно: он выступает лишь прослойкой между HTTP и моделью. Если возникает желание юнит-тестировать контроллер, то он скорее всего содержит что-то лишнее.
>имеет высокий уровень пользования ПК
Что они имеют ввиду "высокий уровень пользования пк", что я должен уметь и знать?
>Вот тут в моем уроке это упомянуто: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Спасибо. И вопрос конечно же платиновый наверняка, но я чет не понял чем TDG от дата маппера отличается.
Вот кстати, эта схема с кучей вложенных коллбеков действительно сложная. В PHP, в React promise сделано интереснее: там есть 2 объекта: Deferred и Promise. И Deferred как раз используется для разрешения или отклонения промиса.
Для тех, кто не в курсе, промисы - это абстракция для асинхронных операций, то есть операций, которые выполняются в фоне в течение какого-то времени. Например, скачивание файла. Функция скачивания файла запускает процесс и возвращает промис, не дожидаясь его окончания. Когда файл скачается, промис разрешится в его содержимое, или в исключение, если произойдет ошибка. Получатель промиса может подписаться на событие, и указанная функция будет вызвана, когда результат будет готов.
Вот как это выглядит в PHP:
$deferred = new Deferred; // объект, управляющий промисом
$promise = $deferred->promise(); // получаем промис
return $promise;
.....
// когда операция завершится, мы задаем ее результат через deferred
$deferred->resolve($result);
По моему, намного нагляднее чем схема с кучей коллбеков в JS.
Понятия не умею, но могу предположить. Программы устанавливать уметь например, удалять, перемещать и копировать файлы, убивать зависшие программы, обновлять драйвера, не пугаться панели управления Windows. В общем-то то, что наверно сегодня каждый школьник умеет так как без этого в ГТА не поиграть.
Кек, только что прочитал и хотел запостить сюда. А ты няша :3
По поводу наглядности: в JS уже если привычно работать то не задумываешься над сложностью конструкций, тем более фреймворки предоставляют свою встроенную реализацию промисов, есть библиотеки опять же (Q)...
Есть такой вот метод с подстановкой данных через плесйхолдеры. Данные передаются хорошо, однако вот такая ошибка в итоге
0SELECT name, second_name, group_number, summary FROM students ORDER BY :sort LIMIT :start, :limit
Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''0', '5'' at line 5 in C:\OpenServer\domains\students.local\app\Models\StudentsGateaway.php:78 Stack trace: #0 C:\OpenServer\domains\students.local\app\Models\StudentsGateaway.php(78): PDOStatement->execute() #1 C:\OpenServer\domains\students.local\app\Controllers\FrontController.php(42): models\StudentsGateaway->getAll('name', 0, 5) #2 C:\OpenServer\domains\students.local\app\Controllers\FrontController.php(27): Controllers\FrontController->actionIndex() #3 C:\OpenServer\domains\students.local\public\index.php(4): Controllers\FrontController->route() #4 {main} thrown in C:\OpenServer\domains\students.local\app\Models\StudentsGateaway.php on line 78
ЧЯДНТ? Данные передаются правильно, если просто вставить без всяких бинд велью - работает.
> Я понимаю, что это неправильно, и класс у меня для авторизации есть, только так как вызывать мне нужно было его каждый раз при переходе на страницы, то я и сделал создание объекта класса Авторизация и его вызов в конструкторе Контроллера, чтобы всегда проверялось, авторизован ли пользователь.
Это не то что бы неправильно, недостаток использования конструктора в том, что этот код вообще всегда будет вызываться, даже если мы не будем выводить шапку с информацией о пользователе.
Может стоит в конструкторе сохранять ссылку на сервис авторизации, а также сделать функцию, собирающую данные для шапки, если там будет не только авторизация. Но если у тебя там только проверка авторизации, можно конечно и в конструкторе ее оставить.
Вопрос про контролллеры и ServiceLocator
Тут есть 2 подхода. Первый: контроллер - это сервис, надо использовать DI, класть его в контейнер. Все красиво и логично, но требует лишней писанины. Второй: контроллер - это не совсем сервис, это скорее стартовая точка приложения и проще использовать тут паттерн ServiceLocator.
Главная проблема ServiceLocator это то, что класс становится зависимым от контейнера со всем его содержимым. Мы не можем этот класс в другой проект перенести или оформить как независимую библиотеку. И мы не можем поменять контейнер, не трогая класс. Ну и некрасиво, что зависимости неточно указаны.
Но контроллер это не повторно используемый код. Он намертво привязан к конкретному проекту и значит, ничего плохого в том, что он знает о контейнере и использует его, нет.
Выбирай сам, какой вариант тебе лучше подходит.
> Просто опять-таки не вижу разницы между ServiceLocator, кроме удобного интерфейса, который и не относится к самой идее DI Container
Никто не запрещает в DI контейнере тоже сделать такой интерфейс, я думаю.
> Можно в контроллере иметь поле name, а в шаблоне шапки сайта константы (можно ли такое в шаблоне хранить?), в самом меню условие:
В шаблоне точно константы объявлять непраивльно. Сделай какой-нибудь класс с вспомогательными функциями и в нем объяви лучше. Либо можно использовать встроенную константу SomeController::class (или CLASS), содержащую имя класса. Вообще, не надо делать константы, соответствующие классам, так как такие константы уже есть.
Вместо поля с именем контроллера лучше просто передавать в шаблон переменную с названием текущего пункта меню.
>>944064
Это ограничители. Они нужны, чтобы отделить шаблон регулярного выражения от флагов, которые идут в конце.
/выражение/флаги
!выражение!флаги
>>944114
Выше написал мнение.
> Но в этом не очень много смысла так как хороший тонкий контроллер юнит-тестировать не нужно: он выступает лишь прослойкой между HTTP и моделью.
Это непрактично и сложно, так как контроллер вызывает кучу других методов и мы можем лишь протестировать его работу в сочетании с другими классами. Ну и да, логики как правило там минимум и тестировать особо нечего.
> Я понимаю, что это неправильно, и класс у меня для авторизации есть, только так как вызывать мне нужно было его каждый раз при переходе на страницы, то я и сделал создание объекта класса Авторизация и его вызов в конструкторе Контроллера, чтобы всегда проверялось, авторизован ли пользователь.
Это не то что бы неправильно, недостаток использования конструктора в том, что этот код вообще всегда будет вызываться, даже если мы не будем выводить шапку с информацией о пользователе.
Может стоит в конструкторе сохранять ссылку на сервис авторизации, а также сделать функцию, собирающую данные для шапки, если там будет не только авторизация. Но если у тебя там только проверка авторизации, можно конечно и в конструкторе ее оставить.
Вопрос про контролллеры и ServiceLocator
Тут есть 2 подхода. Первый: контроллер - это сервис, надо использовать DI, класть его в контейнер. Все красиво и логично, но требует лишней писанины. Второй: контроллер - это не совсем сервис, это скорее стартовая точка приложения и проще использовать тут паттерн ServiceLocator.
Главная проблема ServiceLocator это то, что класс становится зависимым от контейнера со всем его содержимым. Мы не можем этот класс в другой проект перенести или оформить как независимую библиотеку. И мы не можем поменять контейнер, не трогая класс. Ну и некрасиво, что зависимости неточно указаны.
Но контроллер это не повторно используемый код. Он намертво привязан к конкретному проекту и значит, ничего плохого в том, что он знает о контейнере и использует его, нет.
Выбирай сам, какой вариант тебе лучше подходит.
> Просто опять-таки не вижу разницы между ServiceLocator, кроме удобного интерфейса, который и не относится к самой идее DI Container
Никто не запрещает в DI контейнере тоже сделать такой интерфейс, я думаю.
> Можно в контроллере иметь поле name, а в шаблоне шапки сайта константы (можно ли такое в шаблоне хранить?), в самом меню условие:
В шаблоне точно константы объявлять непраивльно. Сделай какой-нибудь класс с вспомогательными функциями и в нем объяви лучше. Либо можно использовать встроенную константу SomeController::class (или CLASS), содержащую имя класса. Вообще, не надо делать константы, соответствующие классам, так как такие константы уже есть.
Вместо поля с именем контроллера лучше просто передавать в шаблон переменную с названием текущего пункта меню.
>>944064
Это ограничители. Они нужны, чтобы отделить шаблон регулярного выражения от флагов, которые идут в конце.
/выражение/флаги
!выражение!флаги
>>944114
Выше написал мнение.
> Но в этом не очень много смысла так как хороший тонкий контроллер юнит-тестировать не нужно: он выступает лишь прослойкой между HTTP и моделью.
Это непрактично и сложно, так как контроллер вызывает кучу других методов и мы можем лишь протестировать его работу в сочетании с другими классами. Ну и да, логики как правило там минимум и тестировать особо нечего.
Ты скорее всего не задумываешься, так как ты только пишешь код. А ты попробуй разобрать чужой код, где много коллбеков.
Я в PHP кое-что писал на промисах и там можно голову сломать от сложности, которая получается (асинхронный запуск разных внешних процессов с разными ветвлениями и таймаутами).
>>944423
Data Mapper заточен именно на загрузку и сохранение объектов (он пытается сделать видимость, что базы нет, а есть просто набор объектов в памяти), а TDG это класс с набором любых действий с таблицей. Также, Data mapper может быть более продвинутым, например, сам уметь обнаруживать изменения в объектах и сохранять их в БД.
Более точные различия можно поискать в книге Фаулера "Шаблоны проектирования корпоративных приложений", где они и описаны.
Разобрался, лол. Оказывается, без дополнительных указаний PDO эмулирует строки(добавляет кавычки) на основе всего этого. Соответсвенно, мне надо сделать такой финт -
$sth->bindValue(":start", $start, \PDO::PARAM_INT);
Однако все равно не работает сортировка, и вот тут я уже не пойму. Даже если вырубить эту эмуляцию через $this->dbh->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false ); все равно сортировка не работает.
Через плейсхолдеры вставляются только значения, цифры и строки в кавычках. Имена полей нельзя. Их придется вставлять напрямую, перед этим проверив по белому списку разрешенных полей, чтобы избежать уязвимостей.
Выключение эмуляции значит, что PDO не будет подставлять данные в запрос сам, а попросит базу это сделать, при условии что она поддерживает такую возможность (иначе он все равно подставит сам).
Понял, что-то уже слышал об этом.
>>944618
Значит для безопасности мне надо написать код, который проверяет значение $order на существование такого поля в таблице? Бля, у меня и без того есть всякие методы, которые выглядят как не пришей к пизде рукав.
А как проверить на несоответствие какому-то элементу массива? Да в любом случае мне надо делать метод, т.к. сортировка у меня не один раз будет, а копипаст это западло, не?
>>940172
Тоже насчёт исключения думал.
1. В FileService валидируем файл.
2. Если есть ошибки - кладём их в исключение, выбрасываем наверх
3. Наверху кладём ошибки в форму? Они ведь в ней и так содержатся.
Бывают ситуации, когда надо вернуть нескоько результатов. Тут есть такие варианты:
- можно вернуть массив вида ['error' => null, 'file' => $file]
- можно вернуть массив вида [$file, $error] - его удобно распаковать через list(..) = ...
- можно вернуть $file через return, а ошибку как аргумент переданный по ссылке:
$error = null;
$file = upload(..., &$error);
- можно сделать объект UploadResult с 2 полями (больше всего писанины)
Не очень удобно конечно, в других языках вроде Го есть синтаксис для возврата нескольких значений с указанием типов: func something() (File, Error) { ... }
Насчет кода - стоит ли FileNameGenerator делать полноценной зависимостью? Может проще метод вроде generateName поместить в этот же класс?
> Почему сразу не использую UploadedFIle: https://github.com/symfony/http-foundation/blob/master/File/UploadedFile.php
> Его тоже можно переместить, даже если он получен не из HTTP (параметр $test в конструкторе), но, как я понял, параметр $test тут только для тестов.
UploadedFile это именно что загруженный в $FILES файл, хранящийся во временной папке, его возвращать нелогично.
> как я понял, параметр $test тут только для тестов.
Именно так.
>>944639
Насчет исключений есть разные мнения. Есть те, кто считает что исключения мжно выбрасывать при валидации, есть мнение что неправильно.
Мне больше нравится идея возвращать объект исключения, если это не фатальная ошибка и выбрасывать, если что-то например передано неправиьно. Например, в комментарии не указан IP адрес автора.
Бывают ситуации, когда надо вернуть нескоько результатов. Тут есть такие варианты:
- можно вернуть массив вида ['error' => null, 'file' => $file]
- можно вернуть массив вида [$file, $error] - его удобно распаковать через list(..) = ...
- можно вернуть $file через return, а ошибку как аргумент переданный по ссылке:
$error = null;
$file = upload(..., &$error);
- можно сделать объект UploadResult с 2 полями (больше всего писанины)
Не очень удобно конечно, в других языках вроде Го есть синтаксис для возврата нескольких значений с указанием типов: func something() (File, Error) { ... }
Насчет кода - стоит ли FileNameGenerator делать полноценной зависимостью? Может проще метод вроде generateName поместить в этот же класс?
> Почему сразу не использую UploadedFIle: https://github.com/symfony/http-foundation/blob/master/File/UploadedFile.php
> Его тоже можно переместить, даже если он получен не из HTTP (параметр $test в конструкторе), но, как я понял, параметр $test тут только для тестов.
UploadedFile это именно что загруженный в $FILES файл, хранящийся во временной папке, его возвращать нелогично.
> как я понял, параметр $test тут только для тестов.
Именно так.
>>944639
Насчет исключений есть разные мнения. Есть те, кто считает что исключения мжно выбрасывать при валидации, есть мнение что неправильно.
Мне больше нравится идея возвращать объект исключения, если это не фатальная ошибка и выбрасывать, если что-то например передано неправиьно. Например, в комментарии не указан IP адрес автора.
Тогда как насчёт такого алгоритма:
- собрать форму без валидатора
- если форма отправлена - вытащить из неё файл и передать его в FileService#upload, получить результат с объектами ConstraintViolationList и Entity\File
- если список ошибок пуст, перенаправить пользователя на страницу с файлом
- если не пуст, положить ошибки в форму, отрисовать страницу с формой
Если ничего не упустил, то такой FileService можно и из консоли запускать.
>Может проще метод вроде generateName поместить в этот же класс?
Думаю, что проще, раз FileGenerator больше ни одному из сервисов не нужен.
оператор == использует (неявное) приведение типов, а оператор === не использует его.
>тут не очень хорошо сделано. Если ты хочешь требовать регистрацию, то надо не выводить форму на любой странице, а делать редирект на страницу регистрации. Если ты хочешь потом вернуться назад, то надо еще передавать URL исходной страницы (а перед редиректом назад проверять, что он на твоем сайте, иначе это будет открытый редирект).
Я то понял как, только зачем?
Вот код:
<?php
class Test
{
public $text;
public function __construct()
{
$this->text = fgets(STDIN);
}
public function strln()
{
echo strlen($this->text);
}
}
$a=new Test;
$a->strln();
sleep(20);
Что за хуита?
class Test
{
public $text;
public function __construct()
{
$this->text = fgets(STDIN);
}
public function strln()
{
echo strlen($this->text);
}
}
$a=new Test;
$a->strln();
sleep(20);
Ты кириллицу туда вводишь? Простой strlen() показывает тебе количество байтов, тогда как кириличный символ два байта занимает, т.е. длину кириличной строки ты так не определишь, юзай mb_strlen().
Я все-таки был прав, это проблемы с fgets(STDIN);
var_dump вывел
string<5> "aaa
"
Как это фиксить?
когда поправлю читабельность от своих привычек - покажу. Но мне интересен теоретический необходимый баланс знаний.
http://ideone.com/BKOWHE
Я не понимат, кому выше и каким образом кидать виджет-ивенты, если я использую сложные V/C виджеты.
Попробовал вот так: в методе PopupController.prototype._attachToPopupView я кину искусственное событие в parentView. Правильно?
Так же вопрос #2, как ловить вот такие события. Так как popup у меня вроде как динамический, я решил сделать количество кнопок по выбору. Их количество и текст внутри зависит от параметра массива optionTxt и его длины, соответственно. Ну а как мне обозначать тип таких событий вот здесь - см. строку "вопрос 2" в коде? Мне всего лишь надо передать номер в старший класс (DomGameView в моем случае) где уже с выбранной опцией вызовется нужный метод из модели. Не делать же заранее 100 констант с номером опций попапа?
Пока не проверяй на гитхабе моего сапера, я коммит готовлю с исправлениями твоих первых правок.
Уточню на счет второго вопроса. В методе dispatcher.addEventListener(EVENT.SOME, func) (его код кстати здесь https://github.com/greenTea242/MinesweeperMVC/blob/master/src/models/EventDispatcher.js , напоминаю что это не нативный addEventListener) первое событие уникальное. А у меня может n-ое количество ивентов, так как кнопок может быть много.
});
Ну с кнопками я бы предложил например такой вариант:
назначить каждой кнопке свой id и при нажатии передавать его в событии:
var popupCtrl = new PopupCtrl(...);
popupCtrl.addButton(SOME_ID, 'Do something');
popupCtrl.addButton(BTN_YES, 'Yes');
или:
popupCtrl.setButtons([
{ id: SOME_ID, title: 'Do Something'},
...
]);
и далее мы подписываемся на события попапа:
popupCtrl.getDispatcher().addEventListener(Popup.EVENT_SELECT, function (buttonId) {
...
});
Вот этот код выше можно прописать в контроллере игрового поля. View игрового поля про попап тогда знать не обязательно.
Также, я не вижу смысла передавать в контроллер попапа объект его view. Можно наверно создавать view прямо в нем. Все равно ведь никто другой этот view использовать не будет.
Еще может быть стоит подумть об объединении PopupView и PopupCtrl, например в PopupView. В случае с игровым полем - может смысл есть разделять, так как там относительно сложный код, мы можем использовать view без контроллера для отображения данных итд. А тут не очень понятно, в чем выгода.
Может сделать один класс с такими методами?
- addButton() или setButtons() - задают кнопки
- show() - создает HTML-код и ставит обработчики на кнопки
- destroy() - закрывает попап
- getDispatcher() - позволяет подписаться на событие закрытия попапа кнопкой
Вариант с таким кодом: parentViewDispatcher.dispatchEvent(new PopupEvent(PopupEvent.CLOSE_OPTION)); явно сомнительный, так как мы лезем в другое вью и от его имени выбрасываем какие-то события. Это нарушение инкапсуляции, один объект не должен выбрасывать события от имени другого.
И также тут не нужен this._parentContainer.addEventListener("click" - события логично ловить в самом попапе. А твой код пытается ловить события и в родительском вью.
> Не делать же заранее 100 констант с номером опций попапа?
Делать или не делать константы - это задача того, кто вызывает попап, а не попапа.
> напоминаю что это не нативный addEventListener) первое событие уникальное. А у меня может n-ое количество ивентов, так как кнопок может быть много.
Можно сделать единственное событие, соответствующее закрытию попапа, хранящее, какой опцией или кнопкой он закрыт.
Вообще, у нас конечно сложноватая схема получилась. Такие иерархии из виджетов обычно используются при построении GUI в десктопных приложениях.
Для сравнения, напишу, как это реализуется в HTML-фремворках вроде angular/react:
- там мы не пишем код для view в виде класса, не пишем код создания дочерних виджетов, а только делаем HTML-шаблон для компонента, в котором могут указываться ссылки на другие компоненты примерно такого вида:
<div>
{$text}
<SomeWidget x={$x} y={$y}/>
<button onclick={doDomething}>Do domething</button>
</div>
Вот это весь view. Нет кода, только разметка.
- вместо явной подписки на события мы указываем название метода-обработчика из контроллера в HTML-разметке
- для обновления данных на странице и чтения данных с нее используется data-binding, когда мы например можем привязать инпут к переменной и она будет автоматически обновляться при вводе туда данных, и наоборот, при изменении значения переменной обновляется инпут.
Это в сумме позволяет избавиться от написания рутинного кода работы с DOM (поиск элементов по селектору, подписка на события, обновление HTML), в итоге view у нас просто представлен куском HTML-подобной разметки, и controller не содержит работы с DOM. Ну к примеру, чтобы обновить текст на экране, мы в контроллере просто меняем значение переменной, которая привязана к этому блоку текста.
Примерно так получается (тут используется синтаксис ES6):
// компонент (по сути контроллер из MVC)
class Counter extends React.Component {
constructor() {
this.setState({
count: 0
});
}
// обработчик нажатия на кнопку
increment() {
this.setState({ count: this.state.count + 1 });
}
// в реакте view описывается разметкой прямо в компоненте
render() {
return <div>Count: {this.state.count}</div>
<button onclick={increment}>+1</button>
}
}
Как видишь, много рутинного кода для работы с DOM здесь выкинуто. Ну это конечно такой простой пример, где мы данные храним прямо в контроллере. Если ты захочешь добавить какую-то сложную модель, то код конечно усложнится, придется городить всякие хранилища, преобразователи (redux) и прочее, так как сама по себе эта штука отслеживать изменения в модели не умеет.
Но для попапа, у которого нет модели, этот подход дал бы такой же простой код, как в примере выше.
Надеюсь я тебя заинтересовал после реализации MVC без фреймворков посмотреть на react. Но сначала с тем, что есть, разберись.
Ну с кнопками я бы предложил например такой вариант:
назначить каждой кнопке свой id и при нажатии передавать его в событии:
var popupCtrl = new PopupCtrl(...);
popupCtrl.addButton(SOME_ID, 'Do something');
popupCtrl.addButton(BTN_YES, 'Yes');
или:
popupCtrl.setButtons([
{ id: SOME_ID, title: 'Do Something'},
...
]);
и далее мы подписываемся на события попапа:
popupCtrl.getDispatcher().addEventListener(Popup.EVENT_SELECT, function (buttonId) {
...
});
Вот этот код выше можно прописать в контроллере игрового поля. View игрового поля про попап тогда знать не обязательно.
Также, я не вижу смысла передавать в контроллер попапа объект его view. Можно наверно создавать view прямо в нем. Все равно ведь никто другой этот view использовать не будет.
Еще может быть стоит подумть об объединении PopupView и PopupCtrl, например в PopupView. В случае с игровым полем - может смысл есть разделять, так как там относительно сложный код, мы можем использовать view без контроллера для отображения данных итд. А тут не очень понятно, в чем выгода.
Может сделать один класс с такими методами?
- addButton() или setButtons() - задают кнопки
- show() - создает HTML-код и ставит обработчики на кнопки
- destroy() - закрывает попап
- getDispatcher() - позволяет подписаться на событие закрытия попапа кнопкой
Вариант с таким кодом: parentViewDispatcher.dispatchEvent(new PopupEvent(PopupEvent.CLOSE_OPTION)); явно сомнительный, так как мы лезем в другое вью и от его имени выбрасываем какие-то события. Это нарушение инкапсуляции, один объект не должен выбрасывать события от имени другого.
И также тут не нужен this._parentContainer.addEventListener("click" - события логично ловить в самом попапе. А твой код пытается ловить события и в родительском вью.
> Не делать же заранее 100 констант с номером опций попапа?
Делать или не делать константы - это задача того, кто вызывает попап, а не попапа.
> напоминаю что это не нативный addEventListener) первое событие уникальное. А у меня может n-ое количество ивентов, так как кнопок может быть много.
Можно сделать единственное событие, соответствующее закрытию попапа, хранящее, какой опцией или кнопкой он закрыт.
Вообще, у нас конечно сложноватая схема получилась. Такие иерархии из виджетов обычно используются при построении GUI в десктопных приложениях.
Для сравнения, напишу, как это реализуется в HTML-фремворках вроде angular/react:
- там мы не пишем код для view в виде класса, не пишем код создания дочерних виджетов, а только делаем HTML-шаблон для компонента, в котором могут указываться ссылки на другие компоненты примерно такого вида:
<div>
{$text}
<SomeWidget x={$x} y={$y}/>
<button onclick={doDomething}>Do domething</button>
</div>
Вот это весь view. Нет кода, только разметка.
- вместо явной подписки на события мы указываем название метода-обработчика из контроллера в HTML-разметке
- для обновления данных на странице и чтения данных с нее используется data-binding, когда мы например можем привязать инпут к переменной и она будет автоматически обновляться при вводе туда данных, и наоборот, при изменении значения переменной обновляется инпут.
Это в сумме позволяет избавиться от написания рутинного кода работы с DOM (поиск элементов по селектору, подписка на события, обновление HTML), в итоге view у нас просто представлен куском HTML-подобной разметки, и controller не содержит работы с DOM. Ну к примеру, чтобы обновить текст на экране, мы в контроллере просто меняем значение переменной, которая привязана к этому блоку текста.
Примерно так получается (тут используется синтаксис ES6):
// компонент (по сути контроллер из MVC)
class Counter extends React.Component {
constructor() {
this.setState({
count: 0
});
}
// обработчик нажатия на кнопку
increment() {
this.setState({ count: this.state.count + 1 });
}
// в реакте view описывается разметкой прямо в компоненте
render() {
return <div>Count: {this.state.count}</div>
<button onclick={increment}>+1</button>
}
}
Как видишь, много рутинного кода для работы с DOM здесь выкинуто. Ну это конечно такой простой пример, где мы данные храним прямо в контроллере. Если ты захочешь добавить какую-то сложную модель, то код конечно усложнится, придется городить всякие хранилища, преобразователи (redux) и прочее, так как сама по себе эта штука отслеживать изменения в модели не умеет.
Но для попапа, у которого нет модели, этот подход дал бы такой же простой код, как в примере выше.
Надеюсь я тебя заинтересовал после реализации MVC без фреймворков посмотреть на react. Но сначала с тем, что есть, разберись.
Тут будут только ответы на старые вопросы. Если вас пропустили - напомните о себе в новом треде.
ну а куда развиваться то?
на работе будет тоже самое, что курс хохла про интернет магазин же, я хз. Как они синьоров отличают? Три сайта в день шлепает вместо одного?
Да. Нет.
Правильно ли?
Ошибка в функции?
https://t.me/joinchat/AAAAAAlT02py0soe6xnbMw
Десятки опытных программистов без труда ответят на любой ваш вопрос!
include_once 'connect.php';
$ip = $_SERVER['REMOTE_ADDR'];
$stmt = $pdo->query("SELECT * FROM admins WHERE admin_ip='$ip'");
while ($row = $stmt->fetch())
{
$ad = $row["admin_ip"];
}
if ($ad == $ip){
echo 'Йов битч ты админ';
}
else {
echo 'Ты не админ, ухади';
}
да
Вы видите копию треда, сохраненную 15 марта 2017 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.