Это копия, сохраненная 1 декабря 2015 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко.
Это тред для начинающих. Не написал за свою жизнь ни одной программы? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Netbeans PHP или PhpStorm (с ним будет удобнее).
Предыдущий тред был тут: >>558058 (OP)
Что самое главное для программиста? Умение аккуратно оформлять код (читай второй пост).
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП отвечает даже на самые нубские вопросы. ОП заходит где-то раз в день-два, не жди его, решай задачки дальше.
У нас есть уроки по основам PHP, они собраны и выложены по адресу http://archive-ipq-co.narod.ru/ Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь, то надо начать с него. Он простой и понятный (по крайней мере в начале). Там есть задачи, их надо решать обязательно (чтобы стать программистом, надо писать код — иначе никак). Пости ссылки на решения в тред, мы их проверим, напишем замечания и дадим советы по улучшению.
Если не знаешь как решать, запости код, напиши в каком месте остановился и попроси подсказку.
Учебник дает основы языка PHP, но чтобы делать сайты, этого недостаточно. Если ты его прошел, то надо переходить в более серьезным задачкам, которые научат тебя как выдавать страницы в браузер, работе с таблицами в БД, работе с формами, MVC.
- Простая, но полезная задача сделать список студентов: https://github.com/codedokode/pasta/blob/master/student-list.md
- Более сложная задача сделать файлообменник на микрофреймворке Slim: https://gist.github.com/codedokode/9424217
- Еще более сложная и долгая задача на Yii/Yii2: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование
- Если ты все решил, переходи к Symfony 2/Doctrine 2
Чтобы делать эти задания, тебе надо установить Апач + PHP (можно заодно сразу и MySQL) на компьютер. Вот полезные инструкции:
https://gist.github.com/codedokode/10774100
https://gist.github.com/codedokode/7054af4a03865c4cc863
Может тебе понадобится пользоваться командной строкой, вот гайд https://gist.github.com/codedokode/10539568
Вот небольшой туториал по тому как начать использовать PHP на сервере для отдачи странички в браузер: https://php.net/manual/ru/tutorial.php Увы, уроков плавно подводящих к тому, как сделать задачи выше, пока нет, так что если что, задавай вопросы.
Решения задач лучше показать мне, особенно на ООП,так как сам ты вряд ли увидишь все ошибки. Пости свой код на гитхаб и вкидывай ссылку в тред по мере решения. Я прокомментирую и укажу на ошибки.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания JS/HTML/CSS/SQL. Решай их параллельно с задачами выше.
- HTML/CSS: https://gist.github.com/codedokode/58ebc90bd006baf4b35c
- JS: https://gist.github.com/codedokode/ce30e7a036f18f416ae0
- Проверялка решений на JS: http://dkab.github.io/jasmine-tests/
- MySQL: https://gist.github.com/codedokode/10539213
Что почитать
- Мануал по PHP — http://www.php.net/manual/ru/langref.php
- Сайт phptherightway (перевод на русский: http://getjump.me/ru-php-the-right-way/ )
- По PHP: Профессиональное программирование на PHP Джордж Шлосснейгл
- По PHP: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- JS: learn.javascript.ru
- Про Git: https://git-scm.com/book/ru/v1
Нужен ли ООП, фреймворки, MVC? — Да, однозначно. Посмотри любую вакансию.
Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.net/45000175 и получи личную немного устаревшую копию сайта
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет.
Подскажи сайты для поиска работы, я не умею гуглить? — hh.ru, geekjob.ru, moikrug.ru (склеен с brainstorage.me), fl.ru, upwork.com (бывший одеск). Имей в виду, что кроме фриланса есть еще постоянная удаленная работа (remote job) когда тебе не надо тратить время на поиск заказов и переговоры с неадекватными заказчиками.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
А я напомню составить задачку на написание клиентского приложения на JS и может быть, на командную строку в Линуксе.
Так и не понял как мне поможет простая замена echo на return
Правда я сортировку по столбцам еще не сделала там. Но все же.
http://ideone.com/lF688L
Норм. Но в твоём случае можно вместо elseif ($tx != $xt) писать просто else. Зачем лишний раз проверять условие?
notOP
Ну это ведь очевидно, что если ты вносишь изменения в View в виде дополнительных полей, то тебе надо внести изменения и в контроллер, который будет работать с данными из этих полей, и изменить запрос в модели которая работает с товарами.
>По поводу использования одного хеша с многими ключами (page:visits:unique) против хранения отдельных ключей (visit:{IP}:{id})
Да, про масштабируемость не подумал.
Ну и у меня уже подсознательная тяга к инкапсуляции (положить все в один хеш, а не держать миллионы несвязанных ключей) и реляционности.
И был вопрос о выборке необходимых ключей, я тогда знал только команду KEYS, которая перебирает все ключи, поэтому непродуктивна.
Кажется, там еще есть команда SCAN, но я пока не понимаю, как она работает.
http://redis.io/commands/keys
>Don't use KEYS in your regular application code. If you're looking for a way to find keys in a subset of your keyspace, consider using SCAN or sets
http://redis.io/commands/scan
>SCAN is a cursor based iterator. This means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call.
Что за "cursor"? О каком итераторе они говорят? Это какой-то алгоритм? яннп
Ну хорошо, я согласен что нужно хранить отдельные ключи вместо хеша, только не понимаю как по ним искать нужные.
>в чем проблема накопления одинаковых id в очереди?
Лишняя память. Зачем мне по сто-тысяче раз хранить в списке повторяющиеся значения, если можно хранить только уникальные?
Хотя здесь наверное это не критично, и лучше хранить лишнюю информацию, чем добавлять лишние операции в алгоритм (проверка сета с уникальными посещениями перед записью в очередь).
Хорошо, сет тоже выбрасываем.
>с транзакциями не получится простое масштабирование на несколько серверов.
Я пока не в курсе, как реализованы транзакции в редисе, поэтому не понимаю почему транзакции препятствуют масштабированию.
>редис не гарантирует что все команды в транзакции будут выполнены. Это совсем не то же что транзакции в MySQL например.
Этого тоже не знал, я думал транзакция = acid.
>мы же можем просто класть каждое уникальное посещение в очередь. А при выборке суммировать их и группировать, чтобы получить дельты.
Думал об этом, но отказался по соображениям: если "суммировать и группировать" на стороне редиса, то я пока просто не знаю как это сделать; если на стороне php, то нам понадобится выгрести из редиса все пары ip:id, не много ли памяти это займет? Если каждый ключ допустим в среднем 30 байт, то десять миллионов посещений в сутки даст почти 300 мб.
А, ну да, можно же выгребать это по частям, все время забываю.
>- в каком порядке коммитим mysql/redis?
Сначала mysql конечно. Он же пишет на диск. Redis пишет в оперативную память, это пара милисекунд. Да, у нас остается точка отказа между этими двумя коммитами, но во-первых это промежуток наверное меньше милисекунды, во-вторых я просто не знаю, что тут можно сделать.
Ну хорошо, значит упрощаем алгоритм.
При просмотре страницы:
Если ключ ip:id существует, то конец. Иначе создаем такой ключ, и добавляем его в очередь.
Скрипт по расписанию:
1. Берет длину очереди (LLEN), фиксирует это значение в переменную. Так что нас не беспокоит, что в конец очереди будут продолжать добавляться записи, мы будем работать только с этими N записей из головы очереди.
2. Запрашиваем порциями данные из редиса (без удаления их из очереди), пока не достигнем этой суммы N.
2.1. Парсим каждую строку вида "ip:id", чтобы получить ip и id, сохраняем в некий массив вида ['id страницы'=>кол-во посещений,].
2.2. Получаем минимальное и максимальное значение этого массива. Затем в цикле ходим от min до max, осуществляя поиск ключей для каждого такого значения.
В этом поможет функция array_keys со вторым параметром. В конце этого шага получаем массив вида ['кол-во посещений'=>array('все id страниц с таким кол-вом посещений')].
2.3. Ходим в цикле и для каждого ключа массива, упомянутого выше, подставляем массив айдишников в запрос:
UPDATE tbl_name SET visit_counter = visit_counter + delta WHERE id IN (implode($arr, ',')).
2.4. Коммитим mysql, затем удаляем обработанное кол-во записей из начала очереди.
Значит транзакция редиса нам тут не нужна, поскольку мы сначала получаем данные из начала очереди, а затем в конце их оттуда удаляем, посторонний процесс не будет работать с началом очереди, на то она и очередь.
Как видишь, здесь значительно больше телодвижений в самом php-скрипте, тогда в предыдущем алгоритме я пытался как бы сымитировать реляционность, типа таблиц mysql за счет создания нескольких структур типа сетов и хешей, чтобы несколькими запросами сразу получить нужные данные, а не вычленять их в скрипте.
>- настройки сохранения данных на диск, AOF и RDB, придется сначала разобраться как каждая работает. Ими ты выбираешь между надежностью, производительностью и объемом дискового трафика.
Да, я что-то читал, типа выгоднее всего сбрасывать на диск каждую секунду, тогда и производительность будет все еще хорошей, и возможная потеря сводится только к данным за одну секунду.
Ну ладно, пошел читать матчасть.
>По поводу использования одного хеша с многими ключами (page:visits:unique) против хранения отдельных ключей (visit:{IP}:{id})
Да, про масштабируемость не подумал.
Ну и у меня уже подсознательная тяга к инкапсуляции (положить все в один хеш, а не держать миллионы несвязанных ключей) и реляционности.
И был вопрос о выборке необходимых ключей, я тогда знал только команду KEYS, которая перебирает все ключи, поэтому непродуктивна.
Кажется, там еще есть команда SCAN, но я пока не понимаю, как она работает.
http://redis.io/commands/keys
>Don't use KEYS in your regular application code. If you're looking for a way to find keys in a subset of your keyspace, consider using SCAN or sets
http://redis.io/commands/scan
>SCAN is a cursor based iterator. This means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call.
Что за "cursor"? О каком итераторе они говорят? Это какой-то алгоритм? яннп
Ну хорошо, я согласен что нужно хранить отдельные ключи вместо хеша, только не понимаю как по ним искать нужные.
>в чем проблема накопления одинаковых id в очереди?
Лишняя память. Зачем мне по сто-тысяче раз хранить в списке повторяющиеся значения, если можно хранить только уникальные?
Хотя здесь наверное это не критично, и лучше хранить лишнюю информацию, чем добавлять лишние операции в алгоритм (проверка сета с уникальными посещениями перед записью в очередь).
Хорошо, сет тоже выбрасываем.
>с транзакциями не получится простое масштабирование на несколько серверов.
Я пока не в курсе, как реализованы транзакции в редисе, поэтому не понимаю почему транзакции препятствуют масштабированию.
>редис не гарантирует что все команды в транзакции будут выполнены. Это совсем не то же что транзакции в MySQL например.
Этого тоже не знал, я думал транзакция = acid.
>мы же можем просто класть каждое уникальное посещение в очередь. А при выборке суммировать их и группировать, чтобы получить дельты.
Думал об этом, но отказался по соображениям: если "суммировать и группировать" на стороне редиса, то я пока просто не знаю как это сделать; если на стороне php, то нам понадобится выгрести из редиса все пары ip:id, не много ли памяти это займет? Если каждый ключ допустим в среднем 30 байт, то десять миллионов посещений в сутки даст почти 300 мб.
А, ну да, можно же выгребать это по частям, все время забываю.
>- в каком порядке коммитим mysql/redis?
Сначала mysql конечно. Он же пишет на диск. Redis пишет в оперативную память, это пара милисекунд. Да, у нас остается точка отказа между этими двумя коммитами, но во-первых это промежуток наверное меньше милисекунды, во-вторых я просто не знаю, что тут можно сделать.
Ну хорошо, значит упрощаем алгоритм.
При просмотре страницы:
Если ключ ip:id существует, то конец. Иначе создаем такой ключ, и добавляем его в очередь.
Скрипт по расписанию:
1. Берет длину очереди (LLEN), фиксирует это значение в переменную. Так что нас не беспокоит, что в конец очереди будут продолжать добавляться записи, мы будем работать только с этими N записей из головы очереди.
2. Запрашиваем порциями данные из редиса (без удаления их из очереди), пока не достигнем этой суммы N.
2.1. Парсим каждую строку вида "ip:id", чтобы получить ip и id, сохраняем в некий массив вида ['id страницы'=>кол-во посещений,].
2.2. Получаем минимальное и максимальное значение этого массива. Затем в цикле ходим от min до max, осуществляя поиск ключей для каждого такого значения.
В этом поможет функция array_keys со вторым параметром. В конце этого шага получаем массив вида ['кол-во посещений'=>array('все id страниц с таким кол-вом посещений')].
2.3. Ходим в цикле и для каждого ключа массива, упомянутого выше, подставляем массив айдишников в запрос:
UPDATE tbl_name SET visit_counter = visit_counter + delta WHERE id IN (implode($arr, ',')).
2.4. Коммитим mysql, затем удаляем обработанное кол-во записей из начала очереди.
Значит транзакция редиса нам тут не нужна, поскольку мы сначала получаем данные из начала очереди, а затем в конце их оттуда удаляем, посторонний процесс не будет работать с началом очереди, на то она и очередь.
Как видишь, здесь значительно больше телодвижений в самом php-скрипте, тогда в предыдущем алгоритме я пытался как бы сымитировать реляционность, типа таблиц mysql за счет создания нескольких структур типа сетов и хешей, чтобы несколькими запросами сразу получить нужные данные, а не вычленять их в скрипте.
>- настройки сохранения данных на диск, AOF и RDB, придется сначала разобраться как каждая работает. Ими ты выбираешь между надежностью, производительностью и объемом дискового трафика.
Да, я что-то читал, типа выгоднее всего сбрасывать на диск каждую секунду, тогда и производительность будет все еще хорошей, и возможная потеря сводится только к данным за одну секунду.
Ну ладно, пошел читать матчасть.
>не должна передавать лишние аргументы внутренней функции: partialAny(fn, 1, undefined, 3, undefined)(2) -> [1, 2, 3, undefined]
Я так понял partialAny(fn, 1, undefined, 3, undefined)(2) -> [1, 2, 3, undefined] вот так и не должно происходить? Что-то если честно не могу додуматься куда деть этот второй массив аргументов. Ковырял отладчик, не особо помогло, дай подсказочку :3
>главное что нужно, это понимание принципов нормализации (именно понимание, а не заучивание определений)
А понимание откуда берется? Нужно нащупать определенную точку в толстой кишке, и оно включится? Или благодаря практическому опыту?
У тебя как всегда неадекватно сложные задачи. Мне так почему-то кажется, что проектировали itunes и google play люди с немного большим опытом, чем у меня, и потратили чуть больше чем полчаса.
Я не знаю, как это решить.
Например, как хранить ссылку на исполнителя трека? Исполнителем может быть либо группа, либо сольный артист (который может одновременно принадлежать к одной группе, а может даже и не одной). Если я сделаю две таблицы, для групп и для артистов, то я не смогу у трека выставить внешний ключ одновременно на две таблицы. Или создать два ключа? Тогда половина значений будет null, тоже некрасиво.
Можно создать таблицу исполнителей, где будут и группы, и артисты. Внешние ключи хранить в таблице групп и артистов на эту таблицу. Как например советуют здесь http://stackoverflow.com/questions/2002985/mysql-conditional-foreign-key-constraints
Скажем так
artist
- id
- name
...
- performer_id
foreign key (performer_id) references performer (id)
ensemble
- id
...
- performer_id
foreign key (performer_id) references performer (id)
performer
- id
- type (enum: artist, ensemble)
Но тогда я не представляю, как получить информацию по исполнителю.
Дай хоть слова, по которым гуглить.
>главное что нужно, это понимание принципов нормализации (именно понимание, а не заучивание определений)
А понимание откуда берется? Нужно нащупать определенную точку в толстой кишке, и оно включится? Или благодаря практическому опыту?
У тебя как всегда неадекватно сложные задачи. Мне так почему-то кажется, что проектировали itunes и google play люди с немного большим опытом, чем у меня, и потратили чуть больше чем полчаса.
Я не знаю, как это решить.
Например, как хранить ссылку на исполнителя трека? Исполнителем может быть либо группа, либо сольный артист (который может одновременно принадлежать к одной группе, а может даже и не одной). Если я сделаю две таблицы, для групп и для артистов, то я не смогу у трека выставить внешний ключ одновременно на две таблицы. Или создать два ключа? Тогда половина значений будет null, тоже некрасиво.
Можно создать таблицу исполнителей, где будут и группы, и артисты. Внешние ключи хранить в таблице групп и артистов на эту таблицу. Как например советуют здесь http://stackoverflow.com/questions/2002985/mysql-conditional-foreign-key-constraints
Скажем так
artist
- id
- name
...
- performer_id
foreign key (performer_id) references performer (id)
ensemble
- id
...
- performer_id
foreign key (performer_id) references performer (id)
performer
- id
- type (enum: artist, ensemble)
Но тогда я не представляю, как получить информацию по исполнителю.
Дай хоть слова, по которым гуглить.
https://ideone.com/u5hpTU
Сделал все функции, кроме проверки на ошибки. Это нужно делать через try..catch? Там получается каждый метод в него нужно будет оборачивать?
Госпади, да что ты его задачки читаешь вообще, он же шизик? Он хорош только советы давать по уже готовым проектам, которые ты сам реализовал и выложил. Возьми фреймворк и пиши нормальный стандартный интернет магазин по продаже хуйни, коих тысячи в сети. Не забудь админку. Все это уже будет твоя наработка, с которой можно пиздовать во фриланс и на ее основе штамповать бабершопы.
>> Ну я сделал так, но все равно не понимаю как посчитать количество клеток по горизонтали и вертикали одновременно без моей функции.
>Хорошо, давай подойдем с другой стороны. Допустим кошка стоит выше мышки на 5 клеток. Сколько ей надо ходов чтобы добраться до нее (при условии что мышка стоит на месте)?
>
>А если мы сместим мышку право на 1 клетку? На 2? На 5? на 10?
Предлагаешь что ли отдельно считать расстояние по X и по Y?
>> Еще я не понимаю, почему плохо использовать декартово расстояние:
>В общем это не плохо но число ходов даст более точное значение. Ведь мир искажен, кошка по диагонали делает ход, проходя 1.44 клеточки (корень из 2).
Не правда, кошка всегда будет проходить целое количество клеток. Высчитывание расстояния на это никак не влияет, только оценивает ход.
Я совершенно не понимаю эту задачу.
>Так и не понял как мне поможет простая замена echo на return
Пользоваться echo считается дурным тоном в программировании. Им в большинстве случаев пользуются только чтобы искать ошибки в коде.
А использование return сделает код, как минимум, гибче: можно будет приписывать значение функции к переменной.
function getName($name) {
return $name;
}
$var = getName('Bill');
Если ты будешь использовать здесь echo за место return, то значение $var не измениться.
Так же не забывай что использование return останавливает работу функции.
Пишу с дивана.
Не правда, ОП успешный человек и занимается самым благородным и чистым делом - его уроки помогли уже многим анонам устроиться на работу, в том числе и мне. ОП своими силами расширяет границы достоинства всех людей занимаясь этим. Он великий.
Ты давай лучше вместо того чтобы вылизывать опу очко, расскажи о работе, интересно же чем занимаются на практике.
Какие технологии, проекты какой сложности, какие темпы?
Если ты хочешь оказаться правым, то не меняй тему спора. Что ты можешь противопоставить ОПу из моего поста выше?
Сделал пока только часть задания (оно слишком большое).
Учитывая то, что исполнителем и автором трека может быть как группа, так и сольный артист, сделал одну общую таблицу, на которую будет ссылаться трек (author_id, performer_id). Для групп и артистов две отдельные таблицы, так как у них могут быть разные свойства, внешний ключ на таблицу исполнителей. На самом деле мелькала мысль сделать по принципу eav, но мне кажется это большим усложнением в данном случае.
performer
- id
- type (enum: artist, ensemble)
artist
- id
- name
- surname
- birth_year
- ensemble_id (fk, null если сольная карьера)
- performer_id (fk)
ensemble
- id
- name
- foundation_year
- performer_id (fk)
track
- id
- title
- short_description
- full_description
- release_date
- category_id (fk)
- type (enum: single, remake, remix, cover)
track_performer
- track_id
- performer_id (fk)
track_author
- track_id (pk, fk)
- author_id (pk, fk)
genre
- id
- name
track_genre
- track_id (pk, fk)
- genre_id (pk, fk)
album
- id
- short_description
- full_description
- release_date
- type (enum: album, tribute, remix, remake, collection)
- author_id (может быть null)
album_track
- album_id (pk, fk)
- track_id (pk, fk)
label
- id
- name
category
- id
- title
- label_id (fk)
price
- country_id (pk, fk)
- category_id (pk, fk)
- available (enum: y, n)
- base_price
- royalties
Не сделал пока подписки, продажи, отчеты, и что там еще требуется.
Сделал пока только часть задания (оно слишком большое).
Учитывая то, что исполнителем и автором трека может быть как группа, так и сольный артист, сделал одну общую таблицу, на которую будет ссылаться трек (author_id, performer_id). Для групп и артистов две отдельные таблицы, так как у них могут быть разные свойства, внешний ключ на таблицу исполнителей. На самом деле мелькала мысль сделать по принципу eav, но мне кажется это большим усложнением в данном случае.
performer
- id
- type (enum: artist, ensemble)
artist
- id
- name
- surname
- birth_year
- ensemble_id (fk, null если сольная карьера)
- performer_id (fk)
ensemble
- id
- name
- foundation_year
- performer_id (fk)
track
- id
- title
- short_description
- full_description
- release_date
- category_id (fk)
- type (enum: single, remake, remix, cover)
track_performer
- track_id
- performer_id (fk)
track_author
- track_id (pk, fk)
- author_id (pk, fk)
genre
- id
- name
track_genre
- track_id (pk, fk)
- genre_id (pk, fk)
album
- id
- short_description
- full_description
- release_date
- type (enum: album, tribute, remix, remake, collection)
- author_id (может быть null)
album_track
- album_id (pk, fk)
- track_id (pk, fk)
label
- id
- name
category
- id
- title
- label_id (fk)
price
- country_id (pk, fk)
- category_id (pk, fk)
- available (enum: y, n)
- base_price
- royalties
Не сделал пока подписки, продажи, отчеты, и что там еще требуется.
Какого спора, поехавший? Я спросил, чем занимается твоя конторка, интересно же чем занимаются люди на практике.
В твоем посте жополизство, зачем мне ему что-то противопоставлять.
>интересно же чем занимаются люди на практике.
А, значит ты безработный, но при этом хуесосишь ОП-а который многим помог найти работу и рассуждаешь как правильно?
Кекнул с тебя.
другой анон если что
Ты идиот что ли?
https://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BC%D0%B0%D0%B3%D0%BE%D0%B3%D0%B8%D1%8F#.D0.9A.D0.BE.D0.BD.D1.86.D0.B5.D0.BD.D1.82.D1.80.D0.B0.D1.86.D0.B8.D1.8F_.D0.BD.D0.B0_.D1.87.D0.B0.D1.81.D1.82.D0.BD.D0.BE.D1.81.D1.82.D1.8F.D1.85
Если тебе больше нечего ответить, то ты просто жалок.
//все тот же анон если что//
Может быть глумиться над больными людьми и плохо, но это все-таки смешно.
На что ответить, дурачок?
Вот мой пост, где я выражаю недовольство слишком сложным уровнем оповских задач.
>>569667
Мне зачем-то подпездывает быдлокодер и советует писать говнокод. >>569750
Ты наверное решил, что это мой пост, возбудился и зачем-то начал рассказывать ему том, как оп кому-то помог устроиться на работу.
Я тебя в этом посте >>570252 попросил не лизать опу очко, а рассказать о работе. Я скоро тоже планирую устраиваться, поэтому интересно, какую нагрузку дают на джуниоров.
Так вот, ты сумасшедший или просто дурак?
> отдельно лейбл задает отчисления для бесплатного прослушивания, ограничение на число прослушиванных треков лейбла данной категории пользователем в ходе пробного периода
Вот это вообще не понял.
Что за отчисления для бесплатного прослушивания? Это значит, что наш интернет-магазин обязан платить пеню правообладателям даже за те немногие бесплатные прослушивания, которые разрешены? Безобразие. Копирасты не люди, форсим тор вконтакте, чтобы простые люди могли пользоваться рутрекером.
что там было?
Я тут студент пак от гитхаба получил, думаю себе сайт-резюме заделать, посоветуйте чего туда напихать. Стоит ли вообще делать?
Может ещё кто годных примеров накидает.
All greetings and nyashe experience separate.
I've been a student pack from githaba received, I think the site itself resumes repaired, advise what to cram. Whether it is worth doing?
Maybe someone suitable examples promptly inserted.
Открываешь любой фриланс сайт->смотришь заказы на сайты -> ищешь идею которая тебе понравится
Если для себя то возьми например какой нибудь любой сайт, заскринь главную, в html набросай сайт без функционала, просто чтобы элементы те же самые были, и попробуй добиться схожести своего сайта с тем что на скрине.
Если бы я снова учился верстать - я бы поступил так.
Вот, есть у меня класс с методом, в котором используются константные данные, но только в одном методе. Надо ли их объявлять, как константу класса? Или же достаточно описать просто, как переменную в конкретном методе?
вот той из шапки.
По моим ощущениям он работает преподом. Дело в том, что его задачки лишены практической ценности, такое ощущение, что любые задания и правки, которые он делают, имеют единственную цель - потянуть время, чтобы человек сидел и десятилетиями копался в переусложненных вещах. Как раз по такой методике обучают школьников и студентах в наших шарагах, чтобы они пришли и тупо отсидели учебные часы, занимаясь херней.
Возможно чисто теоритически это все полезно, но мы-то уже не школота, у наст столько времени в запасе нет, нам нужно скорее в практику, а конекретно в веб.
при чем тут быдлокодер? я тебе дал практический совет, в какую сторону копать, такие задачи мне давали аж два раза в сети.
Ну не обижайся, мелкобуквенный, но
>нормальный стандартный интернет магазин по продаже хуйни, коих тысячи в сети
это уже другая крайность. Тебе интересна такая работа, штамповать посредственные сайты за маленькие деньги?
Мне точно нет, я хочу гордиться своей работой. Я с прошлой работы ушел, когда меня пытались заставить дописывать гнилой порно-сайт.
И стандартные магазины пишутся не на фреймворках, а на cms типа opencart, собственно там писать нечего, нужно только натянуть верстку.
>>570707
Есть в них ценность, только нужно как-то рассчитывать уровень сложности.
Что-то я жалею, что ввязался во флуд. По моим ощущениям, вы тут ни хера не делаете, только обсуждаете опа или жалуетесь на злую судьбу.
Не понимаю, что я вообще среди вас делаю. (Пикрандом)
>за маленькие деньги?
А ты думал тебя сразу в гугол позовут? Тех, кого в гугол зовут, на сосаче кодить не учатся. Будешь чистить говно на вордпрессе с жумолой и радоваться. Всем бабершопы нужны, просто так говорю, а уж на филигранность твоего кода всем строго похуй.
>Я с прошлой работы ушел, когда меня пытались заставить дописывать гнилой порно-сайт.
вот дурачок, классная работа, пишешь, а в перервыха дрочишь, тьфу на тебя.
Есть ли анонасы, у которых такая смесь работает именно на этой версии бубунты? Как вы добились профита?
Тот же вопрос касательно покупки альбомов и треков.
Альбомы и треки хранятся в отдельных таблицах. Как мне сделать таблицу покупок, чтобы внешний ключ в ней указывал на две таблицы? Очевидно никак, но что делать? Создать доп.таблицу "товар" и ссылаться на нее из таблиц альбомов и треков?
Мне не очень нравится такой подход с точки зрения размера полученной таблицы. Наверняка есть даже какие-то паттерны на эту тему, но я не знаю в каком направлении гуглить.
Недавно поднял бложик на Wordpress.
Год назад я написал на three.js один проект и теперь хочу разместить его на своем бложике. Проект представляет собой каталог с кучей файлов; есть один, типа main.html Там намешано куча всякого javascript, в каталоге лежат ещё картинки, файлы моделей и т.д. Как мне это разместить на моем Wordpress? Хочу разместить целиком всю папку. Ещё желательно, чтобы в админке оно было доступно. FTP-доступ есть.
>>569091
Simpla я помню, когда-то давно его еще пиарил автор на Хабре и он стоил 500 долларов. Я на нем делал какой-то интернет магазин. Я еще удивлялся, что там автор все классы как-то странно от одного общего наследует, своеобразное понимание ООП, но интерфейс приятный.
Придется разобраться, куда отправляются данные при отправке формы, какой скрипт их обрабатывает и исправить.
Алсо патчить код это глупая идея, лучше бы посмотреть нет ли в CMS средств расширения и сделать все это в виде какого-то плагина. Иначе нельзя ни обновить движок, ни понять где чей код. Я попробовал посмотреть, но на сайте автора нет ссылки на документацию - где искать информацию, непонятно. А код скачивать (скачивать! в 2015 году) и распаковывать мне некогда.
>>569163
>Так и не понял как мне поможет простая замена echo на return
Я и не давал таких советов. Я написал что надо в функции создать массив и класть в него все сгенерированные слова (включая сгенерированные вложенными вызовами функции), а в конце функции возвращать.
> https://github.com/never3ver/catsandmice/blob/master/animals.php
В этом файле код плохой (с копипастой, все одной простыней без разбиения на функции, с логическими ошибками). К примеру:
> https://github.com/never3ver/catsandmice/blob/master/animals.php#L16
> $world->addAnimal(new Mouse(rand(0, $world->getWidth()), rand(0, $world->getHeight())));
Нет гарантий что клетка свободна
> https://github.com/never3ver/catsandmice/blob/master/animals.php#L10
> $tempX = rand(0, $world->getWidth());
Это потенциально бесконечный цикл, если вся строчка будет занята. Надо делать for с ограничением попыток вместо while.
Ну и код читается тяжело. Вот как можно было написать:
функция добавитьЖивотное(карта, животное) {
попытаться 10 раз {
сгенерировать координаты животного;
если (карта говорит что клеткаCвободна()) {
на карту поместитьЖивотное();
выйти;
}
}
выдать ошибку;
}
А у тебя все идет простыней, без разделения на функции. Плохо. Это конечно вспомогательный файл, но все равно нехорошо как-то, что ты привыкаешь так писать.
> https://github.com/never3ver/catsandmice/blob/master/animals.php#L30
тут вообще кошмар какой-то, версии с двоеточием предназначены для использования в HTML-шаблонах, а не в коде.
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php
Советую конструктор ставить выше других методов ради читабельности (но после списка полей), так как когда я читаю код, я его первым хочу прочесть, чтобы узнать что происходит при создании объекта.
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L43
Тут нет проверки что $key не пуст и животное вообще есть на карте, неаккуратненько
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L49
> $x > $this->getWidth()
Тут не >= должен быть?
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L59
> if ($critter instanceof Cat) {
> return "Cat";
Это неправильно и не позволяет добавлять новые виды животных без переделки кода World. А надо, чтобы это было возможно. да и какая выгода возвращать строку вместо полноценного объекта в котором гораздо больше информации? Надо возвращать само животное.
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L67
И тут тогда тоже надо будет поменять. Обычно если функция возвращает объект, его отсутствие обозначают как null, null для этого и придуман, чтобы показать отсутсвие объекта.
> https://github.com/never3ver/catsandmice/blob/master/classes/Animal.php#L43
> public function setWorld($world) {
Нужен тайп-хинт. Так как тут можно передавать и объект и null, не забудь приписать его как значение по умолчанию (ClassName $world = null), а то null проходить не будет. Мануал: http://php.net/manual/ru/language.oop5.typehinting.php
> https://github.com/never3ver/catsandmice/blob/master/classes/Animal.php#L6
> protected $awareness;
Вообще, это свойство мне кажется лишним. У нас ведь есть функция которая возвращает это число, зачем множить сущности? Свойство бы пригодилось если бы процесс вычисления был долгим, но там ведь одна строчка return, нет смысла усложнять код.
> https://github.com/never3ver/catsandmice/blob/master/classes/Mouse.php#L16
> public function makeMove(World $world)
зачем передавать world если он есть в животном?
> https://github.com/never3ver/catsandmice/blob/master/classes/Mouse.php#L80
Мне кажется этот алгоритм выглядел бы проще и читабельнее, если бы мы не совмещали цикл поиска кошек, вычисления расстояния и поиск минимуа, а разбили на 2 части: сделали массив расстояний и взяли от него min(). В общем конечно можно и так оставить, но читается чуть хуже.
Также, можно было добавить в World метод, возвращающий всех животных определенного типа.
> https://github.com/never3ver/catsandmice/blob/master/classes/Mouse.php#L88
> $rate = $distanceToCat + $pathsQuantity;
Ты складываешь 2 фактора, но одинаков ли вес этих факторов? Вот 2 ситуации:
- до ближайшей кошки 1 ход, из клетки 4 выхода, сумма = 5
- до ближайшей кошки 3 хода, из клетки 1 выход (допустим там пара мышей стоит), сумма = 4
Получается ход навстречу кошке выгоднее? Надо домножать факторы на вес при сложении, чтобы один был более весомый чем другой.
Насчет расчета расстояния, ты считаешь декартово расстояние. Оно актуально в тех случаях когда мир изотропен (одинаков во всех направлениях), но у нас кошка при ходе по диагонали ходит не на 1, а на 1.41 (корень из 2) условных единицы. В таком мире логичнее считать не декартово расстояние, а сколько ходов надо кошке чтобы добраться до цели (с учетом что она ходит в 8 сторон). Это кстати можно вынести в отдельную функцию тоже.
> https://github.com/never3ver/catsandmice/blob/master/classes/Cat.php#L15
> public function getSymbol() {
> if ($this->sleepCounter == 8) { // если кошка съела мышку или сделала 8 ходов
> $this->sleepCounter = 0;
Вот это мина замедленного действия. У тебя функция называется getSymbol, то есть судя по названию она только возвращает текущее обозначение. А на деле она меняет состояние кошки (это называется побочный эффект). Что же будет если вызвать ее 2 раза?
$s1 = $cat->getSymbol(); // @
$s2 = $cat->getSymbol(); // C
Такого быть не должно. Методы которые называются «получить имя», «получить значок» не должны ничего менять в состоянии объекта и возвращать разные результаты при нескольких вызовах подряд.
Очищать счетчик надо в функции выполнения хода. Плохо, это серьезная ошибка конечно. Больше так не делай.
> https://github.com/never3ver/catsandmice/blob/master/classes/Cat.php#L62
> $length = min($length);
> return $length;
Лучше писать return min($length);, а также не использовать одну переменную для хранения разных типов данных (массив и число).
В коде кошки несколько раз повторяется число 8, вынеси это в приватное поле либо константу с понятным именем.
И еще, у кошки и мышки функция makeMove похожа. Есть ли возможность вынести общий код в функцию базового класса?
В общем, у меня ощущение что ты основы ООП понимаешь потихоньку, но код надо еще доработать. Тут вроде немного, давай доделывай и перейдем к более сложным (но близким к жизни) задачам, наверно про студентов?
> https://github.com/never3ver/catsandmice/blob/master/animals.php
В этом файле код плохой (с копипастой, все одной простыней без разбиения на функции, с логическими ошибками). К примеру:
> https://github.com/never3ver/catsandmice/blob/master/animals.php#L16
> $world->addAnimal(new Mouse(rand(0, $world->getWidth()), rand(0, $world->getHeight())));
Нет гарантий что клетка свободна
> https://github.com/never3ver/catsandmice/blob/master/animals.php#L10
> $tempX = rand(0, $world->getWidth());
Это потенциально бесконечный цикл, если вся строчка будет занята. Надо делать for с ограничением попыток вместо while.
Ну и код читается тяжело. Вот как можно было написать:
функция добавитьЖивотное(карта, животное) {
попытаться 10 раз {
сгенерировать координаты животного;
если (карта говорит что клеткаCвободна()) {
на карту поместитьЖивотное();
выйти;
}
}
выдать ошибку;
}
А у тебя все идет простыней, без разделения на функции. Плохо. Это конечно вспомогательный файл, но все равно нехорошо как-то, что ты привыкаешь так писать.
> https://github.com/never3ver/catsandmice/blob/master/animals.php#L30
тут вообще кошмар какой-то, версии с двоеточием предназначены для использования в HTML-шаблонах, а не в коде.
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php
Советую конструктор ставить выше других методов ради читабельности (но после списка полей), так как когда я читаю код, я его первым хочу прочесть, чтобы узнать что происходит при создании объекта.
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L43
Тут нет проверки что $key не пуст и животное вообще есть на карте, неаккуратненько
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L49
> $x > $this->getWidth()
Тут не >= должен быть?
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L59
> if ($critter instanceof Cat) {
> return "Cat";
Это неправильно и не позволяет добавлять новые виды животных без переделки кода World. А надо, чтобы это было возможно. да и какая выгода возвращать строку вместо полноценного объекта в котором гораздо больше информации? Надо возвращать само животное.
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L67
И тут тогда тоже надо будет поменять. Обычно если функция возвращает объект, его отсутствие обозначают как null, null для этого и придуман, чтобы показать отсутсвие объекта.
> https://github.com/never3ver/catsandmice/blob/master/classes/Animal.php#L43
> public function setWorld($world) {
Нужен тайп-хинт. Так как тут можно передавать и объект и null, не забудь приписать его как значение по умолчанию (ClassName $world = null), а то null проходить не будет. Мануал: http://php.net/manual/ru/language.oop5.typehinting.php
> https://github.com/never3ver/catsandmice/blob/master/classes/Animal.php#L6
> protected $awareness;
Вообще, это свойство мне кажется лишним. У нас ведь есть функция которая возвращает это число, зачем множить сущности? Свойство бы пригодилось если бы процесс вычисления был долгим, но там ведь одна строчка return, нет смысла усложнять код.
> https://github.com/never3ver/catsandmice/blob/master/classes/Mouse.php#L16
> public function makeMove(World $world)
зачем передавать world если он есть в животном?
> https://github.com/never3ver/catsandmice/blob/master/classes/Mouse.php#L80
Мне кажется этот алгоритм выглядел бы проще и читабельнее, если бы мы не совмещали цикл поиска кошек, вычисления расстояния и поиск минимуа, а разбили на 2 части: сделали массив расстояний и взяли от него min(). В общем конечно можно и так оставить, но читается чуть хуже.
Также, можно было добавить в World метод, возвращающий всех животных определенного типа.
> https://github.com/never3ver/catsandmice/blob/master/classes/Mouse.php#L88
> $rate = $distanceToCat + $pathsQuantity;
Ты складываешь 2 фактора, но одинаков ли вес этих факторов? Вот 2 ситуации:
- до ближайшей кошки 1 ход, из клетки 4 выхода, сумма = 5
- до ближайшей кошки 3 хода, из клетки 1 выход (допустим там пара мышей стоит), сумма = 4
Получается ход навстречу кошке выгоднее? Надо домножать факторы на вес при сложении, чтобы один был более весомый чем другой.
Насчет расчета расстояния, ты считаешь декартово расстояние. Оно актуально в тех случаях когда мир изотропен (одинаков во всех направлениях), но у нас кошка при ходе по диагонали ходит не на 1, а на 1.41 (корень из 2) условных единицы. В таком мире логичнее считать не декартово расстояние, а сколько ходов надо кошке чтобы добраться до цели (с учетом что она ходит в 8 сторон). Это кстати можно вынести в отдельную функцию тоже.
> https://github.com/never3ver/catsandmice/blob/master/classes/Cat.php#L15
> public function getSymbol() {
> if ($this->sleepCounter == 8) { // если кошка съела мышку или сделала 8 ходов
> $this->sleepCounter = 0;
Вот это мина замедленного действия. У тебя функция называется getSymbol, то есть судя по названию она только возвращает текущее обозначение. А на деле она меняет состояние кошки (это называется побочный эффект). Что же будет если вызвать ее 2 раза?
$s1 = $cat->getSymbol(); // @
$s2 = $cat->getSymbol(); // C
Такого быть не должно. Методы которые называются «получить имя», «получить значок» не должны ничего менять в состоянии объекта и возвращать разные результаты при нескольких вызовах подряд.
Очищать счетчик надо в функции выполнения хода. Плохо, это серьезная ошибка конечно. Больше так не делай.
> https://github.com/never3ver/catsandmice/blob/master/classes/Cat.php#L62
> $length = min($length);
> return $length;
Лучше писать return min($length);, а также не использовать одну переменную для хранения разных типов данных (массив и число).
В коде кошки несколько раз повторяется число 8, вынеси это в приватное поле либо константу с понятным именем.
И еще, у кошки и мышки функция makeMove похожа. Есть ли возможность вынести общий код в функцию базового класса?
В общем, у меня ощущение что ты основы ООП понимаешь потихоньку, но код надо еще доработать. Тут вроде немного, давай доделывай и перейдем к более сложным (но близким к жизни) задачам, наверно про студентов?
Боюсь, тебе придется пересматривать структуру БД.
Смотри:
Один Трек содержится только в одном альбоме, но один Альбом может содержать несколько треков. Значит, таблица Треков содержит внешний ключ на таблицу Альбомов. Насколько я понял из задания, да и вообще знаю, в Альбоме могут быть Треки нескольких Исполнителей та же "дискотека восьмидесятых" значит связь с исполнителями должна быть на уровне Трека (но это не имеет значения). Таким образом, я считаю, что в таблице Заказ связь должна быть только с таблицей Трек. Заказ будет связан с Альбом по транзитивности, если надо будет, то данные будут выбираться джойнами.
https://github.com/lexdss/abitur - у меня не открывается (ошибка 404). Репозиторий удален? Баг на гитхабе? Перезалей, а то я не смогу проверить.
>>569335
> $xt, $tx
Плохие названия, только путают, назови например $a и $b или $letter1 и $letter2.
> $lText = mb_strtolower($text);
Можно тут не заводить новую переменную, а использовать ту же.
Насчет того, как после цикла понять палиндром или нет, лучше сделать так: завести переменную, занести в нее 1 (или специальное значение true что значит истина), а при несовпадении внести 0 (или false = ложь). И в конце проверять через if.
Сам алгоритм верный, надо только код причесать ради читаемости.
Ну и то что анон выне написал, elseif можно заменить на else без условия.
Нет. Покупка альбома не то же самое что купить все треки в альбоме. Посмотри цены сам:
https://itunes.apple.com/ru/album/all-that-you-cant-leave-behind/id14888276
Треки: 19р × 12 ~ 228
Альбом: 149 р
Также менеджеры хотят отчеты, сколько альбомов куплено целиком, а сколько треков. Эти данные нельзя терять.
> Ну и у меня уже подсознательная тяга к ... реляционности.
Редис это NoSQL и тут надо мыслить именно ключами-значениями.
> И был вопрос о выборке необходимых ключей, я тогда знал только команду KEYS, которая перебирает все ключи, поэтому непродуктивна.
> Кажется, там еще есть команда SCAN, но я пока не понимаю, как она работает.
SCAN позволяет получать список ключей порциями. Она возвращает тебе допустим 10 ключей и специальное число-идентифиактор, который ты указываешь при следущем вызове чтобы получить следующие 10. Преимщуества очевидны: редис блокируется ненадолго в отличие от KEYS, на стороне PHP не нужна память чтобы хранить все ключи. Вообще KEYS это команда не для продакшена, это больше для отладки у себя на компьютере. Да и SCAN тоже больше для целей вроде экспорта данных, создания бекапов, отладки, и тд. Не стоит алгоритмы на ней основывать. NoSQL так неэффективно использовать.
В твоем случае тебе не нужен ни KEYS, ни SCAN, а нужен алгоритм обходящийся без них. например, использующий очередь, идею которой мы подсмотрели в статье про мейл ру.
> Что за "cursor"? О каком итераторе они говорят?
Это из баз данных, некоторые базы данных позволяют читать результаты запроса по мере выполнения (стримить), и там «курсор» это абстрактная структура которая позволяет получить следующий кусок ответа. В редисе он нужен чтобы следующий вызов SCAN вернул не те же самые данные, а следующий блок.
Там в мануале по SCAN первый раз выполняют команду scan 0, она возвращает 10 записей и курсор - число «17». Его передают во второй вызов scan 17, и получают следующие записи и новое значение курсора. И т д.
При использовании этой команды надо проверить в документации, а что если в процессе обхода ключей ключи добавляются или удаляются? Какие гаратии дает эта команда? Там отдельно есть абзац на эту тему.
> Ну хорошо, я согласен что нужно хранить отдельные ключи вместо хеша, только не понимаю как по ним искать нужные.
Можно оставить хеш, я не против. Но масштабируется лучше с отдельными ключами. Искать их не надо. Лучше сделать алгоритм который не требует обхода хеша (и не придется думать, что будет если хеш меняется в процессе обхода). Как раз очередь тем и хороша что данные там добавляются с одного конца, а снимаются с другого, и конфликтов не возникает, даже если мы делаем несколько операций (чтение + удаление блока отдельно).
Ты по моему недооцениваешь потенциал этой самой очереди. Очереди вообще исплоьзуются в хайлоаде часто для группировки записей, вместо того чтобы все вставлять в базу сразу, мы кладем данные в очередь, а крон-скрипт их переиодически собирает, группирует и вставляет большими пачками.
>>в чем проблема накопления одинаковых id в очереди?
> Лишняя память. Зачем мне по сто-тысяче раз хранить в списке повторяющиеся значения, если можно хранить только уникальные?
Мы платим памятью за упрощение алгоритма, производительность и снижение нагрузки на базу (которая упирается в IOPS диска = число отдельных операций которые диск делает за секунду). И это во многих случаях выгодный обмен. Плюс, я думаю там даже за день больше пары гигабайт не набежит, а ведь мы можем сбраывать данные каждые 5-10 минут и не доводить до такого.
Плюс, если у тебя просмотры идут на разные объявления, так что на одно приходится по 2-3 просмотра, то много мы не сэконоим, а вот алгоритм сильно усложняется: нам теперь надо атомарно очищать этот хеш p:v:a.
>Хорошо, сет тоже выбрасываем.
Рад что мы пришли к пониманию
> Я пока не в курсе, как реализованы транзакции в редисе, поэтому не понимаю почему транзакции препятствуют масштабированию.
Как минимум тем, что масштабирование подразумевает распределение нагрузки на N серверов (так как 1 не справляется), а транзакция выполняется на одном сервере. Механизмы распределенных транзакций между несколькими серверами сложны и в редисе их нет. Также они обычно бьют по производительности. Если используется отдельный сервер-координатор, то мы получаем еще и единую точку отказа. Если интересно, можешь на досуге подумать про то как сделать распределенную транзакцию c ACID, которая была бы устойчива к отключению серверов и нарушению каналов связи, но только не сломай голову, это реально сложная тема, сложнее чем все что ты тут изучал. Для начала можешь гуглить по словам «Distributed transaction», «two-phase commit», «CAP теорема».
Вот тут вроде простая статья: https://ru.wikipedia.org/wiki/XA_%D0%A2%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D0%B8
Если тебе нужен простой пример где нужна распределенная транзакция: представь банк, у которого данные пользователей разнесены на N серверов. Со счета на сервере A надо перевести сумму на счет, хранящийся на сервере B. Описанный выше протокол может использоваться для выполнения этого перевода с поддержкой ACID, но конечно производительность тут будет значительно ниже из-за многочисленных коммуникаций между серверами, приложением и координатором. Это не для хайлоада, а для случаев когда на первом месте надежность.
На практике при шардинге MySQL (разнесении базы на несколько серверов) обычно просто отказываются от транзакций, внешних ключей и джойнов (и ACID). Все это хорошо работает только в пределах одного сервера.
В редисе «транзакция» это не SQL транзакция с ACID, это просто команды накапливаются в буфере и выполняются атомарно (так как редис однопоточный) по команде EXEC. можешь почитать описание в документации редиса ( http://redis.io/topics/transactions ) и сделать сравнение с SQL ACID транзакциями.
Ой, я опять про сложные вещи начинаю рассказывать. Ну не беда, полезно для кругозора, я же не прошу тебя свой алгоритм распределенных транзакций написать.
> Сначала mysql конечно. Он же пишет на диск. Redis пишет в оперативную память, это пара милисекунд. Да, у нас остается точка отказа между этими двумя коммитами, но во-первых это промежуток наверное меньше милисекунды, во-вторых я просто не знаю, что тут можно сделать.
Я тоже. Есть конечно решение, которое описано выше, распределенные транзакции, но я думаю реализовать XA (который не поддерживается ни редисом ни MySQL как я понял) ради учета просморов объявления это излишне.
> 2.1. Парсим каждую строку вида "ip:id", чтобы получить ip и id, сохраняем в некий массив вида ['id страницы'=>кол-во посещений,].
А зачем нам в очереди хранить ip? Мы же кладем туда только уникальные просмотры (неуникальные отсеиваются хешем pvu).
> В этом поможет функция array_keys со вторым параметром. В конце этого шага получаем массив вида ['кол-во посещений'=>array('все id страниц с таким кол-вом посещений')].
Возможно тебе поможет функция array_count_values. array_keys это как-то сложно, лучше тогда просто циклом массив обойти и сгруппировать.
> Как видишь, здесь значительно больше телодвижений в самом php-скрипте,
Пусть. Это часть которую масштабирвать проще всего, просто ставим еще пару серверов.
> тогда в предыдущем алгоритме я пытался как бы сымитировать реляционность, типа таблиц mysql за счет создания нескольких структур типа сетов и хешей, чтобы несколькими запросами сразу получить нужные данные, а не вычленять их в скрипте.
Я думаю, простое решение тут лучше сложного, так как в нем меньше мест где что-то может сломаться. да и отлаживать код будет проще. Да и в редисе транзакции все равно не настоящие и не масштабируемые. Если еще бы тестами этот счетчик покрыть...
В общем, алгоритм меня устраивает, кроме вопроса про хранение IP в очереди (зачем?) и настройки сохранения на диск. Когда будешь писать код, не поленись в комментарии хотя бы скопипастить текст алгоритма из поста, а то поверь, в такой штуке будет непросто разобраться. Также (чтобы попрактиковаться в ООП и хорошем коде), постарайся сделать универсальный счетчик, который можно подключить к любому сайту (для этого тебе скорее всего придется разбить его на универсальный класс и небольшой класс, который реализует особенности, относящиеся к твоему сайту).
> Ну и у меня уже подсознательная тяга к ... реляционности.
Редис это NoSQL и тут надо мыслить именно ключами-значениями.
> И был вопрос о выборке необходимых ключей, я тогда знал только команду KEYS, которая перебирает все ключи, поэтому непродуктивна.
> Кажется, там еще есть команда SCAN, но я пока не понимаю, как она работает.
SCAN позволяет получать список ключей порциями. Она возвращает тебе допустим 10 ключей и специальное число-идентифиактор, который ты указываешь при следущем вызове чтобы получить следующие 10. Преимщуества очевидны: редис блокируется ненадолго в отличие от KEYS, на стороне PHP не нужна память чтобы хранить все ключи. Вообще KEYS это команда не для продакшена, это больше для отладки у себя на компьютере. Да и SCAN тоже больше для целей вроде экспорта данных, создания бекапов, отладки, и тд. Не стоит алгоритмы на ней основывать. NoSQL так неэффективно использовать.
В твоем случае тебе не нужен ни KEYS, ни SCAN, а нужен алгоритм обходящийся без них. например, использующий очередь, идею которой мы подсмотрели в статье про мейл ру.
> Что за "cursor"? О каком итераторе они говорят?
Это из баз данных, некоторые базы данных позволяют читать результаты запроса по мере выполнения (стримить), и там «курсор» это абстрактная структура которая позволяет получить следующий кусок ответа. В редисе он нужен чтобы следующий вызов SCAN вернул не те же самые данные, а следующий блок.
Там в мануале по SCAN первый раз выполняют команду scan 0, она возвращает 10 записей и курсор - число «17». Его передают во второй вызов scan 17, и получают следующие записи и новое значение курсора. И т д.
При использовании этой команды надо проверить в документации, а что если в процессе обхода ключей ключи добавляются или удаляются? Какие гаратии дает эта команда? Там отдельно есть абзац на эту тему.
> Ну хорошо, я согласен что нужно хранить отдельные ключи вместо хеша, только не понимаю как по ним искать нужные.
Можно оставить хеш, я не против. Но масштабируется лучше с отдельными ключами. Искать их не надо. Лучше сделать алгоритм который не требует обхода хеша (и не придется думать, что будет если хеш меняется в процессе обхода). Как раз очередь тем и хороша что данные там добавляются с одного конца, а снимаются с другого, и конфликтов не возникает, даже если мы делаем несколько операций (чтение + удаление блока отдельно).
Ты по моему недооцениваешь потенциал этой самой очереди. Очереди вообще исплоьзуются в хайлоаде часто для группировки записей, вместо того чтобы все вставлять в базу сразу, мы кладем данные в очередь, а крон-скрипт их переиодически собирает, группирует и вставляет большими пачками.
>>в чем проблема накопления одинаковых id в очереди?
> Лишняя память. Зачем мне по сто-тысяче раз хранить в списке повторяющиеся значения, если можно хранить только уникальные?
Мы платим памятью за упрощение алгоритма, производительность и снижение нагрузки на базу (которая упирается в IOPS диска = число отдельных операций которые диск делает за секунду). И это во многих случаях выгодный обмен. Плюс, я думаю там даже за день больше пары гигабайт не набежит, а ведь мы можем сбраывать данные каждые 5-10 минут и не доводить до такого.
Плюс, если у тебя просмотры идут на разные объявления, так что на одно приходится по 2-3 просмотра, то много мы не сэконоим, а вот алгоритм сильно усложняется: нам теперь надо атомарно очищать этот хеш p:v:a.
>Хорошо, сет тоже выбрасываем.
Рад что мы пришли к пониманию
> Я пока не в курсе, как реализованы транзакции в редисе, поэтому не понимаю почему транзакции препятствуют масштабированию.
Как минимум тем, что масштабирование подразумевает распределение нагрузки на N серверов (так как 1 не справляется), а транзакция выполняется на одном сервере. Механизмы распределенных транзакций между несколькими серверами сложны и в редисе их нет. Также они обычно бьют по производительности. Если используется отдельный сервер-координатор, то мы получаем еще и единую точку отказа. Если интересно, можешь на досуге подумать про то как сделать распределенную транзакцию c ACID, которая была бы устойчива к отключению серверов и нарушению каналов связи, но только не сломай голову, это реально сложная тема, сложнее чем все что ты тут изучал. Для начала можешь гуглить по словам «Distributed transaction», «two-phase commit», «CAP теорема».
Вот тут вроде простая статья: https://ru.wikipedia.org/wiki/XA_%D0%A2%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D0%B8
Если тебе нужен простой пример где нужна распределенная транзакция: представь банк, у которого данные пользователей разнесены на N серверов. Со счета на сервере A надо перевести сумму на счет, хранящийся на сервере B. Описанный выше протокол может использоваться для выполнения этого перевода с поддержкой ACID, но конечно производительность тут будет значительно ниже из-за многочисленных коммуникаций между серверами, приложением и координатором. Это не для хайлоада, а для случаев когда на первом месте надежность.
На практике при шардинге MySQL (разнесении базы на несколько серверов) обычно просто отказываются от транзакций, внешних ключей и джойнов (и ACID). Все это хорошо работает только в пределах одного сервера.
В редисе «транзакция» это не SQL транзакция с ACID, это просто команды накапливаются в буфере и выполняются атомарно (так как редис однопоточный) по команде EXEC. можешь почитать описание в документации редиса ( http://redis.io/topics/transactions ) и сделать сравнение с SQL ACID транзакциями.
Ой, я опять про сложные вещи начинаю рассказывать. Ну не беда, полезно для кругозора, я же не прошу тебя свой алгоритм распределенных транзакций написать.
> Сначала mysql конечно. Он же пишет на диск. Redis пишет в оперативную память, это пара милисекунд. Да, у нас остается точка отказа между этими двумя коммитами, но во-первых это промежуток наверное меньше милисекунды, во-вторых я просто не знаю, что тут можно сделать.
Я тоже. Есть конечно решение, которое описано выше, распределенные транзакции, но я думаю реализовать XA (который не поддерживается ни редисом ни MySQL как я понял) ради учета просморов объявления это излишне.
> 2.1. Парсим каждую строку вида "ip:id", чтобы получить ip и id, сохраняем в некий массив вида ['id страницы'=>кол-во посещений,].
А зачем нам в очереди хранить ip? Мы же кладем туда только уникальные просмотры (неуникальные отсеиваются хешем pvu).
> В этом поможет функция array_keys со вторым параметром. В конце этого шага получаем массив вида ['кол-во посещений'=>array('все id страниц с таким кол-вом посещений')].
Возможно тебе поможет функция array_count_values. array_keys это как-то сложно, лучше тогда просто циклом массив обойти и сгруппировать.
> Как видишь, здесь значительно больше телодвижений в самом php-скрипте,
Пусть. Это часть которую масштабирвать проще всего, просто ставим еще пару серверов.
> тогда в предыдущем алгоритме я пытался как бы сымитировать реляционность, типа таблиц mysql за счет создания нескольких структур типа сетов и хешей, чтобы несколькими запросами сразу получить нужные данные, а не вычленять их в скрипте.
Я думаю, простое решение тут лучше сложного, так как в нем меньше мест где что-то может сломаться. да и отлаживать код будет проще. Да и в редисе транзакции все равно не настоящие и не масштабируемые. Если еще бы тестами этот счетчик покрыть...
В общем, алгоритм меня устраивает, кроме вопроса про хранение IP в очереди (зачем?) и настройки сохранения на диск. Когда будешь писать код, не поленись в комментарии хотя бы скопипастить текст алгоритма из поста, а то поверь, в такой штуке будет непросто разобраться. Также (чтобы попрактиковаться в ООП и хорошем коде), постарайся сделать универсальный счетчик, который можно подключить к любому сайту (для этого тебе скорее всего придется разбить его на универсальный класс и небольшой класс, который реализует особенности, относящиеся к твоему сайту).
> Я так понял partialAny(fn, 1, undefined, 3, undefined)(2) -> [1, 2, 3, undefined] вот так и не должно происходить?
Так должно происходить. Твой алгоритм добавляет лишние аргументы в конец списка, попробуй посмотреть что в итоге передается в fn.
> Что-то если честно не могу додуматься куда деть этот второй массив аргументов. Ковырял отладчик, не особо помогло, дай подсказочку :3
Посмотри что передается в fn если передать аргументы, которые описаны в примере выше.
>>569667
>>главное что нужно, это понимание принципов нормализации (именно понимание, а не заучивание определений)
> А понимание откуда берется?
Понимание берется из чтения теории и применения ее на практике. Так что тут ты прав, когда просишь придумать задачу посложнее (а близкая к практике это и значит посложнее).
Суть нормализации можно свести к тому, что мы стремимся избежать хранения любых избыточных данных, а сами данные храним в первичной, атомарной форме (например размер файла числом а не строкой вида «12 Гб»). Если не соблюдать нормализацию, то при работе с базой вылезут проблемы рано или поздно. Почитай реальную историю на примере NoSQL хранилища MongoDB, там люди наслушались хипстеров и отказались от нормализации: http://habrahabr.ru/post/231213/ (я не говорю что монго зло, надо просто применять ее где это уместно).
Кроме нормализации важно еще знать виды связей между сущностями (1:1, 1:M, M:N), а также понимать суть этих связей. Ну например между сущностями «лейбл» и «альбом» есть связь «выпустил» вида «1:M»: один лейбл выпустил много (0...N) альбомов. И наоборот, каждый альбом «выпущен» каким-то одним лейблом. Эти связи еще можно рисовать стрелочками, есть несколько форматов таких ER диаграмм (и даже программы для этого). ВОт например:
http://www.intuit.ru/EDI/23_03_15_1/1427062716-22593/tutorial/531/objects/5/files/05_02.gif
https://en.wikipedia.org/wiki/File:ERD_Representation.svg
Может быть тебе полезно попробовать ее нарисовать.
Ну и как дополнение, полезно знать паттерн EAV, паттерны наследования таблиц.
> У тебя как всегда неадекватно сложные задачи
Ну ведь неадекватно легкие про лайки и кинотеатр ты уже решил. Тебе ведь неинтересно еще одну такую же получить.
> Мне так почему-то кажется, что проектировали itunes и google play люди с немного большим опытом,
Ну тебя никто и не просит сделать сервис такого уровня целиком (да его и не один человек делал). А лишь предложить схему БД, да еще и с возможностью попросить подсказку.
> Например, как хранить ссылку на исполнителя трека? Исполнителем может быть либо группа, либо сольный артист (который может одновременно принадлежать к одной группе, а может даже и не одной)
Для начала можно сделать это 2 отдельными сущностями: Артист и Группа. Так как у них много общего, тут уместно применить одну из схем наследования таблиц, унаследовав их от абстрактной сущности «Исполнитель». Паттерны наследования таблиц, попробуй сравнить их преимущества и недостатки: Single Table Inheritance, Class Table Inheritance, Concrete Table Inheritance. Ссылки не даю, найдешь сам.
Соответственно мы можем ввести отношение «Исполнитель является автором Трека».
Попробуй для начала думать не на уровне таблиц/полей, а сущностей и отношений. А только потом перенести эту схему на таблицы.
> Можно создать таблицу исполнителей, где будут и группы, и артисты. Внешние ключи хранить в таблице групп и артистов на эту таблицу. Как например советуют здесь
Почитай про паттерны наследования. Ты явно пытаешься их переизобрести. И начни с определния сущностей и отношений, а только потом думай как их уложить в возможности SQL.
> ensemble
> - id
...
> - performer_id
Если это Class Table INheritance то тут один из этих 2 идентификаторов является лишним. Тут уместнее совместить первичный и внешний ключ, а не добавлять искуственный.
> Я так понял partialAny(fn, 1, undefined, 3, undefined)(2) -> [1, 2, 3, undefined] вот так и не должно происходить?
Так должно происходить. Твой алгоритм добавляет лишние аргументы в конец списка, попробуй посмотреть что в итоге передается в fn.
> Что-то если честно не могу додуматься куда деть этот второй массив аргументов. Ковырял отладчик, не особо помогло, дай подсказочку :3
Посмотри что передается в fn если передать аргументы, которые описаны в примере выше.
>>569667
>>главное что нужно, это понимание принципов нормализации (именно понимание, а не заучивание определений)
> А понимание откуда берется?
Понимание берется из чтения теории и применения ее на практике. Так что тут ты прав, когда просишь придумать задачу посложнее (а близкая к практике это и значит посложнее).
Суть нормализации можно свести к тому, что мы стремимся избежать хранения любых избыточных данных, а сами данные храним в первичной, атомарной форме (например размер файла числом а не строкой вида «12 Гб»). Если не соблюдать нормализацию, то при работе с базой вылезут проблемы рано или поздно. Почитай реальную историю на примере NoSQL хранилища MongoDB, там люди наслушались хипстеров и отказались от нормализации: http://habrahabr.ru/post/231213/ (я не говорю что монго зло, надо просто применять ее где это уместно).
Кроме нормализации важно еще знать виды связей между сущностями (1:1, 1:M, M:N), а также понимать суть этих связей. Ну например между сущностями «лейбл» и «альбом» есть связь «выпустил» вида «1:M»: один лейбл выпустил много (0...N) альбомов. И наоборот, каждый альбом «выпущен» каким-то одним лейблом. Эти связи еще можно рисовать стрелочками, есть несколько форматов таких ER диаграмм (и даже программы для этого). ВОт например:
http://www.intuit.ru/EDI/23_03_15_1/1427062716-22593/tutorial/531/objects/5/files/05_02.gif
https://en.wikipedia.org/wiki/File:ERD_Representation.svg
Может быть тебе полезно попробовать ее нарисовать.
Ну и как дополнение, полезно знать паттерн EAV, паттерны наследования таблиц.
> У тебя как всегда неадекватно сложные задачи
Ну ведь неадекватно легкие про лайки и кинотеатр ты уже решил. Тебе ведь неинтересно еще одну такую же получить.
> Мне так почему-то кажется, что проектировали itunes и google play люди с немного большим опытом,
Ну тебя никто и не просит сделать сервис такого уровня целиком (да его и не один человек делал). А лишь предложить схему БД, да еще и с возможностью попросить подсказку.
> Например, как хранить ссылку на исполнителя трека? Исполнителем может быть либо группа, либо сольный артист (который может одновременно принадлежать к одной группе, а может даже и не одной)
Для начала можно сделать это 2 отдельными сущностями: Артист и Группа. Так как у них много общего, тут уместно применить одну из схем наследования таблиц, унаследовав их от абстрактной сущности «Исполнитель». Паттерны наследования таблиц, попробуй сравнить их преимущества и недостатки: Single Table Inheritance, Class Table Inheritance, Concrete Table Inheritance. Ссылки не даю, найдешь сам.
Соответственно мы можем ввести отношение «Исполнитель является автором Трека».
Попробуй для начала думать не на уровне таблиц/полей, а сущностей и отношений. А только потом перенести эту схему на таблицы.
> Можно создать таблицу исполнителей, где будут и группы, и артисты. Внешние ключи хранить в таблице групп и артистов на эту таблицу. Как например советуют здесь
Почитай про паттерны наследования. Ты явно пытаешься их переизобрести. И начни с определния сущностей и отношений, а только потом думай как их уложить в возможности SQL.
> ensemble
> - id
...
> - performer_id
Если это Class Table INheritance то тут один из этих 2 идентификаторов является лишним. Тут уместнее совместить первичный и внешний ключ, а не добавлять искуственный.
Константы надо хранить не отдельным объектом, а поместить в «класс» Гамбургер. То есть можно так:
Hamburger.SIZE_BIG = 'big';
или
Hamburger.SIZE_BIG = 1;
Значение константы это число или строка, не стоит в ней хранить данные, это будет уже не константа, а что-то странное.
Вместо того чтобы создавать методы при создании объекта, лучше создать их один раз, с помощью прототипов. Разберись с прототипами и реализацией классов на их основе. Твой подход имеет недостатки:
- методы создаются на каждом объекте заново
- конструктор получается гигантский что ухудшает читаемость кода
> var price = size.price + stuffing.price;
Вот это плохая идея, цену надо вычислять а не хранить, а то замучаешься если надо будет например удалить добавку или реализовать изменение цен на полуфабрикаты.
У тебя в коде нет никаких проверок на ошибки, передачу неправильных значений.
> кроме проверки на ошибки. Это нужно делать через try..catch? Там получается каждый метод в него нужно будет оборачивать?
Почитай урок про исключения и зачем они нужны (чтобы сообщать об ошибках). try/catch используется для ловли исключений тем, кто использует твой класс. А от тебя требуется только выкидывать их через throw.
То есть если ты раньше писал console.log("Неверно что-то передано"), теперь замени это на throw new HamburgerError(...) или new Error(...).
>>569750
Зачем писать 100500-й движок ИМ если есть готовые?
>>569771
Для начала начни с наших задач на HTML/CSS в ОП-посте. Показывай их на проверку. После того, как успешно выучишь все основы, там в конце есть макет, на нем можно будет потренироваться в адаптивности.
Вообще адаптивность это умение хорошо отображаться на разных устройствах. Она достигается за счет правильной «резиновой» верстки и переключения раскладки страницы на маленьких экранах через @media.
Я думаю для начала надо полностью освоить основы CSS и позиционирование, прежде чем браться за адаптивность. В заданиях есть ссылка на хорший блог softwaremaniacs, где хорошо это объясняется.
>>569831
Почему бы потихоньку не попробовать?
>>569896
Это справочник, а не учебник. А учиться лучше по нашим задачкам из ОП поста.
>>570096
> Предлагаешь что ли отдельно считать расстояние по X и по Y?
Ну число ходов нужное кошке чтобы добраться до мышки зависит от расстояния по X и Y. Вопрос, как? Попробуй рассмореть примеры которые я дал и найти зависимость. Не видишь?
> Не правда, кошка всегда будет проходить целое количество клеток. Высчитывание расстояния на это никак не влияет, только оценивает ход.
Если кошка на 3 клетки выше и на 3 правее, то твой код даст декартово расстояние = sqrt(18) = 4.24. Если кошка на той же строчке, в 4 клетках справа, расстояние до нее = 4. Кажется что первый вариант лучше, кошка дальше.
В нашем изотропном мире, где все направления равноценны, это так. Но в игре мир неизотропен. Кошка по диагонали двигается быстрее. В первом случае кошка доберется до мышки не за 4.24, а за 3 хода (по диагонали). Он хуже.
> Я совершенно не понимаю эту задачу.
Давай еще чуть помучаем расстояния. Разбери примеры которые я дал и сколько там ходов надо кошке.
Константы надо хранить не отдельным объектом, а поместить в «класс» Гамбургер. То есть можно так:
Hamburger.SIZE_BIG = 'big';
или
Hamburger.SIZE_BIG = 1;
Значение константы это число или строка, не стоит в ней хранить данные, это будет уже не константа, а что-то странное.
Вместо того чтобы создавать методы при создании объекта, лучше создать их один раз, с помощью прототипов. Разберись с прототипами и реализацией классов на их основе. Твой подход имеет недостатки:
- методы создаются на каждом объекте заново
- конструктор получается гигантский что ухудшает читаемость кода
> var price = size.price + stuffing.price;
Вот это плохая идея, цену надо вычислять а не хранить, а то замучаешься если надо будет например удалить добавку или реализовать изменение цен на полуфабрикаты.
У тебя в коде нет никаких проверок на ошибки, передачу неправильных значений.
> кроме проверки на ошибки. Это нужно делать через try..catch? Там получается каждый метод в него нужно будет оборачивать?
Почитай урок про исключения и зачем они нужны (чтобы сообщать об ошибках). try/catch используется для ловли исключений тем, кто использует твой класс. А от тебя требуется только выкидывать их через throw.
То есть если ты раньше писал console.log("Неверно что-то передано"), теперь замени это на throw new HamburgerError(...) или new Error(...).
>>569750
Зачем писать 100500-й движок ИМ если есть готовые?
>>569771
Для начала начни с наших задач на HTML/CSS в ОП-посте. Показывай их на проверку. После того, как успешно выучишь все основы, там в конце есть макет, на нем можно будет потренироваться в адаптивности.
Вообще адаптивность это умение хорошо отображаться на разных устройствах. Она достигается за счет правильной «резиновой» верстки и переключения раскладки страницы на маленьких экранах через @media.
Я думаю для начала надо полностью освоить основы CSS и позиционирование, прежде чем браться за адаптивность. В заданиях есть ссылка на хорший блог softwaremaniacs, где хорошо это объясняется.
>>569831
Почему бы потихоньку не попробовать?
>>569896
Это справочник, а не учебник. А учиться лучше по нашим задачкам из ОП поста.
>>570096
> Предлагаешь что ли отдельно считать расстояние по X и по Y?
Ну число ходов нужное кошке чтобы добраться до мышки зависит от расстояния по X и Y. Вопрос, как? Попробуй рассмореть примеры которые я дал и найти зависимость. Не видишь?
> Не правда, кошка всегда будет проходить целое количество клеток. Высчитывание расстояния на это никак не влияет, только оценивает ход.
Если кошка на 3 клетки выше и на 3 правее, то твой код даст декартово расстояние = sqrt(18) = 4.24. Если кошка на той же строчке, в 4 клетках справа, расстояние до нее = 4. Кажется что первый вариант лучше, кошка дальше.
В нашем изотропном мире, где все направления равноценны, это так. Но в игре мир неизотропен. Кошка по диагонали двигается быстрее. В первом случае кошка доберется до мышки не за 4.24, а за 3 хода (по диагонали). Он хуже.
> Я совершенно не понимаю эту задачу.
Давай еще чуть помучаем расстояния. Разбери примеры которые я дал и сколько там ходов надо кошке.
Разберись с наследованием, чтобы не изобретать велосипеды.
> На самом деле мелькала мысль сделать по принципу eav, но мне кажется это большим усложнением в данном случае.
EAV это замена наследованию когда классов сущностей огромное число (сотни), чтобы упростить систему. Но у нас всего 2 наследуемые сущности и EAV не подходит.
Ну и начинать лучше с сущностей и отношений. Мы бы могли например обсудить правильно ли ты их определил, а только потом реализовывать это таблицами.
> ensemble
Тут id и performer_id по моему дублируют друг друга.
> track
> - category_id (fk)
По моему там на уровне альбомов же проставляется категория?
Связь между альбомами и треками M:N, почему? Один трек на разных альбомах разных лейблов может иметь разную стоимость и условия распространения.
>>570286
>>570310
>>570290
>>570326
Вот зачем вы это тут разводите? В таком случае надо промолчать, а не разжигать бессмысленный спор не имеющий никакого отношения к программрованию. Лучше бы ни одного из этих постов тут не было.
Алсо я в общем согласен с постом, что ОП неплохой человек.
>>570319
Зависит от того, как ты настроишь. Там есть такие возможности:
- для разработки локально ты можешь встроить компилятор LESS/SASS на яваскрипте в веб-страницу. Тогда код компилируется в браузере, удобно тем что все изменения в файле применяются сразу, но страдает скорость загрузки и браузер притрмаживает.
- для выкладки кода на сервер ты компилируешь код в CSS с помощью компилятора, вызвая его напрямую из командной строки или через инструмент вроде gulp/grunt. Соответственно пользователь загружает итоговый CSS файл. Кстати, grunt/gulp умеют еще много чего: склеивать, сжимать файлы, делать CSS спрайты и тд. Правильные разработчики делают один скрипт деплоя который все это делает и автоматически загружает изменения на сервер.
Проверить что у тебя исплоьзуется можно глянув на исходный код страницы в браузере (Ctrl + U), или в dev tools (Ctrl + Shift + I) на вкладке network.
>>570374
Должен, если так написан договор. Более того, ты только разрешение продавать их треки у лейблов выпрашивать замучаешься, и там будет куча условий: минимальный объем продаж который ты обеспечишь (неустойка за провал плана), география распространения, обязательные отчеты, итд. Ты не можешь просто взять и продать 10 песенок, там счет идет на миллионы минимум. Раньше лейблы еще требовали использовать DRM, но благодаря Джобсу удалось это отстоять.
Также, лейблы не готовы продавать все песни. Например Битлз долгое время были недоступны в электронных сервисах, также бывает что новинки дают не всем, а только избранным. Есть по моему 3 крупных лейбла, с каждым надо договариваться отдельно. Также, есть еще мелкие лейблы, с которыми надо заключать отдельные договоры.
Истории по теме:
https://m.roem.ru/11-06-2015/197756/apple-music-no-rocks/
https://m.roem.ru/22-06-2015/198520/swift-wins/
Разберись с наследованием, чтобы не изобретать велосипеды.
> На самом деле мелькала мысль сделать по принципу eav, но мне кажется это большим усложнением в данном случае.
EAV это замена наследованию когда классов сущностей огромное число (сотни), чтобы упростить систему. Но у нас всего 2 наследуемые сущности и EAV не подходит.
Ну и начинать лучше с сущностей и отношений. Мы бы могли например обсудить правильно ли ты их определил, а только потом реализовывать это таблицами.
> ensemble
Тут id и performer_id по моему дублируют друг друга.
> track
> - category_id (fk)
По моему там на уровне альбомов же проставляется категория?
Связь между альбомами и треками M:N, почему? Один трек на разных альбомах разных лейблов может иметь разную стоимость и условия распространения.
>>570286
>>570310
>>570290
>>570326
Вот зачем вы это тут разводите? В таком случае надо промолчать, а не разжигать бессмысленный спор не имеющий никакого отношения к программрованию. Лучше бы ни одного из этих постов тут не было.
Алсо я в общем согласен с постом, что ОП неплохой человек.
>>570319
Зависит от того, как ты настроишь. Там есть такие возможности:
- для разработки локально ты можешь встроить компилятор LESS/SASS на яваскрипте в веб-страницу. Тогда код компилируется в браузере, удобно тем что все изменения в файле применяются сразу, но страдает скорость загрузки и браузер притрмаживает.
- для выкладки кода на сервер ты компилируешь код в CSS с помощью компилятора, вызвая его напрямую из командной строки или через инструмент вроде gulp/grunt. Соответственно пользователь загружает итоговый CSS файл. Кстати, grunt/gulp умеют еще много чего: склеивать, сжимать файлы, делать CSS спрайты и тд. Правильные разработчики делают один скрипт деплоя который все это делает и автоматически загружает изменения на сервер.
Проверить что у тебя исплоьзуется можно глянув на исходный код страницы в браузере (Ctrl + U), или в dev tools (Ctrl + Shift + I) на вкладке network.
>>570374
Должен, если так написан договор. Более того, ты только разрешение продавать их треки у лейблов выпрашивать замучаешься, и там будет куча условий: минимальный объем продаж который ты обеспечишь (неустойка за провал плана), география распространения, обязательные отчеты, итд. Ты не можешь просто взять и продать 10 песенок, там счет идет на миллионы минимум. Раньше лейблы еще требовали использовать DRM, но благодаря Джобсу удалось это отстоять.
Также, лейблы не готовы продавать все песни. Например Битлз долгое время были недоступны в электронных сервисах, также бывает что новинки дают не всем, а только избранным. Есть по моему 3 крупных лейбла, с каждым надо договариваться отдельно. Также, есть еще мелкие лейблы, с которыми надо заключать отдельные договоры.
Истории по теме:
https://m.roem.ru/11-06-2015/197756/apple-music-no-rocks/
https://m.roem.ru/22-06-2015/198520/swift-wins/
Вместо демонстрационных работ лучше потратить время на изучение чего-то нового, а в ходе изучения можно что-то и сделать.
>>570470
Это плохой способ, там задачи часто неинтересные и малополезные. Полезные кстати есть в нашем треде.
>>570465
Учебник на народе из оп поста.
>>570475
Лучше наши задания на html/css из оп поста.
>>570521
В первую очередь для обозначения каких-то опций, где есть несколько вариантов выбора. Константы там используются для замены «магических» (т.е. непонятных) чисел и строк. Допустим ты делаешь игру, и в ней есть танки трех типов: легкий, средний, тяжелый. Ты хочешь обозначать как-то их типы:
$tank->type = 1; // плохо: что это значит? какие есть еще варианты? непонятно
$tank->type = 'light'; // что это значит? какие есть еще варианты? непонятно
Также, если ты опечатаешься то PHP не укажет тебе на это.
А вот как надо:
class Tank
{
// содержательный комментарий
const TYPE_LIGHT = 'light';
const TYPE_MEDIUM = ...
...
$tank->type = Tank::TYPE_LIGHT; // прекрасно: читабельно, PHP или IDE укажет на ошибку при опечатке, IDE покажет автодополнение, можно искать поиском по коду
Также, константы можно использовать например для хранения постоянных величин, например максимальная скорость танка:
const MAX_SPEED = 80;
> Вот, есть у меня класс с методом, в котором используются константные данные, но только в одном методе. Надо ли их объявлять, как константу класса? Или же достаточно описать просто, как переменную в конкретном методе?
зависит от ситуации и хочешь ли ты чтобы она была видна снаружи.
>>570655
Нужно больше подробностей. Что используешь, PDO или MySQLi, включен ли режим PDO::ERRMODE_EXCEPTION, в случае mysqli пишешь ли if после query, и тд. Что в логе ошибок ? Включен ли display_errors?
Вместо демонстрационных работ лучше потратить время на изучение чего-то нового, а в ходе изучения можно что-то и сделать.
>>570470
Это плохой способ, там задачи часто неинтересные и малополезные. Полезные кстати есть в нашем треде.
>>570465
Учебник на народе из оп поста.
>>570475
Лучше наши задания на html/css из оп поста.
>>570521
В первую очередь для обозначения каких-то опций, где есть несколько вариантов выбора. Константы там используются для замены «магических» (т.е. непонятных) чисел и строк. Допустим ты делаешь игру, и в ней есть танки трех типов: легкий, средний, тяжелый. Ты хочешь обозначать как-то их типы:
$tank->type = 1; // плохо: что это значит? какие есть еще варианты? непонятно
$tank->type = 'light'; // что это значит? какие есть еще варианты? непонятно
Также, если ты опечатаешься то PHP не укажет тебе на это.
А вот как надо:
class Tank
{
// содержательный комментарий
const TYPE_LIGHT = 'light';
const TYPE_MEDIUM = ...
...
$tank->type = Tank::TYPE_LIGHT; // прекрасно: читабельно, PHP или IDE укажет на ошибку при опечатке, IDE покажет автодополнение, можно искать поиском по коду
Также, константы можно использовать например для хранения постоянных величин, например максимальная скорость танка:
const MAX_SPEED = 80;
> Вот, есть у меня класс с методом, в котором используются константные данные, но только в одном методе. Надо ли их объявлять, как константу класса? Или же достаточно описать просто, как переменную в конкретном методе?
зависит от ситуации и хочешь ли ты чтобы она была видна снаружи.
>>570655
Нужно больше подробностей. Что используешь, PDO или MySQLi, включен ли режим PDO::ERRMODE_EXCEPTION, в случае mysqli пишешь ли if после query, и тд. Что в логе ошибок ? Включен ли display_errors?
Чтобы не быть пустословом, напиши важные пункты которые я не даю и наоборот, бесполезные знания которые я заставляю учить. Не больше 10 слов на 1 пункт. Пока будем считать тебя пустословом.
И да, хорошо анон написал, давайте меньше обсуждать личность ОПа, а больше программирование.
>>570790
А зачем ты в куках имя хранишь? Там достаточно токен (секретный идентификатор) пользователя. Я думаю, что незарегистрированным пользователям куки не нужны, если не требуется их как-то различать.
>>570794
> смертной казни за оскорбление святынь ислама
Какая дикость. Но возможно мы в ту же сторону движемся, если следить за новостями. Так что анончики, сохраняйте видео на черный день, может завтра вы его посмотреть не сможете.
>>570793
Через пакетный менеджер ubuntu (apt-get install?)
> Есть ли анонасы, у которых такая смесь работает именно на этой версии бубунты?
А как нгинкс взаимодействует с постгрес? По моему никак и соответсвтенно проблем совместимости быть не может.
>>570814
Наследование может быть? Ну или 2 отношения вида «пользователь купил альбом» и «пользователь купил трек» с 2 внешними ключами.
> Создать доп.таблицу "товар" и ссылаться на нее из таблиц альбомов и треков?
Это одна из форм наследования
> Мне не очень нравится такой подход с точки зрения размера полученной таблицы.
Тут нужна оценка. Мы создаем глобальный сервис и лишний гигабайт ничего не решает.
>>570842
Никогда не использовал. Не нравится много статических методов.
>>570845
Самый простой способ загрузить папку на сервер отдельно и обращаться к ней напрямую. Чтобы интегрироваться с админкой, надо изучать документацию и писать плагин к вордпрессу который добавит нужную страницу админки.
>>570847
В документацию можно и тут послать.
Чтобы не быть пустословом, напиши важные пункты которые я не даю и наоборот, бесполезные знания которые я заставляю учить. Не больше 10 слов на 1 пункт. Пока будем считать тебя пустословом.
И да, хорошо анон написал, давайте меньше обсуждать личность ОПа, а больше программирование.
>>570790
А зачем ты в куках имя хранишь? Там достаточно токен (секретный идентификатор) пользователя. Я думаю, что незарегистрированным пользователям куки не нужны, если не требуется их как-то различать.
>>570794
> смертной казни за оскорбление святынь ислама
Какая дикость. Но возможно мы в ту же сторону движемся, если следить за новостями. Так что анончики, сохраняйте видео на черный день, может завтра вы его посмотреть не сможете.
>>570793
Через пакетный менеджер ubuntu (apt-get install?)
> Есть ли анонасы, у которых такая смесь работает именно на этой версии бубунты?
А как нгинкс взаимодействует с постгрес? По моему никак и соответсвтенно проблем совместимости быть не может.
>>570814
Наследование может быть? Ну или 2 отношения вида «пользователь купил альбом» и «пользователь купил трек» с 2 внешними ключами.
> Создать доп.таблицу "товар" и ссылаться на нее из таблиц альбомов и треков?
Это одна из форм наследования
> Мне не очень нравится такой подход с точки зрения размера полученной таблицы.
Тут нужна оценка. Мы создаем глобальный сервис и лишний гигабайт ничего не решает.
>>570842
Никогда не использовал. Не нравится много статических методов.
>>570845
Самый простой способ загрузить папку на сервер отдельно и обращаться к ней напрямую. Чтобы интегрироваться с админкой, надо изучать документацию и писать плагин к вордпрессу который добавит нужную страницу админки.
>>570847
В документацию можно и тут послать.
Спасибо!
>Константы надо хранить не отдельным объектом, а поместить в «класс» Гамбургер. То есть можно так:
>Hamburger.SIZE_BIG = 'big';
>или
>Hamburger.SIZE_BIG = 1;
>Значение константы это число или строка, не стоит в ней хранить данные, это будет уже не константа, а что-то странное.
Если про перенос объекта в класс понятно, то с тем что в константе нужно хранить только одно значение или строку - не согласен. И вот почему:
- Из 9 аккуратных строк кода мы получаем 14 (в нашем случае, а ведь в других может быть и в разы больше).
- Если объявлять константы отдельно на цену и на калории, то получается и передавать в аргументы при создании нового гамбургера нужно не 2 значения а 4. Раньше было (размер, начинка), сейчас (размер_цена, размер_калории, начинка_цена, начинка_калории). А это противоречит указанной у тебя строке для создания нового гамбургера var hamburger = new Hamburger(Hamburger.SIZE_SMALL, Hamburger.STUFFING_CHEESE);
И для методов такая же ситуация.
что бы начать нужно хотя бы маленько знать, что бы делать вьюхи в админку и хотя бы понимать как на бутстрапе сверстать простенький проект.
Чем меньше проектик, тем за больше всего ты в нем отвечаешь, и тем больше ты должен уметь "всего понемногу". В большом проекте наверное сидишь чисто бэкэнд ковыряешь и в душе не ебешь что-то во фронтенде, ибо фронтэнд там тоже дохуя сложный и им отдельный человек занимается, или отдельная команда.
>>571005
Понял свою ошибку. На пикче ни первый, ни второй вариант не работают.
>Константы надо хранить не отдельным объектом, а поместить в «класс» Гамбургер
В буквальном смысле воспринял.
Хотя не во всем я оказался не прав. Все-таки теперь приходится передавать в 2 раза больше аргументов.
Вот что сейчас получилось.
https://ideone.com/FsDztW
LoadModule php5_module "C:/php/php5apache2_4.dll"
AddHandler application/x-httpd-php .php
PHPIniDir "C:/php"
В httpd.conf, сервис перестаёт запускаться. Удаляю, всё нормально. Файл php5apache2_4.dll в С:/php точно есть. В логи ничего не пишется.
Пардон, я имбецил, каким-то образом скачал архив для 32ух битной версии, вместо 64.
Охуительные истории, прям открыл книгу по html5/css3 и на следующий день верстаю профессиональные страницы за час-два, ага.
> Из 9 аккуратных строк кода мы получаем 14 (в нашем случае, а ведь в других может быть и в разы больше).
5 строк разницы не решают, важно чтобы код был надежный и понятный и им было удобно пользоваться. А то по твоей логике от комментариев тоже один вред.
> Если объявлять константы отдельно на цену и на калории,
Это не требуется. Это не константа из физики вроде ускорения свободного падения. В программировании константы могут иметь другой смысл.
Константа в данном случае только обозначает тип гамбургера. Ну например размер. Она не обозначает сколько он стоит или сколько в нем калорий. Должны же мы как-то указать, какого размера мы хотим гамбургер? Мы это делаем, используя константу.
А чему она равна абсолютно не важно так как мы никогда не используем ее значение. Hamburger.SIZE_SMALL может быть равно 1 или 'small' - это ни на что не влияет.
Вот глянь еще тут пример с танками: >>570869
> то получается и передавать в аргументы при создании нового гамбургера нужно не 2 значения а 4
Нет. Мы передаем размер и начинку, а класс должен сам знать сколько они стоят.
>>571005
В первом примере:
SIZE_BIG: ...
SIZE_BIG это не константа, а лишь строка 'SIZE_BIG', ты не можешь в программе написать например console.log(SIZE_BIG), это выдаст ошибку о несуществующей переменной.
Во втором примере: не надо стоимость делать константами. КОнстанты нужны только для обозначения параметров гамбургера вроде размера или начинки.
Ну сам подумай: а как без констант указать какого размера ты хочешь гамбургер? с какой начинкой? Строки не годятся так как это не понятно, легко опечататься, непонятно какие еще есть варианты.
> Из 9 аккуратных строк кода мы получаем 14 (в нашем случае, а ведь в других может быть и в разы больше).
5 строк разницы не решают, важно чтобы код был надежный и понятный и им было удобно пользоваться. А то по твоей логике от комментариев тоже один вред.
> Если объявлять константы отдельно на цену и на калории,
Это не требуется. Это не константа из физики вроде ускорения свободного падения. В программировании константы могут иметь другой смысл.
Константа в данном случае только обозначает тип гамбургера. Ну например размер. Она не обозначает сколько он стоит или сколько в нем калорий. Должны же мы как-то указать, какого размера мы хотим гамбургер? Мы это делаем, используя константу.
А чему она равна абсолютно не важно так как мы никогда не используем ее значение. Hamburger.SIZE_SMALL может быть равно 1 или 'small' - это ни на что не влияет.
Вот глянь еще тут пример с танками: >>570869
> то получается и передавать в аргументы при создании нового гамбургера нужно не 2 значения а 4
Нет. Мы передаем размер и начинку, а класс должен сам знать сколько они стоят.
>>571005
В первом примере:
SIZE_BIG: ...
SIZE_BIG это не константа, а лишь строка 'SIZE_BIG', ты не можешь в программе написать например console.log(SIZE_BIG), это выдаст ошибку о несуществующей переменной.
Во втором примере: не надо стоимость делать константами. КОнстанты нужны только для обозначения параметров гамбургера вроде размера или начинки.
Ну сам подумай: а как без констант указать какого размера ты хочешь гамбургер? с какой начинкой? Строки не годятся так как это не понятно, легко опечататься, непонятно какие еще есть варианты.
А я могу накатить IIS как веб-сервер? Или поставить Apache и не париться?
Зашёл на скачать пхп 5.5.30 и там написано:
>If you are using PHP as FastCGI with IIS you should use the Non-Thread Safe (NTS) versions of PHP.
Какие это версии? Платформа Windows 8.0 Корпоративная.
Да. Только PHP это слишком просто. Еще и SQL придется выучить, и наверно много всего другого. Библиотеки, фреймворки, инструменты.
Программирование это не работа на конвеере где достаточно запомнить простейшие движения и всю жизнь повторять.
>>571034
Не надо делать цену и калории константами. Надо сделать чтобы константа обозначала размер гамбургера или вид начинки.
У тебя неудобно: пользователь должен явно передать и цену и калории. а надо чтобы он только передал тип, а класс сам все посчитал.
>>571058
Конечно не один день, недели уйдут. У нас в ОП посте есть неплохие задачи на HTML/CSS которые помогут тебе разобраться в основах CSS, позиционировании, без которых нормально в верстке не разберешься.
>>571072
да, нормальная.
Спасибо.
То есть мы можем сделать константы в виде
Hamburger.SIZE_BIG = "BIG";
А уже в функции (size) {
if (size = "BIG") {
price = 100;
caloric = 40;
}
}
Ну это так грубо говоря. Правильно?
Но какого хуя все так сложно? Почему где локалхост все окей и ничего не нужно, а открываю другую папку с другим названием, просто рядом, и там уже нихуя не работает?
На опенсервер не смотри, я пока что от него только интерпритатор подключил, хотя я думал еще дебагер подключить, не знаю нахуя, но вроде там можно смотреть как работает твой код мол не спеша, но я скорее стану танцором с бубном 80 лвла, весь день дрочу эти сервера и пхпштормы, а получается хуйня.
Нет. Ты не должен что-то делать с значением константы и сраванивать ее со строкой. Ты должен писать
if (size == Hamburger.SIZE_BIG)
Ты навключал лишних настроек, наскачивал лишних плагинов, ставишь сборки вместо апача + php, не читаешь документацию к IDE и теперь заслуженно страдаешь.
На втором скриншоте ты пытаешься запустить юнит тесты. Зачем ты это делаешь, я не знаю.
Соответственно начни с чтения документации по IDE и прочитай что делают команды которые ты используешь. Пойми где именно ты подключил запуск юнит-тестов. Затем удали опенсервер.
>Нет. Ты не должен что-то делать с значением константы
Тогда вообще без разницы что в значении константы? Мы его и не используем ни разу? Нам нужно только хорошо читаемое название этой константы?
Именно. Константа только обозначает что-то, а что в ней хранится, неважно, лишь бы это было уникальное значение. Обычно используют либо числа либо строку либо строку совпадающую с названием (текст удобен тем что при отладке проще понять что в переменной).
Соответственно если поменять значение такой константы на любое другое код должен работать как ни в чем не бывало.
где ты плагины и кучу настроек увидел?
все, что я сделал, так это увеличил шрифт кода и изменил оформление его уже, включил показ номера строчки.
Потом, я скачал опен сервер, но с пхпшторма просто взял и подключил интерпритатор от опен сервера ничего более, потом в папке локалхост создал пхп файл и закинул туда свой недоделанный код и все заебись. Создал в той же папке где и локалхост другой проект, опять создал же пхп файл, закинул тот же код и уже нихуяшечки не работает, а выдает ошибку.
Ты что-то нажал или включил не то, потому что ты не запускаешь программу, а пытаешься запустить юнит-тесты (которых у тебя нет, как и самого тестового фреймворк).
Почитай документацию по функции запуска и выясни почему она работает не так.
Переустанови шторм, может поможет.
Я вот запускаю php-файлы даже с рабочего стола, все нормально работает.
Или у тебя нелицензионная версия? В пиратских кряках можно запускать только из папки локалхост, с этим ничего не поделаеш...
>>571184
Ну и советчики конечно.
>при каждой ошибке читать тонны макулатуры
Кароч, причина была в том, что рядом с кнопочкой запуска программа есть хуйня какая-то мол конфигурации, не знаю зачем это нужно, но дело в том, что если я переключаюсь на другой файл с кодом, то этот файл с конфигурациями остается таким же какого-то хуя, да и вообще его поначалу создать надо
И чтобы он изменился, то нужно либо вручную создать либо пкм и т.д. как на скрине нижняя стрелка.
Дебагер вообще не ебу как подключить, геммороая до жопы
А я кстати знал про эти паттерны, но не понимал в каких ситуациях их можно применить на практике.
Тогда применим Class Table Inheritance для групп и артистов.
Performer
- id
- еще какие-то общие свойства
Artist
- id (pk + внешний на performer(id))
- first_name
- surname
- birth_year и т.д.
Ensemble
- id (pk + внешний на performer(id))
- name
- foundation_year и т.д.
Я так понимаю, что выбирать из этих таблиц двумя джойнами с юнионом
SELECT p.id, a.name, a.surname FROM performer p JOIN artist a ON p.id = a.id
UNION (SELECT p.id, e.name FROM performer p JOIN ensemble e ON p.id = e.id)
Еще одна особенность такого подхода в том, что мы должны внимательно следить за бизнес-логикой в приложении, чтобы не заинсертить что-то неправильное, например в Artist и Ensemble записи с одним id.
>Почитай про паттерны наследования. Ты явно пытаешься их переизобрести
Я о них не знал/не понимал их.
А я кстати знал про эти паттерны, но не понимал в каких ситуациях их можно применить на практике.
Тогда применим Class Table Inheritance для групп и артистов.
Performer
- id
- еще какие-то общие свойства
Artist
- id (pk + внешний на performer(id))
- first_name
- surname
- birth_year и т.д.
Ensemble
- id (pk + внешний на performer(id))
- name
- foundation_year и т.д.
Я так понимаю, что выбирать из этих таблиц двумя джойнами с юнионом
SELECT p.id, a.name, a.surname FROM performer p JOIN artist a ON p.id = a.id
UNION (SELECT p.id, e.name FROM performer p JOIN ensemble e ON p.id = e.id)
Еще одна особенность такого подхода в том, что мы должны внимательно следить за бизнес-логикой в приложении, чтобы не заинсертить что-то неправильное, например в Artist и Ensemble записи с одним id.
>Почитай про паттерны наследования. Ты явно пытаешься их переизобрести
Я о них не знал/не понимал их.
Куки же нужно как-то вставлять и доставать.
if(!$cookie=$app->request->cookies->get('username')){ //setting cokkie if the user doesn't have it
$cookieToken = new Token();
$cookieKey=$cookieToken->generatePassword(8);
$app->setCookie('username', $cookieKey, time()+86400*4);
}
$cookie=$app->request->cookies->get('username');
$out = simplexml_load_string($out),
потом смотрю, что получилось (1 пик)
$out -> Pvz[0],
пытаюсь получить значение
$out -> Pvz[0]["City"],
а получаю объект (2 пик).
Как мне получить строковое значение?
У меня верстка 3х страничного магаза по всем правилам адаптивности, резиновости, каруселей, модальных окон и прочей хрени не меньше недели займет.
Самого волнует этот вопрос, ибо я ни разу ни дизинер, жабаскрипт и кроссбраузерность вызывают рвотные позывы, вообще терпеть не могу верстку, какое-то издевательство над программистом.
>Если да то за каким фигом программист занимается дизайном?
Программист дизайном не занимается, для этого должен быть дизайнер, который рисует макет страницы. Задача программиста нарезать макет и сверстать страничку и соответственно навесить на неё необходимый функционал/анимации. В более крупных конторах есть верстальщики, там от программиста уже вёрстка не требуется, но разбираться в ней всё равно необходимо.
гугли xdebug + phpstorm
там правда проблема в том, что потом для каждого проекта тебе придется настраивать дебаггер - порты прописывать и т.п. но это не особо геморройно. во всяком случае оно того стоит.
вкратце - ставишь расширение xdebug, включаешь его в php.ini, в конфиге xdebug прописываешь необходимые настройки, дублируешь их в проекте в phpstorm.
далее рабочий процесс - расставляешь точки выполнения, жмешь debug и можешь переключаться пошагово и смотреть, как у тебя выполняется код - что выводится, сохраняется в переменные и т.п.
в общем, стоит запариться, советую.
php-программист натягивает сверстанные html-странички на php - режет страницы на повторяющиеся блоки, реализует задуманный функционал и т.п.
т.е. понятное дело, что тебе не придется именно верстать макеты. однако об этом процессе тебе нужно иметь представление, чтобы быстро и качественно выполнять свою работу по натягиванию верстки на php.
при создании каждого нового проекта в phpstorm тебе придется заново прописывать все настройки. проверь.
причем с задачами типовыми, бытовыми в программировании и крутыми
гостевуха - имиджобрда - магазин.
>что тебе не придется именно верстать макеты.
ой вей, сказали уже смотря где. везде где я был - нужны были человеки-оркестры.
> Да. Только PHP это слишком просто. Еще и SQL придется выучить, и наверно много всего другого. Библиотеки, фреймворки, инструменты.
Это как раз нормально и ожидаемо, так во всех языках программирования.
Все правильно?
> Artist
- id (pk + внешний на performer(id))
Лучше назвать поле performer_id чтобы было понятнее.
А ты сравнивал варианты? Сравни-ка Single Table Inheritance против Class TI.
> Я так понимаю, что выбирать из этих таблиц двумя джойнами с юнионом
Юнион работает только когда список полей одинаковый. У тебя таблицы с разными полями и это осложнит жизнь, так что выбирай просто 2 джойнами.
> Еще одна особенность такого подхода в том, что мы должны внимательно следить за бизнес-логикой в приложении, чтобы не заинсертить что-то неправильное, например в Artist и Ensemble записи с одним id.
Это да. Или рассмотреть еще другие варианты наследования.
>>571205
Нет, погоди, зачем незарегистрированному пользователю кука? Куками обычно идентифицируют зарегистрированных.
>>571207
strval(...), почитай примеры по smplexml в мануале PHP, там отдельная страница есть.
>>571208
Ты терпеть не можешь потому что не разбираешься. И тебе приходится переставлять наугад свойства, а оно все время куда-то вываливается, съезжает итд.
А человек который разбирается видит у себя в голове все эти блоки, представляет как они позиционированы и что будет если поменять то или иное свойство.
Потому тебе надо разбраться в CSS как следует. Гармоничный HTML и CSS код вряд ли вызовет у тебя неприятные ощущения, скорее наоборот, удовлетворение от хорошо сделанного дела.
> Artist
- id (pk + внешний на performer(id))
Лучше назвать поле performer_id чтобы было понятнее.
А ты сравнивал варианты? Сравни-ка Single Table Inheritance против Class TI.
> Я так понимаю, что выбирать из этих таблиц двумя джойнами с юнионом
Юнион работает только когда список полей одинаковый. У тебя таблицы с разными полями и это осложнит жизнь, так что выбирай просто 2 джойнами.
> Еще одна особенность такого подхода в том, что мы должны внимательно следить за бизнес-логикой в приложении, чтобы не заинсертить что-то неправильное, например в Artist и Ensemble записи с одним id.
Это да. Или рассмотреть еще другие варианты наследования.
>>571205
Нет, погоди, зачем незарегистрированному пользователю кука? Куками обычно идентифицируют зарегистрированных.
>>571207
strval(...), почитай примеры по smplexml в мануале PHP, там отдельная страница есть.
>>571208
Ты терпеть не можешь потому что не разбираешься. И тебе приходится переставлять наугад свойства, а оно все время куда-то вываливается, съезжает итд.
А человек который разбирается видит у себя в голове все эти блоки, представляет как они позиционированы и что будет если поменять то или иное свойство.
Потому тебе надо разбраться в CSS как следует. Гармоничный HTML и CSS код вряд ли вызовет у тебя неприятные ощущения, скорее наоборот, удовлетворение от хорошо сделанного дела.
Хотя я по старинке, вар-дампами отлаживаю, но ты не бери дурной пример, а научись пользоваться отладчиком.
>>571208
Потому предлагаю прорешать задачки из ОП поста и показать решения.
>>571236
Нужны значит нужны. Обычно отдельный фронтенд в больших проектах, но чтобы туда попасть нужен хороший уровень знаний, ООП, фреймворки, тесты - само собой разумеется.
>>571216
А задачи из ОП поста как тебе? Правда, без ответов.
> причем с задачами типовыми, бытовыми в программировании и крутыми
Типовая задача сделать огромный проект или сделать доработки по сложному проекту. Она сложная и требует много времени, а начинабщий там сломает голову.
Потому учебные задачи обычно проще, но позволяют сконцентрироваться на изчении тех или иных вещей.
У нас есть хоршие задачи на студентов и файлообменник в ОП посте.
Вот переменные, если что.
Урок по работе с датами: https://gist.github.com/codedokode/10539805
Мне ООП-версия DateTime нравится больше.
>>571279
> т.е. $app->render('File.php', ['files'=>$files]); вот это должно вызываться при создании метода другого класса.
Это очень странно что ты рендеришь шаблоны из класса. Скорее всего это неправильно.
А вообще, в таких случаях объект обычно передают либо через конструктор если он нужен всему классу, либо в аргументы если он по логике нужен только методу.
>>571293
Вроде на первый взгляд да. Не забудь внешние ключи и NULL/NOT NULL правильно прописать.
А для файла и комментария всегда есть автор или там может быть NULL? Тогда нужен не JOIN а LEFT JOIN. Почитай про разницу между видами джойнов.
Если юзер разлогинивается то надо удалить авторизационную куку. Если время ее жизни истекло и браузер ее удалил, то юзер тоже фактически разлогинилсяи должен перезалогиниться.
Командная строк винды использует кодировку cp866. Потому ты должен сделать SET NAMES cp866; в начале работы.
Этим ты говоришь MySQL что ты присылаешь и хочешь получать данные в такой кодироке. Это никак не влияет на кодировку таблиц, полей, и тд, MySQL сделает все преобразования сама.
Благодарю. Я почему-то думал, что SET NAMES только на отображение влияет, не думал что из-за этого не получается вставлять значения.
> пусть цены формируются так: лейбл выделяет несколько категорий, каждый альбом относит к категории и задает цену на трек и на альбом отдельно
Категория задается для альбома? Значит, трек обязан входить минимум в один альбом, даже если это сингл?
>Лейбл устанавливает как цену продажи трека, альбома, так и лицензионные отчисления за это.
Лицензионные отчисления зависят от страны, как это было в случае с ценообразованием? И они вообще в чем измеряются, в процентах?
>>571340
>анон, который Юи делает, а ты дебаггер в своей IDE настроил и освоил?
Пробовал хелловорлды, но не особо понравилось. Шторм стоит, почти не пользуюсь, тоже ставлю вардампы в саблайме. Но наверное когда пойдут большие проекты, дебаггер будет выручать.
Не знаю когда вернусь к сайту объявлений, заколупался теперь с этой базой, буду наверное месяц делать, спасибо блин большое. Бросить принципиально не могу, нужно всегда доводить начатое до конца.
>>571337
>А ты сравнивал варианты? Сравни-ка Single Table Inheritance против Class TI.
Сравнивал. STI мне не нравится из-за большого кол-ва нулевых значений. Хотя да, она нас обезопасит от вставки одинаковых id, с чем мы можем столкнуться в Class TI.
И джойны будут не нужны. Ок, заменяем на STI.
Concrete TI не рассматриваю, потому что эта схема имеет все недостатки Class TI, плюс отсутствует "общая" таблица, ради которой эти пляски и затевались.
Короче, у меня пока получилось так
Performer
- id
- first_name
- surname
- group_id (внешний ключ на эту же таблицу, null если музыкант не состоит в группе)
- birth_year и пр.атрибуты музыканта
- group_name
- foundation_year и пр.атрибуты группы
- type (enum: artist, group)
Country
- id
- name
Label
- id
- name
- country_id (fk)
Category
- id
- name
- label_id (fk)
- album_base_price
- track_base_price
Category_country
- category_id (pk, fk)
- country_id (pk, fk)
- local_album_price
- local_track_price
- available (enum: y, n)
Tariff
- id
- label_id (fk)
- type (enum: trial, premium)
- tracks_total (макс.кол-во треков доступно для прослушивания)
- tracks_total_free
- one_track_per_day (допустимое кол-во прослушиваний треков в день)
- one_track_per_day_free
- one_album_per_day (допустимое кол-во прослушиваний альбомов в день)
- one_album_per_day_free
- one_performer_per_day (треков исполнителя в день доступно для прослушивания)
- one_performer_per_day_free
- purchase_fee (отчисления за покупки)
- trial_fee (отчисления за бесплатные прослушивания)
Subscription
- id
- name (trial, premium)
- duration
Audition
- id
- user_id
- track_id
User
- id
- email (null для незарегистрированных)
- password (null для незарегистрированных)
- country_id
- прочие свойства пользователя
Content
- id
- title
- length
- track_album (fk на эту же таблицу)
- album_type (enum: simple, remix, tribute и т.д.)
- release_year
- content_type (enum: album, track)
Genre
- id
- name
Content_genre
- content_id
- genre_id
Content_author
- content_id
- performer_id
Content_performer
- content_id
- performer_id
Purchase
- id
- content_id
- дата покупки и т.д.
User_subscription
- user_id
- subscription_id
- expired
Там еще какие-то рекомендации, непонятно как их делать, и нужно ли их хранить отдельно в базе, или выводить из анализа продаж в приложении.
>некоторые альбомы можно исключать из бесплатного прослушивания на определенный период
Не знаю, допустим сделаем таблицу
Promo
- album_id
- expired
Теперь нужно еще написать запросы на выборку из этой свалки.
> пусть цены формируются так: лейбл выделяет несколько категорий, каждый альбом относит к категории и задает цену на трек и на альбом отдельно
Категория задается для альбома? Значит, трек обязан входить минимум в один альбом, даже если это сингл?
>Лейбл устанавливает как цену продажи трека, альбома, так и лицензионные отчисления за это.
Лицензионные отчисления зависят от страны, как это было в случае с ценообразованием? И они вообще в чем измеряются, в процентах?
>>571340
>анон, который Юи делает, а ты дебаггер в своей IDE настроил и освоил?
Пробовал хелловорлды, но не особо понравилось. Шторм стоит, почти не пользуюсь, тоже ставлю вардампы в саблайме. Но наверное когда пойдут большие проекты, дебаггер будет выручать.
Не знаю когда вернусь к сайту объявлений, заколупался теперь с этой базой, буду наверное месяц делать, спасибо блин большое. Бросить принципиально не могу, нужно всегда доводить начатое до конца.
>>571337
>А ты сравнивал варианты? Сравни-ка Single Table Inheritance против Class TI.
Сравнивал. STI мне не нравится из-за большого кол-ва нулевых значений. Хотя да, она нас обезопасит от вставки одинаковых id, с чем мы можем столкнуться в Class TI.
И джойны будут не нужны. Ок, заменяем на STI.
Concrete TI не рассматриваю, потому что эта схема имеет все недостатки Class TI, плюс отсутствует "общая" таблица, ради которой эти пляски и затевались.
Короче, у меня пока получилось так
Performer
- id
- first_name
- surname
- group_id (внешний ключ на эту же таблицу, null если музыкант не состоит в группе)
- birth_year и пр.атрибуты музыканта
- group_name
- foundation_year и пр.атрибуты группы
- type (enum: artist, group)
Country
- id
- name
Label
- id
- name
- country_id (fk)
Category
- id
- name
- label_id (fk)
- album_base_price
- track_base_price
Category_country
- category_id (pk, fk)
- country_id (pk, fk)
- local_album_price
- local_track_price
- available (enum: y, n)
Tariff
- id
- label_id (fk)
- type (enum: trial, premium)
- tracks_total (макс.кол-во треков доступно для прослушивания)
- tracks_total_free
- one_track_per_day (допустимое кол-во прослушиваний треков в день)
- one_track_per_day_free
- one_album_per_day (допустимое кол-во прослушиваний альбомов в день)
- one_album_per_day_free
- one_performer_per_day (треков исполнителя в день доступно для прослушивания)
- one_performer_per_day_free
- purchase_fee (отчисления за покупки)
- trial_fee (отчисления за бесплатные прослушивания)
Subscription
- id
- name (trial, premium)
- duration
Audition
- id
- user_id
- track_id
User
- id
- email (null для незарегистрированных)
- password (null для незарегистрированных)
- country_id
- прочие свойства пользователя
Content
- id
- title
- length
- track_album (fk на эту же таблицу)
- album_type (enum: simple, remix, tribute и т.д.)
- release_year
- content_type (enum: album, track)
Genre
- id
- name
Content_genre
- content_id
- genre_id
Content_author
- content_id
- performer_id
Content_performer
- content_id
- performer_id
Purchase
- id
- content_id
- дата покупки и т.д.
User_subscription
- user_id
- subscription_id
- expired
Там еще какие-то рекомендации, непонятно как их делать, и нужно ли их хранить отдельно в базе, или выводить из анализа продаж в приложении.
>некоторые альбомы можно исключать из бесплатного прослушивания на определенный период
Не знаю, допустим сделаем таблицу
Promo
- album_id
- expired
Теперь нужно еще написать запросы на выборку из этой свалки.
>Программирование это не работа на конвеере где достаточно запомнить простейшие движения и всю жизнь повторять
В 95% случаев как раз нужно повторять заученные действия. Если ты не старший разработчик, конечно, но большинство собравшихся здесь не дорастут даже до миддла.
Я понимаю, что ты умен и тебе скучно выполнять рутинную работу, но зачем ты грузишь сложными задачами тупых нубов, которым это не уперлось?
Или, быть может, чтобы искать работу мне надо браться за что-то другое?
> Значит, трек обязан входить минимум в один альбом, даже если это сингл?
На сингле редко бывает ровно 1 трек. Например, вот на одном из синглов я вижу 2 песни + 2 инструментальных версии.
Я думаю, лучше вместо понятия «альбом» ввести понятие «пластинка/диск». То есть вполне может быть диск с 1 треком. Разумеется в таком случае показывать 2 цены нелогично.
> Лицензионные отчисления зависят от страны, как это было в случае с ценообразованием? И они вообще в чем измеряются, в процентах?
В валюте, для простоты пусть будет в долларах.
> Там еще какие-то рекомендации, непонятно как их делать, и нужно ли их хранить отдельно в базе, или выводить из анализа продаж в приложении.
От тебя требуется спроектировать систему их хранения, а алгоритм их составления это уже другое дело. Рекомендация, это по моему (если я не путаю) просто отношение между альбомами вида «c альбомом A рекомендуют альбом B, C, D».
ОП, проверь пожалуйста.
https://github.com/never3ver/catsandmice
>И еще, у кошки и мышки функция makeMove похожа.
Похожа то она похожа, но стоит ли городить ради пары-тройки общих строчек целую функцию? Там по сути только реакция на отсутствие хода общая и собственно вызов функции изменения координат.
>перейдем к более сложным (но близким к жизни) задачам, наверно про студентов?
Да, планировал студентов после кошек-мышек. Если я правильно понял, то для решения нужно дополнительно к текущему багажу изучить верстку и sql?
Я не понимаю откуда вы такие беретесь, вот что значит
>выучил html, php & sql от корки до корки
Ты книжку прочитал, или что?
По теме не берись сразу за уии2, судя по вопросам которые ты задаешь ты совсем к нему не готов. Лучше сделай файлообменник из заданий ОПа, это будет тебе намного полезней, а еще лучше начни с задания про студентов, заодно и проверишь как ты выучил все от корки до корки
https://2ch.hk/pr/res/561925.html
Ура, нашел. Им оказался Solarflare. Не представляю, что бы я без него делал.
А я посрал только что.
Так использовать хеш для хранения уникальных посещений?
Вообще я первоначально использовал сет, поскольку нам нужны только значения, причем уникальные.
Сет собственно для того и предназначен, для проверки наличия в нем значения. Так что лучше использую сет.
При запросе страницы проверяем, есть ли в сете pagesVisitsUnique текущее значение "id:ip". Если нет, то записываем его туда, и кладем в очередь id страницы.
Скрипт по расписанию:
1. Берет текущую длину очереди, фиксирует это значение в переменную.
2. Запрашиваем порциями данные из редиса (без удаления их из очереди на этом шаге), пока не достигнем этой суммы N.
2.1. Преобразуем полученный массив в ['кол-во посещений'=>array('все id страниц с таким кол-вом посещений')].
2.2. Ходим в цикле и для каждого ключа массива, упомянутого выше, подставляем массив айдишников в запрос:
UPDATE tbl_name SET visit_counter = visit_counter + delta WHERE id IN (implode($arr, ',')).
2.3. Коммитим mysql, затем удаляем обработанное кол-во записей из начала очереди.
>алгоритм меня устраивает, кроме вопроса про хранение IP в очереди (зачем?) и настройки сохранения на диск.
Действительно, ip в самой очереди не нужен, просто пришлось несколько раз переписывать, немного замаялся, недосмотрел.
По поводу настройки на диск мануал читал http://redis.io/topics/persistence , но мало что вынес.
Короче, настройки "save 1 1" вполне хватит. (сбрасывать на диск каждую секунду)
Кстати, мы в прошлом треде рассматривали оптимизацию запросов, еще тогда назрел вопрос по тому, как выполняется оператор IN в условии. Explain почему-то пишет join type "range", это кажется не совсем очевидным. Range это диапазон, следовательно должен употребляться только с операторами > и <.
Я так думаю, что mysql при запросе where id in (1, 44, 88) берет сначала этот диапазон между min 1 и max 88 (поэтому Range), а потом ходит по полученному промежутку бинарным поиском и ищет указанные значения.
оп-пост читай, там ссылка на учебник и блог опа. Там же задания, начиная от простых типа кредитного калькулятора, заканчивая большими полноценными приложениями вроде файлообменника и сервиса для тестов.
Ой, отправилось раньше времени. Я сам не стал бы наверно участвовать в хакатоне, и вообще, мне тяжело целые сутки не спать, но в общем такие вещи это хорошо. Смотрите, как много юных программистов пришло. Это очень важная штука, ведь в будущем роль информационных технологий будет только расти, они будут исплоьзоваться повсеместно и те страны, в которых много хороших специалистов, будут впереди тех, у кого их нет.
Ну и программирование, я думаю, станет не уделом небольшого количества странных людей, а огромной отраслью. Так что учитесь хорошо, анончики, если хотите занять места в этом поезде.
Что это значит? Я уже весь день ебусь с этим слимом и пытаюсь подружить его с твигом и это похоже на танцы с бубном, так как я нихера не понимаю, просто копирую какие-то советы, которые нахожу на stackoverflow.
> Короче, настройки "save 1 1" вполне хватит. (сбрасывать на диск каждую секунду)
Придется перечитать мануал. Жаль. что ты в нем не разобрался. Ведь если ты будешь работать в нашей сфере, то тебе придется разбираться с какими-то новыми библиотеками или инструментами по документации.
А чтобы ты знал что искать в этом мануале, объясню. В редисе есть 2 способа сохранения: RDB и AOF.
RDB - это сохранение снапшота (снимка) всей базы в файл. Как сделать снимок многогигабайтного массива данных и сохранить, не останавливая работу редиса? Для этого использется форк. В линукс-системах форк - это операция, которая создает полную копию процесса, то есть выделяется память и все содержимое памяти редиса копируется в нее, и появляется второй процесс-клон первого, который не спеша сбрасывает на диск гигабайты данных. Так как это отдельный процесс, то он никак не мешает редису. Редис блокируется только на время выполнения форка.
Это значит что редис никогда не должен занимать больше половины памяти. Иначе процессу-копии ее может не хватить.
Разумеется сделать копию огромного объема данных мгновенно нельзя. Но ОС оптимизирует форк, вместо реального копирования она использует copy-on-write ( http://freesource.info/wiki/Texnologii/CopyOnWrite& ), то есть одни и те же страницы памяти отображаются в оба процесса и реально копируются только когда один из них пытается что-то в них поменять. Если редис особо ничего не меняет в памяти, то копирования не происходит, оба процесса работают с одной областью памяти.
После того как произошел форк, процесс-копия записывает данные в файл. Последовательная запись данных на диск - это наиболее эффективный режим его работы и скорость записи может достигать 100-200 Мб/с. Но даже так, на сброс 3 гб данных нужно около 15 секунд.
Очевидно что твоя идея с save 1 1 во-первых, не сможет сбрасывать данные каждую секунду, во-вторых, будет генерировать огромный дисковый трафик, полностью загружая диск постоянной перезаписью текущего снапшота. Соответственно для всех остальных процессов диск начнет отвечать с большой задержкой.
Ну и как я помню, редис не перезаписывает старый файл снапшота, а создает рядом новый, и после завершения записи переименовывает его на старый. Это делается для того, чтобы замена файла была атомарной и мы не могли ни при каких обстоятельствах получить повреженный файл снапшота.
Потому снапшоты нельзя делать часто.
И потому в редисе есть второй способ сохранения данных - AOF. AOF значит append only file, то есть файл, данные в котором дописываются только в конец. Почему только в конец? Чтобы ни при какой ситуации не повредить ранее записанные данные и потому что тут опять получается последовательная запись, самый эффективный режим работы магнитных дисков.
Редис пишет в AOF файл все полученные от клиентов команды, например SET key value. При успешном сбросе RDB снапшота на диск лог очищается (так как эти данные есть в снапшоте).
Восстановление данных происходит так. При перезапуске сервера редис сначала читает RDB файл, если он есть, и загружает в память данные из него. Это идет со скоростью чтения диска с файла и время зависит от объема данных. Затем редис читает AOF файл (хранящий изменения с момента последнего снапшота) и проигрывает все эти команды заново. Это тоже фактически по времени упирается в скорость чтения.
Если мы будем писать данные в AOF после каждой операции, да еще и дожидаться физического сброса на диск, то наша система будет очень надежной, и будет гарантировать сохранение данных в любой ситуации. Как MySQL. Но при этом она станет медленной так как редис однопоточный, на сильно загруженной системе диск занят еще и другими делами и большую часть времени мы будем ждать его реакции. И никаких десятков и сотен тысяч записей в секунду нам не видать.
Потому редис позволяет жертвовать надежностью и сбрасывать изменения в лог реже. Например раз в секунду или вообще ограничиться передачей данных операционной системе, которая будет сбрасывать их в фоновом режиме как ей удобно. В последнем случае теряются только при сбое по питанию или аппаратной проблеме, но не теряются при падении процесса редиса.
Лог обычно генерирует небольшую нагрузку на диск так как записываются в него только приходящие команды, если одна команда имеет размер 100 байт и идет 10 000 команд в секунду, это 1 Мб/с дискового трафика.
Однако лог имеет свои недостатки. Если не использовать RDB то лог будет расти неограниченно. Такой лог может занять все свободное место на диске, и восстановление данных будет очень долгим.
Потому у администратора есть большой выбор вариантов сохранения данных:
- отключить RDB/AOF
- использовать только редкий RDB
- использовать RDB + AOF с разными степенями надежности
Также, даю тебе почитать вот такую статью, сделанную разработчиками вконтакте (они тоже пилили свои хранилища наподобие редиса, у них же хайлоад), вдруг что полезного найдешь: https://github.com/vk-com/kphp-kdb/blob/master/docs/ru/DBMS_Storage_Comparison.wiki
> Короче, настройки "save 1 1" вполне хватит. (сбрасывать на диск каждую секунду)
Придется перечитать мануал. Жаль. что ты в нем не разобрался. Ведь если ты будешь работать в нашей сфере, то тебе придется разбираться с какими-то новыми библиотеками или инструментами по документации.
А чтобы ты знал что искать в этом мануале, объясню. В редисе есть 2 способа сохранения: RDB и AOF.
RDB - это сохранение снапшота (снимка) всей базы в файл. Как сделать снимок многогигабайтного массива данных и сохранить, не останавливая работу редиса? Для этого использется форк. В линукс-системах форк - это операция, которая создает полную копию процесса, то есть выделяется память и все содержимое памяти редиса копируется в нее, и появляется второй процесс-клон первого, который не спеша сбрасывает на диск гигабайты данных. Так как это отдельный процесс, то он никак не мешает редису. Редис блокируется только на время выполнения форка.
Это значит что редис никогда не должен занимать больше половины памяти. Иначе процессу-копии ее может не хватить.
Разумеется сделать копию огромного объема данных мгновенно нельзя. Но ОС оптимизирует форк, вместо реального копирования она использует copy-on-write ( http://freesource.info/wiki/Texnologii/CopyOnWrite& ), то есть одни и те же страницы памяти отображаются в оба процесса и реально копируются только когда один из них пытается что-то в них поменять. Если редис особо ничего не меняет в памяти, то копирования не происходит, оба процесса работают с одной областью памяти.
После того как произошел форк, процесс-копия записывает данные в файл. Последовательная запись данных на диск - это наиболее эффективный режим его работы и скорость записи может достигать 100-200 Мб/с. Но даже так, на сброс 3 гб данных нужно около 15 секунд.
Очевидно что твоя идея с save 1 1 во-первых, не сможет сбрасывать данные каждую секунду, во-вторых, будет генерировать огромный дисковый трафик, полностью загружая диск постоянной перезаписью текущего снапшота. Соответственно для всех остальных процессов диск начнет отвечать с большой задержкой.
Ну и как я помню, редис не перезаписывает старый файл снапшота, а создает рядом новый, и после завершения записи переименовывает его на старый. Это делается для того, чтобы замена файла была атомарной и мы не могли ни при каких обстоятельствах получить повреженный файл снапшота.
Потому снапшоты нельзя делать часто.
И потому в редисе есть второй способ сохранения данных - AOF. AOF значит append only file, то есть файл, данные в котором дописываются только в конец. Почему только в конец? Чтобы ни при какой ситуации не повредить ранее записанные данные и потому что тут опять получается последовательная запись, самый эффективный режим работы магнитных дисков.
Редис пишет в AOF файл все полученные от клиентов команды, например SET key value. При успешном сбросе RDB снапшота на диск лог очищается (так как эти данные есть в снапшоте).
Восстановление данных происходит так. При перезапуске сервера редис сначала читает RDB файл, если он есть, и загружает в память данные из него. Это идет со скоростью чтения диска с файла и время зависит от объема данных. Затем редис читает AOF файл (хранящий изменения с момента последнего снапшота) и проигрывает все эти команды заново. Это тоже фактически по времени упирается в скорость чтения.
Если мы будем писать данные в AOF после каждой операции, да еще и дожидаться физического сброса на диск, то наша система будет очень надежной, и будет гарантировать сохранение данных в любой ситуации. Как MySQL. Но при этом она станет медленной так как редис однопоточный, на сильно загруженной системе диск занят еще и другими делами и большую часть времени мы будем ждать его реакции. И никаких десятков и сотен тысяч записей в секунду нам не видать.
Потому редис позволяет жертвовать надежностью и сбрасывать изменения в лог реже. Например раз в секунду или вообще ограничиться передачей данных операционной системе, которая будет сбрасывать их в фоновом режиме как ей удобно. В последнем случае теряются только при сбое по питанию или аппаратной проблеме, но не теряются при падении процесса редиса.
Лог обычно генерирует небольшую нагрузку на диск так как записываются в него только приходящие команды, если одна команда имеет размер 100 байт и идет 10 000 команд в секунду, это 1 Мб/с дискового трафика.
Однако лог имеет свои недостатки. Если не использовать RDB то лог будет расти неограниченно. Такой лог может занять все свободное место на диске, и восстановление данных будет очень долгим.
Потому у администратора есть большой выбор вариантов сохранения данных:
- отключить RDB/AOF
- использовать только редкий RDB
- использовать RDB + AOF с разными степенями надежности
Также, даю тебе почитать вот такую статью, сделанную разработчиками вконтакте (они тоже пилили свои хранилища наподобие редиса, у них же хайлоад), вдруг что полезного найдешь: https://github.com/vk-com/kphp-kdb/blob/master/docs/ru/DBMS_Storage_Comparison.wiki
\n для консоли, в html нужно использовать <br> для перевода строки, или просто блочный объект типа параграфа или дива.
>>571577
Давай ссылку на гитхаб, так сложно догадаться, что у тебя в коде.
Для того чтобы слим подцепил твиг, нужен пакет slim/views
https://github.com/slimphp/Slim-Views
В конфиге соответственно нужно передать объект твига в качестве объекта представления.
$app = new Slim\Slim(
array(
'view' => new Slim\Views\Twig(),
));
> Я так думаю, что mysql при запросе where id in (1, 44, 88) берет сначала этот диапазон между min 1 и max 88 (поэтому Range), а потом ходит по полученному промежутку бинарным поиском
По моему, она делает N бинарных поисков. Это будет наверно быстрее.
И ведь между 1 и 44 может быть очень много строк, особенно если индекс не уникальный.
По поводу алгоритма, все выглядит ок.
тут http://dev.mysql.com/doc/refman/5.7/en/explain-output.html#jointype_range написано
> range can be used when a key column is compared to a constant using any of the =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, or IN() operators:
заметь что и = тут упомянуто, но возможно это для случаев когда у нас много колоночный индекс и мы ищем по первой его части.
Чтобы переносы строк нормально работали и в браузере и в ideone (и в консоли), можно использовать для этого \n, а в начале программы поставить
header("Content-Type: text/plain; charset=utf-8");
Это заставит браузер воспринимать то, что выводит твоя программа, как обычный текст, а не HTML, и уважать переносы строк в нем (так как в языке HTML перенос строки равносилен пробелу).
Иначе перенос строки будет в исходном коде страницы (его можно увидеть нажав Ctrl + U), но на самой странице его не будет.
5.6. Через композер. Вырезал из папки vendor/slim/slim/ папку слима и вставил в корневую директорию сайта, там создал папку Extras/Views, туда вставил содержимое vendor/slim/extras/views, В Twig.php изменил значение public static $twigDirectory на "Twig" (причем нигде не было написано, что это нужно было сделать, даже в репозитории твига. нашел в гугле).
> Вырезал из папки vendor/slim/slim/ папку слима
Это неправильно. Ты не должен ничего трогать в вендор, композер для того и придуман чтобы не надо было ничего скачивать, копировать и переносить.
Так что удаляй все что ты напереносил и подключай тот слим что в vendor. для этого тебе достаточно приреквайрить vendor/autoload.php и все остальное подключится само собой. Это по моему в документации Слима написано.
> В Twig.php изменил значение public static $twigDirectory на "Twig" (
Нельзя править сторонние библиотеки. Удаляй их и установи композером чистые заново.
> причем нигде не было написано, что это нужно было сделать, даже в репозитории твига
Потому что так нельзя делать. а тот кто написал это в гугле - тот быдллокодер какой-то наверно.
Идея композера в том что ты лишь добавляешь в свой проект compoer.json и после этого любой скачавший твой проект запускает композер и он устанаваливает те же версии библиотек что у тебя.
Обязательно потом покажи свой код на проверку. Я вижу у тебя есть какие-то нездоровые наклонности.
>>571629
Ладно, завтра со свежей головой удалю всё к херам и поставлю заново композером. Просто в этой статье написано сделать структуру папок как на пикрелейтеде, вот я и повыдергивал всё из вендора http://code.tutsplus.com/tutorials/rapid-application-prototyping-in-php-using-a-micro-framework--net-21638
Еще вот тут вот https://github.com/codeguy/Slim-Extras написано DEPRECATED то есть проект устарел и не поддерживается.
Тебе надо:
1) разобраться как работает композер, почитать хотя бы пару страниц документации слима, и не делать ничего наугад или по советам из гугла , а все делать осознанно
2) вот более новый проект для твига в Слим: https://github.com/slimphp/Slim-Views#twig
> 2011
Видимо тогда еще автор статьи про композер не слышал. Это статья с ручной установкой Слима, где ты должен руками скачать архив с сайта и распоковать. Не советую.
При установке композером от тебя требуется только прописать стрчку в composer.json и сделать composer install. Ничего копировать не надо. Разберись как это работает.
Алсо Paris и Idiorm мне тоже не нравятся. Почитай например мой урок про паттерны работы с БД - они им плохо соответствуют. У нас в PHP есть только один полноценный ORM - Doctrine 2.
Статью можешь почитать, но тебе придется самому править код под современную ситуацию.
https://ideone.com/K8jt1E
Правильно переделал? Насчет добавления ошибок не уверен, это не проверял пока. И еще у меня цену и калории нужно где-то обнулить, а то неправильно высчитывается при нескольких вызовах, но пока я не придумал где.
Пофиксил неправильные расчеты.
> if (topping == Hamburger.TOPPING_SAUCE) ++this.topping_sause;
Пиши со скобками в 3 строчки, ++ перед именем смотрится как-то непривычно и сбивает с толку. Почему ты экономишь место? У тебя слишком громоздкие функции? Надо значит их разбивать на части, а не пытаться впихнуть как можно больше кода в одну строку.
> if (this.topping_sause >= 2) throw new HamburgerError("Приправа добавлена более 1 раза");
Ты сначала меняешь состояние объекта на недопустимое (выбрано 2 добавки), а потом бросаешь исключение. Лучше бросить исключение, оставив объект в корректном состоянии.
Проверки надо вынести в отдельные функции, например:
this.validateSize(size);
> И еще у меня цену и калории нужно где-то обнулить, а то неправильно высчитывается при нескольких вызовах, но пока я не придумал где.
Проще всего их не сохранять. Я по моему уже 2 раза об этом писал, цена и калории определяются свойствами гамбургера и их всегда можно посчитать заново. Незачем их хранить.
> throw new HamburgerError(
Не вижу определения этой функции
> this.topping_sause = 0;
Добавки лучше хранить в массиве так как в будущем мы можем добавить новые
.
Методы надо сделать через прототипы. Посмотри какой у тебя гигантский конструктор и как все смешано из-за этого.
> return this.price
Точка с запятой
>Методы надо сделать через прототипы. Посмотри какой у тебя гигантский конструктор и как все смешано из-за этого.
this.addTopping => Hamburger.prototype.addTopping
Но что это дает? Пойду перечитывать главу про это, но на леарн.яваскрипт в этом уроке настолько хороший пример, что ничего не понятно. Там просто заменили this. на class.prototype. в том классе, где остальной код и не подлежит изменению, поэтому и непонятно на что это влияет. Но, возможно, в предыдущих уроках этой главы объяснялось. Буду перечитывать.
Расчет цены и калорий уже починил. Правильно или опять не то?
>Проверки надо вынести в отдельные функции, например:
>this.validateSize(size);
Зачем? И почему через this. Сделал, но не пойму зачем.
>Ты сначала меняешь состояние объекта на недопустимое (выбрано 2 добавки), а потом бросаешь исключение. Лучше бросить исключение, оставив объект в корректном состоянии.
А если ввести и вывести? В общем мое решение подойдет?
>>this.topping_sause = 0;
>Добавки лучше хранить в массиве так как в будущем мы можем добавить новые
Пока не дошло как это сделать, но буду думать.
Ну и >не пытаться впихнуть как можно больше кода в одну строку
Переделал.
Я так понял, методы через прототипы помогут мне намного сократить этот код и как-то убрать кучу этих ифов? Ну после прочтения главы посмотрим, что смогу сделать.
Пока вариант такой.
https://ideone.com/yUstCm
>Методы надо сделать через прототипы. Посмотри какой у тебя гигантский конструктор и как все смешано из-за этого.
this.addTopping => Hamburger.prototype.addTopping
Но что это дает? Пойду перечитывать главу про это, но на леарн.яваскрипт в этом уроке настолько хороший пример, что ничего не понятно. Там просто заменили this. на class.prototype. в том классе, где остальной код и не подлежит изменению, поэтому и непонятно на что это влияет. Но, возможно, в предыдущих уроках этой главы объяснялось. Буду перечитывать.
Расчет цены и калорий уже починил. Правильно или опять не то?
>Проверки надо вынести в отдельные функции, например:
>this.validateSize(size);
Зачем? И почему через this. Сделал, но не пойму зачем.
>Ты сначала меняешь состояние объекта на недопустимое (выбрано 2 добавки), а потом бросаешь исключение. Лучше бросить исключение, оставив объект в корректном состоянии.
А если ввести и вывести? В общем мое решение подойдет?
>>this.topping_sause = 0;
>Добавки лучше хранить в массиве так как в будущем мы можем добавить новые
Пока не дошло как это сделать, но буду думать.
Ну и >не пытаться впихнуть как можно больше кода в одну строку
Переделал.
Я так понял, методы через прототипы помогут мне намного сократить этот код и как-то убрать кучу этих ифов? Ну после прочтения главы посмотрим, что смогу сделать.
Пока вариант такой.
https://ideone.com/yUstCm
>Ну число ходов нужное кошке чтобы добраться до мышки зависит от расстояния по X и Y. Вопрос, как? Попробуй рассмореть примеры которые я дал и найти зависимость. Не видишь?
Да, теперь я вижу в чем проблема. На вопрос, как количество ходов зависит от расстояния по X и Y мне было сложно ответить, но я нашел решение: Берем наибольшее значение по модулю между разницами расстояний между иксами и игркиками - max(abs(x1 - x2), abs(y1 - y2)); Это так? [spo]Если это так, то это просто ломает мой мозг[/spo]
http://pastebin.com/EEBnYTBq
http://pastebin.com/vPz7cwz8
Что именно выдает? Мы не телепаты гадать. Если echo $_POST, то денвер твой php не видит, ставь по-отдельности веб-сервер и php и настраивай через конфиги. Готовые сборки для школьников.
>Редис пишет в AOF файл все полученные от клиентов команды, например SET key value. При успешном сбросе RDB снапшота на диск лог очищается
Напомнило индексы сфинкса: дисковый полностью переиндексирует таблицу раз в большой промежуток времени, а rt используется как временное дельта-хранилище, очищается при переиндексации основного.
Все это чудесно в теории, но где искать настройки? save единственная, которая упоминается в мануале, потому ее и притащил. Но я уже понял, что она отвечает за RDB, то есть за снапшоты, их не нужно делать часто.
save 900 1 // делаем снимок каждые 15 мин
В документации вижу только ссылку на файл с примерами тысяч настроек https://raw.githubusercontent.com/antirez/redis/2.8/redis.conf
Мне это все читать ради 2-3 строчек, которые мне реально понадобятся?
А нет, пронесло, вроде не так и много, ладно.
>By default Redis does not run as a daemon.
А у меня почему-то запускается автоматически (ставил апт-гетом).
Там они говорят только запускать его руками командой ./redis-server /path/to/redis.conf
>daemonize no
У меня прописано yes.
loglevel notice
logfile /var/log/redis/redis-server.log
dir /var/lib/redis // папка, куда складываются rdb и aof файлы
Нашел наконец-то
appendonly yes // +
appendfilename "appendonly.aof"
appendsync everysec // +
no-appendfsync-on-rewrite yes // +
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
Должно хватить. Был отключен режим appendonly и синхронизацию ставим в everysec. И еще опция no-appendfsync-onrewrite выставить в yes, чтобы лог продолжал вестись даже в момент сброса rdb. Остальное по умолчанию.
А, самое главное забыли
maxmemory 512mb
Я не знаю как быть, у меня на нетбуке всего 2 гигабайта, из них убунту съедает 800mb при запуске, хром еще 300-400.
Как-то можно войти в систему минуя графический интерфейс, как на настоящий сервер, чтобы оно мне не засирало память?
>Редис пишет в AOF файл все полученные от клиентов команды, например SET key value. При успешном сбросе RDB снапшота на диск лог очищается
Напомнило индексы сфинкса: дисковый полностью переиндексирует таблицу раз в большой промежуток времени, а rt используется как временное дельта-хранилище, очищается при переиндексации основного.
Все это чудесно в теории, но где искать настройки? save единственная, которая упоминается в мануале, потому ее и притащил. Но я уже понял, что она отвечает за RDB, то есть за снапшоты, их не нужно делать часто.
save 900 1 // делаем снимок каждые 15 мин
В документации вижу только ссылку на файл с примерами тысяч настроек https://raw.githubusercontent.com/antirez/redis/2.8/redis.conf
Мне это все читать ради 2-3 строчек, которые мне реально понадобятся?
А нет, пронесло, вроде не так и много, ладно.
>By default Redis does not run as a daemon.
А у меня почему-то запускается автоматически (ставил апт-гетом).
Там они говорят только запускать его руками командой ./redis-server /path/to/redis.conf
>daemonize no
У меня прописано yes.
loglevel notice
logfile /var/log/redis/redis-server.log
dir /var/lib/redis // папка, куда складываются rdb и aof файлы
Нашел наконец-то
appendonly yes // +
appendfilename "appendonly.aof"
appendsync everysec // +
no-appendfsync-on-rewrite yes // +
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
Должно хватить. Был отключен режим appendonly и синхронизацию ставим в everysec. И еще опция no-appendfsync-onrewrite выставить в yes, чтобы лог продолжал вестись даже в момент сброса rdb. Остальное по умолчанию.
А, самое главное забыли
maxmemory 512mb
Я не знаю как быть, у меня на нетбуке всего 2 гигабайта, из них убунту съедает 800mb при запуске, хром еще 300-400.
Как-то можно войти в систему минуя графический интерфейс, как на настоящий сервер, чтобы оно мне не засирало память?
> Как-то можно войти в систему минуя графический интерфейс, как на настоящий сервер, чтобы оно мне не засирало память?
Попробуй sudo telinit 3
Если сработает, надо будет добавить пункт в загрузчик и при загрузке можно будет выбирать.
А какой эффект я должен наблюдать? Ничего не произошло.
Файла /etc/inittab нет.
Это для дебиана наверное, в убунте по-другому.
Прокомментируй это http://igorka.com.ua/2010-04-06/otklyuchit-zagruzku-graficheskogo-rezhima-v-ubuntu-910/
Мужик говорит отредактировать /etc/init/gdm.conf и прописать "and runlevel [5]"
Но это тоже старая статья, там девятая убунту.
Я пока потренируюсь в нано, а то потом не смогу вернуть все назад без гедита и саблайма.
Посоветуйте, вот решал я задачки ОПа, дошел до чисел с прописью, числа с прописью еще кое как с костылями наполовину решил и забил, но блять остальные задачки которые идут типа калькулятор, раздел "Еще немного задачек".
Я просто, блять, сижу и туплю, могу хоть 2 часа сидеть и в итоге нихуя не решу, мой мозг просто не может придумать решения этой задаче в итоге я из-за этого подзабиваю на изучение, мол хули учить, все равно ща зайду и опять буду тупить и нихуя не сделаю.
Может стоит пропустить этот остаток задач и продолжать учить остальное? Или без этого никуда?
Обидно просто, столько времени проебываю и туплю и в итоге никуда не двигаюсь.
А нет, тоже не работает. Нет такого файла gdm.conf.
Попробую еще такой вариант
http://forum.ubuntu.ru/index.php?topic=99298.msg754852#msg754852
Аноны, подскажите плиз по wordpress.
Хочу вывести список пользователей, но не всех, а только тех, у кого стоит галочка в произвольном поле в профиле.
Я нашёл на просторах сети код, который позволяет вывести список пользователей, он работает и подходит мне, но не получается его модифицировать с учётом моих потребностей.
Вот данный код: http://pastebin.com/MRX04PMC
Я так понял, что можно сделать нужные мне изменения, путём внесения дополнительных параметров в функцию "get_users()"
Судя по документации: https://codex.wordpress.org/Function_Reference/get_users
Я добавляю meta_key, который у меня называется "test", добавляю значение meta_value, которое равно у меня "1" и добавляю meta_compare, где ставим "=". Если я правильно понял, данная модификация выведет пользователей со значением 1 в произвольном поле профиля пользователя.
Но не получается. Вообще всё перестаёт выводиться: http://pastebin.com/90CYx0wq
Подскажите пожалуйста, что я делаю не так. Или как по другому ещё можно вывести только пользователей со значением 1 в произвольном поле test в профиле?
P.S.: Произвольные поля в профиль добавлял через плагин Types.
Если Убунта перешла на systemd то та статья про upstart уже не актуальна. Вот тут
http://www.freedesktop.org/wiki/Software/systemd/FrequentlyAskedQuestions/
http://www.dynacont.net/documentation/linux/Useful_SystemD_commands/
пишут надо попробовать
systemctl isolate multi-user.target
Технологии в линуксе меняются быстрее чем ты их успеваешь изучить.
Числа с прописью
https://ideone.com/DtZfAY
Проверить функцию, правильно ли я сделал, много велосипедов сделал? Есть решение оптимальней? уже постил, но ты почему-то проигнорил
Еще, я не понял зачем ты сделал функцию, чтобы вводить ворд1,2,3 снаружи function inclineWord($number, $word1, $word2, $word5 если все равно там постоянно будет "рубль, рубля, рублей", поэтому просто занес эти переменные в функцию и на входе функции оставил только $number.
А также, что делать потом, когда нужно будет превращать числа в словесный вариант?
По сколько правил дохуя, то я подумал посчитать количество символов в числе, а потом для каждого числа сделать свой код, типа "если символов 1, то сделать так, если 2, то так, если 3 то так... Если 7, то так.
Но все равно это выливается в огромные куски кода, а как все написать лаконично и грамотно, я не знаю.
Калькулятор
https://ideone.com/iFhFA2
Прошу подсказки, вот не знаю, что писать там, по идее мне нужно сделать как-то, чтобы если выпал, допустим, минус, то мне нужно сделать какое-то действие, но как его делать то, если мне доступно только число до минуса, а число после минуса я смогу получить только в следующей итерации.
Все поломалось, пишу с виндовс (((
Отписался в тред линукса, но там хрен дождешься помощи, выручай.
Пытался отключить графику, изменил в /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" на "text", как советуют здесь
http://forum.ubuntu.ru/index.php?topic=99298.msg754852#msg754852
Сначала все было хорошо, запустил графику startx.
Затем решил вернуть все как было, заменил обратно "text" на "quiet spash"
Теперь после ребута бесконечно запрашивает пароль. Ввожу пароль, на пару секунд черный экран с мигающим курсором, и снова появляется окошко с вводом пароля. Что за дела?
Могу теперь только зайти через виртуальный терминал.
Да, кстати, что меня удивило, когда в первый раз запустился без графики и вызвал startx, запустилась lxde, а не xfce, которой я пользуюсь постоянно. Вдруг это важно.
Плюсом к собственно похапэ имею базовый набор быдловебкодера (цэсэс, аштиэмэль, 3/5.
В общем, два с половиной вопроса:
Первый - филосовский, его можно не читать:1) сильно я проебался, выбрав похапэ? Пишу хорошо, красиво, соблюдаю все гласные и негласные правила, самому нравится, но я не против того, чтобы меня обзывали обезьяно, быдлокодером и т.п., мне лишь бы работа была и платили
2) Что нужно в первую очередь подтянуть, чтобы не краснеть за собеседованиях, в какую сторону развиваться? В общем, что курить дальше?
3) И самое главное - как/чем набить портфолио? Я об этом даже и не задумывался.
Та же проблема была с твигом и слимом. При загрузке слима через композер он вообще не подключается хз.
Вот блин, тогда нужно делать по несколько запросов к бд и таблицам. При загрузке файла:
1. Создать юзера в бд. Взять куки и по ним проверить, нет ли в таблице юзеров уже этого юзера с этой кукой и не зареген ли он. Если юзера нет, то сгенерить ему кук и положить его в таблицу.
2. Взять из таблицы юзеров по сгенеренной куке присвоенный ему айди.
3. Передать этот айди в таблицу с файлами в колонку user_id.
4. При отображении файла взять по колонке user_id из таблицы файлов и по id из таблицы юзеров название юзера. Если логин юзера не задан, то писать Анонимус в авторах файла. Тоже с комментами.
Какая-то тормозная жесть выходит.
>>571337
Потому что даже будучи анонимом только автор файла может его редактировать, а авторство выбирается по куке.
>>571337
нет, я все прекрасно понимаю, мне сама верстка не нравится. Уж поверь мне я начинал с верстки, даже зная один лишь синтакс пхп уже тогда пхп мне нравился гораздо больше, как инстурмент программирования, верстку себе оставьте жалкие дизинеры.
Тут уже сто раз объясняли, что чисто-пхпшники с начальными знаниями никому не нужны. Учи js и просись за еду фулстек-макакой. В портфолио покажи задачи из оппоста - файлообменник и сайт с тестами.
Вот смотри я решил от нехуй делать, и установи нормальное ИДЕ и компилятор, идеон глаза режет.
https://ideone.com/SZoDin
че думаешь два вшивеньких сайта один из которых на микрофреймворке (еще и неизвестно, как написанных) это достаточно для трудоустрйоства?
У меня пхп шторм стоит.
Просто код почтить сюда удобно через идеоне, а не тупо копипастить, согласись.
Ну и зачем ты все решил, блять, за меня? Может ты и работать за меня планируешь?
решил за тебя, чтобы показать тебе какой ты дурачок)
а вообще хочешь научиться делать сайты - иди и делай сайты, по видео с утюба, по мануалам, все попутно освоишь и не будешь делать таких дурацких ошибок, как повторяющийся код без функций.
Вопрос снят, в gite нашел ветку где все работает, теперь осталось только найти отличия.
Ты решил за меня второе задание, но повторяющийся код, как я понял, в первой ссылке, почему ты там не показал как правильно?
Смотри, у меня есть простая страница, на которой отображаються сообщения оставленные посетителями. Форму сообщения я отправляю через ajax. Как мне, без перезагрузки страницы обновить дерево сообщений? Единственное что я придумал, это отдавать обратно массив json, который потом обрабатывать js функцией, отрисовывающей сообщения на странице, но это какое-то сильно умножение сущностей получаеться.
Можешь что-то посоветовать?
Но ведь неправильно (вроде-бы) формировать в контроллере html и потом его выводить в вьюхе. Меня за такое на собеседовании сразу домой пошлют
Какая разница возвращаешь ты из контроллера целую страницу или часть. Ну и попгугли про разновидности MVC (MVVM, MVP например)
Зашей шаблон прямо в html-код, затем в js подставляй в него переменные.
<script type="text/template" id="myTemplate">
<div class="msg-container">
<div class="msg-author"> {{author}} </div>
<div class="msg-text"> {{text}} </div>
</div>
</script>
Заменяешь плейсхолдеры значениями, полученными из json.
один вопрос, нахуй тогда ты за меня решал второе задание? почему как в случае с первым не подсказал куда смотреть? за то когда прошу показать как правильно делать первое ты мне говоришь гуглить. Ты совсем даун?
а как сделать? создать новый класс, в который поместить работу с этими классами?
какой впечатлительный шкальник. любую работу с повторяющимся кодом ты можешь вынести в функцию finction array($a) {foreach($a as $c){echo $c['d']}}
echo array($kek);
echo array($kek2);
почему я должен такие прописные истины объяснять, и тебе уже говорили, функции юзать вместо повторов, но ты же необучаемый.
Ты можешь пока порешать что-то еще, ОП придет, проверит твои задачи и даст тебе нормальные советы.
По этой >>571991 задаче один из вариантов решения такой:
Заводим переменные для результата, накапливаемого числа, знака операции.
- смотрим на строку посимвольно, в цикле, один симол за раз
- если это цифра добавляем ее в накапливаемое число
- если это знак, и мы видим его впервые, запоминаем его, а результат делаем равным накапливаемому числу
- если же не впервые, то выполняем запомненную ранее операцию и сохраняем ее результат
Ты можешь попробовать это расписать так: допустим у нас строка 25 + 34 - 17 = . Вот как она обрабатывается
Текущий Символ | результат | накопитель | операция
---------------
2 0 2 ?
5 0 25 ?
+ 25 0 +
3 25 3 +
4 25 34 +
- 59 0 -
1 59 1 -
7 59 17 -
= 42 0 =
Другой вариант: разбить выражение на массив чисел и операций регуляркой и вычислить в цикле.
Ты можешь пока порешать что-то еще, ОП придет, проверит твои задачи и даст тебе нормальные советы.
По этой >>571991 задаче один из вариантов решения такой:
Заводим переменные для результата, накапливаемого числа, знака операции.
- смотрим на строку посимвольно, в цикле, один симол за раз
- если это цифра добавляем ее в накапливаемое число
- если это знак, и мы видим его впервые, запоминаем его, а результат делаем равным накапливаемому числу
- если же не впервые, то выполняем запомненную ранее операцию и сохраняем ее результат
Ты можешь попробовать это расписать так: допустим у нас строка 25 + 34 - 17 = . Вот как она обрабатывается
Текущий Символ | результат | накопитель | операция
---------------
2 0 2 ?
5 0 25 ?
+ 25 0 +
3 25 3 +
4 25 34 +
- 59 0 -
1 59 1 -
7 59 17 -
= 42 0 =
Другой вариант: разбить выражение на массив чисел и операций регуляркой и вычислить в цикле.
Хорошо, я необучаемый, я понял о чем ты говоришь, но не совсем понимаю как это применить в своем коде, вот тебе кусок кода где повторяется код. Сделай так, чтобы была функция.
Вот код - https://ideone.com/guLlzx .
Да. Ты можешь сделать высокоуровневый класс-сервис, который будет находиться выше уровня мапперов и вызывать их методы.
>если это знак, и мы видим его впервые, запоминаем его
>если же не впервые, то выполняем запомненную ранее операцию
Не совсем понимаю как это реализовать.
Занести знак в виде строчки в переменную? Как его запомнить? И потом использовать.
Вылечился, удалив файл .Xauthority
В общем, вот что у меня получилось с заданием про сервис продажи музыки.
Условие (утонуло где-то в прошлом треде) http://pastebin.com/FJwFWYnZ
Сущности базы: http://pastebin.com/ShT7CjdE
Пока только первый запрос
> Поиск треков по году выхода, названию, исполнителю, альбому с выводом цены для данного пользователя
SELECT c1.id, c1.name, c1.release_year, c3.track_base_price, cc.local_track_price FROM content c1
JOIN content c2 ON c1.track_album = c2.id
JOIN content_performer cp ON c1.id = cp.content_id
JOIN performer p ON p.id = cp.performer_id
JOIN category c3 ON c1.category_id = c3.id
LEFT JOIN category_country ON cc.category_id = c3.id
JOIN user u ON cc.country_id = u.country_id
WHERE c.release_year = 2012 OR c.name = 'track_name' OR c2.name = 'album_name'
OR p.artist_full_name LIKE '%name%' OR p.group_name LIKE '%name%' AND u.id = 'user_id'
Чем отличается Tariff от Subscrption? Надо писать комментарии в таких неочевидных местах.
> Audition
Как понять бесплатное, платное это прослушивание если да, то по какому тарифу?
Там где используются схемы наследования, хорошо бы пояснить какие и какие сущности хранятся в таблице.
> User_subscription
> - expired
Лучше дата начала (и может быть конца). А поле expired по моему избыточное и может быть определено на основе первичных данных.
Тарифом я назвал условия, которые лейбл ставит на свою продукцию. А именно общее кол-во треков, доступных пользователю на бесплатном периоде, лицензионные отчисления, отчисления за бесплатные прослушивания.
Подписка насколько я понимаю относится не к лейблу, а к нашему интернет-магазину, там хранятся типы акаунтов: бесплатный, подписка на опред.кол-во времени (duration).
user_subsription отвечает за историю покупок подписок. Да, наверное лучше хранить дату покупки, и к ней прибавлять длительность покупки.
Вообще я плохо понимаю условие задания, что за отчисления, что за условия.
Сложнее понять условие задачи, чем ее решить. Хотя наверное это всегда так.
Магазин платит лейблу-издателю альбома лицензионные отчисления (роялти) в таких случаях:
- прослушивание трека пользователем (на бесплатном и платном тарифе роялти может быть разными)
- покупка трека пользователем
- покупка альбома пользователем
Чтобы не ставить цену для каждого альбома отдельно, они разделяются на категории и лейбл задает общие условия для альбомов одной категории.
Роялти может зависеть от страны пользователя (так как цена разная, то и роялти может быть разным, например лейбл может согласиться брать меньшее роялти при условии более низкой цены в бедных странах). Также, лейбл может запретить продажу (на уровне категорий по моему) в определенных странах.
Запреты на продажу по странам могут использоваться например для релиза альбома в хорошо и много платящей стране на несколько недель раньше чем в плохо платящей.
https://ideone.com/F239Ru
Решение через ООП, ты какой-то глупый мальчик, желающий привлечь к себе внимание трусиками, как видишь обладательницы мокренькой дырочки могут быть гораздо умнее тебя. Наверное эта мысль тебя унижает и ты решил запостить эту картинку, чтобы самоутвердиться, как и полагается безмозглому шкальнику с перманентным спермотоксикозом и прыщами на ебле.
лол, мой код по длине будет такой же длины как и твой если я возвращать буду сразу строку, а не переменную со строкой - https://ideone.com/guLlzx
Я попросил тебя применить функцию в моем коде для наглядности твоего совета, а ты просто решил эту задачу по другому с такой же длиной кода.
Ты наверное не понял, но мы тут не соревнуемся с тобой кто круче, я задал тебе конкретный вопрос, а в ответ получил оскорбления и т.д. И я еще школьник, да.
побежал рыдать в подушку
И вообще, как компьютер понимает, сколько раз нужно перепроходить массив? Т.е., как я понимаю, внутренний цикл for отвечает за проход по элементам массива, а за что отвечает внешний цикл?
Внешний перебирает все элементы массива. Внутренний "поднимает пузырек" на сколько возможно.
Вот зачем ты пришел сюда самоутверждаться? Смотри, мы меняем в твоем коде выражение, добавив несколько слагаемых, и все перестает работать: https://ideone.com/otEbN4
Тебе еще рано кому-то давать советы. Для начала изучи PHP.
Во-первых ты неправильно создал конструктор, опечатка в имени, несуществующая переменная. Ну и использовать объект тут не имеет смысла, тут максимум статический метод годится.
Также, switch нельзя использовать так, как ты пытаешься. Попробуем например добавить проверку на 0: https://ideone.com/4AyGnP
Упс. Не работает.
Тебе рано учить других. Тебе самому надо язык изучить сначала. И код выравнивать научиться а то какая-то даунская лестница получается.
Как сделать зачеркнутым только текст, а пробелы оставить?
как не надо делать
>а вообще хочешь научиться делать сайты - иди и делай сайты
А зачем тогда задачки в шапке?
Кстати, вот после этих задач уже можно на джуна идти?
http://archive-ipq-co.narod.ru/i-am-smart.html
https://github.com/codedokode/pasta/blob/master/student-list.md
https://github.com/codedokode/pasta/blob/master/interview-tasks.md
https://2ch.hk/pr/res/561925.html
https://ideone.com/fiKf5c
Методы в прототипный стиль переделал.
Добавки перенес в объект.
Проверки сделал по твоему совету отдельными функциями. Они работают, но ведь их нужно вызывать отдельно. Зачем это? Не правильнее ли было сразу при создании класса выдавать ошибки, если они есть, а не проверять их отдельно?
Хочу изучить какой-нибудь фреймворк. Ибо заебало.
Стоит выбор между:
Zend
Symfony
Yii
Laravel
Больше смотрю в сторону Zend или Symfony (ибо вакансий в моём городе на них больше)
Что посоветуется? Плюсы, минусы. Опыт работы.
Был анон которому понравилось. Как по мне так сильно не очень. Все оооооочень растянуто. Дибильные кривляния препода тоже заебывают.
Вроде как рекомендуют все, как полному дауну с нуля подойдет. Совмещая с книжками и документацией думаю оптимальный вариант
Ну и оп-задачки конечно же
А я его и не учу, я лишь говорю, что он долбоеб и доказываю это практически. Свитчи можно и на елсы заменить.
Мне тоже интересно зачем задачки, если этот долбоеб не может ни в классы ни в функции, да еще и попросил решить вторую задачу после решенной первой. Типичное школобыдло.
>тебе уже говорили, функции юзать вместо повторов
>вот тебе кусок кода, покажи как
>пишет ответ на ООП
не устаю с тебя проигрывать
> Роялти может зависеть от страны пользователя
Ну тогда избавляемся от таблицы tariff, переносим следующие атрибуты в таблицу category_country
- tracks_total (макс.кол-во треков доступно для прослушивания при наличии подписки)
- tracks_total_free (... во время пробного периода)
- track_perday (макс.кол-во прослушиваний одного трека в день)
- track_perday_free (... во время пробного периода)
- purchase_royalty (отчисления за покупки)
- audition_royalty (отчисления за бесплатные прослушивания)
Второй запрос
>Поиск доступных пользователю альбомов, треков по тем же критериям
Под "доступными" подразумеваются надо полагать те альбомы и треки, на которые не истек лимит прослушиваний. Те, которые запрещены в данной стране, отсеиваются условием WHERE category_country.available = 'y'.
Прослушивания сохраняются в таблицу audition. Условие для проверки исчерпания лимита прослушиваний будет выглядеть как-то так:
SELECT x FROM content c1
JOIN content c2 ON c1.track_album = c2.id
JOIN category c3 ON c2.category_id = c3.id
JOIN category_country c4 ON c3.id = c4.category_id
JOIN audition a ON c1.id = a.track_id
WHERE (SELECT COUNT(*) FROM audition WHERE user_id = 'uid' AND audition.date = CURDATE()) <
(
IF("проверяем тип подписки" = 'premium',
SELECT c5.track_perday FROM category_country c5 WHERE c3.id = c5.category_id AND c5.country_id=(SELECT country_id FROM user WHERE user_id = 'uid') ),
SELECT c5.track_perday_free FROM category_country c5 WHERE c3.id = c5.category_id AND c5.country_id=(SELECT country_id FROM user WHERE user_id = 'uid') ),
)
AND (то же самое для проверки tracks_total).
Хотя еще не учел длительность подписки.
Короче, это какой-то пздц. Вроде бы ничего сложного, если разбить на мелкие части, но удержать в голове целую картину, все эти связи, таблицы и т.д., немного проблематично.
Я лучше пока вернусь к заданиям попроще.
> Роялти может зависеть от страны пользователя
Ну тогда избавляемся от таблицы tariff, переносим следующие атрибуты в таблицу category_country
- tracks_total (макс.кол-во треков доступно для прослушивания при наличии подписки)
- tracks_total_free (... во время пробного периода)
- track_perday (макс.кол-во прослушиваний одного трека в день)
- track_perday_free (... во время пробного периода)
- purchase_royalty (отчисления за покупки)
- audition_royalty (отчисления за бесплатные прослушивания)
Второй запрос
>Поиск доступных пользователю альбомов, треков по тем же критериям
Под "доступными" подразумеваются надо полагать те альбомы и треки, на которые не истек лимит прослушиваний. Те, которые запрещены в данной стране, отсеиваются условием WHERE category_country.available = 'y'.
Прослушивания сохраняются в таблицу audition. Условие для проверки исчерпания лимита прослушиваний будет выглядеть как-то так:
SELECT x FROM content c1
JOIN content c2 ON c1.track_album = c2.id
JOIN category c3 ON c2.category_id = c3.id
JOIN category_country c4 ON c3.id = c4.category_id
JOIN audition a ON c1.id = a.track_id
WHERE (SELECT COUNT(*) FROM audition WHERE user_id = 'uid' AND audition.date = CURDATE()) <
(
IF("проверяем тип подписки" = 'premium',
SELECT c5.track_perday FROM category_country c5 WHERE c3.id = c5.category_id AND c5.country_id=(SELECT country_id FROM user WHERE user_id = 'uid') ),
SELECT c5.track_perday_free FROM category_country c5 WHERE c3.id = c5.category_id AND c5.country_id=(SELECT country_id FROM user WHERE user_id = 'uid') ),
)
AND (то же самое для проверки tracks_total).
Хотя еще не учел длительность подписки.
Короче, это какой-то пздц. Вроде бы ничего сложного, если разбить на мелкие части, но удержать в голове целую картину, все эти связи, таблицы и т.д., немного проблематично.
Я лучше пока вернусь к заданиям попроще.
Мне не очень нравится подзапрос в WHERE, если тебе нужен подзапрос то лучше использовать EXISTS, они как минимум чуть лучше оптимизированы (MySQL не ищет все строки, а просто проверяет их наличие или отсутствие).
И там где c5.country_id =(подзапрос) правильнее наверно сделать JOIN, или вообще на строне приложения заранее выбрать нужное значение.
> Под "доступными" подразумеваются надо полагать те альбомы и треки, на которые не истек лимит прослушиваний
Давай считать доступными те которые доступны в стране пользователя. Те, которые он прослушал, лучше отсеивать на стороне приложения. Можно для этого например сделать отдельный запрос (запрос который получает на вход список id категорий и отбирает те, которые нельзя слушать).
Имей в виду что такое приложение конечно будет активно использовать кеши и денормализацию. Никто не будет искать альбом по названию гиганским запросом с фуллсканом и джойнами. Будет подключен внешний поисковый движок (например сфинкс или что-то помощнее) для этой цели. этот же поисковый движок может хранить денормализованные данные, например id категории для кажого трека, по которой можно отсеять недоступные треки условием вида
WHERE (category_id NOT IN (?))
Нет. Да и нахуя он нужен? Изменений мизер + новые баги.
>>573009
Мы не очень одобряем пиратство. Ведь если ты не покупаешь продукт то топ-менеджеры и акционеры не получат от тебя денежку и не смогу купить себе новую яхту. Ну и как следствие может быть разработчик в этой компании не получит премию и не сможет купить себе новую машину. Тебе их не жалко?
>>572995
ООП там вообще не нужен.
>>572987
Что ты тут забыл? Если ты действительно бы хотел помочь, ты бы написал полезный ответ, который что-то объясняет. Также, ты сам плохо разбираешься в программировании (это видно из твоего неграмотного кода) и помочь вряд ли мог бы даже если захотел.
Значит ты не хочешь никому помогать, а преследуешь какие-то другие цели.
Какие - я понять не могу, потому просто предположу что тебе скучно и ты заходишь в разные треды и пишешь там всякий бред, заодно пытаясь спровоцировать перепалку.
Не надо так делать. У нас медленный тред и никаких горячих споров тут не получится. Я уверен, большиство анонов просто проигнорировали твои посты (что легко проверить, сопоставив число анонов в треде и число отвечающих тебе). Тебе лучше пойти в какой-то другой раздел, где людей больше.
Разумеется, все замечания которые я написал, они не просто так, а имеют какую-то причину.
Например константы нам нужны чтобы как-то обозначать виды гамбургеров и пользователь класса мог из них выбирать те, что ему нужны. Мы делаем их свойствами Hamburger, а не отдельными переменными, так как они по смыслу относятся к нему.
Аналогично и совет про функции валидации имеет под собой причину. Если код разбит на отдельные части, то его проще читать и поддерживать, чем когда он идет большой простыней.
Ты понял меня не так. Во-первых, ты почему-то засунул эти функции в конструктор (а не в прототип) и в итоге мы никакой выгоды в виде уменьшения и упрощения его кода не получили. Во-вторых, ты их не вызываешь, а идея была в том что ты их будешь сам и вызывать.
То есть код должен выглядеть примерно так:
function Hamburger(size, stuffing) {
this.validateSize(size);
this.validateStuffing(stuffing);
...
Функции здесь нужны чтобы не раздувать размер конструктора и потому, что их названия служат комментарием. Ты как-то не так меня понял.
По коду
> this.toppingsArr = {
> toppingSause: 0,
У тебя жестко заложен список добавок, лучше бы использовать в качестве ключей сами константы:
this.toppings[Hamb.TOPING_ZZZZ] = true;
Так добавление нового вида добавки не потребует правки кода в этом месте.
также, Arr это информационный мусор не несущий ничего полоезного и только зря удлиняющий название переменной. Просто toppings будет лучше.
Почему ты выбрал словарь для хранения добавок, а не обычный массив?
> if (topping == Hamburger.TOPPING_SAUCE) {
> this.toppingsArr.toppingSause++;
> }
> if (topping == Hamburger.TOPPING_MAYO) {
> this.toppingsArr.toppingMayo++;
> }
Тут нужен более обобщенный код, не перечисляющий каждый вид добавки
Далее, функция Hamburger.prototype.addTopping написана неправильно. Вот как она сделана у тебя:
- добавить добавку
- проверить правильность переданных аргументов
А вот как надо:
- проверить правильность переданных аргументов
- добавить добавку
Ты понимаешь в чем разница? Во втором случае при передаче неправильных аргументов объект остается неизменным и в корректном состоянии. В первом случае он переходит в некорректное состояние (то есть число добавок в нем становится больше разрешенного).
Мой вариант лучше защищен от ошибок. Это называется exception safety, безопасность относительно исключений. В моем варианте если объект выбросил исключение, им все равно можно пользоваться так как я гарантирую что он остается в корректном состоянии.
В первом варианте нет exception safety. Если объект выбросил исключение, то им дальше пользоваться нельзя так как не гарантий что он в корректном состоянии.
Я вижу, ты в коде попытался добиться exception safety, вручную сбрасывая число добавок до 1 при ошибке. Но это лишнее усложнение, достаточно просто поставить проверку в начало функции и это будет не нужно.
> Hamburger.prototype.calculateCalories = function () {
Всю эту кучу ифов лучше заменить на словарь, хранящий цену и калорийность разных начинок и добавок. Тогда добавление новых видов добавок потребует лишь добавить константу, элемент с ценой и калориями в словарь и может быть в условие валидации.
Разумеется, все замечания которые я написал, они не просто так, а имеют какую-то причину.
Например константы нам нужны чтобы как-то обозначать виды гамбургеров и пользователь класса мог из них выбирать те, что ему нужны. Мы делаем их свойствами Hamburger, а не отдельными переменными, так как они по смыслу относятся к нему.
Аналогично и совет про функции валидации имеет под собой причину. Если код разбит на отдельные части, то его проще читать и поддерживать, чем когда он идет большой простыней.
Ты понял меня не так. Во-первых, ты почему-то засунул эти функции в конструктор (а не в прототип) и в итоге мы никакой выгоды в виде уменьшения и упрощения его кода не получили. Во-вторых, ты их не вызываешь, а идея была в том что ты их будешь сам и вызывать.
То есть код должен выглядеть примерно так:
function Hamburger(size, stuffing) {
this.validateSize(size);
this.validateStuffing(stuffing);
...
Функции здесь нужны чтобы не раздувать размер конструктора и потому, что их названия служат комментарием. Ты как-то не так меня понял.
По коду
> this.toppingsArr = {
> toppingSause: 0,
У тебя жестко заложен список добавок, лучше бы использовать в качестве ключей сами константы:
this.toppings[Hamb.TOPING_ZZZZ] = true;
Так добавление нового вида добавки не потребует правки кода в этом месте.
также, Arr это информационный мусор не несущий ничего полоезного и только зря удлиняющий название переменной. Просто toppings будет лучше.
Почему ты выбрал словарь для хранения добавок, а не обычный массив?
> if (topping == Hamburger.TOPPING_SAUCE) {
> this.toppingsArr.toppingSause++;
> }
> if (topping == Hamburger.TOPPING_MAYO) {
> this.toppingsArr.toppingMayo++;
> }
Тут нужен более обобщенный код, не перечисляющий каждый вид добавки
Далее, функция Hamburger.prototype.addTopping написана неправильно. Вот как она сделана у тебя:
- добавить добавку
- проверить правильность переданных аргументов
А вот как надо:
- проверить правильность переданных аргументов
- добавить добавку
Ты понимаешь в чем разница? Во втором случае при передаче неправильных аргументов объект остается неизменным и в корректном состоянии. В первом случае он переходит в некорректное состояние (то есть число добавок в нем становится больше разрешенного).
Мой вариант лучше защищен от ошибок. Это называется exception safety, безопасность относительно исключений. В моем варианте если объект выбросил исключение, им все равно можно пользоваться так как я гарантирую что он остается в корректном состоянии.
В первом варианте нет exception safety. Если объект выбросил исключение, то им дальше пользоваться нельзя так как не гарантий что он в корректном состоянии.
Я вижу, ты в коде попытался добиться exception safety, вручную сбрасывая число добавок до 1 при ошибке. Но это лишнее усложнение, достаточно просто поставить проверку в начало функции и это будет не нужно.
> Hamburger.prototype.calculateCalories = function () {
Всю эту кучу ифов лучше заменить на словарь, хранящий цену и калорийность разных начинок и добавок. Тогда добавление новых видов добавок потребует лишь добавить константу, элемент с ценой и калориями в словарь и может быть в условие валидации.
$sql = "SELECT `articul` FROM `h78162_a`.`cash`";;
$query=$mysqli->query($sql);
$row=mysqli_fetch_assoc($query);
$array = array($row);
Почему последняя строка может возвращать лишь один артикул, а не все значения в таблице? То есть возникает массив только с одним значением артикула. Как сделать все значения в массиве?
Если делаю вот так, то выводит все значения
while($row){
echo $row['articul'];}
Все эти усложнения лишь запутывают пользователя и не нужны. Если у тебя у статьи может быть ровно 1 категория, используй категории, если несколько то исплоьзуй теги.
> Например, на автомобильном сайте по типу статьи и по производителю
Что значит «тип статьи» ? В любом случае все эти навигации как правило делаются для поисковых роботов, а люди ими редко пользуются так как они запутанные или там сотни тегов и ничего не понять.
Насчет того как делать URL есть простое правило: 1 страница = 1 URL. не может быть 2 одинаковых страниц с разными URL, не может быть 2 разных страницы с одним URL. Твоя схема навигации должна это учитывать.
Ну и традиционно URL рассматривают как путь по разделам, вроде
/news/politics/some-new.html - раздел новости, подраздел политика, страница новости
Вот мой урок с обзором схем URL на примерах: https://gist.github.com/codedokode/772a4ccc03e41d6b7cba
> Не будет ли путаницы, ведь кто-то может захотеть с несколькими тегами сразу работать
Живой человек вряд ли
> Вроде избавляет от лишних полей в БД, но вместо поиска по varchar будет полнотекстовый индекс и поиск по большим текстовым полям.
Ты мешаешь все в кучу. Какая база данных, мы вроде систему URL проектируем? Проектируй схему URL не оглядываясь на БД, она тут вообще никаким боком не относится.
Схема URL на яндекс-маркете очень адекватная.
> https://market.yandex.ru/product/123456/spec?hid=567839&track=tabs
Сразу все понятно: это продукт с id = 123456, страница характеристик. Дополнительные параметры видимо используются для статистики.
> видишь менюшку сверху? И менюшку справа? Обрати внимание, категории пересекаются, они разных типов. Вот тебе двойная навигация.
Пересечение легко решается тегами. у статьи может быть нескоько тегов. Пример с сайта брать не стоит, так как неизвестно кто и как его проектировал.
Если то-то непонятно, задавай конкретные вопросы.
Все эти усложнения лишь запутывают пользователя и не нужны. Если у тебя у статьи может быть ровно 1 категория, используй категории, если несколько то исплоьзуй теги.
> Например, на автомобильном сайте по типу статьи и по производителю
Что значит «тип статьи» ? В любом случае все эти навигации как правило делаются для поисковых роботов, а люди ими редко пользуются так как они запутанные или там сотни тегов и ничего не понять.
Насчет того как делать URL есть простое правило: 1 страница = 1 URL. не может быть 2 одинаковых страниц с разными URL, не может быть 2 разных страницы с одним URL. Твоя схема навигации должна это учитывать.
Ну и традиционно URL рассматривают как путь по разделам, вроде
/news/politics/some-new.html - раздел новости, подраздел политика, страница новости
Вот мой урок с обзором схем URL на примерах: https://gist.github.com/codedokode/772a4ccc03e41d6b7cba
> Не будет ли путаницы, ведь кто-то может захотеть с несколькими тегами сразу работать
Живой человек вряд ли
> Вроде избавляет от лишних полей в БД, но вместо поиска по varchar будет полнотекстовый индекс и поиск по большим текстовым полям.
Ты мешаешь все в кучу. Какая база данных, мы вроде систему URL проектируем? Проектируй схему URL не оглядываясь на БД, она тут вообще никаким боком не относится.
Схема URL на яндекс-маркете очень адекватная.
> https://market.yandex.ru/product/123456/spec?hid=567839&track=tabs
Сразу все понятно: это продукт с id = 123456, страница характеристик. Дополнительные параметры видимо используются для статистики.
> видишь менюшку сверху? И менюшку справа? Обрати внимание, категории пересекаются, они разных типов. Вот тебе двойная навигация.
Пересечение легко решается тегами. у статьи может быть нескоько тегов. Пример с сайта брать не стоит, так как неизвестно кто и как его проектировал.
Если то-то непонятно, задавай конкретные вопросы.
Эти задачи надо как минимум показать на проверку и исправить все замечания. Если ты их сделал и не показал то это не считается (даже если код работает), так как никто не видел твой код и не может сказать, усвоил ли ты принципы ООП, MVC или нет.
>>572605
Так же где и обычный дизайн. В британке может?
> Как сделать зачеркнутым только текст, а пробелы оставить?
Окружить каждое слово тегами
>>572546
Ты бы ссылку дал на код. Внешний цикл сортирует массив до тех пор пока там есть хотя бы одна неупорядоченная пара значений.
>>572537
Не понял вопрос. Ты про расширение файла? Я бы советовал phtml для PHP-шаблонов
>>572483
Твой код по ссылке какой-то запутанный. Зачем там массивы? Эта функция пишется в 2 или 3 ифа:
if (число заканчивается на 2, 3,4 и это не 11-19 ) {
вернуть одну форму слова;
}
if (число заканчивается на 1 и это не 11-19 ) {
вернуть другую форму слова;
}
И так далее.
Эти задачи надо как минимум показать на проверку и исправить все замечания. Если ты их сделал и не показал то это не считается (даже если код работает), так как никто не видел твой код и не может сказать, усвоил ли ты принципы ООП, MVC или нет.
>>572605
Так же где и обычный дизайн. В британке может?
> Как сделать зачеркнутым только текст, а пробелы оставить?
Окружить каждое слово тегами
>>572546
Ты бы ссылку дал на код. Внешний цикл сортирует массив до тех пор пока там есть хотя бы одна неупорядоченная пара значений.
>>572537
Не понял вопрос. Ты про расширение файла? Я бы советовал phtml для PHP-шаблонов
>>572483
Твой код по ссылке какой-то запутанный. Зачем там массивы? Эта функция пишется в 2 или 3 ифа:
if (число заканчивается на 2, 3,4 и это не 11-19 ) {
вернуть одну форму слова;
}
if (число заканчивается на 1 и это не 11-19 ) {
вернуть другую форму слова;
}
И так далее.
Зачем?
- чтобы использовать правильный подход. Например в мире Node.JS очень много маленьких библиотек, делающих только одну вещь
- чтобы его можно было подключить к любому сайту на любом фреймворке и любом redis клиенте
- больше шансов что код кому-то пригодится и не умрет в безвестности
- можно продемонстрировать что ты умеешь выделять отдельные компоненты в библиотеку
- можно попробовать пропиарить библиотеку в виде поста в блог или на хабр, попробовать добавить ее в packagist (не знаю впрочем, стоит ли)
Затраты по времени примерно такие:
- создать новый репозиторий = 10 мин
- сделать composer.json = 10 мин
- сделать readme с понятным описанием = 1 час
- рефакторинг кода с вынесением зависимостей = 2 часа
- сделать пример подключения (в качестве документации) = 1час
- подключить к основному проекту сайта объявлений через композер = 10 мин
Конечно хорошая библиотека еще должна быть покрыта тестами, но это требует времени (разобраться с тестами = 6 ч, написать и отладить = 8 ч) и я не знаю, есть ли оно у тебя.
подробнее попробую расписать позже, пока некогда. Ну подумай сам, ты хочешь использовать современный подход и выносить часть компонентов в отдельные универсальные библиотеки или хочешь как натягивальщик шаблонов на вордпресс, копировать файлики и править в них цифры?
>>572483
Это скучающий тролль тебе пишет. Самое лучшее это игнорировать посты с провокациями (их легко определить по стилю текста и попытке разжечь спор) и ждать пока ОП проверит твой код. ну и решать задачки чтобы время зря не тратить.
Зачем?
- чтобы использовать правильный подход. Например в мире Node.JS очень много маленьких библиотек, делающих только одну вещь
- чтобы его можно было подключить к любому сайту на любом фреймворке и любом redis клиенте
- больше шансов что код кому-то пригодится и не умрет в безвестности
- можно продемонстрировать что ты умеешь выделять отдельные компоненты в библиотеку
- можно попробовать пропиарить библиотеку в виде поста в блог или на хабр, попробовать добавить ее в packagist (не знаю впрочем, стоит ли)
Затраты по времени примерно такие:
- создать новый репозиторий = 10 мин
- сделать composer.json = 10 мин
- сделать readme с понятным описанием = 1 час
- рефакторинг кода с вынесением зависимостей = 2 часа
- сделать пример подключения (в качестве документации) = 1час
- подключить к основному проекту сайта объявлений через композер = 10 мин
Конечно хорошая библиотека еще должна быть покрыта тестами, но это требует времени (разобраться с тестами = 6 ч, написать и отладить = 8 ч) и я не знаю, есть ли оно у тебя.
подробнее попробую расписать позже, пока некогда. Ну подумай сам, ты хочешь использовать современный подход и выносить часть компонентов в отдельные универсальные библиотеки или хочешь как натягивальщик шаблонов на вордпресс, копировать файлики и править в них цифры?
>>572483
Это скучающий тролль тебе пишет. Самое лучшее это игнорировать посты с провокациями (их легко определить по стилю текста и попытке разжечь спор) и ждать пока ОП проверит твой код. ну и решать задачки чтобы время зря не тратить.
> Еще, я не понял зачем ты сделал функцию, чтобы вводить ворд1,2,3 снаружи function inclineWord($number, $word1, $word2, $word5 если все равно там постоянно будет "рубль, рубля, рублей"
А как ты склоняешь другие слова, например «тысяч» и «миллионов»? Еще 2 функции накопипастишь?
В твоей функции слишком сложный код. Склонение слов в русском языке определяется так:
- если число от 11 до 19 или кончается на 5-9 то ...
- иначе если оно кончается на 1 то ..
- иначе оно кончается на 2-4 и ...
То есть реально там 2 или 3 ифа будет, проверябщих последнюю или 2 последних цифры.
Попробуй упростить код.
> Но все равно это выливается в огромные куски кода, а как все написать лаконично и грамотно, я не знаю.
Пиши по одной функции, проверяй ее, затем дальше, все постепенно.
> А также, что делать потом, когда нужно будет превращать числа в словесный вариант?
Можно сделать примерно так:
- функция которая выбирает нужную форму слова (рубль/рубля)
- функция которая берет число 0-999 и преобразует его в текст используя массив слов из учебника (123 = сто двадцать три)
- функция которая берет большое число, разбивает на группы по 3 цифры, превращает их в текст и дописывает слова миллионов, тысяч, рублей.
Вот и вся задача.
Собирать фразу (сто двадцать три) из слов удобно, складывая числа в массив.
Да, правильно, максимум из X и Y. Оптимальная траектория кошки такая: двигаемся по диагонали пока не окажемся на одной прямой, затем по прямой.
Нет. ЧТобы понимать паттерны, нужно иметь пример кода где они нужны. Сами по себе их учить бесполезно.
Обычно паттерны стоит изучать либо когда у тебя большой опыт, либо когда ты работаешь с фреймворком и там какие-то из них используются.
Тебе по моему надо сначала язык PHP выучить, циклы.
Ну вот смотри, ты пишешь:
> Если делаю вот так, то выводит все значения
> while($row){
> echo $row['articul'];}
Это ведь не так. У тебя тут вечный цикл, который никогда не завершится и будет бесконечно выводить одно и то же значение.
Потому я и пишу что тебе сначала наверно надо изучить цикл while, потом почитать документацию по mysqli_fetch_assoc (или например найти хорошую, поодробную статью про mysqli). Если ты это сделаешь то наверно и сам поймешь почему.
Может быть конечно ты знаешь циклы, просто опечатался и там был другой код. Тогда тебе надо почитать учебник или документацию по mysqli. Вот в твоем коде, ты можешь объяснить каждую строчку, что она берет, что делает и что возвращает? как работает функция mysqli_fetch_assoc? Если не знаешь то надо сначала изучить.
Если пропускать и перепрыгивать темы, то ты и дальше не сможешь разобраться ни в чем.
Ну и еще, ты зачем-то смешиваешь ООП и процедурную версию mysqli.
Ну вот посмотри, как mobile-review сделан. У них есть типы обозреваемых материалов - статьи, обзоры, сравнения и т. д. под каждую отдельный раздел и есть тип обозреваемого материала - андроид, айос, девайсы, телефоны, планшеты... под это в другой менюшке тоже под каждое раздел. Вот такая вот получается двойная навигация, когда я хочу посмотреть все андроид-устройства или только все сравнения и соответственно использую разные менюшки. Или хочу посмотреть все сравнения андроид-устройств (хотя тут уже лучше тегами, наверное)
код нужно не читать, а перепечатывать и смотреть, как работает. у меня месяц на это ушло.
Ну а в чем проблема?
/reviews/ - все обзоры
/reviews/android/ - все обзоры андроида
/all/android/ - все (статьи, обзоры и тд), что относится к андроиду
/reviews/android/some-review - конкретная статья
Зря вы Зандстру целиком перепечатывает. Если у вас мало опыта вы большинство не поймете. Лучше сделайте студентов, файлообменник, сайт на Юи, исправьте все замечания и тогда вам паттерны будут ближе, и может к этому моменту вы даже некоторые будете знать.
Ебать, не сделаешь же в меню пункт "все"?
Плюс, категорий каждого типа может быть десяток, это что, сто вариантов в .htaccess прописывать? Вот так, как ты описал - это правда логичнее и правильнее, чем делать две независимые категории?
Может вообще ВСЕ категории сделать на тегах? Просто если к твоему варианту добавить теги, то /tags/android и /all/android по сути будет вести на один и тот же набор материалов, нормально ли это?
Или наоборот принципиально отказываться от тегов типа материала и типа статьи. Например, не будет тега "ноутбуки" или "windows" - это тоже как-то пиздец.
Нарезать в фотошопе не твоя задача. Тебе уже дают PSD файл, а ты делаешь его копию-сайт. В ОП посте есть курс штмл\ксс, выполни все задания и в конце тебя как раз ждет такой PSD файл, по которому ты будешь делать макет сайта.
http://plnkr.co/edit/7VYoU4KXe3mKV6G4spjd?p=preview
Не совсем понял что должно происходить при даблклике по названию города в списке. Т.е. мы получаем возможность таким образом отредактировать город, но что это за собой должно повлечь? Трансформацию элемента списка в инпут, который тоже обладает автокомплитом?
Каким образом из списка добавленных городов сделать форму, чтобы ее отправить на сервер? Пока из идей - добавить в конец списка сабмит, и при клике конвертировать жикверей все элементы списка в инпуты, что будет уродливо и наверное неверно.
Ты про плейсхолдеры в PDO? В mysqli? В какой-то другой библиотеке?
Некоторые базы данных поддерживают плейсхолдеры нативно, то есть драйвер посылает отдельно запрос с плейсхолдерами, отдельно данные. Данные в запрос подставляются на стороне базы.
В таком случае можно выполнить несколько однотипных запросов, посылая только новые данные, и база не будет повтрно парсить и анализировать запрос (получится крошечная экономия).
Если же база не поддерживает плейсхолдеры, то драйвер на стороне PHP экранирует данные, вставляет в запрос и посылает собранный запрос.
Мы тут в прошлом треде выяснили, что в случае с PDO и MySQL нативно поддерживаются базой только плейсхолдеры-вопросики, а те что с двоеточниями, заменяются на стороне PDO.
В любом случае в PDo можно включить EMULATE_PREPARE и тогда он будет заниматься подстановкой в любом случае.
> Делается регулярка типа ^:[a-z0-9]+
Там код на Си, и он по моему обходится без реглярок, посимвольно анализирует запрос. Ну и данные перед вставкой экранируются.
для начала прорешай наши задачки на HTML/CSS из ОП-поста чтобы закрепить знания CSS. Когда решишь все задачи, там в последней задаче будет PSD-макет который надо сверстать.
> Вот как ставится задача верстале на настоящей работе?
Бывает дают макет, бывают на словах объясняют («тут надо бы меню покрупнее сделать и чуть вправо сдвинуть»).
> дальше это особым образом режут в фотошопе ну и поехали сверху вниз каждый кусок заворачивать в теги и применять к ним стили
Нет, неправильный порядок. Сначала ты смотришь на макет и пишешь семантичный (осмысленный) HTML-код, который содержит все основные блоки, текст, праивльно размеченный тегами. А только потом пишешь CSS чтобы этот код выглядел как требуется. И по мере написания подставляешь нарезанные картинки.
Я тебе советую делать наши задачи и вбрасывать решения на проверку. После того, как ты все их пройдешь, у тебя будет хороший запас начальных знаний и ты сможешь верстать макеты.
>>573321
Ты чего злой такой?
> Ебать, не сделаешь же в меню пункт "все"?
А где я написал что его надо сделать?
> Плюс, категорий каждого типа может быть десяток, это что, сто вариантов в .htaccess прописывать?
у тебя ограничены знания о работе сервера и из-за этого непонимание. Никто эти категории в htaccess не прописывает, в наше время роутинг делается на уровне фреймворка и можно реализовать любую схему URL. Потому я и пишу что ты не должен о таких вещах думать, а составлять схему URL исходя исключительно из логики и структуры сайта.
Кстати, в наших задачах есть задача на файлообменник и там мы используем микрофреймворк Слим для роутинга.
> Просто если к твоему варианту добавить теги, то /tags/android и /all/android по сути будет вести на один и тот же набор материалов, нормально ли это?
нет, у одной страницы должен быть один URL, а не два.
> Может вообще ВСЕ категории сделать на тегах?
Ты под архитектуру вордпресса подгоняешь систему? Так бы и написал. Я исходил из того что мы можем сделать любую схему URL и соответтвенно никаких ограничений нет.
>>573322
Не очень понимаю. ЧТо нам мешает сделать свойства «тип материала» (статья, обзор) и «категория» (андроид, ноутбуки ) и тд? Теги используются когда нужно отнести материал к нескольким категориям. Или ты на вордпресс ориентируешсяь? Тогда надо было так и писать, а не сбивать людей с толку.
>>573323
Нарезать задача верстальщика. Так же как и выбрать оптимальный формат картинок например.
для начала прорешай наши задачки на HTML/CSS из ОП-поста чтобы закрепить знания CSS. Когда решишь все задачи, там в последней задаче будет PSD-макет который надо сверстать.
> Вот как ставится задача верстале на настоящей работе?
Бывает дают макет, бывают на словах объясняют («тут надо бы меню покрупнее сделать и чуть вправо сдвинуть»).
> дальше это особым образом режут в фотошопе ну и поехали сверху вниз каждый кусок заворачивать в теги и применять к ним стили
Нет, неправильный порядок. Сначала ты смотришь на макет и пишешь семантичный (осмысленный) HTML-код, который содержит все основные блоки, текст, праивльно размеченный тегами. А только потом пишешь CSS чтобы этот код выглядел как требуется. И по мере написания подставляешь нарезанные картинки.
Я тебе советую делать наши задачи и вбрасывать решения на проверку. После того, как ты все их пройдешь, у тебя будет хороший запас начальных знаний и ты сможешь верстать макеты.
>>573321
Ты чего злой такой?
> Ебать, не сделаешь же в меню пункт "все"?
А где я написал что его надо сделать?
> Плюс, категорий каждого типа может быть десяток, это что, сто вариантов в .htaccess прописывать?
у тебя ограничены знания о работе сервера и из-за этого непонимание. Никто эти категории в htaccess не прописывает, в наше время роутинг делается на уровне фреймворка и можно реализовать любую схему URL. Потому я и пишу что ты не должен о таких вещах думать, а составлять схему URL исходя исключительно из логики и структуры сайта.
Кстати, в наших задачах есть задача на файлообменник и там мы используем микрофреймворк Слим для роутинга.
> Просто если к твоему варианту добавить теги, то /tags/android и /all/android по сути будет вести на один и тот же набор материалов, нормально ли это?
нет, у одной страницы должен быть один URL, а не два.
> Может вообще ВСЕ категории сделать на тегах?
Ты под архитектуру вордпресса подгоняешь систему? Так бы и написал. Я исходил из того что мы можем сделать любую схему URL и соответтвенно никаких ограничений нет.
>>573322
Не очень понимаю. ЧТо нам мешает сделать свойства «тип материала» (статья, обзор) и «категория» (андроид, ноутбуки ) и тд? Теги используются когда нужно отнести материал к нескольким категориям. Или ты на вордпресс ориентируешсяь? Тогда надо было так и писать, а не сбивать людей с толку.
>>573323
Нарезать задача верстальщика. Так же как и выбрать оптимальный формат картинок например.
> Не совсем понял что должно происходить при даблклике по названию города в списке. Т.е. мы получаем возможность таким образом отредактировать город, но что это за собой должно повлечь? Трансформацию элемента списка в инпут, который тоже обладает автокомплитом?
Хороший вопрос. Можно загружать город в существующий инпут (тогда хорошо бы и менять надпись на кнопке), можно превращать в инпут.
> Каким образом из списка добавленных городов сделать форму, чтобы ее отправить на сервер?
Самый простой - к каждому городу добавлять input hidden с его id, а список городов обернуть тегом form.
Замеченные баги:
Шлется дофига запросов (смотри вкладку Network). Например, если очистить поле ввода, то шлется запрос с пустым значением q. Если быстро печатать, то на каждую букву шлется запрос. Так не должно быть. При пустом поле запрос слать не надо, также надо сделать ограничение на частоту отправки запросов, например не чаще раза в 400 мс. Или например если значение поля не менялось в течение 400 мс.
Вываливается слишком много вариантов, больше 20, человек не способен столько прочесть. Ограничься 7-8 значениями.
По коду. Определения функций у тебя зачем-то внесены внутрь $(), зачем? Не надо так делать. Также, у тебя внутри $() гигансткая простыня на 106 строк, функции не должны быть такими огромными. Для начала попробуй вынести вложенные функции наружу.
Сборку HTML-кода для нового города лучше делать не вручную, а с помощью шаблона, размещенного в HTML-коде, как описано тут: https://learn.javascript.ru/templates
Ты не обязан использовать готовый шаблонизатор, можешь написать велосипед на основе String.replace и регулярки. А можешь взять какой-нибудь готовый микрошаблонизатор.
Таким образом верстка будет в HTML-коде, и ее легко поддерживать, менять.
Имена функций должны начинаться с глагола и иметь вид сделайЧтоТо. Не NewCityString а например insertNewCity.
Вместо того чтобы вешать обработчик на каждую кнопку закрытия, лучше повесить один обработчик на список через $.on(...). Ты ведь знаешь про всплытие событий?
> google.maps.event.addDomListener(window, 'load', initialize)
не рекомендую использовать событие load . Оно срабатывает после полной загрузки страницы, скриптов, стилей , картинок и достаточно однйо подвисшей картинки чтобы это событие тоже откладывалось на потом. Ты запускаешь свой код по событию DOM ready, может быть это и не требуется? Разберись как работает код гуглокарт и почему там эта строчка.
> new NewCityString(cityName);
Не надо вызывать функции так. new предназначен для создания новых объетов, и у тебя явно ничего подобного тут нет.
> $('#city').on('input autocompleteselect', function(e) {
У jQuery UI Autocomplete есть опция где можно указать функцию-коллбек. Перехватывать события в обход нее неправильно. Почитай документацию по этому виджету.
При отправке нового запроса надо отменять старый. Также, при отправке jsonp надо включить кеширование (Jquery.ajax по умолчанию его отключает но тут то случай где оно полезно).
> getCts
Не надо так сокращать, названия должны быть понятными
> var geocoder = new google.maps.Geocoder();
Я думаю, незачем создавать по геокодеру на каждый новый город. Это точно необходимо?
Также, хорошо бы сделать код более разделенным на компоненты и готовым к повторному использованию. Например инпут с выбором города вполне можно сделать как отдельный компонент (например jquery-плагин) так, чтобы его можно было подключить и в других местах. Сейчас у тебя монолитный код, который заточен только под эту задачу и который нельзя повторно использовать.
> Не совсем понял что должно происходить при даблклике по названию города в списке. Т.е. мы получаем возможность таким образом отредактировать город, но что это за собой должно повлечь? Трансформацию элемента списка в инпут, который тоже обладает автокомплитом?
Хороший вопрос. Можно загружать город в существующий инпут (тогда хорошо бы и менять надпись на кнопке), можно превращать в инпут.
> Каким образом из списка добавленных городов сделать форму, чтобы ее отправить на сервер?
Самый простой - к каждому городу добавлять input hidden с его id, а список городов обернуть тегом form.
Замеченные баги:
Шлется дофига запросов (смотри вкладку Network). Например, если очистить поле ввода, то шлется запрос с пустым значением q. Если быстро печатать, то на каждую букву шлется запрос. Так не должно быть. При пустом поле запрос слать не надо, также надо сделать ограничение на частоту отправки запросов, например не чаще раза в 400 мс. Или например если значение поля не менялось в течение 400 мс.
Вываливается слишком много вариантов, больше 20, человек не способен столько прочесть. Ограничься 7-8 значениями.
По коду. Определения функций у тебя зачем-то внесены внутрь $(), зачем? Не надо так делать. Также, у тебя внутри $() гигансткая простыня на 106 строк, функции не должны быть такими огромными. Для начала попробуй вынести вложенные функции наружу.
Сборку HTML-кода для нового города лучше делать не вручную, а с помощью шаблона, размещенного в HTML-коде, как описано тут: https://learn.javascript.ru/templates
Ты не обязан использовать готовый шаблонизатор, можешь написать велосипед на основе String.replace и регулярки. А можешь взять какой-нибудь готовый микрошаблонизатор.
Таким образом верстка будет в HTML-коде, и ее легко поддерживать, менять.
Имена функций должны начинаться с глагола и иметь вид сделайЧтоТо. Не NewCityString а например insertNewCity.
Вместо того чтобы вешать обработчик на каждую кнопку закрытия, лучше повесить один обработчик на список через $.on(...). Ты ведь знаешь про всплытие событий?
> google.maps.event.addDomListener(window, 'load', initialize)
не рекомендую использовать событие load . Оно срабатывает после полной загрузки страницы, скриптов, стилей , картинок и достаточно однйо подвисшей картинки чтобы это событие тоже откладывалось на потом. Ты запускаешь свой код по событию DOM ready, может быть это и не требуется? Разберись как работает код гуглокарт и почему там эта строчка.
> new NewCityString(cityName);
Не надо вызывать функции так. new предназначен для создания новых объетов, и у тебя явно ничего подобного тут нет.
> $('#city').on('input autocompleteselect', function(e) {
У jQuery UI Autocomplete есть опция где можно указать функцию-коллбек. Перехватывать события в обход нее неправильно. Почитай документацию по этому виджету.
При отправке нового запроса надо отменять старый. Также, при отправке jsonp надо включить кеширование (Jquery.ajax по умолчанию его отключает но тут то случай где оно полезно).
> getCts
Не надо так сокращать, названия должны быть понятными
> var geocoder = new google.maps.Geocoder();
Я думаю, незачем создавать по геокодеру на каждый новый город. Это точно необходимо?
Также, хорошо бы сделать код более разделенным на компоненты и готовым к повторному использованию. Например инпут с выбором города вполне можно сделать как отдельный компонент (например jquery-плагин) так, чтобы его можно было подключить и в других местах. Сейчас у тебя монолитный код, который заточен только под эту задачу и который нельзя повторно использовать.
Нене, бро, я не злой, извини, если что-то резко сказал. Нет, не под вордпресс, мне логика всего этого интересна. Хм, переадресация на уровне фреймворка? Так бывает? Ну, красивые урлы, я про них.
Да. Почитай как работают исключения. После throw управление переходит вверх к ближайшему catch , а если его нет, программа завершается.
Невнимательно прочитал и сначала подумал, что ты хочешь меня заставить написать универсальный клиент-обертку.
Если только команды для счетчика, то это еще ничего.
Я так прикинул, мне понадобятся обертки для шести команд редиса, ну и конфигурация подключения для каждого клиента.
Три класса: один собственно для манипуляций с редисом, второй для работы с mysql, ну и третий для всего остального.
Прям для всех клиентов наверное слишком жирно (их там 13 штук), хватит 3 самых популярных и надежных для начала (Predis, PhpRedis и Rediska). Если грамотно спроектировать, то остальные можно будет потом добавлять по желанию, расширять код кому нужно.
Я буду тогда сюда сливать вопросы по мере поступления.
Например, что означает эта строка в документации редиски:
Читать дальше http://pastebin.com/BMXGeGka
Вакаба как всегда увидела слово из спам-листа.
О, и пастбин меня заставил вводить капчу.
Your paste has triggered our automatic SPAM detection filter. This happens when links or certain keywords are detected in a paste. It can also happen if you are creating a lot of items in a short period of time. To confirm you are not a bot, please fill out the captcha below.
Может они на домен третьего уровня агрятся? Тест http://rediska.geometria-lab.net/api/0-5-10/index.html
Меня пастебин просит ввести капчу в 100% случаев - видимо IP подозрительный.
> что ты хочешь меня заставить написать универсальный клиент-обертку.
Нет, универсальную реализацию алгоритма счетчика. Идея такая: мы разбиваем код на 2 части: универсальную (часть A) и относящуюся к конкретному сайту (назовем ее часть B, или адаптер). Что делать с редисом, я окончательно не решил, тут есть 2 варианта:
1) абстрагироваться от конкретной библиотеки-клиента и разрешить любую
2) выбрать одну, например редиску, и работать только с ней
Вариант 1 конечно универсальнее но чуть сложнее.
В универсальной части реализуется сам алгоритм, при это там:
- нет никакого упоминания конкретного сайта или фрйемворка
- нет работы с БД и указания на тип БД (так как ясен пень у каждого сайта она разная, кто-то исплоьзует PDO, кто-то mysqli, кто-то вообще смелый и в MongoDB все хранит)
- (по желанию) нет указания на конкретную библиотеку для работы с редисом
То есть только алгоритм, абстрагированный от всех зависимостей. Он общается с внешним миром (редис, база) через часть B. То есть когда ему надо сбросить данные в БД, он не делает это напрямую, а вызвыает часть B, где есть реализаия этого метода под конкретный сайт с конкретными именами таблиц.
Часть B может быть одним классом, а может не одним. Тут надо подумать как лучше.
Можно сделать так, чтобы человек писал часть B с нуля. Может быть можно сделать готовые компоненты, например компоненту для Юи, чтобы человеку надо было только имя таблицы и поля в нем указать.
Если кто-то хочет прикрутить его к себе в проект, он берет образец части B (либ оберет готовую) и переделывает под себя либо пишет с нуля. Тут разумеется надо применить интерфейс или абстрактные методы чтобы заставить человека реализовать все, что требуется.
В общем это хорошая возможность поучиться проектировать ООП-библиотеки.
Начать стоит наверно с проектирования части A. Сделай класс, определи публичные методы, и как он будет взаимодействовать с частью B.
Кстати есть такая штука как UML-диаграммы, для рисования классов и их взаимодействия.
> Прям для всех клиентов наверное слишком жирно (их там 13 штук)
Согласен. Кому надо, сам напишет, от нас нужен только интерфейс или абстрактный класс и пара строк в ридми.
> хватит 3 самых популярных и надежных для начала (Predis, PhpRedis и Rediska)
Если сложно то можно даже меньше.
> Какой-то итератор, какой-то буффер сокета.
Если ты указываешь false то как я понял, редиска шлет запрос, принимает ответ, возвращает гигантский массив результатов.
Если true то она шлет запрос и создает итератор, который позволяет читать ответ кусками, напрмер 1 ключ за раз. Таким образом даже если у нас миллион ключей, нам не требуется много памяти.
Итератор - это объект для перебора значений какого-то списка. В PHP есть набор встроенных итераторов: http://php.net/manual/ru/class.iterator.php и встроенный интерфейс для них. Любой класс, реализующий этот интерфейс. можно использовать в стандартном цикле foreach для перебора значений итератора.
Итераторы используются в первую очередь как средство экономии памяти, чтобы не создавать массив результатов. Также итераторы позволяют работать с бесконечными списками, например можно сделать итератор, обходящий бесконечный список целых чисел.
В версии 5.5 добавили генераторы - они позволяют делать примерно то же, но проще, одной функцией, без реализации 6 методов.
Меня пастебин просит ввести капчу в 100% случаев - видимо IP подозрительный.
> что ты хочешь меня заставить написать универсальный клиент-обертку.
Нет, универсальную реализацию алгоритма счетчика. Идея такая: мы разбиваем код на 2 части: универсальную (часть A) и относящуюся к конкретному сайту (назовем ее часть B, или адаптер). Что делать с редисом, я окончательно не решил, тут есть 2 варианта:
1) абстрагироваться от конкретной библиотеки-клиента и разрешить любую
2) выбрать одну, например редиску, и работать только с ней
Вариант 1 конечно универсальнее но чуть сложнее.
В универсальной части реализуется сам алгоритм, при это там:
- нет никакого упоминания конкретного сайта или фрйемворка
- нет работы с БД и указания на тип БД (так как ясен пень у каждого сайта она разная, кто-то исплоьзует PDO, кто-то mysqli, кто-то вообще смелый и в MongoDB все хранит)
- (по желанию) нет указания на конкретную библиотеку для работы с редисом
То есть только алгоритм, абстрагированный от всех зависимостей. Он общается с внешним миром (редис, база) через часть B. То есть когда ему надо сбросить данные в БД, он не делает это напрямую, а вызвыает часть B, где есть реализаия этого метода под конкретный сайт с конкретными именами таблиц.
Часть B может быть одним классом, а может не одним. Тут надо подумать как лучше.
Можно сделать так, чтобы человек писал часть B с нуля. Может быть можно сделать готовые компоненты, например компоненту для Юи, чтобы человеку надо было только имя таблицы и поля в нем указать.
Если кто-то хочет прикрутить его к себе в проект, он берет образец части B (либ оберет готовую) и переделывает под себя либо пишет с нуля. Тут разумеется надо применить интерфейс или абстрактные методы чтобы заставить человека реализовать все, что требуется.
В общем это хорошая возможность поучиться проектировать ООП-библиотеки.
Начать стоит наверно с проектирования части A. Сделай класс, определи публичные методы, и как он будет взаимодействовать с частью B.
Кстати есть такая штука как UML-диаграммы, для рисования классов и их взаимодействия.
> Прям для всех клиентов наверное слишком жирно (их там 13 штук)
Согласен. Кому надо, сам напишет, от нас нужен только интерфейс или абстрактный класс и пара строк в ридми.
> хватит 3 самых популярных и надежных для начала (Predis, PhpRedis и Rediska)
Если сложно то можно даже меньше.
> Какой-то итератор, какой-то буффер сокета.
Если ты указываешь false то как я понял, редиска шлет запрос, принимает ответ, возвращает гигантский массив результатов.
Если true то она шлет запрос и создает итератор, который позволяет читать ответ кусками, напрмер 1 ключ за раз. Таким образом даже если у нас миллион ключей, нам не требуется много памяти.
Итератор - это объект для перебора значений какого-то списка. В PHP есть набор встроенных итераторов: http://php.net/manual/ru/class.iterator.php и встроенный интерфейс для них. Любой класс, реализующий этот интерфейс. можно использовать в стандартном цикле foreach для перебора значений итератора.
Итераторы используются в первую очередь как средство экономии памяти, чтобы не создавать массив результатов. Также итераторы позволяют работать с бесконечными списками, например можно сделать итератор, обходящий бесконечный список целых чисел.
В версии 5.5 добавили генераторы - они позволяют делать примерно то же, но проще, одной функцией, без реализации 6 методов.
Если хочешь попрактиковаться в итераторах, можешь сделать итератор, генерирующий все комбинации из заданного (переданного в конструктор массивом) набора купюр разных номиналов. Используя его, реши задачу про банкомат из учебника (найти комбинацию купюр, дающих данную сумму).
Это конечно бесполезное применение, так как задачу проще решить обычным циклом или рекурсией. В PHP встроены более полезные итераторы, например итератор по файлам в папке, в том числе рекурсивный: http://php.net/manual/ru/class.recursivedirectoryiterator.php
Ну и я написал, итераторы используют для экономии памяти, а еще их используют для абстракции перебора списка. То есть если мы хотим сделать функцию, принимающую на вход какой-то абстрактный список (а не только массив), мы можем указать в тайп-хинте интерфейс Iterator, и она будет работать с любой его реализцией (в том числе ArrayIterator который является оберткой над массивом).
>Во-первых, ты почему-то засунул эти функции в конструктор (а не в прототип) и в итоге мы никакой выгоды в виде уменьшения и упрощения его кода не получили. Во-вторых, ты их не вызываешь, а идея была в том что ты их будешь сам и вызывать.
Разобрался. Сделал как надо.
>У тебя жестко заложен список добавок, лучше бы использовать в качестве ключей сами константы.
>Тут нужен более обобщенный код, не перечисляющий каждый вид добавки
Переделал в массив. Если добавляется добавка, она добавляется в массив, а если в массиве уже есть, то выдается ошибка.
Правда почему-то это не работает. Пробовал разными способами это реализовать, но не получается.
К примеру взять добавление приправы, в if идет (если приправа есть в массиве (а ее нету, что уже значит фолс) и если добавка равна приправе) то выдать ошибку. Но ошибка выдается всегда, хоть тру, хоть фолс в иф. Не могу разобраться почему.
>кучу ифов лучше заменить на словарь, хранящий цену и калорийность разных начинок и добавок.
Лучше заменить да, но вот как это использовать? Нужно как-то соединить размер, начинку и добавку с ключами объекта, в котором хранятся калории и цены. Но как это сделать не понимаю. Ведь если делать построчно типа высчитываем цену if (size == x) price +=arr.y.price, то смысл в этом теряется, длина и сложность кода становится еще больше чем с кучей ифов.
https://ideone.com/XWczdX
> join(Hamburger.TOPPING_SAUCE);
Это не добавление в массив, почитай про методы массива
https://learn.javascript.ru/array-methods
https://learn.javascript.ru/array
> indexOf(topping)
Этот метод возвращает не true/false а число
> Нужно как-то соединить размер, начинку и добавку с ключами объекта, в котором хранятся калории и цены.
У тебя уже есть обозначение добавки - константа. Используй ее как ключ объекта.
> Интерфейс это такой же абстрактный класс
дальше можно не читать. Алсо паста
------------
Интерфейс это набор требований к классу. «требование» здесь значит требование чтобы в классе был определенный метод.
Если класс реализует интерфейс, в нем обязаны быть эти методы.Обычно интерфйес представляет собой какое-то умение: классы, реализующие этот интерфейс, умеют что-то делать.
Допустим мы делаем сайт где можно ставить лайки постам и комментам. Допустим у нас есть классы User, Post и Comment.
Вот у нас есть пост и коммент, мы можем увеличить число лайков, допустим методом increaseLikeCount и узнавать сколько у них лайков методом getLikeCount, то есть у них есть что-то общее, но как описать это в коде? Как сказать что эти 2 класса в отличие от других умеют работать с лайками?
Второй пример, мы хотим сделать функцию, которая допустим ставит лайк от определенного пользователя определенному посту или комментарию:
function addLike(User $user, $object) ...
Мы сказали что $user должен быть объектом класса User, а как сказать что $object должен быть классом, умеющим работать с лайками?
Обе проблемы решают интерфейсы. Объявим интерфейс Likeable, который представляет собой умение получать лайки. Опишем какие методы обязаны реализовать такие классы:
interface Likeable
{
public function increaseLikeCount( );
public function getLikeCount( );
}
Теперь укажем в коде что посты и комменты можно лайкать:
class Post implements Likeable { ... }
class Comment implements Likeable { ... }
на этом этапе php проверит, не забыли ли мы реализовать в классах упомянутые методы. Если забыли — выдаст ошибку. Как удобно!
Ну и теперь мы можем использовать интерфейс чтобы указать какие аргументы принимает функция addLike:
function addLike(User $user, Likeable $object) { ... }
заметь что благодаря интферйесам наш код стал расиряем. Допустим завтра мы добавим класс Photo который тоже можно лайкать. Если он будет реализовывать интерфейс Likeable то функция вроде addLike сможет работать и с ним без переписывания кода.
В общем интерфейс представляет какую-то способность класса и требует от него реализовать определенные методы.
> Интерфейс это такой же абстрактный класс
дальше можно не читать. Алсо паста
------------
Интерфейс это набор требований к классу. «требование» здесь значит требование чтобы в классе был определенный метод.
Если класс реализует интерфейс, в нем обязаны быть эти методы.Обычно интерфйес представляет собой какое-то умение: классы, реализующие этот интерфейс, умеют что-то делать.
Допустим мы делаем сайт где можно ставить лайки постам и комментам. Допустим у нас есть классы User, Post и Comment.
Вот у нас есть пост и коммент, мы можем увеличить число лайков, допустим методом increaseLikeCount и узнавать сколько у них лайков методом getLikeCount, то есть у них есть что-то общее, но как описать это в коде? Как сказать что эти 2 класса в отличие от других умеют работать с лайками?
Второй пример, мы хотим сделать функцию, которая допустим ставит лайк от определенного пользователя определенному посту или комментарию:
function addLike(User $user, $object) ...
Мы сказали что $user должен быть объектом класса User, а как сказать что $object должен быть классом, умеющим работать с лайками?
Обе проблемы решают интерфейсы. Объявим интерфейс Likeable, который представляет собой умение получать лайки. Опишем какие методы обязаны реализовать такие классы:
interface Likeable
{
public function increaseLikeCount( );
public function getLikeCount( );
}
Теперь укажем в коде что посты и комменты можно лайкать:
class Post implements Likeable { ... }
class Comment implements Likeable { ... }
на этом этапе php проверит, не забыли ли мы реализовать в классах упомянутые методы. Если забыли — выдаст ошибку. Как удобно!
Ну и теперь мы можем использовать интерфейс чтобы указать какие аргументы принимает функция addLike:
function addLike(User $user, Likeable $object) { ... }
заметь что благодаря интферйесам наш код стал расиряем. Допустим завтра мы добавим класс Photo который тоже можно лайкать. Если он будет реализовывать интерфейс Likeable то функция вроде addLike сможет работать и с ним без переписывания кода.
В общем интерфейс представляет какую-то способность класса и требует от него реализовать определенные методы.
на джунов-хуюнов делить только быдло, набивающее себе цену. Знать нужно вообще все, тут уже не раз перечисляли, никому не нужны профаны-недоучки, которые только синтаксис освоили и сделали две задачки от опа.
Различия начнешь понимать, если перепечатаешь паттерны из книги в шапке.
Зачем тогда нужен контролллер, если у тебя все классы сводятся и обрабатываются в другом классе?
RewriteEngine On
RewriteBase /
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^(.*)$ index.php?$1 [L,QSA]
однако, при попытке обратиться к, например, несуществующему адресу, меня просто кидает на 404 страницу, не заходя в индекс вообще. Что за хуйня?
А почему тебя должно кидать на индекс? Пропиши перенаправление для 404 ошибки, либо рендери во фреймворке страницу, которая будет выводиться при ошибке.
javascript
Веб-сервер перезапустил? .htaccess в какую папку положил? что в адресной строке браузера?
>>573632
В контроллере остается разбор параметров запроса (GET/POST), вызов шаблона для вывода.
Алсо не забудь код на проверку показать. Я на скриншотах вижу вещи, которые можно сделать лучше. И HTMl кода в контроллере не должно быть.
if(1 != 1) {
//записываем в массив текст
}
И так несколько условий, а потом вывод в html странице foreach.
$db = new PDO('mysql:host='.$this->host.';dbname='.$this->dbname.";charset=utf8",$this->user,$this->password);
\t\t\t$conditions = array();
\t\t\t$sql = "SELECT id,rate,login, date, message FROM post";
\t\t\tif(isset($params['column'])&&isset($params['value']))
\t\t\t{
\t\t\t\t$sql = $sql." WHERE ? = ?";
\t\t\t\tarray_push($conditions,$params['column']);
\t\t\t\tarray_push($conditions,$params['value']);
\t\t\t}
\t\t\tif(isset($params['order']))
\t\t\t{
\t\t\t\t$sql = $sql." ORDER BY ?";
\t\t\t\tarray_push($conditions,$params['order']);
\t\t\t\tif(isset($params['desc']))
\t\t\t\t{
\t\t\t\t\t$sql=$sql." ?";
\t\t\t\t\tarray_push($conditions,$params['desc']);
\t\t\t\t}
\t\t\t}
\t\t\t$sth = $db->prepare($sql,array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
\t\t\t$sth->execute($conditions);
так вот, если я передаю пустой массив в качестве параметра, то есть запрос - обычная выборка, то всё норм получается, а если запрос содержит условие, например, для столбца "login" то запрос выполняется, но результат пустой. В чём может быть хуйня?
$db = new PDO('mysql:host='.$this->host.';dbname='.$this->dbname.";charset=utf8",$this->user,$this->password);
\t\t\t$conditions = array();
\t\t\t$sql = "SELECT id,rate,login, date, message FROM post";
\t\t\tif(isset($params['column'])&&isset($params['value']))
\t\t\t{
\t\t\t\t$sql = $sql." WHERE ? = ?";
\t\t\t\tarray_push($conditions,$params['column']);
\t\t\t\tarray_push($conditions,$params['value']);
\t\t\t}
\t\t\tif(isset($params['order']))
\t\t\t{
\t\t\t\t$sql = $sql." ORDER BY ?";
\t\t\t\tarray_push($conditions,$params['order']);
\t\t\t\tif(isset($params['desc']))
\t\t\t\t{
\t\t\t\t\t$sql=$sql." ?";
\t\t\t\t\tarray_push($conditions,$params['desc']);
\t\t\t\t}
\t\t\t}
\t\t\t$sth = $db->prepare($sql,array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
\t\t\t$sth->execute($conditions);
так вот, если я передаю пустой массив в качестве параметра, то есть запрос - обычная выборка, то всё норм получается, а если запрос содержит условие, например, для столбца "login" то запрос выполняется, но результат пустой. В чём может быть хуйня?
PDO::ERRMODE_EXCEPTION включен? Если нет то включи. Может тогда и текст ошибки появится.
также посмотри что за запрос получается.
Также, в этой ситуации лучше использовать плейсхолдеры с именами, а не вопросики, так как с вопросиками проще перепутать число или их порядок. Более того, для сборки запроса по частям есть паттерн Query Builder и библиотеки которые его реализуют (например: Doctrine DBAL). Надо использовать их, а не изобретать велосипед.
Ох и долго же я придумывал как высчитывать калории и цену через константы, чего только не перепробовал.
Протестировал, вроде все работает, все ошибки выдает, калории и цены для разных начинок, размеров и добавок высчитывает правильно.
https://ideone.com/nnDZkD
не сгенерило ошибку, запрос потому что сам по себе-то выполняется и не возвращает false
А, понял. Плейсхолдеры используются только для значений, ими нельзя заменить имена полей и таблиц.
Твой запрос превращается в WHERE 'name' = 'Ivan' вместо WHERE name = 'Ivan'
Имена таблиц придется вставлять напрямую. Не забудь принять меры безопасности: эти имена должны проверяться по «белому» списку (например в массиве) разрешенных значений в той же функции где вставляются или как-то еще гарантировать что там будет одно из разрешенных значений.
В твоем случае имя поля в $params['column'] надо проверять по списку допустимых полей.
а, то есть мне стоит завести массив $names_whitelist и проверять наличие вставляемого имени колонки в нём?
> "BIG": {price: 100, calories: 40},
И где тут константа, я не вижу? Мы же договорились что для обозначения свойств гамбургера будут использоваться только константы. Это не константа, а строка которая к ней никакого отношения не имеет.
Проверка параметров функции на правильность должна идти самой первой, а не в середине или конце.
> Hamburger.prototype.addTopping
Это надо сделать универсальнее, чтобы добавление 2 новых видов добавок не требовало переделывать код.
> this.pricesAndCalorics[this.toppings].calories;
> this.pricesAndCalorics[this.toppings].calories;
Это сложное выражение, много точек и скобок, надо бы упростить его вынеся часть в переменную или заменив на вызов метода.
Сам код надо чуть переупорядочить так:
- конструктор
- константы
- методы
>И где тут константа, я не вижу? Мы же договорились что для обозначения свойств гамбургера будут использоваться только константы. Это не константа, а строка которая к ней никакого отношения не имеет.
Ну вроде как имеет, это ведь значение константы. Я не понимаю как ее туда записать. Если пытаться вместо "BIG" в ключ записать константу как есть Hamburger.SIZE_BIG то браузер выдает ошибку о том, что точка запрещена, а если обернуть в кавычки или написать просто SIZE_BIG, то это уже не тот ключ, что константа.
Набросал пока на скорую руку, зацени
https://github.com/nsdvw/visit-counter/tree/master/src
Базу потом сделаю, сегодня устал, не привык так много работать. Пока только PDO.
$app->error(function (\Exception $e) use ($app) {
$app->render('error.php');
});
а потом просто вызывать метод $app->error(); на случай ошибки и исключения через ифы? и оно автоматом на еррор.пхп перенаправится? просто не могу ща проверить.
define("GREETING", "Hello you.", true);
echo GREETING; // outputs "Hello you."
echo Greeting; // outputs "Hello you."
слушай, где ты учился так каждый пиксель высчитывать, сепараторы с директориями добавлять и байты в степень возводить. шарага какая-то?
Верно, в используемом тобой синтаксисе выражения в качестве ключа использовать нельзя, только числа и строки. Но может просто использовать другой синтаксис?
>>574299
http://docs.slimframework.com/errors/500/
Эта функция либо вызвает либо задает обработчик PHP-ошибок.
>>574303
> https://github.com/nsdvw/visit-counter/blob/master/src/VisitCounter.php#L20
Вот это плохо место. У твоего класса есть зависимость - клиент редис. Вместо того чтобы использовать одобренный архитекторами принцип DI ( https://gist.github.com/codedokode/e1d31a31b37d5f635057 ) ты сам создаешь эту зависимость прямо в конструкторе. Вот к чему это приводит (помни что пользователь твоей библиотеки не может править ее код: у него нет доступа к твоему гитхабу):
- пользователь твоей библиотеки не может добавить поддержку нового драйвера редиса сам
- пользователь твоей библиотеки не может подсунуть свой драйвер. Например он написал свой класс, унаследовав его от Rediska и добавив туда какие-то полезные функции (например логгирование запросов). Он не может использовать свой класс с твоей библиотекой.
- твоя библиотека создает новое соединение с редисом а не использует существующее
- пользователь твоей библиотеки не может вызывать какие-то методы у объекта драйвера редиса, так как он хранится в protected поле.
- ну и код выглядит плохо на мой взгляд. Твой класс кроме подсчета посещений занимается какими-то побочными вещами вроде создания клиента редиса
Все эти проблемы решаются использованием DI, то есть объект драйвера передается в конструктор.
В качестве драйвера БД используется только PDO?
> const PER_TRANSACTION = 1000;
Я бы не делал это константой, а приватным полем. Ибо может возникнуть желание поменять это число.
> $keyPrefix = 'visitCounter'
Это хорошо, что у тебя можно задавать префикс. Удобно, особенно когда счетчиков несколько. Но надо ли это значение передавать через конструктор или же лучше сделать метод-сеттер? В чем разница между этими вариантами?
> public function appendToQueue($pageID, $userIP, $expire = 0, $keyValue = '')
Во-первых, это странный метод который выдет наружу реализацию очереди. По моему он должен называться «учесть посещение» а не «добавить в очередь». Во-вторых, почему в него передаются 2 послежних параметра? Почему я чтобы учесть просмотр страницы, должен передавать expire и keyValue? Не вижу логики.
Насчет адаптеров редиса. Во-первых их надо переименовать, желательно добавив слово «адаптер». Во-вторых, они должны быть объединены либо абстрактным классом либо интерфейсом. Вот представь что я хочу написать новый адаптер. Где список требований которые я должен реализовать?
> https://github.com/nsdvw/visit-counter/blob/master/src/VisitCounter.php#L69
> $sql = "UPDATE $tblName SET $columnName = $columnName + $count
Предлагаю попробовать абстрагироваться от базы данных. Давай просто представим что у нас есть некое абстрактное хранилище, а как оно реализовано - mySQL, PostgreSQL, MongoDB, текстовый файлик - не наша проблема. Может быть класс станет универсальнее.
Верно, в используемом тобой синтаксисе выражения в качестве ключа использовать нельзя, только числа и строки. Но может просто использовать другой синтаксис?
>>574299
http://docs.slimframework.com/errors/500/
Эта функция либо вызвает либо задает обработчик PHP-ошибок.
>>574303
> https://github.com/nsdvw/visit-counter/blob/master/src/VisitCounter.php#L20
Вот это плохо место. У твоего класса есть зависимость - клиент редис. Вместо того чтобы использовать одобренный архитекторами принцип DI ( https://gist.github.com/codedokode/e1d31a31b37d5f635057 ) ты сам создаешь эту зависимость прямо в конструкторе. Вот к чему это приводит (помни что пользователь твоей библиотеки не может править ее код: у него нет доступа к твоему гитхабу):
- пользователь твоей библиотеки не может добавить поддержку нового драйвера редиса сам
- пользователь твоей библиотеки не может подсунуть свой драйвер. Например он написал свой класс, унаследовав его от Rediska и добавив туда какие-то полезные функции (например логгирование запросов). Он не может использовать свой класс с твоей библиотекой.
- твоя библиотека создает новое соединение с редисом а не использует существующее
- пользователь твоей библиотеки не может вызывать какие-то методы у объекта драйвера редиса, так как он хранится в protected поле.
- ну и код выглядит плохо на мой взгляд. Твой класс кроме подсчета посещений занимается какими-то побочными вещами вроде создания клиента редиса
Все эти проблемы решаются использованием DI, то есть объект драйвера передается в конструктор.
В качестве драйвера БД используется только PDO?
> const PER_TRANSACTION = 1000;
Я бы не делал это константой, а приватным полем. Ибо может возникнуть желание поменять это число.
> $keyPrefix = 'visitCounter'
Это хорошо, что у тебя можно задавать префикс. Удобно, особенно когда счетчиков несколько. Но надо ли это значение передавать через конструктор или же лучше сделать метод-сеттер? В чем разница между этими вариантами?
> public function appendToQueue($pageID, $userIP, $expire = 0, $keyValue = '')
Во-первых, это странный метод который выдет наружу реализацию очереди. По моему он должен называться «учесть посещение» а не «добавить в очередь». Во-вторых, почему в него передаются 2 послежних параметра? Почему я чтобы учесть просмотр страницы, должен передавать expire и keyValue? Не вижу логики.
Насчет адаптеров редиса. Во-первых их надо переименовать, желательно добавив слово «адаптер». Во-вторых, они должны быть объединены либо абстрактным классом либо интерфейсом. Вот представь что я хочу написать новый адаптер. Где список требований которые я должен реализовать?
> https://github.com/nsdvw/visit-counter/blob/master/src/VisitCounter.php#L69
> $sql = "UPDATE $tblName SET $columnName = $columnName + $count
Предлагаю попробовать абстрагироваться от базы данных. Давай просто представим что у нас есть некое абстрактное хранилище, а как оно реализовано - mySQL, PostgreSQL, MongoDB, текстовый файлик - не наша проблема. Может быть класс станет универсальнее.
> https://github.com/nsdvw/visit-counter/blob/master/src/RedisClient.php#L9
> public function __construct($options)
Это плохое решение так как этот конструктор тут не нужен. зачем конструктор абстрактному классу? Плюс, ты все равно не можешь заставить его вызывать. Лучше просто убери его вместе с функией setInstance.
Оно никуда ничего не перенаправляет. Оно просто выводит указанный шаблон. По задумке там страница вида «произошла ошибка, попробуйте обновить страницу».
также, если ты ставишь свой обработчик исключения ты должен залоггировать его функцией error_log а то о нем никто не узнает.
Если ты не разбираешьяс в исключениях на 100% то сначала прочти по ним урок https://gist.github.com/codedokode/65d43ca5ac95c762bc1a
Ну и немножечко информации для расширения кругозора. Почему нам приходится писать адаптеры под редис клиенты и драйверы БД? потому что в мире PHP много драйверов БД и клиентов редиса. PRO, mysqli, + встроенные в фреймворки обертки над ними.
В этом есть и плохие и хорошие стороны. Плохо то, что нет единого стандарта или интферйеса для таких вещей, и мы вынуждены писать адаптеры.
Аналогичная ситуация и с другими компонентами, например каждый фреймворк изобретает свои классы для работы с кешем, логгирования, хранения информации о GET/POST переменных (класс Request).
В противоположность PHP, в некоторых других языках такие стандарты есть. В Яве есть JDBC (чем-то напоминает PDO), https://en.wikipedia.org/wiki/Java_Database_Connectivity , первоначально это была сторонняя библиотека, но потом на ее основе сделали стандарт JSR 54.
В Питоне есть PEP 0249 который описывает интерфейс драйвера для работы с БД: https://www.python.org/dev/peps/pep-0249/
Как ты видишь из цифр в номерах спецификаций, в обоих этих языках их довольно много.
В PHP аналогичный процесс, увы, только начинается. У нас пока только есть спецификации на логгер: http://www.php-fig.org/psr/psr-3/ и на информацию о HTTP запросе/ответе: http://www.php-fig.org/psr/psr-7/
И еще в разработке PSR-6 определяющий интерфейс для кеша и интерфейс для DI контейнеров.
Соответственно, если например тебе где-то понадобится логгер, лучше всего предпочесть PSR-3 совместимый интерйфес - тогда любая совместимая библиотека сможет работать с твоим кодом.
>>574275
>> "BIG": {price: 100, calories: 40},
>И где тут константа, я не вижу?
Теперь видно. Иные способы добавления элементов в объект я как-то не учел сразу.
>Проверка параметров функции на правильность должна идти самой первой, а не в середине или конце.
Понял, логично, переделал во всех местах.
>> Hamburger.prototype.addTopping
>Это надо сделать универсальнее, чтобы добавление 2 новых видов добавок не требовало переделывать код.
Я так понял заменить индивидуальные ошибки на одну общую. Сделано. Теперь при введении новых добавок код трогать не потребуется.
>>this.pricesAndCalorics[this.toppings].calories;
>Это сложное выражение, много точек и скобок, надо бы упростить его вынеся часть в переменную или заменив на вызов метода.
Упростил как-то мутно по-моему через переменную.
>Сам код надо чуть переупорядочить...
Готово.
Я еще думаю, что можно сделать отдельные массивы, хранящие размеры\начинки\добавки чтобы укоротить код в валидаторах, ведь если ввести много новых по длине кода это будет выгоднее.
>>574275
>> "BIG": {price: 100, calories: 40},
>И где тут константа, я не вижу?
Теперь видно. Иные способы добавления элементов в объект я как-то не учел сразу.
>Проверка параметров функции на правильность должна идти самой первой, а не в середине или конце.
Понял, логично, переделал во всех местах.
>> Hamburger.prototype.addTopping
>Это надо сделать универсальнее, чтобы добавление 2 новых видов добавок не требовало переделывать код.
Я так понял заменить индивидуальные ошибки на одну общую. Сделано. Теперь при введении новых добавок код трогать не потребуется.
>>this.pricesAndCalorics[this.toppings].calories;
>Это сложное выражение, много точек и скобок, надо бы упростить его вынеся часть в переменную или заменив на вызов метода.
Упростил как-то мутно по-моему через переменную.
>Сам код надо чуть переупорядочить...
Готово.
Я еще думаю, что можно сделать отдельные массивы, хранящие размеры\начинки\добавки чтобы укоротить код в валидаторах, ведь если ввести много новых по длине кода это будет выгоднее.
трай кэтч эксепшн я знаю, и фроу нью эксепшн знаю, непонятно, куда это все "логируется" и что выводится в случае ошибки на экран - пустота?_?.
https://github.com/someApprentice/Cat-and-Mouse
Несколько вопросов:
Когда срабатывает исключение https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Mouse.php#L88 выдается ошибка:
>Fatal error: Uncaught exception 'Exception' with message ' - They Die ~((‡> <br>' in C:\Program Files\xampp\www\Classes\Mouse.php:88 Stack trace: #0 C:\Program Files\xampp\www\index.php(30): Mouse->move() #1 {main} thrown in C:\Program Files\xampp\www\Classes\Mouse.php on line 88
Погуглив я узнал что это из-за отсутствия конструкции try\catch, но ты по моему говорил что можно без нее обойтись. Как тут поступить?
https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Mouse.php#L20
https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Mouse.php#L25
Как сокращать такие длинные условия?
>>574482
Зависть - плохое чувство.
Исключение говорит об ошибке в твоей программе (попытка сходить мертвым животным). Следовательно ловить его не нужно. Нужно исправить ошибку.
И вы не помогаете на счёт гита?
Никак не могу с ним разобраться, а везде меня игнорируют.
помогаем
>>574503
Данные не идут напрямую в _POST. Ты все путаешь.
https://webref.ru/html/form
http://htmlbook.ru/samhtml5/formy/otpravka-dannykh-formy
Браузер при отправке собирает данные из всех полей внтури формы в виде пар element_name=element_value, и в случае POST отправляет в теле HTTP-запроса на сервер.
А вот уже на сервере, когда запусается PHP скрипт, он читает поступившие теле запроса данные, анализирует их и заполняет массив _POST/_GET.
http://php.net/manual/ru/language.variables.external.php
Тебе бы стоило почитать где-нибудь про протокол HTTP (не знаю правда хорошей статьи).
> А если я поместил несколько инпутов в таблицу внутри формы, как мне их передать?
Не понял этот вопрос. А как таблица влияет на возможность передавать данные форм?
Или ты имеешь в виду что у них одинаковые имена? Это конечно плохо.
Тогда надо использовать массивы в POST: http://php.net/manual/ru/faq.html.php#faq.html.arrays
помогаем
>>574503
Данные не идут напрямую в _POST. Ты все путаешь.
https://webref.ru/html/form
http://htmlbook.ru/samhtml5/formy/otpravka-dannykh-formy
Браузер при отправке собирает данные из всех полей внтури формы в виде пар element_name=element_value, и в случае POST отправляет в теле HTTP-запроса на сервер.
А вот уже на сервере, когда запусается PHP скрипт, он читает поступившие теле запроса данные, анализирует их и заполняет массив _POST/_GET.
http://php.net/manual/ru/language.variables.external.php
Тебе бы стоило почитать где-нибудь про протокол HTTP (не знаю правда хорошей статьи).
> А если я поместил несколько инпутов в таблицу внутри формы, как мне их передать?
Не понял этот вопрос. А как таблица влияет на возможность передавать данные форм?
Или ты имеешь в виду что у них одинаковые имена? Это конечно плохо.
Тогда надо использовать массивы в POST: http://php.net/manual/ru/faq.html.php#faq.html.arrays
>keyPrefix ... надо ли это значение передавать через конструктор или же лучше сделать метод-сеттер? В чем разница между этими вариантами?
Разница в том, что через конструктор мы передаем значение один раз, и больше не сможем его поменять, разве что пересоздать объект. А с сеттерами свойство можно изменить в любой момент. Я понял недочет, просто тяжело учитывать нюансы, когда приходится держать в голове большую систему классов, недоглядел.
Про DI читал в теории, но без практических примеров не понимал применения. Теперь (кажется) разобрался.
Переписал код. Добавил адаптер для объекта соединения с базой.
>Почему я чтобы учесть просмотр страницы, должен передавать expire и keyValue?
Expire вынес в конфиг адаптера редиса. keyValue убрал. Насчет значения ключа, то я подумал, что в дальнейшем код можно будет расширить, и он будет учитывать не только уникальные посещения, но и кол-во посещений данным юзером. Например 'VisitCounter:123:127.0.0.1'=>7.
Впрочем, я пока не думал, как это реализовать, так что лучше не забегать вперед, и добавить параметр, когда он реально понадобится.
Хорошо, пока удалил его. Кстати, заметил что гитхаб съедает слова, начинающиеся со значка $ в комментариях. Вот я сделал коммит с комментарием "Remove $value parameter from considerVisit method". Слово $value пропало, наверное это связано с особенностями баша, там же тоже переменные начинаются с доллара?
Ну в общем вторая попытка. Давай я на словах опишу что я пытался натворить.
В глобальном коде теперь необходимо перед созданием собственно объекта счетчика создать и настроить все его зависимости: клиент редиса, адаптер редиса, объект соединения с базой, адаптер базы.
Это должно выглядеть так:
$rediska = new Rediska($options);
$redisAdapter = new VisitCounter\RediskaAdapter($rediska, array('keyPrefix'=>'VisitCounter', 'keyExpire'=>3600x24x30);
$pdo = new PDO($dsn, $user, $pass);
$dbAdapter = new VisitCounter\PdoAdapter($pdo, array('pk'=>'id', 'tblName'=>'ad', 'colName'=>'visitCounter'));
$visitCounter = new VisitCounter\VisitCounter($redisAdapter);
$visitCounter->setDb($dbAdapter); // подключение нужно не всегда, например для обновления счетчика в редисе оно не нужно
Много кода, некрасиво как-то. В этом твоем уроке я видел упоминание о каких-то контейнерах DI и ссылку на доки симфони, но я еще толком не разобрался с самим DI, не хочу запутаться, потом углублюсь в более продвинутые вещи.
Так вот, система классов теперь выглядит так: есть основной класс VisitCounter. У него есть зависимости от базы данных и от клиента редиса. Даем ему объекты-адаптеры для редиса и для базы. У объектов-адаптеров в свою очередь зависимость от нативных объектов (rediska и pdo например).
Все адаптеры должны иметь одинаковый интерфейс (собственно я сделал абстрактный класс). У класса RedisAdapter есть несколько абстрактных методов, которые должен реализовать программист, который хочет расширить этот класс и написать адаптер для конкретного клиента (например PhpRedis).
У класса DbAdapter есть один абстрактный метод save, который нужно реализовать.
Мне конечно тяжело пока охватить все это дело взглядом и учесть все нюансы, но кажется должно работать.
https://github.com/nsdvw/visit-counter/tree/master/src
>keyPrefix ... надо ли это значение передавать через конструктор или же лучше сделать метод-сеттер? В чем разница между этими вариантами?
Разница в том, что через конструктор мы передаем значение один раз, и больше не сможем его поменять, разве что пересоздать объект. А с сеттерами свойство можно изменить в любой момент. Я понял недочет, просто тяжело учитывать нюансы, когда приходится держать в голове большую систему классов, недоглядел.
Про DI читал в теории, но без практических примеров не понимал применения. Теперь (кажется) разобрался.
Переписал код. Добавил адаптер для объекта соединения с базой.
>Почему я чтобы учесть просмотр страницы, должен передавать expire и keyValue?
Expire вынес в конфиг адаптера редиса. keyValue убрал. Насчет значения ключа, то я подумал, что в дальнейшем код можно будет расширить, и он будет учитывать не только уникальные посещения, но и кол-во посещений данным юзером. Например 'VisitCounter:123:127.0.0.1'=>7.
Впрочем, я пока не думал, как это реализовать, так что лучше не забегать вперед, и добавить параметр, когда он реально понадобится.
Хорошо, пока удалил его. Кстати, заметил что гитхаб съедает слова, начинающиеся со значка $ в комментариях. Вот я сделал коммит с комментарием "Remove $value parameter from considerVisit method". Слово $value пропало, наверное это связано с особенностями баша, там же тоже переменные начинаются с доллара?
Ну в общем вторая попытка. Давай я на словах опишу что я пытался натворить.
В глобальном коде теперь необходимо перед созданием собственно объекта счетчика создать и настроить все его зависимости: клиент редиса, адаптер редиса, объект соединения с базой, адаптер базы.
Это должно выглядеть так:
$rediska = new Rediska($options);
$redisAdapter = new VisitCounter\RediskaAdapter($rediska, array('keyPrefix'=>'VisitCounter', 'keyExpire'=>3600x24x30);
$pdo = new PDO($dsn, $user, $pass);
$dbAdapter = new VisitCounter\PdoAdapter($pdo, array('pk'=>'id', 'tblName'=>'ad', 'colName'=>'visitCounter'));
$visitCounter = new VisitCounter\VisitCounter($redisAdapter);
$visitCounter->setDb($dbAdapter); // подключение нужно не всегда, например для обновления счетчика в редисе оно не нужно
Много кода, некрасиво как-то. В этом твоем уроке я видел упоминание о каких-то контейнерах DI и ссылку на доки симфони, но я еще толком не разобрался с самим DI, не хочу запутаться, потом углублюсь в более продвинутые вещи.
Так вот, система классов теперь выглядит так: есть основной класс VisitCounter. У него есть зависимости от базы данных и от клиента редиса. Даем ему объекты-адаптеры для редиса и для базы. У объектов-адаптеров в свою очередь зависимость от нативных объектов (rediska и pdo например).
Все адаптеры должны иметь одинаковый интерфейс (собственно я сделал абстрактный класс). У класса RedisAdapter есть несколько абстрактных методов, которые должен реализовать программист, который хочет расширить этот класс и написать адаптер для конкретного клиента (например PhpRedis).
У класса DbAdapter есть один абстрактный метод save, который нужно реализовать.
Мне конечно тяжело пока охватить все это дело взглядом и учесть все нюансы, но кажется должно работать.
https://github.com/nsdvw/visit-counter/tree/master/src
> Разница в том, что через конструктор мы передаем значение один раз, и больше не сможем его поменять, разве что пересоздать объект. А с сеттерами свойство можно изменить в любой момент. Я понял недочет, просто тяжело учитывать нюансы, когда приходится держать в голове большую систему классов, недоглядел.
Хотя ты и прав, но я имел в виду не это. Вряд ли эту настройку надо будет менять после создания объекта. Более того, в случае ее смены по ходу дела объект скорее всего может работать некорректно.
Я имел в виду что через конструктор передаются обязательные параметры (без которых нельзя создать объект), а через сеттеры - такие, для которых прописаны значения по умолчанию и которые скорее всего не придется менять. к примеру логгер часто передают через сеттер так как без него объект может работать, только логи писаться не будут (это конечно не всегда применимо, иногда мы хотим сделать логгирование обязательным).
Также настройки можно передавать через конструктор со значениями по умолчанию или в виде массива с необязальными ключами. Вариантов много.
Потому ты должен подумать что ты хочешь сделать обязательным а что нет.
И я не говорил кстати что тут надо использовать сеттер. Лишь предложил подумать.
> Вот я сделал коммит с комментарием "Remove $value parameter from considerVisit method". Слово $value пропало, наверное это связано с особенностями баша, там же тоже переменные начинаются с доллара?
Ты уверен что это гитхаб съел а не баш у тебя? Проверить можно добавив echo перед комадой, например
echo git commit ....
И стоит брать сообщение в одиночные кавычки. Тогда переменные там не подставляются.
> $redisAdapter = new VisitCounter\RediskaAdapter($rediska, array('keyPrefix'=>'VisitCounter', 'keyExpire'=>3600x24x30);
Почему префикс и срок хранения - свойства адаптера? Эти настройки применимы только к редиске или к любому драйверу редиса? Какие у тебя аргументы за помещение их в адаптер?
> Много кода, некрасиво как-то.
Нормально. Зато мы получаем универсальную библиотеку. Любой может дописать свои адаптеры.
А дальше пользователь может например поместить этот код в функцию. Или прописать в DI контейнер своего фреймворка. Или унаследовать твой класс и в нем жестко вписать зависимости. Главное, что DI дает нам свободу выбора.
В Сифмони например никто не пишет эти new, а прописывают классы и зависимости в конфиг DI контейнера и создают через
$container->get('visitCounter');
> я еще толком не разобрался с самим DI
DI это когда зависимости передают снаружи: через конструктор, сеттер, и странный способ под названием interface injection. Сами способы описаны например у Фаулера в довольно большой и непростой статье: http://www.martinfowler.com/articles/injection.html#FormsOfDependencyInjection
Я уверен что ты и раньше применял DI может быть не зная об этом. Например передавая объект PDO в класс работы с БД TableGateway.
> Все адаптеры должны иметь одинаковый интерфейс (собственно я сделал абстрактный класс)
Попробуй сравнить использование абстрактного класса и интерфейса для определения требований к адаптеру, какие есть за и против.
> У класса DbAdapter есть один абстрактный метод save, который нужно реализовать.
Можно еще назвать его вроде StorageAdapter чтобы не говорить что нам нужна именно БД. Хотя это будет путать людей наверно.
> if ($this->client->exists($uniqueVisit)) return;
> $this->client->set($uniqueVisit, $keyValue, $this->client->keyExpire);
Это не атомарное действие. Другой процесс может успеть добавить ключ после того как мы сделали exists но до того как мы выполнили set. Можно ли сделать это (проверка + установка) атомарно без сильного усложнения кода и больших затрат времени?
> foreach (array_count_values($data) as $key => $value) {
Нужно переименовать все 3 переменных. $data должно называться напимер visitsByObjectId или objectIds в зависимости от того что оно там хранит.
В конструктор надо добавить минимальную проверку значений (1 if + throw достаточно).
Также в адаптере редиса аж 6 (!) методов. Нельзя ли уменьшить их число без усложнения кода и затрат времени? Может абстрагироваться как-то. Может конечно и не стоит.
> Разница в том, что через конструктор мы передаем значение один раз, и больше не сможем его поменять, разве что пересоздать объект. А с сеттерами свойство можно изменить в любой момент. Я понял недочет, просто тяжело учитывать нюансы, когда приходится держать в голове большую систему классов, недоглядел.
Хотя ты и прав, но я имел в виду не это. Вряд ли эту настройку надо будет менять после создания объекта. Более того, в случае ее смены по ходу дела объект скорее всего может работать некорректно.
Я имел в виду что через конструктор передаются обязательные параметры (без которых нельзя создать объект), а через сеттеры - такие, для которых прописаны значения по умолчанию и которые скорее всего не придется менять. к примеру логгер часто передают через сеттер так как без него объект может работать, только логи писаться не будут (это конечно не всегда применимо, иногда мы хотим сделать логгирование обязательным).
Также настройки можно передавать через конструктор со значениями по умолчанию или в виде массива с необязальными ключами. Вариантов много.
Потому ты должен подумать что ты хочешь сделать обязательным а что нет.
И я не говорил кстати что тут надо использовать сеттер. Лишь предложил подумать.
> Вот я сделал коммит с комментарием "Remove $value parameter from considerVisit method". Слово $value пропало, наверное это связано с особенностями баша, там же тоже переменные начинаются с доллара?
Ты уверен что это гитхаб съел а не баш у тебя? Проверить можно добавив echo перед комадой, например
echo git commit ....
И стоит брать сообщение в одиночные кавычки. Тогда переменные там не подставляются.
> $redisAdapter = new VisitCounter\RediskaAdapter($rediska, array('keyPrefix'=>'VisitCounter', 'keyExpire'=>3600x24x30);
Почему префикс и срок хранения - свойства адаптера? Эти настройки применимы только к редиске или к любому драйверу редиса? Какие у тебя аргументы за помещение их в адаптер?
> Много кода, некрасиво как-то.
Нормально. Зато мы получаем универсальную библиотеку. Любой может дописать свои адаптеры.
А дальше пользователь может например поместить этот код в функцию. Или прописать в DI контейнер своего фреймворка. Или унаследовать твой класс и в нем жестко вписать зависимости. Главное, что DI дает нам свободу выбора.
В Сифмони например никто не пишет эти new, а прописывают классы и зависимости в конфиг DI контейнера и создают через
$container->get('visitCounter');
> я еще толком не разобрался с самим DI
DI это когда зависимости передают снаружи: через конструктор, сеттер, и странный способ под названием interface injection. Сами способы описаны например у Фаулера в довольно большой и непростой статье: http://www.martinfowler.com/articles/injection.html#FormsOfDependencyInjection
Я уверен что ты и раньше применял DI может быть не зная об этом. Например передавая объект PDO в класс работы с БД TableGateway.
> Все адаптеры должны иметь одинаковый интерфейс (собственно я сделал абстрактный класс)
Попробуй сравнить использование абстрактного класса и интерфейса для определения требований к адаптеру, какие есть за и против.
> У класса DbAdapter есть один абстрактный метод save, который нужно реализовать.
Можно еще назвать его вроде StorageAdapter чтобы не говорить что нам нужна именно БД. Хотя это будет путать людей наверно.
> if ($this->client->exists($uniqueVisit)) return;
> $this->client->set($uniqueVisit, $keyValue, $this->client->keyExpire);
Это не атомарное действие. Другой процесс может успеть добавить ключ после того как мы сделали exists но до того как мы выполнили set. Можно ли сделать это (проверка + установка) атомарно без сильного усложнения кода и больших затрат времени?
> foreach (array_count_values($data) as $key => $value) {
Нужно переименовать все 3 переменных. $data должно называться напимер visitsByObjectId или objectIds в зависимости от того что оно там хранит.
В конструктор надо добавить минимальную проверку значений (1 if + throw достаточно).
Также в адаптере редиса аж 6 (!) методов. Нельзя ли уменьшить их число без усложнения кода и затрат времени? Может абстрагироваться как-то. Может конечно и не стоит.
>Почему префикс и срок хранения - свойства адаптера? Эти настройки применимы только к редиске или к любому драйверу редиса? Какие у тебя аргументы за помещение их в адаптер?
Настройки применимы к любому драйверу редиса. Аргументы уровня "свойства должны быть, но не знал куда засунуть". Ну а куда? Срок хранения ключей и префикс относятся к редису, значит это свойства класса адаптера редиса. Класс счетчика занимается более глобальными вещами, его дело манипулировать адаптерами, а не настраивать их. Я себе представляю аналогию между контроллером и моделями в mvc.
Хотя мне не хватает дальновидности, может ты сможешь привести аргументы, почему так нельзя делать, какие негативные последствия это может повлечь.
>сравнить использование абстрактного класса и интерфейса для определения требований к адаптеру, какие есть за и против
Ну у интерфейса например не может быть свойств и реализованных методов (тех же сеттеров).
Я мог конечно вынести абстрактные методы в интерфейс, а затем имплементировать его абстрактным классом. Но зачем?
Какие есть "против" пока не представляю. Поэтому сделал один абстрактный класс.
>Также в адаптере редиса аж 6 (!) методов
А что, это много? Для любой команды редиса придется писать обертку, потому что разработчики клиентов оригинальничают, и называют методы своих классов как захотят. Лучше всего сделано в predis, там метод всегда пишется точно так, как в самом редисе.
Мало того, у редиски еще и создается отдельный объект для ключа.
Например, чтобы взять диапазон из списка (LRANGE) нужно прописать
$key = new Rediska_Key_List('mylist');
$res = $key->getValues([0], [-1]);
Тогда как у Predis это предсказуемая команда $client->lrange($list, $start, $end);
Что здесь можно сделать? Я вижу выход только в написании метода-обертки для каждой команды.
Понадобится не 6, а 60, придется писать шестьдесят.
>Можно ли сделать это (проверка + установка) атомарно без сильного усложнения кода и больших затрат времени?
Первыми в голову приходят транзакции.
А нет, вспомнил, что у команды set есть какая-то опция для проверки существования ключа.
NX -- Only set the key if it does not already exist
Нужно только посмотреть, как это реализовано в клиентах.
Вот не вижу, как это реализовать в редиске, например. Апи:
setValue (line 24)
Set key value
access: public
boolean setValue ($value $value)
$value $value
Что делать, добавлять транзакцию?
>Почему префикс и срок хранения - свойства адаптера? Эти настройки применимы только к редиске или к любому драйверу редиса? Какие у тебя аргументы за помещение их в адаптер?
Настройки применимы к любому драйверу редиса. Аргументы уровня "свойства должны быть, но не знал куда засунуть". Ну а куда? Срок хранения ключей и префикс относятся к редису, значит это свойства класса адаптера редиса. Класс счетчика занимается более глобальными вещами, его дело манипулировать адаптерами, а не настраивать их. Я себе представляю аналогию между контроллером и моделями в mvc.
Хотя мне не хватает дальновидности, может ты сможешь привести аргументы, почему так нельзя делать, какие негативные последствия это может повлечь.
>сравнить использование абстрактного класса и интерфейса для определения требований к адаптеру, какие есть за и против
Ну у интерфейса например не может быть свойств и реализованных методов (тех же сеттеров).
Я мог конечно вынести абстрактные методы в интерфейс, а затем имплементировать его абстрактным классом. Но зачем?
Какие есть "против" пока не представляю. Поэтому сделал один абстрактный класс.
>Также в адаптере редиса аж 6 (!) методов
А что, это много? Для любой команды редиса придется писать обертку, потому что разработчики клиентов оригинальничают, и называют методы своих классов как захотят. Лучше всего сделано в predis, там метод всегда пишется точно так, как в самом редисе.
Мало того, у редиски еще и создается отдельный объект для ключа.
Например, чтобы взять диапазон из списка (LRANGE) нужно прописать
$key = new Rediska_Key_List('mylist');
$res = $key->getValues([0], [-1]);
Тогда как у Predis это предсказуемая команда $client->lrange($list, $start, $end);
Что здесь можно сделать? Я вижу выход только в написании метода-обертки для каждой команды.
Понадобится не 6, а 60, придется писать шестьдесят.
>Можно ли сделать это (проверка + установка) атомарно без сильного усложнения кода и больших затрат времени?
Первыми в голову приходят транзакции.
А нет, вспомнил, что у команды set есть какая-то опция для проверки существования ключа.
NX -- Only set the key if it does not already exist
Нужно только посмотреть, как это реализовано в клиентах.
Вот не вижу, как это реализовать в редиске, например. Апи:
setValue (line 24)
Set key value
access: public
boolean setValue ($value $value)
$value $value
Что делать, добавлять транзакцию?
Вот контроллер читает пост/гет массив и что-то делает. Я не люблю кучи if/else, можно ли это завернуть в няшный switch/case ? У меня не получилось.
Тапками прошу не кидать, я только учусь
Там раньше была отдельная команда http://redis.io/commands/setnx
Делаем поиск по SETNX: https://github.com/Shumkov/Rediska/search?utf8=%E2%9C%93&q=setnx
Что-то есть.
Ну и даже если готового метода нет наверняка можно вызвать команду явно.
Транзакцией по моему SETNX реализовать невозможно. Или как-то с помощью WATCH? Тогда наверно можно.
При работе с редисом обрати еще внимание на обработку ошибок. Любая команда может вернуть ошибку. Некоторые драйверы выбрасывают исключение, некоторые просто сохраняют ошибку внутри и продолжают работать как ни в чем не бывало. Значит, адаптер должен брать проверку ошибок на себя.
Остальное проверю позже.
Можно. Нужно вынести содержимое ифов в отдельные методы.
Алсо я не понимаю, почему ты данные из 5 форм шлешь на один и тот же адрес. Эти формы все на одной странице? Если это разные 5 страниц то у них должно быть 5 контроллеров, а не все вместе.
>>574808
Увы, это плохой совет. Там проблема в том что он пишет все одной длинной простыней и надо это исправлять разбивая на функции. И что он обрабатывает данные от 5 форм в одном экшене.
Свитч тут скорее запутывает, 5 if/else будут смотреться компактнее по моему.
codebin.org/view/d9286a90
А зачем тебе 4 переменных, хранящих сортировку? Используй одну переменную $order.
Формирование ссылок удобно вынести в класс например под названием UrlHelper. Метод может иметь вид
public function getUserTableUrl($order = null, $orderDirection= 1, $page = 1)
{
...
}
Допустим тебе надо дать ссылку на страницу где выводится отсортированный по email список пользователей, 3-я страница. Легко и просто:
$url = $urlHelper->getUserTableUrl('email', 1, 3);
> if((count($_GET)==0)||(count($_GET)==1&&isset($_GET['order'])))
Ты тут чего-то нагородил лишнего.
Ну и для формирования ссылок с параметрами удобно использовать стандартную функцию http_build_query, а не изобретать велосипед. Она заодно сама все как нужно заэкранирует.
> $loginSort = $mailSort = $rateSort = $dateSort = $_SERVER['SCRIPT_NAME']."?";
Зачем так писать, почему не прописать ссылку на скрипт явно?
> foreach ($_GET as $key => $value) {
Непонятно зачем все параметры копировать. А если кто-то допишет &asasas=asas ты это тоже в ссылку скопируешь?
ну лишние параметры просто проигнорируются по идее, но я лучше сейчас вот это перепишу через http_build_query
1) метод для формирования SQL запросов исходя из входного массива стоило разместить непосредственно в model или в model_main? лично я разместил в model, а в model_main оставил только метод отправки содержимого формы на сервер и проверку капчи гугловской.
2) непосредственно controller у меня остался почти пустым, только конструктор без параметров, все экшены я обрабатываю в controller_main
3)нормально ли, что в view_main нет описания класса-наследника от View а только смесь php кода и разметки, формирующая вывод. и обращающаяся к классу View для формирования разметки выводимых постов и индекса для навигации?
собственно, что можете сказать об этом: что мне стоит поправить и как?
Я еще не знаю, как правильно. Пытаюсь познать pure шаблонизацию.
У меня там и кнопки и ссылки обрабатываются и в зависимости от нажатого выполняются действия и выбирается шаблон страницы. Типа точка входа. В конце все это собирается воедино.
include 'head.html.php'; //подгружаем основной шаблон
include $page; //подгружаем тело страницы
include '../views/footer.inc.html.php'; //подгружаем футер
>>574808
Оригинально, но не няшно.
Пара функций serialize()/unserialize() преобразует любые PHP-значения (включая массивы и объекты) в строку и обратно.
unserialize - опасная функция. Вы никогда не должны делать unserialize с данными, которые приходят от пользователя, так как он в итоге может в некоторых случаях получить возможность выполнить произвольный код. В статье описан способ, который в данном случае нашли взломщики, он довольно оригинальный и интересный.
Я давно знаю про эту уязвимость и когда обнаруживаю такой код, переделываю его. Например, можно использовать json_decode/encode - они безопасны и не содержат уязвимости.
Кстати, я помню, пару лет назад похожую проблему нашли в фреймворке ruby on rails - там тоже пользователь мог создать произвольный объект на сервере.
>>574840
Тебе не нужно 4 ссылки и 4 переменных. Тебе достаточно иметь функцию которая их сгенерирует когда понадобится.
>>574860
Большинство тут:
https://github.com/codedokode/pasta
https://gist.github.com/codedokode
Ну и еще одна небольшая паста про то что стоит подучить перед собеседованием:
-------------
Я тут не буду писать очевидные вещи что ты должен знать что такое циклы, функции, классы, и уметь написать простую программу на уровне задач из моего учебника или SQL-запрос. Я напишу про вещи которые любят спрашивать сверх этого.
PHP: перечитай мануал, любят спрашивать тонкости языки типа преобразования одного типа в другой или правил сравнения типов (сработает ли if ("foo" == 0) { } или if ("1 person" == true) ? ). По ООП любят вопросы из серии «что выведет эта дебильная программа где мы переопределяем в наследнике приватный метод публичным с тем же именем», опять же спасает чтение мануала.
По теории могут спросить чем отличается абстрактный класс от интерфейса или как переопределить приватный метод в наследнике (правильный ответ: никак).
Если требуют знания фреймворков, могут спросить как реализовать штуку X (например древовидные категории, хотя это продвинутый вопрос) в фрейморке.
MySQL и SQL вообще: любят вопросы по теории баз данных и SQL: транзакции, внешние ключи, нормализация, виды джойнов, индексы (это у более продвинутых). Могут попросить сделать хитрый SQL запрос типа найти в таблице записи которые встречаются больше N раз или найти записи которые есть в одной таблице и нет в другой.
JS: тонкости вроде преобразования типов, чему равно [] + {} + "hello", вопросы по прототипам, вопросы по замыканиям, вопросы по передаче значений по ссылке (объекты всегда передаются по ссылке, примитивы копируются). Некоторые подвохи освещены в моих задачах по JS, некоторые в learn.javascript.ru
HTML/CSS: тут подвохи вряд ли будут, разве что могут спросить как реализовать ту или иную вещь (например вертикальное выравнивание или прибитый к низу футер) и обычно там много вариантов.
Пара функций serialize()/unserialize() преобразует любые PHP-значения (включая массивы и объекты) в строку и обратно.
unserialize - опасная функция. Вы никогда не должны делать unserialize с данными, которые приходят от пользователя, так как он в итоге может в некоторых случаях получить возможность выполнить произвольный код. В статье описан способ, который в данном случае нашли взломщики, он довольно оригинальный и интересный.
Я давно знаю про эту уязвимость и когда обнаруживаю такой код, переделываю его. Например, можно использовать json_decode/encode - они безопасны и не содержат уязвимости.
Кстати, я помню, пару лет назад похожую проблему нашли в фреймворке ruby on rails - там тоже пользователь мог создать произвольный объект на сервере.
>>574840
Тебе не нужно 4 ссылки и 4 переменных. Тебе достаточно иметь функцию которая их сгенерирует когда понадобится.
>>574860
Большинство тут:
https://github.com/codedokode/pasta
https://gist.github.com/codedokode
Ну и еще одна небольшая паста про то что стоит подучить перед собеседованием:
-------------
Я тут не буду писать очевидные вещи что ты должен знать что такое циклы, функции, классы, и уметь написать простую программу на уровне задач из моего учебника или SQL-запрос. Я напишу про вещи которые любят спрашивать сверх этого.
PHP: перечитай мануал, любят спрашивать тонкости языки типа преобразования одного типа в другой или правил сравнения типов (сработает ли if ("foo" == 0) { } или if ("1 person" == true) ? ). По ООП любят вопросы из серии «что выведет эта дебильная программа где мы переопределяем в наследнике приватный метод публичным с тем же именем», опять же спасает чтение мануала.
По теории могут спросить чем отличается абстрактный класс от интерфейса или как переопределить приватный метод в наследнике (правильный ответ: никак).
Если требуют знания фреймворков, могут спросить как реализовать штуку X (например древовидные категории, хотя это продвинутый вопрос) в фрейморке.
MySQL и SQL вообще: любят вопросы по теории баз данных и SQL: транзакции, внешние ключи, нормализация, виды джойнов, индексы (это у более продвинутых). Могут попросить сделать хитрый SQL запрос типа найти в таблице записи которые встречаются больше N раз или найти записи которые есть в одной таблице и нет в другой.
JS: тонкости вроде преобразования типов, чему равно [] + {} + "hello", вопросы по прототипам, вопросы по замыканиям, вопросы по передаче значений по ссылке (объекты всегда передаются по ссылке, примитивы копируются). Некоторые подвохи освещены в моих задачах по JS, некоторые в learn.javascript.ru
HTML/CSS: тут подвохи вряд ли будут, разве что могут спросить как реализовать ту или иную вещь (например вертикальное выравнивание или прибитый к низу футер) и обычно там много вариантов.
> не менее я помимо корневых классов model, view, controller
Имена классов пишут с большой буквы, также не уверен что тебе так уж нужен View.
> создал отдельные model_main, controller_main, view_main
Вот это говорит о том что ты не понял MVC. Что такое model_main? Модель обычно в простых приложеиях делается из расчета 1 модель на 1 таблицу БД. У тебя таблица main называется?
Контроллеры делаются из расчета 1 контроллер на 1 раздел сайта.
Зачем ты сделал и что лежит в view_main я не понял.
Думаю, ты не очень понял MVC.
> метод для формирования SQL запросов исходя из входного массива
Почитай урок про паттерны работы с БД, в простом приложении подойдет TableGateway: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
> непосредственно controller у меня остался почти пустым,
Может он не особо и нужен?
> нормально ли, что в view_main нет описания класса-наследника от View а только смесь php кода и разметки,
Нет. В Классах не может быть HTML-разметки, она должна быть в шаблонах.
> что мне стоит поправить и как?
У нас есть задача на список студентов с подробными комментариями: https://github.com/codedokode/pasta/blob/master/student-list.md
Почитай их и скорее всего придется переделать все.
> не менее я помимо корневых классов model, view, controller
Имена классов пишут с большой буквы, также не уверен что тебе так уж нужен View.
> создал отдельные model_main, controller_main, view_main
Вот это говорит о том что ты не понял MVC. Что такое model_main? Модель обычно в простых приложеиях делается из расчета 1 модель на 1 таблицу БД. У тебя таблица main называется?
Контроллеры делаются из расчета 1 контроллер на 1 раздел сайта.
Зачем ты сделал и что лежит в view_main я не понял.
Думаю, ты не очень понял MVC.
> метод для формирования SQL запросов исходя из входного массива
Почитай урок про паттерны работы с БД, в простом приложении подойдет TableGateway: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
> непосредственно controller у меня остался почти пустым,
Может он не особо и нужен?
> нормально ли, что в view_main нет описания класса-наследника от View а только смесь php кода и разметки,
Нет. В Классах не может быть HTML-разметки, она должна быть в шаблонах.
> что мне стоит поправить и как?
У нас есть задача на список студентов с подробными комментариями: https://github.com/codedokode/pasta/blob/master/student-list.md
Почитай их и скорее всего придется переделать все.
> Я еще не знаю, как правильно.
для каждой страницы свой экшен или контроллер. Если у тебя 5 форм то делаешь 5 экшенов или контроллеров.
Ну и почитай советы к задаче про студентов - многие из них применимы к твоему приложению: https://github.com/codedokode/pasta/blob/master/student-list.md
Потому что это новая для меня штуковина с интересными возможностями, которую я еще не пердолил. Что за странные впросы.
>>574451
Не стал дожидаться ответа про отдельные массивы. Сделал. Смотри как красиво выглядят все методы, в которых есть валидация. Их все теперь даже трогать не надо при добавлении новых констант.
Старый вариант, еще не проверенный. https://ideone.com/HYPC12
Новый вариант с массивами. https://ideone.com/GlvJrE
Кто-нибудь может сделать 8 задание?
У меня ломается вёрстка когда примечание длиннее основного текста.
Новый вариант неплох, почти все готово. Вот что еще можно улучшить:
> stuffingsArr
Arr это плохое слово, так как во-первых, такого слова нет (есть array и незачем экономить 2 буквы ценой читабельности), во-вторых, оно не добавляет смысла. Лучше назвать stuffings или stuffingTypes
> this.pricesAndCalorics[Hamburger.SIZE_BIG] = ....
> this.pricesAndCalorics[Hamburger.SIZE_SMALL] = ...
Зачем копипастить this.pricesAndCalorics в каждой строчке? Сохрани это в переменную с простым именем, например p, и добавляй элементы в нее.
Более того, я подумал, цены одинаковы для всех гамбургеров, значит их можно сделать статическими полями, то есть не свойствами this, а свойствами Hanburger.
Вынеси-ка создание этой таблицы цен из конструктора, а то он громоздкий какой-то.
> Hamburger.prototype.calculateCalories = function () {
Тут выравнивание почему-то лестницей сделано.
А что ты имеешь в виду под «ломается»? Абзац уезжает вниз? Против этого есть только одно средство: поместить несколько примечаний внутрь одного aside.
Так что с этим бороться не требуется.
Лучше заливать код на сервис вроде jsfiddle, jsbin чтобы можно было сразу просмотреть результат.
> box-sizing: content-box;
Разве не такое стоит по умолчанию?
И вот это по моему плохая штука:
> {
> box-sizing: inherit;
как минимум это приведет к масштабированию картинок (img) если у них есть паддинг или бордер и задан размер. Также, некоторые элементы будут вести себя не как ожидается, это сделает код менее понятным. Если тебе так нужен box-sizing, то ставь его только на конкретные элементы где он нужен и ставь вендорные префиксы для более старых браузеров.
Ну и вообще стили со звездочкой плохая штука.
Ну и все наши задачи решаются и без box-sizing.
Поля надо делать через паддинг на родителе, в примечаниях к задаче это написано по моему.
Переделал.
https://ideone.com/UHnGCj
>Более того, я подумал, цены одинаковы для всех гамбургеров, значит их можно сделать статическими полями, то есть не свойствами this, а свойствами Hanburger.
Не осилил. Сделал через this. и метод.
>Тут выравнивание почему-то лестницей сделано.
А каким стандартом вообще пользоваться, а то я все наугад делаю.
Скажет потом на собеседовании: вот, смотрите, я 2д игру на 7 клеточек пол-года делал.
>А что ты имеешь в виду под «ломается»?
Каждое примечание должно быть на одном уровне со своим абзацем.
https://jsfiddle.net/vhbs77hm/
И спасибо за ответы и советы.
По моему такой вариант даже лучше, да, примечание находится чуть ниже абзаца, но зато оно не наезжает ни на какие другие примечания.
Можно сделать такое решение: при наведении мыши на примечание абзац, к которому оно относится, подсвечивается мягким оранжевым цветом или оранжевой полоской слева. Также, добавить небольшой отступ под примечанием, чтобы 2 идущих друг под другом примечания были визуально отделены.
Также, у тебя плохо сделаны поля: https://jsfiddle.net/vhbs77hm/1/
Если кроме <p> добавить другие теги, то они уезжают влево.
>Большинство тут:
Нет, я про другие. Вот недавно была паста про интерфейсы, еще раньше - про архитектуру веб-серверов. Есть еще что-то подобное?
Правильно, твой гитхаб все за тебя расскажет. Я то сам комфортный слоупок, но все это наводит меня на мысль, что если проект на одном только языке и фреймворке пишется по пол-года, то как вообще возможно писать, что ты знаешь овер дохулион ЯП - разве что по синтаксису проскочился. А еще наводит на мысль, что создание чего-то - это ммаксимум командное мероприятие, ибо в одиночку потребует дохуя обучения и времени.
Вот тут непонятно, если мы добавляем переменную токен в скрытое поле формы на сайте, то откуда мы эту переменную берем? Если мы берем ее из куки юзера, то ведь она будет соответствовать куке, ведь отправляет юзер хоть и с другого сайта, я что-то ничего не понял.
Когда выполнение текущего хода программы далее невозможно и тебе надо выйти на уровень выше.
До этого времени кодировал странички в хтмле + писал всякую мишуру на JS/JQuery, теперь расширю компетенцию на бекенд.
Если у юзера нет куки (он впервые к нам зашел) то генерируем длинный случайный код, ставим в куку и вписываем его же форму.
Если у пользователя есть кука то берем код из нее и ставим в форму.
При проверке формы сравниваем код пришедший из формы с кодом в куке. Их совпадение подтверждает что форма отправлена с нашего сайта, а не с постороннего.
У каждого пользователя своя кука и свой код. Посторонний сайт не может увидеть код из куки и потому не может прислать в форме правильный код.
> Если мы берем ее из куки юзера, то ведь она будет соответствовать куке, ведь отправляет юзер хоть и с другого сайта, я что-то ничего не понял.
Кука отправляется только на тот сайт, который ее выставил. Другой сайт не может видеть куку и не может взять код из нее и подставить в форму.
Если у нас есть сайт good.example.com который выставил куку с кодом, то сайт bad.example.com эту куку не видит. Потому он не может подставить код в форму. Если пользователь отправляет форму с сайта bad.example.com на good.example.com то код в форме не будет соответствовать куке, так как bad не может узнать какой код хранится у пользователя в куке.
Если что-то непонятно, уточняй.
Имей в виду, что эта защита защиащет только от одной ситуации: от скрытой отправки формы н наш сайт посторонней страницей от имени пользователя.
Урок https://gist.github.com/codedokode/65d43ca5ac95c762bc1a
>>575313
Эта папка задается в настройках слима, по моему viewPath или templatesPath, посмотри документацию.
А как быть уверенным, что 2 юзера не получат одинаковой куки, если они генерируются случайным образом?
мимошел
Все там написано.
http://docs.slimframework.com/configuration/overview/
$app->config(array(
'debug' => true,
'templates.path' => '../templates'
));
>>575331
Здесь тред полезной информации, а вы засоряете его своими тупыми постами и мешаете людям заниматься. Так что быдло (вернее тупой школьник) это ты.
Но я не школьник, а джуниор PHP девелопер.
Ну написал человек один раз про юникодные символы в макабе. Он же не еду тут обсуждает, ил биопроблеммы, зачем так агрится сразу?
Но ведь злоумышленник отправляет запрос посредством залогиненного юзера.Что если он добавит в форму поле с кукой и какой-нибудь html cookies request. Ведь отправляет он запрос через залогенного юзера, куки которого позволяют отправить запрос.
http://php.net/manual/ru/function.setcookie.php
Так что проставить куку в поле формы на чужом сайте не получится.
Если последовательность длинная, например 16 цифр 0-9 и букв a-z то мы имеем 36 в 16 степени вариантов (гугл говорит что это 36^16 = 7.95866111 × 10^24 то есть почти 8 триллионов триллионов, так как 1 триллион = 10^12).
Это значительно больше чем число людей на земле, так что вероятность мала. И даже если 2 человека получат одну и ту же куку, что сломается?
>>575340
код должен быть понятным. Если это не так, придется его переделать.
>>575343
А ты понимаешь механизм работы кук в принципе?
Когда браузер делает запрос на получение страницы или отправку формы, то он отправляет заголовок Cookie c куками, которые принадлежат этому сайту.
Таким образом когда пользователь заходит на bad.example.com и загружает страницу с формой, то кука принадлежащая good.example.com не отправляется. Сервер bad ее не видит. И загруженная с него страница с формой тоже не может никак получить эту куку.
А вот когда заполненная форма отправляется с bad на good.example.com то браузер прикладывает к запросу куку. И сервер на good.example.com видит эту куку и видит что она не совпадает с кодом из формы.
> Что если он добавит в форму поле с кукой
Поле он добавить может, а вот какой код у этого юзера, он не знает, так как код хранится в куке доступной только для good.example.com.
> и какой-нибудь html cookies request.
Такого функционала в браузерах нету.
Вообще, в браузере идет изоляция именно на уровне отдельных доменов. Если где-то это бы нарушалось, то многие сайты могли бы быть взломаны.
То есть сайт A может подключать картинки/скрипты/стили и даже целые страницы (в ифрейме) с сайта B, может отправлять формы на сайт B, но не может никаким образом: видеть куки сайта B, видеть ответ на запрос отправленный на сайт B, видеть содержимое картинки/скрипта/стилей/страницы с сайта B, залезть внутрь принадлежащего сайту B ифрейма.
Если бы изоляции не было, то любой сайт можно было бы взломать за секунду: скрипты на странице сайта A могли бы загрузить сайт B в ифрейм и делать в нем любые действия, жать любые кнопки, читать все что там написано.
Эта изоляция называется same-origin policy. Можешь погуглить.
В идеале конечно было бы лучше всего вообще запретить сайту A что-то подключать или отправлять на сайт B. Тогда все эти токены были бы не нужны и система была бы изначально безопасной. Но исторически в первых версиях HTML не было кук, авторизации, а возможность подключить картинку с другого сайта или огтправить форму казалась удобной.
По идее конечно браузеры должны бы сами предотвращать подобную отправку форм. Например, перед отправкой формы спрашивать у сайта B, желает ли он принять форму с сайта A или нет? Но из соображений совместимости с древними сайтами это сделать пока не получится.
Если последовательность длинная, например 16 цифр 0-9 и букв a-z то мы имеем 36 в 16 степени вариантов (гугл говорит что это 36^16 = 7.95866111 × 10^24 то есть почти 8 триллионов триллионов, так как 1 триллион = 10^12).
Это значительно больше чем число людей на земле, так что вероятность мала. И даже если 2 человека получат одну и ту же куку, что сломается?
>>575340
код должен быть понятным. Если это не так, придется его переделать.
>>575343
А ты понимаешь механизм работы кук в принципе?
Когда браузер делает запрос на получение страницы или отправку формы, то он отправляет заголовок Cookie c куками, которые принадлежат этому сайту.
Таким образом когда пользователь заходит на bad.example.com и загружает страницу с формой, то кука принадлежащая good.example.com не отправляется. Сервер bad ее не видит. И загруженная с него страница с формой тоже не может никак получить эту куку.
А вот когда заполненная форма отправляется с bad на good.example.com то браузер прикладывает к запросу куку. И сервер на good.example.com видит эту куку и видит что она не совпадает с кодом из формы.
> Что если он добавит в форму поле с кукой
Поле он добавить может, а вот какой код у этого юзера, он не знает, так как код хранится в куке доступной только для good.example.com.
> и какой-нибудь html cookies request.
Такого функционала в браузерах нету.
Вообще, в браузере идет изоляция именно на уровне отдельных доменов. Если где-то это бы нарушалось, то многие сайты могли бы быть взломаны.
То есть сайт A может подключать картинки/скрипты/стили и даже целые страницы (в ифрейме) с сайта B, может отправлять формы на сайт B, но не может никаким образом: видеть куки сайта B, видеть ответ на запрос отправленный на сайт B, видеть содержимое картинки/скрипта/стилей/страницы с сайта B, залезть внутрь принадлежащего сайту B ифрейма.
Если бы изоляции не было, то любой сайт можно было бы взломать за секунду: скрипты на странице сайта A могли бы загрузить сайт B в ифрейм и делать в нем любые действия, жать любые кнопки, читать все что там написано.
Эта изоляция называется same-origin policy. Можешь погуглить.
В идеале конечно было бы лучше всего вообще запретить сайту A что-то подключать или отправлять на сайт B. Тогда все эти токены были бы не нужны и система была бы изначально безопасной. Но исторически в первых версиях HTML не было кук, авторизации, а возможность подключить картинку с другого сайта или огтправить форму казалась удобной.
По идее конечно браузеры должны бы сами предотвращать подобную отправку форм. Например, перед отправкой формы спрашивать у сайта B, желает ли он принять форму с сайта A или нет? Но из соображений совместимости с древними сайтами это сделать пока не получится.
Ты тоже не очень понимаешь как работают куки. Кука выставленная сайтом A отправляется только на сервер A и доступна только скриптам на странице сайта A (опция httpOnly отключает ее доступность для скриптов на странице сайта A и делает куку доступной только серверу A).
Независимо от флага httpOnly ни сайт B ни скрипты на загруженной с него странице не могут видеть куки сайта A.
Зачем тогда этот флаг? Он для защиты от XSS, когда злоумышленнику удалось как-то вставить свой яваскрипт-код на страницу сайта A (например из-за того что сервер выводит пришедшие от пользователя данные как есть, без экранирования). httpOnly в этом случае защитит от кражи куки, но не защитит от других действий, так как скрипт на странице сайта A может отправлять и видеть ответы на любые запросы к сайту A. Значит он может жать любые кнопки и вводить любые данные на сайте A от имени пользователя. А также читать содержимое любой страницы на сайте A. И перехватывать нажатия клавиш на этой странице.
Именно потому ты никогда не должен выводить пароль пользователя - чтобы такой зловредный скрипт его не мог прочесть. А при смене пароля надо требовать старый пароль, даже если пользователь залогинен - так как иначе зловредный скрипт сможет его поменять.
Таким образом httpOnly защищает от кражи кук (и возможности хакеру залогиниться с другого компьютера), но не защищает от XSS в общем.
В том то и дело, что делаю я его по всем правилам, обработку всего всего рассовываю по классам и оставляю самый минимум в контроллерах (создание объектов и вызов методов). Но мне вот лично было бы понятнее, если бы все общей лапшой в контроллерах висело. А так у меня все везде по кусочкам и проекто разрастается до больших размеров.
Нет, хороший код и понятный, читабельный код это по сути синонимы. При этом понятен он должен быть не только тебе, а и другим.
И ты покажи код или кусочек кода, мы посмотрим, а то так трудно сказать. Может ты сделал много классов, но как-то неправильно код по ним разложил и оттого путаница.
> А так у меня все везде по кусочкам и проекто разрастается до больших размеров.
Так это как раз не проблема, если все по кусочкам. Ну представь что мы хотим поправить страницу со списком новостей. Нам достаточно открыть 3 небольших файла:
- контроллер новостей
- модели, которые он вызывает (скорее всего модель новостей)
- шаблон страницы со списком
Остальные файлы нас вообще не интересуют.
При этом если нам надо поправить только верстку, то достаточно открыть только шаблон, а если надо поправить условие выборки данных из базы, то только модель.
А когда все в куче, гораздо неудобнее, надо просматривать кучу не относящегося к делу кода, причем хорошо еще если код работы с новостями собран в одном месте, а если он размазан по разным частям огромного файла?
Не понимаю.
За команду set в редиске отвечает метод setValue, у него только один параметр $value
https://github.com/Shumkov/Rediska/blob/master/library/Rediska/Key.php#L24
Да, есть еще какие-то классы с префиксом "command", но я не понимаю как к ним обратиться.
Вижу нужный мне третий параметр $overwrite = true в методе create класса Rediska_Command_Set
https://github.com/Shumkov/Rediska/blob/39a0d41f61f24800aa0846d556d43f395a158540/library/Rediska/Command/Set.php#L25
Что мне с этим делать? Знаю только $key->setValue, не понимаю что за классы "command" и как с ними обращаться.
Кстати второй параметр $valueOrOverwrite = null тоже непонятно за что отвечает.
> Value or overwrite property for array of values. For default true.
Как понимать "value or overwrite property for array of values"? И почему написано "for default true", если там указано null?
>Ну и даже если готового метода нет наверняка можно вызвать команду явно.
Как?
> не понимаю что за классы "command" и как с ними обращаться.
В ООП-мире с этим все просто. Очевидно тебе надо
1) получить объект этого класса
2) вызвать нужный метод
Чтобы получить объект класса, есть 2 варианта:
1) найти какую-то функцию, которая его создает или возвращает
2) если ее нет, создать самому через new
Также, посмотри внимательно в какой ветке этот класс. Возможно что это в отдельной ветке и в мастере его нету.
>>Ну и даже если готового метода нет наверняка можно вызвать команду явно.
> Как?
Посмотреть в коде как работает обычный setValue (или класс Command по моей ссылке) и как он в итоге превращается в команду SET и сделать так же.
> Кстати второй параметр $valueOrOverwrite = null тоже непонятно за что отвечает.
Это пример неудачного проектирования, когда у метода один аргумент может использоваться для разных вещей. Не делай так.
Если посмотреть внимательно на сигнатуру метода:
create($keyOrData, $valueOrOverwrite = null, $overwrite = true)
и на комментарии то мы видим 2 варианта его использования:
create(key, value, overwrite)
create([key => value], overwrite)
Это конечно пример плохого проектирования, и то что ты запутался, это доказательство что оно плохое. В такой ситуации правильно было сделать 2 отдельных метода. Не делай так, не делай аргументы которые могут иметь разное значение, это запутывает код и создает возможность сделать ошибку.
Еще. Вот это место
> https://github.com/nsdvw/visit-counter/blob/master/src/VisitCounter.php#L56
> protected function getQueueName()
> {
> return "{$this->client->getKeyPrefix()}Queue";
У тебя никаких сомнений не вызывает?
> https://github.com/nsdvw/visit-counter/blob/master/src/VisitCounter.php#L41
> protected function deleteFromQueue($queueName, $count)
Незачем тут передавать queueName наверно. Твой код создает впечатление что тут могут передаваться разные имена очередей, но в реальности имя там всегда одно и значит незачем его делать аргументом функции.
На мой взгляд он наоборот, максимально простой. А можешь привести пару примеров абстракций, и как можно было сделать без них?
Ага, то есть вместо того, чтобы пользоваться апи, я должен сидеть и полдня разбираться в последовательности вызовов в этой библиотеке? Замечательно.
Тебе не кажется, что библиотека на то и библиотека, что у нее должны быть только прокомментированные методы api, чтобы я ее установил и начал пользоваться, а не возился с ее внутренностями?
Попытался создать объект команды
$command = new Rediska_Command_Set(
$rediska,
'Set',
array('keyOrData'=>'test', 'valueOrOverwrite'=>'123', 'overwrite'=>false));
Выдает ошибку
Uncaught exception 'Rediska_Command_Exception' with message 'Argument 'keyOrData' not present for command 'Set'' in /vendor/geometria-lab/rediska/library/Rediska/Command/Abstract.php:388
>>575456
Не вызывает. Если бы вызывало, то наверное не сделал бы ошибку.
Вот, например http://www.slimframework.com/docs/concepts/middleware.html. Может я тупой, но не понятно что еще за middleware.
http://docs.slimframework.com/routing/middleware/ вот тут еще более менее понятно за route middleware поясняется, то есть это какие-то, определенные пользователем заранее, функции, которые передаются в функцию роутинга и вызываются до callable функции. Хотя практический смысл этого от меня ускользает.
А, там оказывается нужен был не ассоциативный массив, а обычный индексированный. Методом тыка выяснили это.
Кстати, чисто логически: я создаю объект класса Rediska_Command_Set, и мне нужно передать вторым аргументом название команды Set. Это значит, что при помощи класса Rediska_Command_Set можно создать не только команду Set, но и любую другую? Сомневаюсь.
Это мы только создали команду, а как ее выполнить? А, нашел метод execute, наверное он отвечает за выполнение.
Еще прописан магический метод __invoke, так что можно не выполнять execute, а обратиться к объекту как к функции $command()
Очень содержательный пост.
Middleware - это что-то вроде функции-обертки. Ты можешь завернуть весь процесс обработки запроса (который происходит при вызове $app->run()) в указанную тобой функцию-обертку.
То есть будет вызвана твоя функция-middleware, ей будет передано 3 аргмуента: request (запрос пришедший от пользователя, который надо обработать), response (объект, в который мы записываем ответ который надо отдать) и $next (в данном случае это функция из Слима которая обрабатывает запрос).
Твоя middleware может сделать что-то до того, как вызвать $next, может сделать что-то после того как вызвана $next, а также может в каких-то слчаях вообще не вызывать $next и обработать запрос сама.
Функций-middleware можно добавить несколько.
Таким образом, получается что-то вроде луковицы, несколько оболочек-middleware, а в серединке функция фреймворка Слим, которая обрабатывает запрос.
Что можно сделать с помощью middleware? Вот несколько примеров:
- обертка, которая заменяет на странице русские буквы на транслит если передан параметр ?translit=on. При этом она запоминает что режим транслита включен, в куках. Для отключения надо открыть страницу с параметром ?translit=off
- обертка, которая закрывает доступ к сайту паролем
- обертка которая сжимает отдаваемые страницы (при этом выставляя правильные HTTP-заголовки так, что браузер сможет их разжать обратно)
- обертка которая вырезает лишние пробелы из HTML кода ради уменьшения веса страницы
- обертка которая шифрует или подписывает отдаваемые пользователю куки, и расшифровывает/проверяет подпись у пришедших от пользователя. Цифровая подпись не позволяет пользователю менять содержимое куки, а шифрование - не позволяет еще и просмотреть его.
- обертка которая пишет в лог все запросы от пользователя, код ответа (успешно/ошибка) и время их обработки фреймворком
- отдавать страницу-заглушку если сайт закрыт на техобслуживание
То есть это нужно когда мы что-то хотим сделать с запросом до обработки, или с ответом после его обработки, не трогая код приложения.
Конечно, это наверно можно сделать и без middleware, как-то прописав в коде сайта. Но в этом случае твой код будет работать только с этим сайтом и будет намертво в него вшит. А middlware - это отдельный компонент, который можно прикрутить к любому сайту на Слиме, выложить на гитхаб чтобы все им пользовались, и тд.
Middleware это не слимовская идея. Аналогичная система есть в других веб-фреймворках на других языках программирования, таких как Питон или Node.JS.
Чтобы проверить что ты все понял, предлагаю сделать 1-2 примеров Middleware, например то что про транслит и то что про защиту паролем. Не забудь использовать праивильные заголовки, коды ответа HTTP, чтобы ни в каких случаях не было багов и все соответствовало протоколу HTTP. Транслитерировать надо не любой контент а только если он имеет тип text/html (HTML страница). Не надо например пытаться что-то менять если код отдает картинку с типом image/png, так как получится битая картинка.
Middleware - это что-то вроде функции-обертки. Ты можешь завернуть весь процесс обработки запроса (который происходит при вызове $app->run()) в указанную тобой функцию-обертку.
То есть будет вызвана твоя функция-middleware, ей будет передано 3 аргмуента: request (запрос пришедший от пользователя, который надо обработать), response (объект, в который мы записываем ответ который надо отдать) и $next (в данном случае это функция из Слима которая обрабатывает запрос).
Твоя middleware может сделать что-то до того, как вызвать $next, может сделать что-то после того как вызвана $next, а также может в каких-то слчаях вообще не вызывать $next и обработать запрос сама.
Функций-middleware можно добавить несколько.
Таким образом, получается что-то вроде луковицы, несколько оболочек-middleware, а в серединке функция фреймворка Слим, которая обрабатывает запрос.
Что можно сделать с помощью middleware? Вот несколько примеров:
- обертка, которая заменяет на странице русские буквы на транслит если передан параметр ?translit=on. При этом она запоминает что режим транслита включен, в куках. Для отключения надо открыть страницу с параметром ?translit=off
- обертка, которая закрывает доступ к сайту паролем
- обертка которая сжимает отдаваемые страницы (при этом выставляя правильные HTTP-заголовки так, что браузер сможет их разжать обратно)
- обертка которая вырезает лишние пробелы из HTML кода ради уменьшения веса страницы
- обертка которая шифрует или подписывает отдаваемые пользователю куки, и расшифровывает/проверяет подпись у пришедших от пользователя. Цифровая подпись не позволяет пользователю менять содержимое куки, а шифрование - не позволяет еще и просмотреть его.
- обертка которая пишет в лог все запросы от пользователя, код ответа (успешно/ошибка) и время их обработки фреймворком
- отдавать страницу-заглушку если сайт закрыт на техобслуживание
То есть это нужно когда мы что-то хотим сделать с запросом до обработки, или с ответом после его обработки, не трогая код приложения.
Конечно, это наверно можно сделать и без middleware, как-то прописав в коде сайта. Но в этом случае твой код будет работать только с этим сайтом и будет намертво в него вшит. А middlware - это отдельный компонент, который можно прикрутить к любому сайту на Слиме, выложить на гитхаб чтобы все им пользовались, и тд.
Middleware это не слимовская идея. Аналогичная система есть в других веб-фреймворках на других языках программирования, таких как Питон или Node.JS.
Чтобы проверить что ты все понял, предлагаю сделать 1-2 примеров Middleware, например то что про транслит и то что про защиту паролем. Не забудь использовать праивильные заголовки, коды ответа HTTP, чтобы ни в каких случаях не было багов и все соответствовало протоколу HTTP. Транслитерировать надо не любой контент а только если он имеет тип text/html (HTML страница). Не надо например пытаться что-то менять если код отдает картинку с типом image/png, так как получится битая картинка.
Еще иногда пишут что middleware можно использовать для защиты от CSRF (у меня есть урок на эту тему, CSRF это отправка заполненной злоумышленником формы с другой страницы на твой сайт от имени пользователя). Я считаю, нет, это плохая идея.
С одной стороны да, middleware может генерировать токен, выставлять куку, проверять соответствие токена и куки при отправке формы.
Но ведь при ошибке проверки CSRF кода мы должны показать пользователю заполненную форму и предложить отправить ее снова. Не взаимодействуюя с приложением, из middleware мы это сделать не можем.
Потому только на middleware реализовать нормально и удобно защиту от CSRF не получится. Нужна поддержка со стороны приложения (middleware должно сообщать приложению результат проверки, но не блокировать запрос теряя введенные пользователем данные).
У Слима есть стороннее middleware для защиты от CSRF https://github.com/slimphp/Slim-Csrf/blob/master/src/Guard.php но обзор кода показвает что оно реализовано плохо:
- используется сессия, хотя она не нужна
- используется хранилище, хотя это лишнее
- нет простой возможности отобразить заполненную форму при ошибке
- токены удаляются при отправке формы, что может привести к багам если открыто несколько форм в разных вкладках
- бредовый алгоритм генерации токена:
> $token = hash("sha512", mt_rand(0, mt_getrandmax()));
Хеширование не добавляет энтропии (то есть не увеличивает число возможных значений токена). Оно тут абсолютно бессмысленно.
- нет защиты GET-запросов
Не знаю, попробуй посмотреть куда данные формы отправляются и что там с ними делается, в коде.
>>571442
Не очень понял, что такое «кнопка с переходом по ссылке»? Кнопка это штука которая запускает яваскрипт или отправляет форму. Ссылка это штука которая ведет на другую страницу.
>>571466
Не знаю, ты можешь попробовать почитать статьи для чайников «что такое MVC», но их авторы сами часто делают ошибки и путают людей. У меня есть краткое описание MVC в уроке:
https://github.com/codedokode/pasta/blob/master/student-list.md#mvc
> Да, планировал студентов после кошек-мышек. Если я правильно понял, то для решения нужно дополнительно к текущему багажу изучить верстку и sql?
Да. Но во-первых, это можно изучать параллельно с решением задачи, во-вторых, в ОП-посте есть задания (и ссылки на туториал по моему) по обоим этим технологиям. Ну ты сам наверно уже понял, что если ты пройдешь все эти задания, то нужные знания точно получишь.
> Похожа то она похожа, но стоит ли городить ради пары-тройки общих строчек целую функцию?
Ради пары-тройки не стоит.
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L51
> if ( .... $x > $this->getWidth()
Тут не >= должно быть?
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L61
> if ($critter instanceof Cat || $critter instanceof Mouse) {
Непонятен смысл строчки. Почему другие виды животных тут игнорируются? Вроде функция называется «получить содержимое клетки» и про кошек или мышек в ее названии ничего нету.
> TURNSBEFORESLEEP
TURNS_BEFORE_SLEEP
> https://github.com/never3ver/catsandmice/blob/master/classes/Cat.php#L8
> public $sleepCounter = 0;
Зачем свойство публичное? Лучше всегда ставить как можно более ограниченный тип доступа.
> https://github.com/never3ver/catsandmice/blob/master/classes/Cat.php#L46
> foreach ($this->world->getCritters() as $animal) {
Этот цикл надо заменить на вызов какого-нибудь метода из World, например getCellContent
> https://github.com/never3ver/catsandmice/blob/master/classes/Mouse.php#L85
> $distanceToCat = max($distances);
Это ошибка. Ты берешь расстояние до самой дальней кошки, но ведь опаснее всего та, которая ближе. Надо смотреть как близко к клетке самая ближняя кошка.
> $distanceToCat * 2 + $pathsQuantity;
Уверен что 2 - это достаточный вес?
И еще я заметил такую штуку. Допустим мы имеем ситуацию:
..
M.
.C
То есть кошка по диагонали от мышки. Мышка делает ход от кошки:
M.
..
.C
У кошки есть 2 варианта: встать под мышкой или по диагонали. У тебя она встает по диагонали, но выгоднее встать рядом. Думаю, надо кошке добавить в оценочную функцию фактор с маленьким весом, который при прочих равных будет выбирать ход на соседнюю с мышкой клеточку. Или можно подправить функцию оценки расстояния, чтобы нахождение на одной линии с мышкой было выгоднее чем нахождение по диагонали.
Так в общем, код неплох, меня устраивает, разве что комментариев как-то маловато.
> Да, планировал студентов после кошек-мышек. Если я правильно понял, то для решения нужно дополнительно к текущему багажу изучить верстку и sql?
Да. Но во-первых, это можно изучать параллельно с решением задачи, во-вторых, в ОП-посте есть задания (и ссылки на туториал по моему) по обоим этим технологиям. Ну ты сам наверно уже понял, что если ты пройдешь все эти задания, то нужные знания точно получишь.
> Похожа то она похожа, но стоит ли городить ради пары-тройки общих строчек целую функцию?
Ради пары-тройки не стоит.
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L51
> if ( .... $x > $this->getWidth()
Тут не >= должно быть?
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L61
> if ($critter instanceof Cat || $critter instanceof Mouse) {
Непонятен смысл строчки. Почему другие виды животных тут игнорируются? Вроде функция называется «получить содержимое клетки» и про кошек или мышек в ее названии ничего нету.
> TURNSBEFORESLEEP
TURNS_BEFORE_SLEEP
> https://github.com/never3ver/catsandmice/blob/master/classes/Cat.php#L8
> public $sleepCounter = 0;
Зачем свойство публичное? Лучше всегда ставить как можно более ограниченный тип доступа.
> https://github.com/never3ver/catsandmice/blob/master/classes/Cat.php#L46
> foreach ($this->world->getCritters() as $animal) {
Этот цикл надо заменить на вызов какого-нибудь метода из World, например getCellContent
> https://github.com/never3ver/catsandmice/blob/master/classes/Mouse.php#L85
> $distanceToCat = max($distances);
Это ошибка. Ты берешь расстояние до самой дальней кошки, но ведь опаснее всего та, которая ближе. Надо смотреть как близко к клетке самая ближняя кошка.
> $distanceToCat * 2 + $pathsQuantity;
Уверен что 2 - это достаточный вес?
И еще я заметил такую штуку. Допустим мы имеем ситуацию:
..
M.
.C
То есть кошка по диагонали от мышки. Мышка делает ход от кошки:
M.
..
.C
У кошки есть 2 варианта: встать под мышкой или по диагонали. У тебя она встает по диагонали, но выгоднее встать рядом. Думаю, надо кошке добавить в оценочную функцию фактор с маленьким весом, который при прочих равных будет выбирать ход на соседнюю с мышкой клеточку. Или можно подправить функцию оценки расстояния, чтобы нахождение на одной линии с мышкой было выгоднее чем нахождение по диагонали.
Так в общем, код неплох, меня устраивает, разве что комментариев как-то маловато.
Есть у кого манга про базы данных переведенная на русский? С английским у меня пока не очень. Не прям, чтобы вообще пиздец. pre intermediate
По поводу редиса, правильный ответ такой:
- нам нужен AOF лог чтобы как можно чаще (например раз в секунду) надежно сбрасывать в него накопившиеся изменения
- однако, если использовать только AOF то он будет расти до бесконечности (пока не закончится место или пока не сработает AOF rewrite), потому изредка, например раз в полчаса-час мы делам RDB-дамп, который позволяет нам начать AOF лог заново с момента сохранения RDB
- настраиваем maxmemory-policy чтобы данные никогда не выкидывались при нехватке памяти
- при ошибке записи на диск редис должен отказываться принимать изменения, чтобы приложение упало и сразу была видна ошибка
> Все это чудесно в теории, но где искать настройки?
Вдумчиво читаем https://raw.githubusercontent.com/antirez/redis/2.8/redis.conf
> Мне это все читать ради 2-3 строчек, которые мне реально понадобятся?
Да. Тебе же важно знать какие еще есть настройки.
>>By default Redis does not run as a daemon.
> А у меня почему-то запускается автоматически (ставил апт-гетом).
Это значит в твоем дистрибутиве его прикрутили в автозагрузку (что обычно и требуется).
>>daemonize no
> У меня прописано yes.
Это разработчики твоего дистрибутива сделали, логично.
> Как-то можно войти в систему минуя графический интерфейс, как на настоящий сервер, чтобы оно мне не засирало память?
С этим мы вроде разбирались, в новых системах переключение делается таргетом в systemd, а чтобы оно грузилось без GUI, должна быть какая-то опция ядра, которую мы прописываем в загрузчик и которую прочтет код инициализации системы.
Раньше там были bash-скрипты в /etc/rc.d/ и так называемые runlevel, а сейчас используют systemd. В нем каждый сервис описывается как «таргет», и можно указывать зависимости таргетов друг от друга. Например, приложению для работы может быть необходим запущенный mysql, apache и еще что-нибудь. А Апачу необходима работающая сетевая и дисковая подсистемы.
Соответственно конечные цели называются «graphical.target» (запуск GUI интерфейса логина в систему) и «multi-user.target» (запуск текстового интерфейса логина). После загрузки ты можешь переключиться на одну из этих целей, чтобы запустить нужные им приложения и сервисы. Также, ты можешь в опциях командной строки ядра прописать какой таргет должен запускаться после загрузки системы.
Процесс загрузки с systemd описан например тут: http://manpages.ubuntu.com/manpages/utopic/man7/bootup.7.html
Там упомянута нужная опция: systemd.unit=... Значит, ты можешь добавить в загрузчик grub новый пункт «загрузиться в CLI» с этой опцией, либо добавить эту опцию в пункт по умолчанию. Для этого надо отредактировать grub.conf и затем вызвать команду, которая сгенерирует новый загрузчик. Информацию, как это сделать думаю можно найти в сети.
Во время работы системы можно переключиться в консоль через Ctrl + Alt + F1..6 и в GUI через Alt + F7 по моему.
Кстати systemd можно использовать и для добавления в автозапуск каких-то твоих скриптов. А еще как супервизор, который будет автоматически их перезапускать при ошибках.
По поводу редиса, правильный ответ такой:
- нам нужен AOF лог чтобы как можно чаще (например раз в секунду) надежно сбрасывать в него накопившиеся изменения
- однако, если использовать только AOF то он будет расти до бесконечности (пока не закончится место или пока не сработает AOF rewrite), потому изредка, например раз в полчаса-час мы делам RDB-дамп, который позволяет нам начать AOF лог заново с момента сохранения RDB
- настраиваем maxmemory-policy чтобы данные никогда не выкидывались при нехватке памяти
- при ошибке записи на диск редис должен отказываться принимать изменения, чтобы приложение упало и сразу была видна ошибка
> Все это чудесно в теории, но где искать настройки?
Вдумчиво читаем https://raw.githubusercontent.com/antirez/redis/2.8/redis.conf
> Мне это все читать ради 2-3 строчек, которые мне реально понадобятся?
Да. Тебе же важно знать какие еще есть настройки.
>>By default Redis does not run as a daemon.
> А у меня почему-то запускается автоматически (ставил апт-гетом).
Это значит в твоем дистрибутиве его прикрутили в автозагрузку (что обычно и требуется).
>>daemonize no
> У меня прописано yes.
Это разработчики твоего дистрибутива сделали, логично.
> Как-то можно войти в систему минуя графический интерфейс, как на настоящий сервер, чтобы оно мне не засирало память?
С этим мы вроде разбирались, в новых системах переключение делается таргетом в systemd, а чтобы оно грузилось без GUI, должна быть какая-то опция ядра, которую мы прописываем в загрузчик и которую прочтет код инициализации системы.
Раньше там были bash-скрипты в /etc/rc.d/ и так называемые runlevel, а сейчас используют systemd. В нем каждый сервис описывается как «таргет», и можно указывать зависимости таргетов друг от друга. Например, приложению для работы может быть необходим запущенный mysql, apache и еще что-нибудь. А Апачу необходима работающая сетевая и дисковая подсистемы.
Соответственно конечные цели называются «graphical.target» (запуск GUI интерфейса логина в систему) и «multi-user.target» (запуск текстового интерфейса логина). После загрузки ты можешь переключиться на одну из этих целей, чтобы запустить нужные им приложения и сервисы. Также, ты можешь в опциях командной строки ядра прописать какой таргет должен запускаться после загрузки системы.
Процесс загрузки с systemd описан например тут: http://manpages.ubuntu.com/manpages/utopic/man7/bootup.7.html
Там упомянута нужная опция: systemd.unit=... Значит, ты можешь добавить в загрузчик grub новый пункт «загрузиться в CLI» с этой опцией, либо добавить эту опцию в пункт по умолчанию. Для этого надо отредактировать grub.conf и затем вызвать команду, которая сгенерирует новый загрузчик. Информацию, как это сделать думаю можно найти в сети.
Во время работы системы можно переключиться в консоль через Ctrl + Alt + F1..6 и в GUI через Alt + F7 по моему.
Кстати systemd можно использовать и для добавления в автозапуск каких-то твоих скриптов. А еще как супервизор, который будет автоматически их перезапускать при ошибках.
Там простые слова, она же для детей, попробуй гуглотранслейтом хотя бы переводить или slovari.yandex.ru
ладненько, буду так читать, авось и получится.
insert into table (field1,field2) values (:field1,'YOBA' .:field2)
Бамп.
Что там не понимать? Все просто, как в бочке. Контроллеры, рендеры. Потому что сайты по мануалам с ютаба перепечатывать надо было, а не задачки на массивы решать, страдай терь.
Просто для постороннего человека он может и будет "понятным и читабельным", но не для меня его автора, который затратил в три раза больше времени, чтобы распихать все кусочки по файлам и папкам и классам. Т.е. я даже не знаю, понятно там что-то или нет, потеря времени сводит на нет все эти "структуированные" папочки.
Эти пасты у меня на диске в необработанном виде, так что могу что-то скопировать только по конкретному запросу. Ну и ты можешь полистать этот и предыдущий треды.
>>575654
Откуда цифра в 3 раза больше времени? Допустим у тебя код занимает 1000 строк. Какая разница, это один файл на 1000 строк или 20 файлов на 50 строк? Затраты по времени примерно одни и те же.
И насчет усилий, не знаю, я на автомате пишу как надо и у меня не уходит дополнительных усилий на причесывание кода.
Или хотя бы скажи правильно ли я ее понял.
Нам нужно создать класс "Электрическая сеть" который сначала пустой, а потом добавить методы к нему:
- добавить электростанцию (выработка)
- добавить солнечную панель (выработка)
- добавить жилой дом (кол-во квартир)
- добавить линию электропередач (сколько можно передать по линии, цена за 1 мегаватт) \\Кстати у всех одинаковая цена и возможность передать мегаватты или всем разную можно задать?
- посчитать баланс(время дня): выдаст:
а) нужно закупить Х, будет стоить У
б) можно продать Х, получим прибыли У
в) не хватает линий электропередач чтобы закупить (это сделать отдельным методом)
>Используй продвинутый ООП подход для решения задачи.
Я его уже на примере прошлой задачи изучил или где-то нужно еще о нем прочитать? А то слово "продвинутый" как-то пугает :3
> Нам нужно создать класс "Электрическая сеть" который сначала пустой, а потом добавить методы к нему:
Нет. Ведь это не позволит нам добавлять новые виды потребителей без переписывания кода и вообще некрасиво как-то. Откуда ЭлектроСеть знает все возможные виды элементов которые к ней можно подключить? Она не знает, это не ее дело, ее дело лишь считать баланс.
Метод, очевидно, должен быть такой: добавитьЭлементСети(элемент)
Перечитай пасту про решение ООП задач:
--------
Когда ты решаешь задачу на ООП, ты должен ответить на вопросы:
- какие есть сущности, для которых мы сделаем классы?
- какие у них есть свойства
- что мы хотим от них получить (какие у них должны быть методы)
- как сущности связаны?
-------------
Очевидно Сеть - это одна сущность, а Элементы сети - это другая. Очевидно так как Элементы разные то они реализуются разными классами, но при этом у них есть что-то общее, значит их нужно объединить интерфейсом (которых кстати в JS нет) либо унаследовать от общего предка.
> Кстати у всех одинаковая цена и возможность передать мегаватты или всем разную можно задать?
У всех разная.
> или где-то нужно еще о нем прочитать?
Вообще в этих задачах предполагалось что читатель знает ООП, и лишь изучает особенности его реализации в JS. Если ты хочешь еще задач на ООП, которые помогут лучше в нем разобраться, то можешь взять учебник по PHP из ОП-поста, открыть там главу про ООП, найти задачи «Вектор» и «Кошки-Мышки» и решить их на своем любимом языке, то есть на яваскрипте.
> Нам нужно создать класс "Электрическая сеть" который сначала пустой, а потом добавить методы к нему:
Нет. Ведь это не позволит нам добавлять новые виды потребителей без переписывания кода и вообще некрасиво как-то. Откуда ЭлектроСеть знает все возможные виды элементов которые к ней можно подключить? Она не знает, это не ее дело, ее дело лишь считать баланс.
Метод, очевидно, должен быть такой: добавитьЭлементСети(элемент)
Перечитай пасту про решение ООП задач:
--------
Когда ты решаешь задачу на ООП, ты должен ответить на вопросы:
- какие есть сущности, для которых мы сделаем классы?
- какие у них есть свойства
- что мы хотим от них получить (какие у них должны быть методы)
- как сущности связаны?
-------------
Очевидно Сеть - это одна сущность, а Элементы сети - это другая. Очевидно так как Элементы разные то они реализуются разными классами, но при этом у них есть что-то общее, значит их нужно объединить интерфейсом (которых кстати в JS нет) либо унаследовать от общего предка.
> Кстати у всех одинаковая цена и возможность передать мегаватты или всем разную можно задать?
У всех разная.
> или где-то нужно еще о нем прочитать?
Вообще в этих задачах предполагалось что читатель знает ООП, и лишь изучает особенности его реализации в JS. Если ты хочешь еще задач на ООП, которые помогут лучше в нем разобраться, то можешь взять учебник по PHP из ОП-поста, открыть там главу про ООП, найти задачи «Вектор» и «Кошки-Мышки» и решить их на своем любимом языке, то есть на яваскрипте.
Так что тут не так?
Тебя смущает вызов метода в кавычках или модификатор доступа?
Этот метод вызывается только внутри класса, поэтому protected уместен.
А вот почему вызов метода работает в двойных кавычках, сам не понял, но он работает.
Меня смущает то, что мы храним keyPrefix в адаптерах редиса, но используем в VisitCounter. Тебе это не напоминает нарушение инкапсуляции? Наверно, настройку логично хранить в том классе где она нужна?
Соответственно, надо рассмотреть варианты:
- перенести настройку в VisitCounter
- перенести получение и использование ключа в адаптер редиса. Например, сделав его более высокоуровневым, чтобы в нем были не методы «lrange», а «прочитать блок из очереди»
> А вот почему вызов метода работает в двойных кавычках, сам не понял, но он работает.
Потому что это разрешено правилами: http://php.net/manual/ru/language.types.string.php#language.types.string.parsing
Ничего себе там задачи. Они ведь намного сложнее ну уж точно объемнее чем эта на электросеть. Да и граждане сверху говорили что-то про полгода решения кошек-мышек. Нет, уж лучше их не буду пока трогать. Тем более, что они под другой язык написаны. Ведь после JS я собирался у тебя же здесь учить и PHP, вот тогда и решу. А за теорией если что загляну в JS тред или гугл.
Значит я не понимаю, что такое инкапсуляция. Определение читал сто раз, но не понимаю практического смысла.
>Инкапсуляция – сокрытие реализации работы какого-либо объекта от пользователя с помощью спецификаторов доступа и невозможность воздействовать на объект никак иначе, как только через предоставляемый интерфейс в виде строго определенных публичных методов.
Ну я и сделал свойство защищенным, и поставил публичный геттер, что еще нужно?
Пример с википедии
https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D0%BA%D0%B0%D0%BF%D1%81%D1%83%D0%BB%D1%8F%D1%86%D0%B8%D1%8F_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)
class A
{
private $a; // скрытое свойство
private $b; // скрытое свойство
private function DoSomething() //скрытый метод
{
//actions
}
public function ReturnSomething() //открытый интерфейс
{
//actions
}
}
> В этом примере закрыты свойства $a и $b для класса A с целью предотвращения повреждения этих свойств другим кодом, которому необходимо предоставить только права на чтение.
У меня так же свойство объявлено protected, и есть геттер, который возвращает его значение.
>перенести получение и использование ключа в адаптер редиса. Например, сделав его более высокоуровневым, чтобы в нем были не методы «lrange», а «прочитать блок из очереди»
А что тогда останется в главном классе счетчика, если все перенести в адаптеры? Может он тогда вообще не нужен?
Значит я не понимаю, что такое инкапсуляция. Определение читал сто раз, но не понимаю практического смысла.
>Инкапсуляция – сокрытие реализации работы какого-либо объекта от пользователя с помощью спецификаторов доступа и невозможность воздействовать на объект никак иначе, как только через предоставляемый интерфейс в виде строго определенных публичных методов.
Ну я и сделал свойство защищенным, и поставил публичный геттер, что еще нужно?
Пример с википедии
https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D0%BA%D0%B0%D0%BF%D1%81%D1%83%D0%BB%D1%8F%D1%86%D0%B8%D1%8F_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)
class A
{
private $a; // скрытое свойство
private $b; // скрытое свойство
private function DoSomething() //скрытый метод
{
//actions
}
public function ReturnSomething() //открытый интерфейс
{
//actions
}
}
> В этом примере закрыты свойства $a и $b для класса A с целью предотвращения повреждения этих свойств другим кодом, которому необходимо предоставить только права на чтение.
У меня так же свойство объявлено protected, и есть геттер, который возвращает его значение.
>перенести получение и использование ключа в адаптер редиса. Например, сделав его более высокоуровневым, чтобы в нем были не методы «lrange», а «прочитать блок из очереди»
А что тогда останется в главном классе счетчика, если все перенести в адаптеры? Может он тогда вообще не нужен?
https://github.com/p-5ch/vector
Я там еще из задачи про студентов пару вещей попробовал. Как то очень громоздко получилось.
В данном случае я имел в виду что если keyPrefix нужен только VisitCounter то логично его в нем и хранить и убрать публичные геттеры. таким образом, это свойство станет спрятано внутри VC (инкапсулировано) и все будет логично. Или же сделать, чтобы оно использовалось не в VC, а в адаптере редиса, опять-таки, будет логично.
Инкапсуляция значит скрытие внутри класса каких-то деталей его работы. Если брать пример из реальной жизни, это допустим кофе-машина. Пользователь кофе машины жмет кнопку и получает кофе, при этом увидеть как он производится и повлиять на этот процесс он не должен и не может (и благодаря этому пользование кофе машиной не требует высшего инженерного образования).
Аналогично и с кодом, класс предоставляет наружу кнопки-методы, а механизм их работы скрывает внутри. Потому с таким классом проще работать.
> У меня так же свойство объявлено protected, и есть геттер, который возвращает его значение.
Не совсем. У тебя внутренние особенности работы класса VC (keyPrefix) хранятся почему-то в другом классе. Ты смотришь на инкапсуляцию формально (есть приватное свойство, значит есть инкапсуляция), но если посмотреть с большей высоты, то мы видим что внутренние детали работы класса VC хранятся зачем-то в другой классе. Из нашей кофе-машины тянется шланг куда-то за шкаф.
Зайдем еще с другой стороны. Адаптер редиса - это переходник, подстраивающий разные драйвера под один общий интерфейс. Почему в нем хранится префикс? Как он помогает выполнять задачу адаптера? Нет у тебя ощущения что адаптер может справляться со своей работой без него и keyPrefix просто сбоку прилеплен?
> А что тогда останется в главном классе счетчика, если все перенести в адаптеры? Может он тогда вообще не нужен?
Посмотрим, будет не нужен - всегда можно удалить. Если архитектура хорошая, это особых проблем не вызовет.
Кстати, раз мы заговорили про ООП, архитектуру, то я просто должен дать тебе ссылку про выпечку хлеба на ООП (не помню, может уже давал раньше):
http://habrahabr.ru/post/153225/
Ну и ссылку на 5 принципов SOLID:
http://habrahabr.ru/post/208442/
https://ru.wikipedia.org/wiki/SOLID_(%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)
http://blog.byndyu.ru/2009/10/solid.html
http://habrahabr.ru/post/129534/
Это нормально, если ты что-то не поймешь, это тема сложная, я наверно сам не все смогу объяснить по памяти.
Слушай ОП, а вообще веб программисту нужно знание ООП JS? Или это все на PHP пишется? Вообще есть ли смысл сейчас изучать ООП на примере JS или перейти к DOM, а с ООП дальше разбираться при изучении PHP?
Есть смысл больше упора делать на DOM и затем jQuery, ООП это более сложная тема. Но основы ООП (на уровне задач 11, 12) стоит знать так как в JS почти все это объекты.
Доброе утро, Котик! Я вот поставил Debian и немножко поигрался с консолью, но почему-то он тормазит под VirtualBox, хотя в настройках я выкрутил все на максимум. Сейчас пойду в магазин а потом буду либо гуглить, либо спршаивать тут В интернетах все такое сомнительное, пишут что-то докачивать, устанавливать - я думал Линукс будет чувствовать себя комфортно в любых средах (символПенгвинчика)
А в целом мне нравиться эта ОСь - выглядит приятно.
Все я побежал. Удачи!
так и не решил :(
Vmware лучше, там ничего не тормозит. А если ещё. VmTools поставятся, то вообще шикарно.
Лишнее время у меня уходит на создание файлов, создание классов, потом расфасофка функций по методом, потом создание объектов классов, потом вызов их методов. Вот этот весь принцип ООП. Конечно, код в контроллерах слегка сокращается, но не намного.
А ведь мне еще валидатор, пейджер, класс для чекера кукисов для эдита и публичности файла. Конечно, так оно все будет логично красиво и структуировано, но времени тратится пиздец.
Я вообще не понимаю, как можно "выучить" это "от корки до корки" не сделав ни одного сайта, не зная МВК и ООП. Типа просто зубришь синтаксис без понимания его использования?
Unfortunately, modern browsers do not provide native support for HTTP PUT requests. To work around this limitation, ensure your HTML form’s method attribute is “post”, then add a method override parameter to your HTML form like this:
смешной костыль. чего бы просто пост не использовать в контроллере для апдейта.
$regexp='|<div class=["]img["]>([^"]*)</div>|';
Вообще, стоит ли как следует упороться виртуалками + убунтой/дебианом/ или это нужно только для самообразования и лучше пока просто продолжать решать задачки, развивать мышление и т.п.?
Я пробовал webstorm и она тормозит на моей нищепеке, думаю с phpstrom такаяже хуйня будет. А вообще, я хотел в js-тред написать, ошибся.
Ну хуй знает. Может штормы хуево оптимизированны под мой амд. Атом работает более-менее сносно.
Благодарю за копетанство.
Вот сравнение одного из их поделия. Причём это интелижевское поделие ещё без плагинов, открытого жава проекта и жава системы сборки. Удачи.
кстати симпатичная ide, в сравнении с тем же phpstorm'ом
правда, а как код то запустить? чтобы результат глянуть
аннет, текстовый редактор, хуева
Зачем квадратные скобки вокруг ["] ?
Квадратными скобками задается диапазон, например [123] либо 1, либо 2, либо 3.
И там кстати могут быть не только двойные, но и одинарные кавычки, а может и не быть. Значит правильнее ['"]?
Пробелов между div и class может быть несколько, и даже не пробел, а символ табуляции или переноса строки.
Нужно \s+
Мне бы просто выяснить несколько ньюансов по мере их появления. Например вот щас переклинило: а пустой масив при приведение к булеву это тру или фалс? Типа что вернет if($_GET), если он пустой? Вернет тру, в таких случаях поможет var_dump((bool)$_GET). Ну это один из примеров.
А так получается нужно написать отдельный файл и провести там опыт. А была бы консолечка: пиздык-пиздык и готово, пока мысли не разбежались.
Sorry, the page you are looking for could not be found.
Подскажи, пожалуйста, анон, поскорее.
Код index.php http://ideone.com/5lR8TU
Код контролера: http://ideone.com/LhmLxB
Я хамам в интернете не помогаю. Извинись и попроси по нормальному.
А я, вот, не люблю автодополнение. Отключил, и не знаю проблем с автодополнением. Единственный сниппет, который использую - генератор геттеров/сеттеров. Надо будет ещё xdebug прикрутить.
>$app->get('/product/{$id}', 'Controllers\IndexController::viewProduct')->bind('single.product');
>'/product/{$id}'
В этой строчке что-то не так. Чтобы увидеть, что именно - достаточно заглянуть на главную страницу сайта фреймворка.
Ты общаешься с разными людьми. Но я понял, что максимум что ты знаешь это про php cli, то есть нихуя ты не знаешь, уеба. А значит анус твоей мамаши уходит в зрительный зал.
Мои соседи сейчас пилят дрова (на шестом этаже), а их дети играют в игру "кто не скачет, тот проиграл". Выходные же, каждый развлекается как может.
>>575763
Читал эту статью на хабре.
Не знаю, в чем ее цель (скорее всего пост юмористический, хотя нердский юмор очень специфичен) и к каким выводам подталкивает (скорее всего к использованию принципа KISS), но на данный момент соглашусь с кем-то из комментаторов в том, что нужно искать середину между этими двумя подходами. Один программист строит сложные схемы по шаблонам, в которых судя по всему плохо разбирается. Второй впадает в другую крайность, и сбрасывает весь код в "кучу", в которой разобраться кстати потом будет наверное не легче, чем в сложных диаграммах классов слева. Хотя добавить несколько лишних строк по требованию заказчика легко и быстро, это да.
В общем, мне кажется, что нужно придерживаться kiss, но и не забывать при явной необходимости про паттерны, все-таки они тоже не для мебели существуют.
>>575762
>Адаптер редиса - это переходник, подстраивающий разные драйвера под один общий интерфейс
А, ну тогда ладно, значит я плохо понимал, что такое адаптер и в чем его задача.
Я планировал все относящееся к редису (префикс ключей и их ttl) хранить в объекте адаптера редиса, а все относящееся к базе в объекте адаптера базы.
Если задача адаптера исключительно в том, чтобы подстроить драйвер под интерфейс, и ничего более, тогда я начинаю понимать суть твоих претензий.
И приведи еще примеры адаптеров, потому что тут не до конца ясно.
>>575764
>SOLID
Да, вот как раз мне непонятна "единая ответственность" Адаптера, поэтому код оказался немного размазан по классам, в этом проблема.
Я не могу четко сформулировать, в чем задача адаптера.
>принцип открытости/закрытости
Судя по всему речь об инкапсуляции + создание интерфейсов для возможности расширения.
Одним словом, при необходимости что-то изменить в нашем классе, не лезть в его код, а создать другой класс, реализующий те же интерфейсы, что и первый класс.
Остальное нужно переварить, беглым прочтением не обойтись.
Мои соседи сейчас пилят дрова (на шестом этаже), а их дети играют в игру "кто не скачет, тот проиграл". Выходные же, каждый развлекается как может.
>>575763
Читал эту статью на хабре.
Не знаю, в чем ее цель (скорее всего пост юмористический, хотя нердский юмор очень специфичен) и к каким выводам подталкивает (скорее всего к использованию принципа KISS), но на данный момент соглашусь с кем-то из комментаторов в том, что нужно искать середину между этими двумя подходами. Один программист строит сложные схемы по шаблонам, в которых судя по всему плохо разбирается. Второй впадает в другую крайность, и сбрасывает весь код в "кучу", в которой разобраться кстати потом будет наверное не легче, чем в сложных диаграммах классов слева. Хотя добавить несколько лишних строк по требованию заказчика легко и быстро, это да.
В общем, мне кажется, что нужно придерживаться kiss, но и не забывать при явной необходимости про паттерны, все-таки они тоже не для мебели существуют.
>>575762
>Адаптер редиса - это переходник, подстраивающий разные драйвера под один общий интерфейс
А, ну тогда ладно, значит я плохо понимал, что такое адаптер и в чем его задача.
Я планировал все относящееся к редису (префикс ключей и их ttl) хранить в объекте адаптера редиса, а все относящееся к базе в объекте адаптера базы.
Если задача адаптера исключительно в том, чтобы подстроить драйвер под интерфейс, и ничего более, тогда я начинаю понимать суть твоих претензий.
И приведи еще примеры адаптеров, потому что тут не до конца ясно.
>>575764
>SOLID
Да, вот как раз мне непонятна "единая ответственность" Адаптера, поэтому код оказался немного размазан по классам, в этом проблема.
Я не могу четко сформулировать, в чем задача адаптера.
>принцип открытости/закрытости
Судя по всему речь об инкапсуляции + создание интерфейсов для возможности расширения.
Одним словом, при необходимости что-то изменить в нашем классе, не лезть в его код, а создать другой класс, реализующий те же интерфейсы, что и первый класс.
Остальное нужно переварить, беглым прочтением не обойтись.
>>571958
Если функция не работает как описано в документации, значит в ней или у тебя ошибка. Попробуй посмотреть что происходит с переданным тобой параметрами, куда они идут, какой SQL-запрос генерируется.
Ну и обрати внимание на эту строку:
> meta_key - The meta_key in the wp_usermeta table for the meta_value to be returned. See get_userdata() for the possible meta keys.
Проверь есть ли в wp_usermeta твое поле, по которому ты хочешь фильтрвать.
Также, в документации параметры передаются массивом, а у тебя в первом коде строкой:
> '&offset='.$offset.'&number='.$number);
Во втором примере кода ты передаешь огромный массив, но по моему ты передаешь лишние элементы. например элемент include описан так в документации:
> include - An array of IDs. Only users matching these IDs will be returned. Note
То есть вернуться только пользователи с указанными id. У тебя там пустой массив, то есть по логике ни одного юзера и не должно найтись. Зачем ты передаешь этот параметр, не очень понятно.
>>572049
> Какая-то тормозная жесть выходит.
Вроде нормальный алгоритм.
> Потому что даже будучи анонимом только автор файла может его редактировать, а авторство выбирается по куке.
Тогда да, придется задавать юзеров без имени. Хорошо бы их помечать как-то например полем is_anonymous = 1 или is_registered = 0
>>572239
Тут есть варианты, можно с сервера вернуть JSON для новых собщений и рендерить их на клиенте, можно с сервера вернуть массив HTML-блоков и просто вставлять их в нужные места страницы.
>>572362
Да, там надо сделать несколько переменных, в которых хранится запомненный знак, накапливается число и тд.
>>571958
Если функция не работает как описано в документации, значит в ней или у тебя ошибка. Попробуй посмотреть что происходит с переданным тобой параметрами, куда они идут, какой SQL-запрос генерируется.
Ну и обрати внимание на эту строку:
> meta_key - The meta_key in the wp_usermeta table for the meta_value to be returned. See get_userdata() for the possible meta keys.
Проверь есть ли в wp_usermeta твое поле, по которому ты хочешь фильтрвать.
Также, в документации параметры передаются массивом, а у тебя в первом коде строкой:
> '&offset='.$offset.'&number='.$number);
Во втором примере кода ты передаешь огромный массив, но по моему ты передаешь лишние элементы. например элемент include описан так в документации:
> include - An array of IDs. Only users matching these IDs will be returned. Note
То есть вернуться только пользователи с указанными id. У тебя там пустой массив, то есть по логике ни одного юзера и не должно найтись. Зачем ты передаешь этот параметр, не очень понятно.
>>572049
> Какая-то тормозная жесть выходит.
Вроде нормальный алгоритм.
> Потому что даже будучи анонимом только автор файла может его редактировать, а авторство выбирается по куке.
Тогда да, придется задавать юзеров без имени. Хорошо бы их помечать как-то например полем is_anonymous = 1 или is_registered = 0
>>572239
Тут есть варианты, можно с сервера вернуть JSON для новых собщений и рендерить их на клиенте, можно с сервера вернуть массив HTML-блоков и просто вставлять их в нужные места страницы.
>>572362
Да, там надо сделать несколько переменных, в которых хранится запомненный знак, накапливается число и тд.
Если иксы падают, в таких случаях стоит смотреть логи в /var/log, правда я не знаю какие именно, логи xdm или может еще что-то.
>>572004
Изучить учебник из ОП-поста мало, это лишь основы PHP. Хорошо бы до файлообменника хотя бы дойти.
> Что нужно в первую очередь подтянуть, чтобы не краснеть за собеседованиях, в какую сторону развиваться? В общем, что курить дальше?
Поищи в треде слово собеседование, там есть список.
> как/чем набить портфолио?
Никак. Если у тебя 0 опыта, откуда возьмется портфолио? Алсо, программист не дизайнер чтобы о портфолио беспокоиться.
Если без портфолио никак то можно конечно попробовать на фрилансе заказы поискать, но стоит ли оно того, и получится ли это у тебя, не знаю.
>>571005
> А если у нас скорость автомобиля, надо для каждого значения скорости константу?
Нет конечно, в таком случае у нас будет характеристика «максимальная скорость в км/ч», значения которой - обычные числа. Например:
car.setMaxSpeed(180); // задаем макс. скорость
Константы нужны когда если несколько вариантов значения, и его так просто не выразить числом, например автоматическая коробка/ручная, бензиновый двигатель или электрический. То, что меряется цифрами, конечно должно в виде цифр и храниться. Цены и калории не стоит хранить в константах. А тип начинки - стоит.
>>572739
Yii2, затем симфони 2
Если иксы падают, в таких случаях стоит смотреть логи в /var/log, правда я не знаю какие именно, логи xdm или может еще что-то.
>>572004
Изучить учебник из ОП-поста мало, это лишь основы PHP. Хорошо бы до файлообменника хотя бы дойти.
> Что нужно в первую очередь подтянуть, чтобы не краснеть за собеседованиях, в какую сторону развиваться? В общем, что курить дальше?
Поищи в треде слово собеседование, там есть список.
> как/чем набить портфолио?
Никак. Если у тебя 0 опыта, откуда возьмется портфолио? Алсо, программист не дизайнер чтобы о портфолио беспокоиться.
Если без портфолио никак то можно конечно попробовать на фрилансе заказы поискать, но стоит ли оно того, и получится ли это у тебя, не знаю.
>>571005
> А если у нас скорость автомобиля, надо для каждого значения скорости константу?
Нет конечно, в таком случае у нас будет характеристика «максимальная скорость в км/ч», значения которой - обычные числа. Например:
car.setMaxSpeed(180); // задаем макс. скорость
Константы нужны когда если несколько вариантов значения, и его так просто не выразить числом, например автоматическая коробка/ручная, бензиновый двигатель или электрический. То, что меряется цифрами, конечно должно в виде цифр и храниться. Цены и калории не стоит хранить в константах. А тип начинки - стоит.
>>572739
Yii2, затем симфони 2
Да, бывает. Ты сам можешь так сделать, например с помощью htaccess перенаправить все запросы на index.php (т.е. чтобы при обращении например по URL example.com/hello/world вызывался index.php), а в нем уже с помощью ифов и регулярок анализировать чему равен REQUEST_URI и выводить ту или иную страницу.
>>573618
У меня не спрашивали, но наверно могут.
>>573699
Не знаю, поищи библиотеки HTTP-клиентов.
>>573899
А как ты проверил что index.php не вызвается? Поставил die('Yes'); в начале? Возможно что у тебя не работает или отключен htaccess по каким-то причинам. Какая версия сервера? Что в конфиге Апача (httpd.conf)? Надеюсь, не сборка какая-нибудь?
htaccess разрешается/включается настройкой AllowOverride в конфиге по моему.
>>573936
В случае файлообменника мы создаем классы-сервисы как «синглтоны» в Слиме, там есть нужный функционал: http://docs.slimframework.com/di/overview/#singleton-resources
Незачем загромождать код контроллера.
ПРавильно ты сделал или нет, по маленькому кусочку кода на скриншоте я сказать не могу. Я же не знаю что у тебя в классе Cookie или Token. Но неправильно что ты делаешь класс для кук, в Слиме уже есть для этого функционал.
>>574211
> Как мне записать массив ошибок $error[0] и тд в условиях?
Внутри if работа с массивом делается так же как и в любом другом месте. А ты с массивами умеешь работать? Что-то я не понял суть вопроса.
Да, бывает. Ты сам можешь так сделать, например с помощью htaccess перенаправить все запросы на index.php (т.е. чтобы при обращении например по URL example.com/hello/world вызывался index.php), а в нем уже с помощью ифов и регулярок анализировать чему равен REQUEST_URI и выводить ту или иную страницу.
>>573618
У меня не спрашивали, но наверно могут.
>>573699
Не знаю, поищи библиотеки HTTP-клиентов.
>>573899
А как ты проверил что index.php не вызвается? Поставил die('Yes'); в начале? Возможно что у тебя не работает или отключен htaccess по каким-то причинам. Какая версия сервера? Что в конфиге Апача (httpd.conf)? Надеюсь, не сборка какая-нибудь?
htaccess разрешается/включается настройкой AllowOverride в конфиге по моему.
>>573936
В случае файлообменника мы создаем классы-сервисы как «синглтоны» в Слиме, там есть нужный функционал: http://docs.slimframework.com/di/overview/#singleton-resources
Незачем загромождать код контроллера.
ПРавильно ты сделал или нет, по маленькому кусочку кода на скриншоте я сказать не могу. Я же не знаю что у тебя в классе Cookie или Token. Но неправильно что ты делаешь класс для кук, в Слиме уже есть для этого функционал.
>>574211
> Как мне записать массив ошибок $error[0] и тд в условиях?
Внутри if работа с массивом делается так же как и в любом другом месте. А ты с массивами умеешь работать? Что-то я не понял суть вопроса.
> непонятно, куда это все "логируется" и что выводится в случае ошибки на экран - пустота?_?.
Во-первых, пойманные исключения никуда не логгируются и информация о них никуда не сохраняется. Если ты поймал исключение и не залоггировал, то никто о нем никогда не узнает и не исправит ошибку.
Непойманные исключения завершают программу и записываются в стандартный лог ошибок - туда же куда и другие виды ошибок. Расположение лога зависит от настроек php.ini: http://php.net/manual/ru/errorfunc.configuration.php#ini.error-log
По умолчанию при работе под Апачом ошибки сохраняются в лог ошибок Апача, его местополложение задается в конфиге Апача, по умолчанию в линуксе это /var/log/apache2/.., в винде - папка logs в папке Апача.
Вывод ошибок на экран управляется директивой php.ini display_errors: http://php.net/manual/ru/errorfunc.configuration.php
Если оно включено, то на экран выведется информация об исключении. Если нет, то выведется белый экран. На боевом сайте разумеется эта настройка должна быть отключена, на локальном - включена.
Слим по умолчанию сам ловит (и логгирует) все исключения, и вместо белого экрана выводит заглушку. Если включен режим отладки, то еще и информацию об исключении. На боевом сервере режим отладки надо отключать.
Если ты вместо заглушки слима ставишь свою, обязанность логгировать исключения ложится на тебя. для этого есть стандартная функция error_log: http://php.net/manual/ru/function.error-log.php
Повторим:
- настройка display_errors включает вывод ошибок и непойманных исключений на экран, никак не влияя на логи. На продакшене мы ее отключаем.
- настройка error_reporting позволяет игнорировать (и не писать в логи) некоторые виды ошибок. Этим пользуются только быдлокодеры которым лень их искать и исправлять.
- функция error_log() позволяет записать сообщение в стандартный лог ошибок
Разумеется, все это надо еще проверить. Сделай в своей программе ошибку (обращение к несуществующей переменной) и проверь что произойдет, фиксируется ли ошибка в логах. Выбрось непойманное исключени и посмотри что будет.
Я не раз видел проекты, где быдлокодеры специально или нечаянно ломали механизм обработки ошибок и часть или все не выводились и не логгировались. как следствие, ошибки никто не исправлял и код становился очень ненадежным и ломался при каждом удобном случае.
Вообще, механизм ошибок в PHP спроектирован не удачно. Правильно не делить ошибки на виды, а в любой непонятной ситуации выбрасывать исключение и завершать программу. Только так можно быть уверенным что ошибку заметят и исправят. Слим именно так и делает - он преобразует любые виды ошибок в исключения.
> непонятно, куда это все "логируется" и что выводится в случае ошибки на экран - пустота?_?.
Во-первых, пойманные исключения никуда не логгируются и информация о них никуда не сохраняется. Если ты поймал исключение и не залоггировал, то никто о нем никогда не узнает и не исправит ошибку.
Непойманные исключения завершают программу и записываются в стандартный лог ошибок - туда же куда и другие виды ошибок. Расположение лога зависит от настроек php.ini: http://php.net/manual/ru/errorfunc.configuration.php#ini.error-log
По умолчанию при работе под Апачом ошибки сохраняются в лог ошибок Апача, его местополложение задается в конфиге Апача, по умолчанию в линуксе это /var/log/apache2/.., в винде - папка logs в папке Апача.
Вывод ошибок на экран управляется директивой php.ini display_errors: http://php.net/manual/ru/errorfunc.configuration.php
Если оно включено, то на экран выведется информация об исключении. Если нет, то выведется белый экран. На боевом сайте разумеется эта настройка должна быть отключена, на локальном - включена.
Слим по умолчанию сам ловит (и логгирует) все исключения, и вместо белого экрана выводит заглушку. Если включен режим отладки, то еще и информацию об исключении. На боевом сервере режим отладки надо отключать.
Если ты вместо заглушки слима ставишь свою, обязанность логгировать исключения ложится на тебя. для этого есть стандартная функция error_log: http://php.net/manual/ru/function.error-log.php
Повторим:
- настройка display_errors включает вывод ошибок и непойманных исключений на экран, никак не влияя на логи. На продакшене мы ее отключаем.
- настройка error_reporting позволяет игнорировать (и не писать в логи) некоторые виды ошибок. Этим пользуются только быдлокодеры которым лень их искать и исправлять.
- функция error_log() позволяет записать сообщение в стандартный лог ошибок
Разумеется, все это надо еще проверить. Сделай в своей программе ошибку (обращение к несуществующей переменной) и проверь что произойдет, фиксируется ли ошибка в логах. Выбрось непойманное исключени и посмотри что будет.
Я не раз видел проекты, где быдлокодеры специально или нечаянно ломали механизм обработки ошибок и часть или все не выводились и не логгировались. как следствие, ошибки никто не исправлял и код становился очень ненадежным и ломался при каждом удобном случае.
Вообще, механизм ошибок в PHP спроектирован не удачно. Правильно не делить ошибки на виды, а в любой непонятной ситуации выбрасывать исключение и завершать программу. Только так можно быть уверенным что ошибку заметят и исправят. Слим именно так и делает - он преобразует любые виды ошибок в исключения.
> Как сокращать такие длинные условия?
Вынесением части условия в переменные с понятным именем:
$isInsideMap = $this->getWorld()->isInsideMap($forX, $forY);
$isOccupied (занята ли клетка) = $this->getWorld()->determineTheObject($forX, $forY);
if (!$isInsideMap || $isOccupied) {
...
}
(обрати внимание на знак «не» перед isInsideMap - у тебя его почему-то нет и наоборот, отсеиваются хорошие ходы).
Также, можно вынести проверку доступности клетки в отдельный метод:
if ($this->canMoveTo($x, $y)) {
...
}
Давай начнем с обзора класса Animal. Давай посмотрим на список абстрактных методов у животного:
- abstract function getSymbol();
- abstract function getAllMoves($x, $y);
- abstract function rateMoves($moves, $search);
- abstract public function move();
Многовато. Например наличие функции rateMoves подразуемевает что любое животное оценивает ход, но что если оно просто ходит случайно? Обязаны ли все животные реализовать этот метод?
Предлагаю уменьшить число абстрактных методов. Абстрактные методы лучше писать сразу после конструктора, чтобы они были хорошо заметны.
Далее, посмотрим список публичных методов. Это методы, которые можно вызывать снаружи для любого животного:
- public function returnWorldToTheAnimal($world) {
- public function deleteWorldFromTheAnimal() {
- public function getCoordinate() {
- public function getX() {
- public function getY() {
- public function getView() {
- public function getSpeed() {
- public function getFears() {
- public function getTracks() {
- public function isItDie() {
- public function getWorld() {
- public function searchAnimalsAroundByType(Animal $animal, array $types) {
- public function foundTheNearestAnimal($search) {
- public function isItNotOneOfTrack($x, $y, $tracks) {
- public function isItCorner($x, $y) {
- public function howManyMovesWillDoAnimal(Animal $animal, $distance) {
- public function chooseTheMovement($ratedMoves) {
....
И так далее. Не многовато ли? Есть необходимость вызывать все эти функции снаружи? Надо ставить как можно более ограниченный доступ. Вот у тебя тут 20 функций public и программист вынужден их все просматривать чтобы понять как класс работает. А было бы меньше public функций - было бы проще читать код.
То есть надо пройтись по списку функций и решить: эта функция нужна другим классам, потому она публичная. Или же эта функция используется только внутри животного и незачем выставлять ее наружу. Например, видеть координаты животного или его иконку другим объектам, разумеется, нужно. А вот знать как оно оценивает тот или иной ход или на кого оно охотится - зачем это видеть посторонним? Это внутреннее дело животного.
Я приводил в треде пример про кофе-машину:
-------
Инкапсуляция значит скрытие внутри класса каких-то деталей его работы. Если брать пример из реальной жизни, это допустим кофе-машина. Пользователь кофе машины жмет кнопку и получает кофе, при этом увидеть как он производится и повлиять на этот процесс он не должен и не может (и благодаря этому пользование кофе машиной не требует высшего инженерного образования).
Аналогично и с кодом, класс предоставляет наружу кнопки-методы, а механизм их работы скрывает внутри. Потому с таким классом проще работать.
--------
У тебя слишком много информации выставлено наружу. Это усложняет понимание кода.
https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L7
> protected $speed;
Не очень понятно зачем каждому животному это свойство. А если оно как шахматный конь ходит, какая у него скорость? А если телепортируется на любую клетку? Мне кажется, его надо перенести в тех животных, где оно используется. Или есть какие-то причины делать его в базовом классе?
Кстати, почему скорость и радиус зрения передаются через конструктор? Предполагается что у нас могут быть кошки бегающие с разной скоростью? В принципе, я не против такого варианта, так даже интереснее. Пусть тогда в твоем примере кода эта возможность используется.
> public function returnWorldToTheAnimal($world) {
Нужен тайп хинт.
> $this->world = false; //Так правильно?
Обычно отсутствие объекта обозначают как null. false это «ложь» и используется как противоположность true. А null как раз специально и придуман чтобы обозначить отсутствие объекта.
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L51
> public function getFears() {
> return $this->fears;
Ошибка: объект не может обращаться к полям которых в нем нет. То, что они есть в потомках, не имеет значения. Надо либо добавить это поле в Animal либо перенести метод в наследника.
> public function searchAnimalsAroundByType(Animal $animal, array $types) {
Эту функцию логичнее поместить в World. Это его обязанность знать какие где животные и предоставлять информацию всем желающим.
> public function foundTheNearestAnimal($search) {
Здесь можно не создавать промежуточный массив, а сортировать список животных, вычисляя расстояние до каждого прямо в функции сравнения. Или же, можно еще укоротить код:
- сделать массив, где ключ это расстояние, а значение это объект животного
- отсортировать его по возрастанию ключей
- взять 1-й элемент
> public function isItNotOneOfTrack($x, $y, $tracks) {
1) имя запутанное. Почему нельзя сделать функцию isOneOfTrack, у которой более короткое и понятное название?
2) функция возвращает либо false либо объект. Но функции is... обычно возвращают true/false.
> abstract function rateMoves($moves, $search);
Почему так запутанно? В моем понимании функция оценки берет на вход координаты клеточки и возвращает оценку. Просто и понятно. А что принимает эта функция? Массив ходов и какой-то непонятный массив search. Тебе не кажется что это как-то сложнее получается? Ради чего усложняем функцию?
> $ratedMoves = array_shift($ratedMoves);
Вот это нехорошо. У тебя был в переменной массив ходов, а остался один ход. Для этих целей надо использовать разные переменные.
> public function KillTheAnimal() {
> return true;
Зачем возвращать что-то если всегда возвращается только true? Какую полезную информацию это дает вызвавшему метод?
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Cat.php#L84
> $this->world->isInsideMap($move['x'], $move['y']);
Зачем вызывать этот метод если ты не проверяешь, что он вернет?
> $animal->KillTheAnimal();
Этот метод назван неудачно. $mouse->killTheAnimal читается как «мышь, убей это животное», а не «мышь, ты убита». Надо дать другое название, например $mouse->setKilled (пометить убитой), $mouse->makeDead() (сделать мертвой) или $mouse->setIsDead() или $mouse->removeSelf() (удалить себя). killOneself не предлагаю, а то твой гитхаб закроют за пропаганду суицида.
Наконец еще можно при съедении животного сразу делать $world->remove... то есть обращаться не к животному, а к миру.
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Cat.php#L95
>return $move;
Не очень понятно зачем тут return, эти данные кому-то нужны? Мы не можем их получить через getX/Y?
> if (($forX > $x and $forY > $y) or ($forX > $x and $forY < $y)
Не понимаю что и зачем тут проверяется
> Нужна ли проверка is_a($object, "Animal")?
is_a не надо использовать, лучше использовать оператор instanceof. Думаю, проверка не нужна так как кроме животных на карте никого нет.
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Mouse.php#L39
Здесь не очень просто понять как считается число очков. Сделай отдельные факторы в отдельных переменных и складывай их в конце с весом:
rate = w1 × factor1 + w2 × factor2...
(только имена конечно нужны понятные).
> already have it object - {get_class($animal)}"
Функция get_class не вызовется, а будет ошибка из-за невозможности преобразовать $animal в строку
> public function isInsideMap($x, $y) {
Результат который возвращает функция противоположен ее названию
> Как сокращать такие длинные условия?
Вынесением части условия в переменные с понятным именем:
$isInsideMap = $this->getWorld()->isInsideMap($forX, $forY);
$isOccupied (занята ли клетка) = $this->getWorld()->determineTheObject($forX, $forY);
if (!$isInsideMap || $isOccupied) {
...
}
(обрати внимание на знак «не» перед isInsideMap - у тебя его почему-то нет и наоборот, отсеиваются хорошие ходы).
Также, можно вынести проверку доступности клетки в отдельный метод:
if ($this->canMoveTo($x, $y)) {
...
}
Давай начнем с обзора класса Animal. Давай посмотрим на список абстрактных методов у животного:
- abstract function getSymbol();
- abstract function getAllMoves($x, $y);
- abstract function rateMoves($moves, $search);
- abstract public function move();
Многовато. Например наличие функции rateMoves подразуемевает что любое животное оценивает ход, но что если оно просто ходит случайно? Обязаны ли все животные реализовать этот метод?
Предлагаю уменьшить число абстрактных методов. Абстрактные методы лучше писать сразу после конструктора, чтобы они были хорошо заметны.
Далее, посмотрим список публичных методов. Это методы, которые можно вызывать снаружи для любого животного:
- public function returnWorldToTheAnimal($world) {
- public function deleteWorldFromTheAnimal() {
- public function getCoordinate() {
- public function getX() {
- public function getY() {
- public function getView() {
- public function getSpeed() {
- public function getFears() {
- public function getTracks() {
- public function isItDie() {
- public function getWorld() {
- public function searchAnimalsAroundByType(Animal $animal, array $types) {
- public function foundTheNearestAnimal($search) {
- public function isItNotOneOfTrack($x, $y, $tracks) {
- public function isItCorner($x, $y) {
- public function howManyMovesWillDoAnimal(Animal $animal, $distance) {
- public function chooseTheMovement($ratedMoves) {
....
И так далее. Не многовато ли? Есть необходимость вызывать все эти функции снаружи? Надо ставить как можно более ограниченный доступ. Вот у тебя тут 20 функций public и программист вынужден их все просматривать чтобы понять как класс работает. А было бы меньше public функций - было бы проще читать код.
То есть надо пройтись по списку функций и решить: эта функция нужна другим классам, потому она публичная. Или же эта функция используется только внутри животного и незачем выставлять ее наружу. Например, видеть координаты животного или его иконку другим объектам, разумеется, нужно. А вот знать как оно оценивает тот или иной ход или на кого оно охотится - зачем это видеть посторонним? Это внутреннее дело животного.
Я приводил в треде пример про кофе-машину:
-------
Инкапсуляция значит скрытие внутри класса каких-то деталей его работы. Если брать пример из реальной жизни, это допустим кофе-машина. Пользователь кофе машины жмет кнопку и получает кофе, при этом увидеть как он производится и повлиять на этот процесс он не должен и не может (и благодаря этому пользование кофе машиной не требует высшего инженерного образования).
Аналогично и с кодом, класс предоставляет наружу кнопки-методы, а механизм их работы скрывает внутри. Потому с таким классом проще работать.
--------
У тебя слишком много информации выставлено наружу. Это усложняет понимание кода.
https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L7
> protected $speed;
Не очень понятно зачем каждому животному это свойство. А если оно как шахматный конь ходит, какая у него скорость? А если телепортируется на любую клетку? Мне кажется, его надо перенести в тех животных, где оно используется. Или есть какие-то причины делать его в базовом классе?
Кстати, почему скорость и радиус зрения передаются через конструктор? Предполагается что у нас могут быть кошки бегающие с разной скоростью? В принципе, я не против такого варианта, так даже интереснее. Пусть тогда в твоем примере кода эта возможность используется.
> public function returnWorldToTheAnimal($world) {
Нужен тайп хинт.
> $this->world = false; //Так правильно?
Обычно отсутствие объекта обозначают как null. false это «ложь» и используется как противоположность true. А null как раз специально и придуман чтобы обозначить отсутствие объекта.
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L51
> public function getFears() {
> return $this->fears;
Ошибка: объект не может обращаться к полям которых в нем нет. То, что они есть в потомках, не имеет значения. Надо либо добавить это поле в Animal либо перенести метод в наследника.
> public function searchAnimalsAroundByType(Animal $animal, array $types) {
Эту функцию логичнее поместить в World. Это его обязанность знать какие где животные и предоставлять информацию всем желающим.
> public function foundTheNearestAnimal($search) {
Здесь можно не создавать промежуточный массив, а сортировать список животных, вычисляя расстояние до каждого прямо в функции сравнения. Или же, можно еще укоротить код:
- сделать массив, где ключ это расстояние, а значение это объект животного
- отсортировать его по возрастанию ключей
- взять 1-й элемент
> public function isItNotOneOfTrack($x, $y, $tracks) {
1) имя запутанное. Почему нельзя сделать функцию isOneOfTrack, у которой более короткое и понятное название?
2) функция возвращает либо false либо объект. Но функции is... обычно возвращают true/false.
> abstract function rateMoves($moves, $search);
Почему так запутанно? В моем понимании функция оценки берет на вход координаты клеточки и возвращает оценку. Просто и понятно. А что принимает эта функция? Массив ходов и какой-то непонятный массив search. Тебе не кажется что это как-то сложнее получается? Ради чего усложняем функцию?
> $ratedMoves = array_shift($ratedMoves);
Вот это нехорошо. У тебя был в переменной массив ходов, а остался один ход. Для этих целей надо использовать разные переменные.
> public function KillTheAnimal() {
> return true;
Зачем возвращать что-то если всегда возвращается только true? Какую полезную информацию это дает вызвавшему метод?
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Cat.php#L84
> $this->world->isInsideMap($move['x'], $move['y']);
Зачем вызывать этот метод если ты не проверяешь, что он вернет?
> $animal->KillTheAnimal();
Этот метод назван неудачно. $mouse->killTheAnimal читается как «мышь, убей это животное», а не «мышь, ты убита». Надо дать другое название, например $mouse->setKilled (пометить убитой), $mouse->makeDead() (сделать мертвой) или $mouse->setIsDead() или $mouse->removeSelf() (удалить себя). killOneself не предлагаю, а то твой гитхаб закроют за пропаганду суицида.
Наконец еще можно при съедении животного сразу делать $world->remove... то есть обращаться не к животному, а к миру.
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Cat.php#L95
>return $move;
Не очень понятно зачем тут return, эти данные кому-то нужны? Мы не можем их получить через getX/Y?
> if (($forX > $x and $forY > $y) or ($forX > $x and $forY < $y)
Не понимаю что и зачем тут проверяется
> Нужна ли проверка is_a($object, "Animal")?
is_a не надо использовать, лучше использовать оператор instanceof. Думаю, проверка не нужна так как кроме животных на карте никого нет.
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Mouse.php#L39
Здесь не очень просто понять как считается число очков. Сделай отдельные факторы в отдельных переменных и складывай их в конце с весом:
rate = w1 × factor1 + w2 × factor2...
(только имена конечно нужны понятные).
> already have it object - {get_class($animal)}"
Функция get_class не вызовется, а будет ошибка из-за невозможности преобразовать $animal в строку
> public function isInsideMap($x, $y) {
Результат который возвращает функция противоположен ее названию
> Не осилил. Сделал через this. и метод.
Я имею в виду, цены одинаковы для всех экземпляров класса. Значит их можно сделать статическим полем, полем которое принадлежит всему классу, а не в каждом объекте свое. То есть сделать чтобы было не this.caloriesAndPrices, а Hamburger.caloriesAndPrices, и метод который их создает, тоже чтобы принадлежал классу.
А так, код в общем выглядит верно.
> А каким стандартом вообще пользоваться, а то я все наугад делаю.
Отступ добавляется внутри if, циклов, функций, то есть внутри блоков. Также, мы добавляем отступ когда переносим длинную строку.
Вообще, со стандартом оформления кода в JS беда - единого нет, потому каждый пишет как хочет и готов насмерть драться чтобы показать что его стандарт лучше.
Вот, что я нашел:
- стиль от автора учебника learn.js: https://learn.javascript.ru/coding-style . Мне в нем не нравится отступ в 2 пробела, это побуждает делать большую вложенность
- гугловский стайл гайд: https://google.github.io/styleguide/javascriptguide.xml - мне нравится тем, что каждое правило объясняется, почему так а не иначе
- стайл гайд jquery, мне не нравится тем что требует использовать архаичные и сбивающие с толку табы и использует дурацкие пробелы внутри скобок: http://contribute.jquery.org/style-guide/js/
>>575323
Если что, у нас в ОП посте есть еще и задачки и на CSS, и на JS. Если есть время и желание - можешь глянуть, может быть что-то новое найдешь.
>>575466
> я создаю объект класса Rediska_Command_Set, и мне нужно передать вторым аргументом название команды Set. Это значит, что при помощи класса Rediska_Command_Set можно создать не только команду Set, но и любую другую?
А где написано что это выполняемая команда? Может это просто название. Надо посмотреть код и как этот $name используется.
> Еще прописан магический метод
Это по моему авторы зря делают, толку от этих методов никакого, только код усложняют.
>>575741
> А за теорией если что загляну в JS тред или гугл.
Не помогут тебе там, там мало кто нормально в ООП разбирается,а в гугле и подавно.
> Не осилил. Сделал через this. и метод.
Я имею в виду, цены одинаковы для всех экземпляров класса. Значит их можно сделать статическим полем, полем которое принадлежит всему классу, а не в каждом объекте свое. То есть сделать чтобы было не this.caloriesAndPrices, а Hamburger.caloriesAndPrices, и метод который их создает, тоже чтобы принадлежал классу.
А так, код в общем выглядит верно.
> А каким стандартом вообще пользоваться, а то я все наугад делаю.
Отступ добавляется внутри if, циклов, функций, то есть внутри блоков. Также, мы добавляем отступ когда переносим длинную строку.
Вообще, со стандартом оформления кода в JS беда - единого нет, потому каждый пишет как хочет и готов насмерть драться чтобы показать что его стандарт лучше.
Вот, что я нашел:
- стиль от автора учебника learn.js: https://learn.javascript.ru/coding-style . Мне в нем не нравится отступ в 2 пробела, это побуждает делать большую вложенность
- гугловский стайл гайд: https://google.github.io/styleguide/javascriptguide.xml - мне нравится тем, что каждое правило объясняется, почему так а не иначе
- стайл гайд jquery, мне не нравится тем что требует использовать архаичные и сбивающие с толку табы и использует дурацкие пробелы внутри скобок: http://contribute.jquery.org/style-guide/js/
>>575323
Если что, у нас в ОП посте есть еще и задачки и на CSS, и на JS. Если есть время и желание - можешь глянуть, может быть что-то новое найдешь.
>>575466
> я создаю объект класса Rediska_Command_Set, и мне нужно передать вторым аргументом название команды Set. Это значит, что при помощи класса Rediska_Command_Set можно создать не только команду Set, но и любую другую?
А где написано что это выполняемая команда? Может это просто название. Надо посмотреть код и как этот $name используется.
> Еще прописан магический метод
Это по моему авторы зря делают, толку от этих методов никакого, только код усложняют.
>>575741
> А за теорией если что загляну в JS тред или гугл.
Не помогут тебе там, там мало кто нормально в ООП разбирается,а в гугле и подавно.
О, анон учит PHP с планшетом, какая редкость. Код кстати пока неправильный, насколько я вижу.
>>575803
> Я вот поставил Debian и немножко поигрался с консолью, но почему-то он тормазит под VirtualBox, хотя в настройках я выкрутил все на максимум.
Тормозит командная строка или графический интерфейс? Если второе то проблема с драйвером (нет драйвера обеспечивающего быстрый вывод данных), надо найти и установить дополнения от виртуал бокса в дебиан. По моему диск с ними «вставляется» через меню вирьуальной машины - Install VBox Additions, а дальше запускаешь находящийся на нем скрипт.
Ну и если ты поставил его для изучения, GUI тебе не нужен, отключай и заходи через ssh, например с помощью putty. Паста про настройку сетевой карты для доступа снаружи: https://gist.github.com/codedokode/420c8c12a1edae25f0ec
>>575825
>>575846
Сомневаюсь. По моему вы не понимаете в чем проблема и пишете чушь. Без драйверов так используется какой-то сверхмедленный и неэффективный способ доступа к видеопамяти (VESA по моему), такое ощущение что он на каждый пиксель какое-то АПИ вызывает. На реальных видеокартах та же ерунда, вот быдлокодеры набыдлокодили в свое время.
>>575847
> Лишнее время у меня уходит на создание файлов, создание классов,
Босюь, мне трудно поверить что создать файл или написать class X {} занимает дольше нескольких секунд.
Ничего, со временем научишься и привыкнешь. Ну и мы твой код не видели и не знаем, правильно ли ты все сделал или переусложнил без надобности и из-за этого мучаешься.
>>575901
Этот костыль нужен для прикручивания браузерного кода к REST-апи. Не переделывать же АПи из-за особенностей браузера.
>>575907
Плохая. Не надо парсить HTML регулярками. Используй DOM и основанные на нем библиотеки.
Еще и символ | додумался в качестве ограничителя использовать, какой кошмар.
>>575913
Не понял что ты имеешь в виду.
>>575933
Можно установить и потихоньку осваивать командную строку, права на файла и прочие вещи, пригодится.
>>575966
Непонятно какой именно вид памяти отображется. Например если ты последние полчаса не трогал Атом, он мог выпасть в своп и цифра уменьшится. Непонятно почему процессов атома несколько. Неизвестно какие настройки выставлены для ява машины. Также, ты сравниваешь IDE которая анализирует код с примитивным текстовым редактором - очевидно IDE нужно больше данных держать в памяти.
О, анон учит PHP с планшетом, какая редкость. Код кстати пока неправильный, насколько я вижу.
>>575803
> Я вот поставил Debian и немножко поигрался с консолью, но почему-то он тормазит под VirtualBox, хотя в настройках я выкрутил все на максимум.
Тормозит командная строка или графический интерфейс? Если второе то проблема с драйвером (нет драйвера обеспечивающего быстрый вывод данных), надо найти и установить дополнения от виртуал бокса в дебиан. По моему диск с ними «вставляется» через меню вирьуальной машины - Install VBox Additions, а дальше запускаешь находящийся на нем скрипт.
Ну и если ты поставил его для изучения, GUI тебе не нужен, отключай и заходи через ssh, например с помощью putty. Паста про настройку сетевой карты для доступа снаружи: https://gist.github.com/codedokode/420c8c12a1edae25f0ec
>>575825
>>575846
Сомневаюсь. По моему вы не понимаете в чем проблема и пишете чушь. Без драйверов так используется какой-то сверхмедленный и неэффективный способ доступа к видеопамяти (VESA по моему), такое ощущение что он на каждый пиксель какое-то АПИ вызывает. На реальных видеокартах та же ерунда, вот быдлокодеры набыдлокодили в свое время.
>>575847
> Лишнее время у меня уходит на создание файлов, создание классов,
Босюь, мне трудно поверить что создать файл или написать class X {} занимает дольше нескольких секунд.
Ничего, со временем научишься и привыкнешь. Ну и мы твой код не видели и не знаем, правильно ли ты все сделал или переусложнил без надобности и из-за этого мучаешься.
>>575901
Этот костыль нужен для прикручивания браузерного кода к REST-апи. Не переделывать же АПи из-за особенностей браузера.
>>575907
Плохая. Не надо парсить HTML регулярками. Используй DOM и основанные на нем библиотеки.
Еще и символ | додумался в качестве ограничителя использовать, какой кошмар.
>>575913
Не понял что ты имеешь в виду.
>>575933
Можно установить и потихоньку осваивать командную строку, права на файла и прочие вещи, пригодится.
>>575966
Непонятно какой именно вид памяти отображется. Например если ты последние полчаса не трогал Атом, он мог выпасть в своп и цифра уменьшится. Непонятно почему процессов атома несколько. Неизвестно какие настройки выставлены для ява машины. Также, ты сравниваешь IDE которая анализирует код с примитивным текстовым редактором - очевидно IDE нужно больше данных держать в памяти.
Ты тоже занимаешься глупостью, не стоит парсить HTML регуляркой.
>>576084
Я делаю в командной строке
php -r 'var_dump(2 + 2);'
Но надо смотреть как бы со спецсимволами не накосячить. Также, у PHP есть интерактивный режим ( http://php.net/manual/ru/features.commandline.interactive.php ), плохо работает под виндой:
php -a
После этого вводишь выражение. Есть автодополнение (не под виндой).
Также, если сторонняя программа которая делает то, что тебе нужно (такие программы называются REPL = Read, Evaluate, Print Loop) под названием Boris: http://habrahabr.ru/post/179145/
Еще есть PsySH: http://psysh.org/
Предлагаю сравнить и поделиться опытом с анонами.
>>576095
> '/product/{$id}'
Знак доллара не лишний?
>>576130
Не помогаешь - тогда уходи
>>576170
> Если задача адаптера исключительно в том, чтобы подстроить драйвер под интерфейс, и ничего более
Вообще да, потому он и называется адаптер. Ты должен смотреть как код получается лучше - если сделать класс просто адаптером или если поместить в него все, что относится к редису.
> И приведи еще примеры адаптеров, потому что тут не до конца ясно.
Ну не знаю, мне кажется как раз «адаптер» редиса и есть хороший пример реализации паттерна. Адаптеры используются как раз в таких ситуациях, когда есть несколько библиотек, не имеющих единого интерфейса. Мы организовываем этот интерфейс с помощью адаптеров.
Если проводить аналогии с реальным миром, то адаптер это например переходник с DVI на HDMI, который позволяет соединить 2 несовместимых устройства. Или адаптер питания, который одним концом включается в розетку, а другим в ноутбук.
Примеры: адаптеры для разных драйверов БД (mysqli/PDO), разных кешей, адаптеры для превращения твоей библиотеки в плагин для фреймворка.
Можешь еще в гугле попробовать посмотреть: https://www.google.ru/search?q=%D0%BF%D0%B0%D1%82%D1%82%D0%B5%D1%80%D0%BD+%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B5%D1%80&newwindow=1&gbv=1&sei=jbU_VtfRIsiqsgHh5b3ACg
> Я не могу четко сформулировать, в чем задача адаптера.
Приведение стороннего интерфейса к нужному виду. То есть в разных драйверах редиса команды реализуются разным кодом. Адаптер позволяет скрыть эту разницу и предоставляет один и тот же стандартный интерфейс, независимо от того какой драйвер подключен с другой стороны.
> Одним словом, при необходимости что-то изменить в нашем классе, не лезть в его код, а создать другой класс, реализующий те же интерфейсы, что и первый класс.
Не только. Изменить поведение класса можно унаследовав его или передав в конструктор через DI другой объект. Но суть принципа в том что эти изменения должны быть возможными снаружи.
В случае твоей библиотеки, надо иметь возможность совместить ее с новым драйвером редиса или базы данных, или поменять какие-то настройки, не трогая код самой библиотеки. Ну то есть люди будут подключать ее через тот же композер и ничего в ней изменить напрямую не могут (и не должны).
Ты тоже занимаешься глупостью, не стоит парсить HTML регуляркой.
>>576084
Я делаю в командной строке
php -r 'var_dump(2 + 2);'
Но надо смотреть как бы со спецсимволами не накосячить. Также, у PHP есть интерактивный режим ( http://php.net/manual/ru/features.commandline.interactive.php ), плохо работает под виндой:
php -a
После этого вводишь выражение. Есть автодополнение (не под виндой).
Также, если сторонняя программа которая делает то, что тебе нужно (такие программы называются REPL = Read, Evaluate, Print Loop) под названием Boris: http://habrahabr.ru/post/179145/
Еще есть PsySH: http://psysh.org/
Предлагаю сравнить и поделиться опытом с анонами.
>>576095
> '/product/{$id}'
Знак доллара не лишний?
>>576130
Не помогаешь - тогда уходи
>>576170
> Если задача адаптера исключительно в том, чтобы подстроить драйвер под интерфейс, и ничего более
Вообще да, потому он и называется адаптер. Ты должен смотреть как код получается лучше - если сделать класс просто адаптером или если поместить в него все, что относится к редису.
> И приведи еще примеры адаптеров, потому что тут не до конца ясно.
Ну не знаю, мне кажется как раз «адаптер» редиса и есть хороший пример реализации паттерна. Адаптеры используются как раз в таких ситуациях, когда есть несколько библиотек, не имеющих единого интерфейса. Мы организовываем этот интерфейс с помощью адаптеров.
Если проводить аналогии с реальным миром, то адаптер это например переходник с DVI на HDMI, который позволяет соединить 2 несовместимых устройства. Или адаптер питания, который одним концом включается в розетку, а другим в ноутбук.
Примеры: адаптеры для разных драйверов БД (mysqli/PDO), разных кешей, адаптеры для превращения твоей библиотеки в плагин для фреймворка.
Можешь еще в гугле попробовать посмотреть: https://www.google.ru/search?q=%D0%BF%D0%B0%D1%82%D1%82%D0%B5%D1%80%D0%BD+%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B5%D1%80&newwindow=1&gbv=1&sei=jbU_VtfRIsiqsgHh5b3ACg
> Я не могу четко сформулировать, в чем задача адаптера.
Приведение стороннего интерфейса к нужному виду. То есть в разных драйверах редиса команды реализуются разным кодом. Адаптер позволяет скрыть эту разницу и предоставляет один и тот же стандартный интерфейс, независимо от того какой драйвер подключен с другой стороны.
> Одним словом, при необходимости что-то изменить в нашем классе, не лезть в его код, а создать другой класс, реализующий те же интерфейсы, что и первый класс.
Не только. Изменить поведение класса можно унаследовав его или передав в конструктор через DI другой объект. Но суть принципа в том что эти изменения должны быть возможными снаружи.
В случае твоей библиотеки, надо иметь возможность совместить ее с новым драйвером редиса или базы данных, или поменять какие-то настройки, не трогая код самой библиотеки. Ну то есть люди будут подключать ее через тот же композер и ничего в ней изменить напрямую не могут (и не должны).
Там на сайте на картинках показаны еще другие возможности вроде просмотра информации о классе, какие у него есть поля и методы (включая встроенные PHP классы вроде PDO).
Я кого-то пропустил? Напомните о себе. Про анона https://github.com/p-5ch/vector/ я помню.
>>576148
Спасибо, ребят. Я затупил и не посмотрел. Вообще взял Silex чисто из соображений что микрофреймворк, было бы хорошо осовить.
Взял его для совсем небольшого интернет-магазина. Правда, для очень даже солидной организации, денег дадут не много, но полная свобода с технологиями, кроме сервера, ибо он вообще у всех один. А вообще я тот анон, который устроился на работу в конце весны и работаю с Magento
Зачем писать свой магазин когда есть десятки готовых CMS, включая саму магенто, ведь достаточно взять их настроить и натянуть верстку?
Слишком много функционала, и слишком много весят, а Silex весит меньше чем, долбанный Guzzle , который я на прошлом проекте использывал. Тем более, это чудесная возможность позаниматься ООП и осовить новый фреймворк.
Ах, да, Magento медленная очень, она просто огромная, у нас на dev под Magento выделяют охуённые ресурсы, что бы не тупила.
Судя по количеству аргументов и объему кода, разработчикам удалось тут реализовать паттерн God Object: https://ru.wikipedia.org/wiki/%D0%91%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82
И вдобавок ниже идет функция с названием «угадай где подвох»:
> protected function _construct()
Анон, который делает сайт объявлений на Юи, я ведь помню, ты все время просил тебе что-то дать поближе к реальным задачам, посмотри репозиторий. Может паттерн какой знакомый увидишь.
Это вполне реальный проект (и как я понимаю, прибыльный, раз эта компания смогла продаться ебэю). Над ним судя по объему кода, работает немаленькая команда.
Кстати, у них и тесты есть какие-то:
https://github.com/magento/magento2/tree/develop/dev/tests
Как исправить? пдовыблядкам просьба пройти нахуй
function db_query($mysqli, $query, $vars) {
if(!isset($mysqli)) {
return FALSE;
}
if(!isset($query)) {
return FALSE;
}
if(!isset($vars)) {
return FALSE;
}
$stmt = $mysqli->prepare($query);
$types = str_repeat('s', count($vars));
$vars = array_unshift($vars, $types);
call_user_func_array(array($stmt, 'bind_param'), $vars);
$stmt->execute();
}
$mysqli = db_connect();
$query = 'INSERT INTO `test`(`name`, `server`) VALUES (?,?)';
$vars = array('name' => 'name', 'server' => 'server');
db_query($mysqli, $query, $vars);
Как исправить? пдовыблядкам просьба пройти нахуй
function db_query($mysqli, $query, $vars) {
if(!isset($mysqli)) {
return FALSE;
}
if(!isset($query)) {
return FALSE;
}
if(!isset($vars)) {
return FALSE;
}
$stmt = $mysqli->prepare($query);
$types = str_repeat('s', count($vars));
$vars = array_unshift($vars, $types);
call_user_func_array(array($stmt, 'bind_param'), $vars);
$stmt->execute();
}
$mysqli = db_connect();
$query = 'INSERT INTO `test`(`name`, `server`) VALUES (?,?)';
$vars = array('name' => 'name', 'server' => 'server');
db_query($mysqli, $query, $vars);
кекнул с этого говнокода.
> $vars = array_unshift($vars, $types);
>Описание
>int array_unshift ( array &$array , mixed $value1 [, mixed $... ] )
>int
Смекаешь?
if(1){render->(totot);}
else render(toto);
Ты имеешь ввиду что надо так?
function db_query($mysqli, $query, $vars) {
if(!isset($mysqli)) {
return FALSE;
}
if(!isset($query)) {
return FALSE;
}
if(!isset($vars)) {
return FALSE;
}
$stmt = $mysqli->prepare($query);
$types = str_repeat('s', count($vars));
> array_unshift($vars, $types);
call_user_func_array(array($stmt, 'bind_param'), $vars);
$stmt->execute();
}
$mysqli = db_connect();
$query = 'INSERT INTO `test`(`name`, `server`) VALUES (?,?)';
$vars = array('name' => 'name', 'server' => 'server');
db_query($mysqli, $query, $vars);
Ты имеешь ввиду что надо так?
function db_query($mysqli, $query, $vars) {
if(!isset($mysqli)) {
return FALSE;
}
if(!isset($query)) {
return FALSE;
}
if(!isset($vars)) {
return FALSE;
}
$stmt = $mysqli->prepare($query);
$types = str_repeat('s', count($vars));
> array_unshift($vars, $types);
call_user_func_array(array($stmt, 'bind_param'), $vars);
$stmt->execute();
}
$mysqli = db_connect();
$query = 'INSERT INTO `test`(`name`, `server`) VALUES (?,?)';
$vars = array('name' => 'name', 'server' => 'server');
db_query($mysqli, $query, $vars);
а все уже.
А это нормально запихивать все в классы и объекты классов в классы и повторяющийся код в классы, пока в контроллере не останется пара строчек?
ОП, поправил недостатки.
https://github.com/never3ver/catsandmice
> https://github.com/never3ver/catsandmice/blob/master/classes/World.php#L51
>Тут не >= должно быть?
Думаю нет, в нашем случае координаты равные максимумам ширины и длины мира все-таки находится в его границах. По крайней мере я не замечал, чтобы у меня звери пропадали с карты без причины.
>Так в общем, код неплох, меня устраивает, разве что комментариев как-то маловато.
Комментарии наверно специально дописывать не буду, но на будущее учту. Я могу браться за студентов?
Что есть для почтовых рассылок, какие сервисы? Сайт - магазин (конечно же лол), письма шлются при заказе клиенту и заказавшему + всякие там восстановления паролей и тд. 2к писем с smtp.google.com по пятницам может не хватать. Свой почтовый VPS/VDS сервер поднимать побаиваюсь, никогда не пробовал, что там делать представляю приблизительно (через пердольку поставить готовые велосипеды, настроить их), как через него отправлять - не представляю. Какие ещё есть решения?
Зачем делать конструктор защищенным? Они там синглтон мутят что ли?
Одно подчеркивание это опечатка?
>>576598
Зачем мне смотреть репозиторий magento?
Если честно, не знаю даже в какой последовательности смотреть эту тучу файлов, документацию изучать неохота.
>index.php
} catch (\Exception $e) {
echo <<<HTML
<div style="font:12px/1.35em arial, helvetica, sans-serif;">...
Зачем на лету малевать хердок, если можно подключить шаблон из файла?
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
Чего? Зачем передавать глобальный массив в качестве параметра? 'BP' это константа что ли? А, вот она валяется в файле autoload.php, видимо это самое подходящее для нее место.
define('BP', dirname(__DIR__));
Пытаюсь отыскать класс \Magento\Framework\App\Bootstrap
Удалось это сделать только через гугл.
https://github.com/magento/magento2/blob/develop/lib/internal/Magento/Framework/App/Bootstrap.php
>Может паттерн какой знакомый увидишь.
Наверное, это ирония или другое проявление чувства юмора. Я в паттернах не разбираюсь, разработчики магенто судя по лапше тоже не особо.
Ну ладно, вернемся к нашим баранам.
Если допустить, что адаптеры для редиса являются только оберткой над клиентом, то выношу свойства $keyPrefix, $keyExpiration в класс VisitCounter.
Но теперь если быть последовательным, то нужно удалить все лишнее и из адаптера базы. Там мне необходимы такие свойства как $pk, $tblName и $colName.
Если их тоже перенести в главный класс, то появляется несколько проблем. Во-первых, эти свойства нужны только для реляционных sql хранилищ, а мы пытаемся мутить нечто универсальное, что должно работать и с NoSQL базами. Я с ними никогда не сталкивался, но думаю что там нет таблиц.
Тем не менее, мне нужен универсальный метод save у класса DbAdapter.
Объявить необязательные параметры? Выглядит костыльно:
abstract public function save(array $visits, $pk = '', $tblName = '', $colName = '');
Наверное, мне придется разобраться с тем, как устроена MongoDb и написать отдельный интерфейс под nosql.
Класс VisitCounter судя по всему тоже придется разбить на два отдельных, один для работы с базой, второй для работы с редисом.
Еще несколько вопросов, пока не забыл.
1. Мне еще нужно проверить, не является ли посетитель ботом. Нагуглил функцию http://php.net/manual/ru/function.get-browser.php , она вернет нужную информацию?
2. Как сделать так, чтобы композер дописывал в свою автозагрузку классы пакета?
Я знаю как прописать в composer.json, чтобы он скачал пакет с гитхаба, но не знаю, как добавить классы пакета в автозагрузку.
Могу прописать "autoload" путь к пространству имен вида
"autoload": {
"psr-4": {"RootNamespace\\": "vendor/vendor-name/package-name/"}
}
Но мне кажется это костыльно, наверняка есть правильный способ подключения.
Зачем делать конструктор защищенным? Они там синглтон мутят что ли?
Одно подчеркивание это опечатка?
>>576598
Зачем мне смотреть репозиторий magento?
Если честно, не знаю даже в какой последовательности смотреть эту тучу файлов, документацию изучать неохота.
>index.php
} catch (\Exception $e) {
echo <<<HTML
<div style="font:12px/1.35em arial, helvetica, sans-serif;">...
Зачем на лету малевать хердок, если можно подключить шаблон из файла?
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
Чего? Зачем передавать глобальный массив в качестве параметра? 'BP' это константа что ли? А, вот она валяется в файле autoload.php, видимо это самое подходящее для нее место.
define('BP', dirname(__DIR__));
Пытаюсь отыскать класс \Magento\Framework\App\Bootstrap
Удалось это сделать только через гугл.
https://github.com/magento/magento2/blob/develop/lib/internal/Magento/Framework/App/Bootstrap.php
>Может паттерн какой знакомый увидишь.
Наверное, это ирония или другое проявление чувства юмора. Я в паттернах не разбираюсь, разработчики магенто судя по лапше тоже не особо.
Ну ладно, вернемся к нашим баранам.
Если допустить, что адаптеры для редиса являются только оберткой над клиентом, то выношу свойства $keyPrefix, $keyExpiration в класс VisitCounter.
Но теперь если быть последовательным, то нужно удалить все лишнее и из адаптера базы. Там мне необходимы такие свойства как $pk, $tblName и $colName.
Если их тоже перенести в главный класс, то появляется несколько проблем. Во-первых, эти свойства нужны только для реляционных sql хранилищ, а мы пытаемся мутить нечто универсальное, что должно работать и с NoSQL базами. Я с ними никогда не сталкивался, но думаю что там нет таблиц.
Тем не менее, мне нужен универсальный метод save у класса DbAdapter.
Объявить необязательные параметры? Выглядит костыльно:
abstract public function save(array $visits, $pk = '', $tblName = '', $colName = '');
Наверное, мне придется разобраться с тем, как устроена MongoDb и написать отдельный интерфейс под nosql.
Класс VisitCounter судя по всему тоже придется разбить на два отдельных, один для работы с базой, второй для работы с редисом.
Еще несколько вопросов, пока не забыл.
1. Мне еще нужно проверить, не является ли посетитель ботом. Нагуглил функцию http://php.net/manual/ru/function.get-browser.php , она вернет нужную информацию?
2. Как сделать так, чтобы композер дописывал в свою автозагрузку классы пакета?
Я знаю как прописать в composer.json, чтобы он скачал пакет с гитхаба, но не знаю, как добавить классы пакета в автозагрузку.
Могу прописать "autoload" путь к пространству имен вида
"autoload": {
"psr-4": {"RootNamespace\\": "vendor/vendor-name/package-name/"}
}
Но мне кажется это костыльно, наверняка есть правильный способ подключения.
На сервере сохраняешь файл в нужную директорию, путь к файлу пишешь в базу.
>в фоне
В смысле "не перезагружая страницу в браузере"? Тут нужен AJAX. Чтобы осознанно работать с ним, надо бы познакомиться с яваскриптом.
> яваскрипт
Ну ладно, это не важно.
> охраняешь файл в нужную директорию, путь к файлу пишешь в базу
Это понятно, но как мне одновременно обработать и текст, и картинку? По одиночке я могу это сделать
В каком месте возникают вопросы? Текст достаешь из массива $_POST, файл - из массива $_FILES. Валидируешь, сохраняешь по очереди.
Это не конструктор, в том-то и дело, так как у конструктора 2 подчеркивания. Очевидно это внутренняя функция инициализации, которая может быть вызывается из конструктора и ей не зачем быть публичной. Но называть так функцию - плохая идея, так как я сначала не понял в чем дело и долго думал как они смогли сделать 2 конструктора в одном классе.
Насчет магенто - я вынужден не согласиться с твоей критикой. Когда пишется большой и сложный проект, очень трудно поддерживать в нем хорошее качество кода, постоянно вносятся правки и изменения. Без паттернов и ООП там было бы намного хуже - куча функций и передающихся туда-сюда массивов непонятной структуры и многократной вложенности (как в друпале).
Конечно, проблемы есть и многое наверно требует изменений, но сколько времени уйдет на эти изменения учитывая объем кода? А ведь надо развивать проект, добавлять новые фичи, исправлять баги, просто так закрыть его на рефакторинг на полгодика нельзя. Инвесторам нужен рост и красивые цифры прибыли, а не красивая архитектура.
Неизвестно еще, что бы ты напроектировал, если бы тебе надо было сделать и поддерживать аналогичную CMS для магазинов. Да и во многих проектах код хуже. А у них есть и автоматизированные тесты, и проверялки стиля кода.
Хотя конечно 20 аргументов в конструкторе это признак что дело плохо и надо что-то менять.
Ссылку я дал потому, что ты одно время говорил что тебе хочется что-то поближе к реальным задачам, вот вполне реальная задача, кто-то поддерживает этот код и пишет к нему модули. Собственно, глядя на код ты себе вполне можешь представить себе реальные задачи (там есть Issues и Pull requests, по ним можно понять какие именно задачи решают разработчики).
> Если допустить, что адаптеры для редиса являются только оберткой над клиентом, то выношу свойства $keyPrefix, $keyExpiration в класс VisitCounter.
Но теперь если быть последовательным, то нужно удалить все лишнее и из адаптера базы. Там мне необходимы такие свойства как $pk, $tblName и $colName.
Если ты их вынесешь в VC то ты делаешь лишние ограничения: данные могут сохраняться только в базу, таблица может быть только одна, у нее должен быть первичный ключ из 1 колонки. По моему это неудобно.
> Но теперь если быть последовательным, то нужно удалить все лишнее и из адаптера базы.
Нет, не нужно. Настройки наверно должны задаваться там, где они используются.
> и с NoSQL базами. Я с ними никогда не сталкивался, но думаю что там нет таблиц.
Ну редис представь, это NoSQL хранилище без вторичных индексов (то есть искать можно только по первичному ключу) в общем-то.
> Наверное, мне придется разобраться с тем, как устроена MongoDb и написать отдельный интерфейс под nosql.
Можешь разобраться, если хочешь и есть время.
>Зачем на лету малевать хердок, если можно подключить шаблон из файла?
Так как это фатальная ошибка. Если посмотреть то видно что там аж 2 варианта сообщения - текстовое для запуска из консоли и HTML для веба. Как я понимаю, чтобы если клиент неправильно установил магазин, он увидел не стандартную заглушку, а конкретное напоминание что он забыл сделать.
> Пытаюсь отыскать класс \Magento\Framework\App\Bootstrap
> Удалось это сделать только через гугл.
Надо было начинать с composer.json - там описаны пути для автозагрузки. Чем и хорош композер, что теперь не надо разбирать самописные автолоадеры (а в больших проектах они сложные и запутанные), а достаточно глянуть в composer.json.
> Класс VisitCounter судя по всему тоже придется разбить на два отдельных, один для работы с базой, второй для работы с редисом.
Зачем?
> Мне еще нужно проверить, не является ли посетитель ботом.
Что ты имеешь в виду под ботом? Определись. HTTP-клиент у которого стоит в юзер-агенте GoogleBot? Клиент не загружающий картинки? HTTP-клиент, не выполняющий яваскрипт код?
> Нагуглил функцию http://php.net/manual/ru/function.get-browser.php , она вернет нужную информацию?
Эта функция лишь получает данные из заголовка User-Agent и представляет их в виде массива. Читать:
https://ru.wikipedia.org/wiki/User_Agent
http://www.useragentstring.com/pages/useragentstring.php
https://en.wikipedia.org/wiki/User_agent (тут лучше чем в русской версии)
Вообще с UA связана куча нехороших историй, когда программист определяет браузер по нему и отдает ему чуть другую версию страницы, а потом выходит новая версия браузера и все ломается. Это настолько масштабно, что почти все браузеры (IE, opera, chrome, firefox) содержат в UA строку 'Mozilla' - так как раньше было всего 2 браузера: Mozilla Firefox (поддерживал стандарты) и IE6 (не поддерживал) и по этой строке UA вебмастера определяли какой перед ними браузер и отдавали им разный код. Сейчас все браузеры поддерживают стандарты, но быдлокод с проверкой UA остался на сайтах и в учебниках, и все браузеры включают в UA строку Mozilla.
И не только.
Вот например UA браузера Хром:
Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36
Тут можно найти несколько названий разных браузеров и движков, разработчики добавили все что вспомнили.
То есть из-за неправильного использования UA веб мастерами UA сейчас стали малопонятны человеку.
Еще из похожих историй- разработчики IE убрали из UA в новом IE11 название браузера которым обозначались предыдущие версии (было MSIE/9.0, теперь пишут Trident по названию движка, а не браузера). Опять же, так как вебмастера увидев в UA слово MSIE отдавали тому код рассчитанный на какой-нибудь IE5.5, в то время как уже IE8 начал поддерживать HTML4 и CSS2.1.
http://habrahabr.ru/sandbox/67192/
Вот полная история, я советую ознакомиться: http://geektimes.ru/post/84222/
Ты думаешь, на этом история заканчивается? Как бы не так. В последние года 3 стало модно по UA определять, мобильный браузер или десктопный. В UA ищут названия смартфонов, слово mobile, iOS, и интуиция подсказывает мне что мы придем к новому витку усложнения User-Agent.
Потому в общем, когда я слышу «User-Agent», я хватаюсь за воображаемый пистолет.
Определять кто перед тобой, лучше через feature testing, то есть проверяя наличие конкретных фич (поддерживает ли агент куки? а картинки? а яваскрипт?), а не устраивая гадание на гуще названий движков в UA.
Насчет магенто - я вынужден не согласиться с твоей критикой. Когда пишется большой и сложный проект, очень трудно поддерживать в нем хорошее качество кода, постоянно вносятся правки и изменения. Без паттернов и ООП там было бы намного хуже - куча функций и передающихся туда-сюда массивов непонятной структуры и многократной вложенности (как в друпале).
Конечно, проблемы есть и многое наверно требует изменений, но сколько времени уйдет на эти изменения учитывая объем кода? А ведь надо развивать проект, добавлять новые фичи, исправлять баги, просто так закрыть его на рефакторинг на полгодика нельзя. Инвесторам нужен рост и красивые цифры прибыли, а не красивая архитектура.
Неизвестно еще, что бы ты напроектировал, если бы тебе надо было сделать и поддерживать аналогичную CMS для магазинов. Да и во многих проектах код хуже. А у них есть и автоматизированные тесты, и проверялки стиля кода.
Хотя конечно 20 аргументов в конструкторе это признак что дело плохо и надо что-то менять.
Ссылку я дал потому, что ты одно время говорил что тебе хочется что-то поближе к реальным задачам, вот вполне реальная задача, кто-то поддерживает этот код и пишет к нему модули. Собственно, глядя на код ты себе вполне можешь представить себе реальные задачи (там есть Issues и Pull requests, по ним можно понять какие именно задачи решают разработчики).
> Если допустить, что адаптеры для редиса являются только оберткой над клиентом, то выношу свойства $keyPrefix, $keyExpiration в класс VisitCounter.
Но теперь если быть последовательным, то нужно удалить все лишнее и из адаптера базы. Там мне необходимы такие свойства как $pk, $tblName и $colName.
Если ты их вынесешь в VC то ты делаешь лишние ограничения: данные могут сохраняться только в базу, таблица может быть только одна, у нее должен быть первичный ключ из 1 колонки. По моему это неудобно.
> Но теперь если быть последовательным, то нужно удалить все лишнее и из адаптера базы.
Нет, не нужно. Настройки наверно должны задаваться там, где они используются.
> и с NoSQL базами. Я с ними никогда не сталкивался, но думаю что там нет таблиц.
Ну редис представь, это NoSQL хранилище без вторичных индексов (то есть искать можно только по первичному ключу) в общем-то.
> Наверное, мне придется разобраться с тем, как устроена MongoDb и написать отдельный интерфейс под nosql.
Можешь разобраться, если хочешь и есть время.
>Зачем на лету малевать хердок, если можно подключить шаблон из файла?
Так как это фатальная ошибка. Если посмотреть то видно что там аж 2 варианта сообщения - текстовое для запуска из консоли и HTML для веба. Как я понимаю, чтобы если клиент неправильно установил магазин, он увидел не стандартную заглушку, а конкретное напоминание что он забыл сделать.
> Пытаюсь отыскать класс \Magento\Framework\App\Bootstrap
> Удалось это сделать только через гугл.
Надо было начинать с composer.json - там описаны пути для автозагрузки. Чем и хорош композер, что теперь не надо разбирать самописные автолоадеры (а в больших проектах они сложные и запутанные), а достаточно глянуть в composer.json.
> Класс VisitCounter судя по всему тоже придется разбить на два отдельных, один для работы с базой, второй для работы с редисом.
Зачем?
> Мне еще нужно проверить, не является ли посетитель ботом.
Что ты имеешь в виду под ботом? Определись. HTTP-клиент у которого стоит в юзер-агенте GoogleBot? Клиент не загружающий картинки? HTTP-клиент, не выполняющий яваскрипт код?
> Нагуглил функцию http://php.net/manual/ru/function.get-browser.php , она вернет нужную информацию?
Эта функция лишь получает данные из заголовка User-Agent и представляет их в виде массива. Читать:
https://ru.wikipedia.org/wiki/User_Agent
http://www.useragentstring.com/pages/useragentstring.php
https://en.wikipedia.org/wiki/User_agent (тут лучше чем в русской версии)
Вообще с UA связана куча нехороших историй, когда программист определяет браузер по нему и отдает ему чуть другую версию страницы, а потом выходит новая версия браузера и все ломается. Это настолько масштабно, что почти все браузеры (IE, opera, chrome, firefox) содержат в UA строку 'Mozilla' - так как раньше было всего 2 браузера: Mozilla Firefox (поддерживал стандарты) и IE6 (не поддерживал) и по этой строке UA вебмастера определяли какой перед ними браузер и отдавали им разный код. Сейчас все браузеры поддерживают стандарты, но быдлокод с проверкой UA остался на сайтах и в учебниках, и все браузеры включают в UA строку Mozilla.
И не только.
Вот например UA браузера Хром:
Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36
Тут можно найти несколько названий разных браузеров и движков, разработчики добавили все что вспомнили.
То есть из-за неправильного использования UA веб мастерами UA сейчас стали малопонятны человеку.
Еще из похожих историй- разработчики IE убрали из UA в новом IE11 название браузера которым обозначались предыдущие версии (было MSIE/9.0, теперь пишут Trident по названию движка, а не браузера). Опять же, так как вебмастера увидев в UA слово MSIE отдавали тому код рассчитанный на какой-нибудь IE5.5, в то время как уже IE8 начал поддерживать HTML4 и CSS2.1.
http://habrahabr.ru/sandbox/67192/
Вот полная история, я советую ознакомиться: http://geektimes.ru/post/84222/
Ты думаешь, на этом история заканчивается? Как бы не так. В последние года 3 стало модно по UA определять, мобильный браузер или десктопный. В UA ищут названия смартфонов, слово mobile, iOS, и интуиция подсказывает мне что мы придем к новому витку усложнения User-Agent.
Потому в общем, когда я слышу «User-Agent», я хватаюсь за воображаемый пистолет.
Определять кто перед тобой, лучше через feature testing, то есть проверяя наличие конкретных фич (поддерживает ли агент куки? а картинки? а яваскрипт?), а не устраивая гадание на гуще названий движков в UA.
Еще из истории UA: Opera после выхода 10-й версии начала писать UA так:
# 12 - настоящая версия, presto - название движка оперы
Opera/9.80 Presto/12
Почему? Потому-что быдлокодеры использовали регулярку вроде
Opera/[1-6]
для определения старых версий Оперы, и новая Опера бы тоже под нее попала.
Кстати, по этой же причине новая Windows тоже пропустила версию 9 и перескочила с 8 на 10: http://geektimes.ru/post/238915/
> Я знаю как прописать в composer.json, чтобы он скачал пакет с гитхаба, но не знаю, как добавить классы пакета в автозагрузку.
правила автозагрузки для библиотеки определяются в ее собственном composer.json, а не там где ты ее подключаешь.
Что-то в коде косяки:
https://github.com/nsdvw/visit-counter/blob/master/src/VisitCounter.php#L38
floor тут значит что если записей меньше чем размер транзакции, то ничего не сбрасывается. По моему это плохая идея.
> https://github.com/nsdvw/visit-counter/blob/master/src/VisitCounter.php#L11
> private $keyPrefix = '';
Так как это необязательная опция, для нее надо разумное значение по умолчанию.
> $this->client = $redisAdapter;
Одинаковые вещи надо называть одинаково.
Ну и в шапку класса VisitCounter хорошо бы добавить комментарий с кратким описанием алгоритма.
Сам удивился.
Что не так?
В дампе базы данных присутствуют строки:
SET NAMES utf8; //в самом начале
DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; //после каждого CREATE TABLE
Пикрилейтед.
В настройках соединения доктрины с БД скорее всего не указана кодировка соедиения utf-8. Либо код не в utf-8.
Покажи конфиг или настройки доктрины.
> SET NAMES utf8; //в самом начале
Эта команда лишь говорит о том что дамп закодирован а utf8. Она никак не влияет на работу доктрины.
> DEFAULT CHARSET=utf8
Это говорит что данные в таблице хранятся в utf-8, но опять же доктрина тут не при чем. Важно, какая кодировка соединения выставляется когда доктрина подключается к базе и в какой кодировке она шлет данные (то есть в какой кодировке написано твой код, в какой присылает данные форм браузер и что стоит в meta charset).
>&$array
& какбы говорит нам что переданный массив уже будет изменён. Сам массив, а не его копия.
А потом ты ему зачем-то присваеваешь этому массиву возвращаемое значение типа Int (Возвращает новое количество элементов в array. ).
Почти нихуя не надо. Это всего лишь разметка, там не много элементов. А вот с CSS поебешься.
Сотни нефти за ссылочку
Теперь мне нужно оживить каждое из этих полей, то есть добавить к ним ajax обрабочик. Продавец вводит информацию в поле, ввел первые пару символов, а дальше уже выбирает из появившегося блока что это за дверь. Блок появляется через ajax запрос. И тут у меня появилась куча проблем.
Если делать отдельное поле на странице, то есть не создавать его через jquery, а сразу прописать в html, то форма ajax поиск работает, все хорошо. Однако если создавать такое же поле оно не хочет ну накак работать через ajax. То есть я ввожу туда данные, а скрипт не обрабатывает эту форму. Если проинспектить элемент через "Просмотра кода элемента", то инспектр вообще не ставит там пометку что эта форма обрабатывается каким-либо скриптом. Если же это же поле ввода написать выше вручную, как писал выше, а не создавать через jquery по нажатию на ссылку, то инспектр показывает что к обработчику она привязана. Вот про какие поментки я говорю (надпись ev):
https://gyazo.com/39569e0b92bf243030c4b2e72ef31e28
Помогите пожалуйста, пасаны. Весь день потратил на эту хрень, ничего не получается, а без этого не могу двигаться дальше. Вот код который у меня есть сейчас:
http://ideone.com/lrWZvr
Можно подумать он потом устроится в контору фреймворки писать.
Люди годами делают один сайтик или 2D игру. Не понимаю, как можно работать программистом в одиночку. Как минимум должна быть большая команда и каждый разрабатывает свою часть сайта.
два часа искал ошибку.
Ну я и не говорил, что магенто так уж плох, по сравнению с другими cms он очень даже хорош. Но вот если сравнить с проектами на фреймворках, разница ощущается.
>Неизвестно еще, что бы ты напроектировал
"Сперва добейся", я понял. Только у тебя привилегия пиздеть про говнокод.
>Что ты имеешь в виду под ботом?
Поискового бота, конечно. Как часто они ходят по страницам, кстати? Помечают ли себя как-то, чтобы их заходы на страницу можно было игнорировать?
В общем, переписал еще раз библиотеку для счетчика (версия 3).
https://github.com/nsdvw/visit-counter
Сделал класс редис-адаптера более высокоуровневым, как ты советовал. Тут минус в том, что нужно писать подробную инструкцию для человека, который вздумает написать адаптер для своего клиента. Раньше мне достаточно было заставить его реализовать несколько низкоуровневых методов типа lrange или setnx, а их вызовы я зашил в классе VisitCounter, то есть четко зафиксировал алгоритм работы библиотеки.
>>577257
Ну подробнее.
Как выглядят эти правила автозагрузки для библиотеки?
Так https://github.com/nsdvw/visit-counter/blob/master/composer.json не работает, class not found.
С тестированием давай возиться позже, я хочу привести в порядок сайт с объявлениями, как закончу можно будет для него написать тесты. Ну и твое задание про сайт с тестами тоже собираюсь сделать, мне нравится идея. На нем буду учить yii2. Такой план до конца этого года.
http://ideone.com/s9eMne
Можно ли вызвать метод класса foo?
Разобрался сам, больше все ищется и подставляется в разных формах как нужно. Но есть одно но. Работает только в созданных формах вручную, то есть в формал прописанных в самом файлике кода. Если же формы создаются через jquery по нажатию кнопки, то обработчик не срабатывает для этих форм. Как решить вот это вообще не понимаю. Может тут кто-нибудь подскажет?
https://ideone.com/0BFzHr
И сразу глупейший вопрос, на снисходительный и понятный ответ которого я надеюсь : ... сможет ли найти 18-летний студент не из России, учащийся на юридическом факультете, но с превосходным знанием 1-ого основного и 6 доп. языков, удаленную работу пхп-погромистом с удобным графиком?
Таки доделал, знакомый подсказал в чем дело и как исправить. Просто функцию обработки кейАпа нужно вешать при создании каждого поля.
7 языков программирования? Так может стэк технологий и ссылку на github в студию, а то у нас тут часто бывает разногласие относительно "превосходного знания", "от корки до корки" и т.п.
На фрилансе куча заказов, все зависит от твоего упорства и желания. Разумеется только выучив пхп тебе никто не предложет много проектов и денег за это, нужно понимать что изначально придется работать за гроши чтобы получить опыт + портфолио. Далее уже можешь просить больше, когда поймешь что уровень твоих работ и знаний превышает нынешнюю планку оплаты.
Я например благодаря этому треду основную работу нашел, где сейчас и учусь чему-то новому постоянно, и портфолио набиваю на будущее. Не знал по сути ничего, но учился на программиста при этом, и понял что пора бы задуматься о будущем. За два года я выучил html+css, научился верстать, пиздить идеи и примеры, внедряя их у себя. Далее немного освоил jquery, попутно со всем этим изучал php. На данный момент имею какие-никакие знания из каждой этой области, их вполне хватает чтобы читать чужой код, понимать как работает и при надобности применять у себя, ну и собственные разработки естественно. Я рад что так сложилось, прочитав этот тред впервые и зайдя на сайт ОПа мне понравились уроки, я занимался этим с удовольствием, ну и не прогадал вроде. Однако стоит заметить что от этих уроков я ушел довольно таки быстро и начал смотреть курсы от Специалиста. Хорошая вещь, рекомендую. По крайней мере как базис точно, объясняется все досконально и просто.
Заранее всем спасибо.
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Домашнее задание</title>
</head>
<style type="text/css">
.green1 {
width: 100%;
height: 1000px;
border:solid 2px green;
}
.red1 {
width: auto;
height: 150px;
border:solid 2px red;
margin:20px;
position: relative;
.red1 DIV{position:absolute;}
}
.red2 {
width: auto;
height: 100px;
border:solid 2px red;
margin:20px;
}
.red3 {
width: auto;
height: 450px;
border:solid 2px red;
margin:20px;
}
.red4 {
width: auto;
height: 180px;
border:solid 2px red;
margin:20px;
}
.blue1 {
width: 250px;
height: 120px;
border:solid 2px blue;
float:left;
}
.blue2 {
left:250px;
right:250px;
height: 120px;
border:solid 2px blue;
}
.blue3 {
width: 250px;
right:0;
height: 120px;
border:solid 2px blue;
}
.blue4 {
width: 20%;
height: 70px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue5 {
width: 19%;
height: 70px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue6 {
width: 19%;
height: 70px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue7 {
width: 34%;
height: 70px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue8 {
width: 20%;
height: 420px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue9 {
width: 76%;
height: 420px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue10 {
width: 20%;
height: 155px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue11 {
width: 50%;
height: 155px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue12 {
width: 24%;
height: 155px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.yellow1 {
width: 50%;
height: 40px;
border:solid 2px yellow;
margin:10px;
}
.yellow2 {
width: 40%;
height: 40px;
border:solid 2px yellow;
margin:10px;
}
.yellow3 {
width: auto;
height: 190px;
border:solid 2px yellow;
margin:10px 10px 10px 10px;
float:bottom;
}
.yellow4 {
width: auto;
height: 190px;
border:solid 2px yellow;
margin:10px 10px 10px 10px;
float:bottom;
}
.yellow5 {
width: auto;
height: 60px;
border:solid 2px yellow;
margin:10px 10px 10px 10px;
float:bottom;
}
.yellow6 {
width: auto;
height: 60px;
border:solid 2px yellow;
margin:10px 10px 10px 10px;
float:bottom;
}
</style>
</head>
<body>
<div class="green1">
<div class="red1" >
<div class="blue1"></div>
<div class="blue2"></div>
<div class="blue3">
<div class="yellow1"></div>
<div class="yellow2"></div>
</div>
</div>
<div class="red2">
<div class="blue4"></div>
<div class="blue5"></div>
<div class="blue6"></div>
<div class="blue7"></div>
</div>
<div class="red3">
<div class="blue8"></div>
<div class="blue9">
<div class="yellow3"></div>
<div class="yellow4"></div>
</div>
</div>
<div class="red4">\t
<div class="blue10">
<div class="yellow5"></div>
<div class="yellow6"></div>
</div>
<div class="blue11"></div>
<div class="blue12"></div>
</div>
</div>
</body>
</html>
Заранее всем спасибо.
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Домашнее задание</title>
</head>
<style type="text/css">
.green1 {
width: 100%;
height: 1000px;
border:solid 2px green;
}
.red1 {
width: auto;
height: 150px;
border:solid 2px red;
margin:20px;
position: relative;
.red1 DIV{position:absolute;}
}
.red2 {
width: auto;
height: 100px;
border:solid 2px red;
margin:20px;
}
.red3 {
width: auto;
height: 450px;
border:solid 2px red;
margin:20px;
}
.red4 {
width: auto;
height: 180px;
border:solid 2px red;
margin:20px;
}
.blue1 {
width: 250px;
height: 120px;
border:solid 2px blue;
float:left;
}
.blue2 {
left:250px;
right:250px;
height: 120px;
border:solid 2px blue;
}
.blue3 {
width: 250px;
right:0;
height: 120px;
border:solid 2px blue;
}
.blue4 {
width: 20%;
height: 70px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue5 {
width: 19%;
height: 70px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue6 {
width: 19%;
height: 70px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue7 {
width: 34%;
height: 70px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue8 {
width: 20%;
height: 420px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue9 {
width: 76%;
height: 420px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue10 {
width: 20%;
height: 155px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue11 {
width: 50%;
height: 155px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.blue12 {
width: 24%;
height: 155px;
border:solid 2px blue;
margin:10px 10px 10px 10px;
float:left;
}
.yellow1 {
width: 50%;
height: 40px;
border:solid 2px yellow;
margin:10px;
}
.yellow2 {
width: 40%;
height: 40px;
border:solid 2px yellow;
margin:10px;
}
.yellow3 {
width: auto;
height: 190px;
border:solid 2px yellow;
margin:10px 10px 10px 10px;
float:bottom;
}
.yellow4 {
width: auto;
height: 190px;
border:solid 2px yellow;
margin:10px 10px 10px 10px;
float:bottom;
}
.yellow5 {
width: auto;
height: 60px;
border:solid 2px yellow;
margin:10px 10px 10px 10px;
float:bottom;
}
.yellow6 {
width: auto;
height: 60px;
border:solid 2px yellow;
margin:10px 10px 10px 10px;
float:bottom;
}
</style>
</head>
<body>
<div class="green1">
<div class="red1" >
<div class="blue1"></div>
<div class="blue2"></div>
<div class="blue3">
<div class="yellow1"></div>
<div class="yellow2"></div>
</div>
</div>
<div class="red2">
<div class="blue4"></div>
<div class="blue5"></div>
<div class="blue6"></div>
<div class="blue7"></div>
</div>
<div class="red3">
<div class="blue8"></div>
<div class="blue9">
<div class="yellow3"></div>
<div class="yellow4"></div>
</div>
</div>
<div class="red4">\t
<div class="blue10">
<div class="yellow5"></div>
<div class="yellow6"></div>
</div>
<div class="blue11"></div>
<div class="blue12"></div>
</div>
</div>
</body>
</html>
Пили новый трэд, жопка.
P.S. Это тест от badoo.
попробуй вместо $vars = array_unshift($vars, $types);
поставить array_unshift($vars, $types);
Постоянно проигрываю с этого скорострела.
> opcache.max_accelerated_files integer
> The maximum number of keys (and therefore scripts) in the OPcache hash table.
Что подразумевают под ключами (keys)?
Что такое hits? Кол-во запросов к кешированным скриптам(или "ключам")?
В исходниках написано https://github.com/zendtech/ZendOptimizerPlus/blob/8c3e56f83bf4fda5f6780618638c77ee43867a70/zend_shared_alloc.h#L105
> Amount of shared memory allocated by garbage
Тут http://www.koscheev.ru/nazlobu/?n=194 написано
> OPcache не имеет встроенного менеджера кеша. Когда скрипт изменяется, его старая копия остается в памяти (wasted memory). Кеш очищается автоматически и полностью, если недостаточно доступной памяти для кеширования новых файлов и при этом количество "wasted memory" больше, чем opcache.max_wasted_percentage % от opcache.memory_consumption. По умолчанию opcache.max_wasted_percentage=5.
> Что подразумевают под ключами (keys)?
Кеш можно представить чем-то вроде массива, где ключом является путь к скрипту, а значением - сам скомпилированный скрипт:
$cache['/tmp/file.php'] = ...;
То есть ключ значит элемент хранящийся в кеше.
> Что такое hits? Кол-во запросов к кешированным скриптам(или "ключам")?
Я думаю, число успешных запросов (для которых в кеше нашелся ответ) в противоположность misses
Отношение hits/misses показывает эффективен или бесполезен ли кеш (как именно догадайся сам).
При использовании кеша помни про 2 подвоза:
1) оп-кешер может содержать баги или быть несовместим с какими-то файлами. Я помню случай когда один из оп-кешеров ронял апач (не весь а только один рабочий процесс) при попытке подключить Zend Framework. На такой случай там предусмотрена возможность исключать кеширование по маске.
2) оп-кешер может проверять дату изменения файла на диске каждый раз, не проверять или проверять не чаще раза в N минут ради большей эффективности. В этом случае твои изменения могут не примениться сразу, а только через какое-то время. То есть при выполнении require_once будет подключена старая версия файла из кеша. Для борьбы с этим нужно явно вызывать opcache_reset() при выгрузке нового кода на сервер. А на локалхосте лучше включить режим постоянной перепроверки и все будет работать само.
В исходниках написано https://github.com/zendtech/ZendOptimizerPlus/blob/8c3e56f83bf4fda5f6780618638c77ee43867a70/zend_shared_alloc.h#L105
> Amount of shared memory allocated by garbage
Тут http://www.koscheev.ru/nazlobu/?n=194 написано
> OPcache не имеет встроенного менеджера кеша. Когда скрипт изменяется, его старая копия остается в памяти (wasted memory). Кеш очищается автоматически и полностью, если недостаточно доступной памяти для кеширования новых файлов и при этом количество "wasted memory" больше, чем opcache.max_wasted_percentage % от opcache.memory_consumption. По умолчанию opcache.max_wasted_percentage=5.
> Что подразумевают под ключами (keys)?
Кеш можно представить чем-то вроде массива, где ключом является путь к скрипту, а значением - сам скомпилированный скрипт:
$cache['/tmp/file.php'] = ...;
То есть ключ значит элемент хранящийся в кеше.
> Что такое hits? Кол-во запросов к кешированным скриптам(или "ключам")?
Я думаю, число успешных запросов (для которых в кеше нашелся ответ) в противоположность misses
Отношение hits/misses показывает эффективен или бесполезен ли кеш (как именно догадайся сам).
При использовании кеша помни про 2 подвоза:
1) оп-кешер может содержать баги или быть несовместим с какими-то файлами. Я помню случай когда один из оп-кешеров ронял апач (не весь а только один рабочий процесс) при попытке подключить Zend Framework. На такой случай там предусмотрена возможность исключать кеширование по маске.
2) оп-кешер может проверять дату изменения файла на диске каждый раз, не проверять или проверять не чаще раза в N минут ради большей эффективности. В этом случае твои изменения могут не примениться сразу, а только через какое-то время. То есть при выполнении require_once будет подключена старая версия файла из кеша. Для борьбы с этим нужно явно вызывать opcache_reset() при выгрузке нового кода на сервер. А на локалхосте лучше включить режим постоянной перепроверки и все будет работать само.
Ок, давай разберемся с твоим решением заадчи про Вектор. Надеюсь все это время ты не ждал меня, а изучал что-то полезное или решал другие задачи.
Для начала, стандартная паста на тему как надо решать ООП-задачки:
-----------
Когда ты решаешь задачу на ООП, ты должен ответить на вопросы:
— какие есть сущности, для которых мы сделаем классы? (Сотрудник и Департамент)
— какие у них есть свойства (у Сотрудника есть ранг, базовая ставка, профессия, является ли боссом). Потребление кофе или зарплата не являются свойствами так как они вычисляются из других свойств и хранить их не надо.
— что мы хотим от них получить (какие у них должны быть методы). Например мы хотим узнать сколько сотрудник заработал или сколько он пьет кофе. От департамента мы наверно хотим получить сколько всего выпито кофе и заплачено денег.
— как сущности связаны? Очевидно, Сотрудник работает в каком-то Департаменте.
Также, сразу скажу еще один совет: гораздо удобнее сделать не один класс Сотрудник, а 4 класса: Инженер, Менеджер, и т.д. Тогда мы можем легко менять например правила расчет зарплаты или кофе для каждой профессии. Естественно, копипастить одинаковый код в 4 класса не надо — создай базовый абстрактный класс Сотрудник и унаследуй от него 4 класса-профессии.
Наследование позволяет создавать класс не с нуля. а расширяя сущетсвующий класс: http://php.net/manual/ru/language.oop5.inheritance.php
«Абстрактный» — это класс, объект которого нельзя создать. Он предназначен для наследования от него других классов: http://php.net/manual/ru/language.oop5.abstract.php
------------
Так вот, у тебя код, который должен быть помещен внутри класса, вынесен из него в index.php, например расчет расходов на зарплату по департаменту должен вычисляться в соответствующем методе департамента.
Также, у тебя там используется лишний массив. Вместо того, чтобы передать удобные объекты с методами в шаблон, ты зачем-то берешь из них данные и переносишь в массив сложной структуры. Массив однозначно хуже чем объект: у него нет методов, у него не определена структура, в отличие от класса. Думаю, массив стоит убрать, и передавать в шаблон сами объекты.
Это тоже неправильно, на мой взгляд:
> $salary = array("ме" => "500",
> "ин" => "200",
Если ты хочешь обозначать профессии, надо завести для них константы: http://php.net/manual/ru/language.oop5.constants.php вроде Employee::JOB_ENGINEER. Или, если ты решишь сделать по классу для каждой профессии, то можно использовать в качестве обозначения их имена: 'Manager' (а с версии 5.5 можно писать Manager::class - мануал: http://php.net/manual/ru/language.oop5.basic.php#language.oop5.basic.class.class )
> Order of properties and methods in class (best practices)
Порядок обычно такой: константы, поля, конструктор, публичные методы, непубличные методы.
> Specified in PSR?
А PSR доступен даже на русском, почитай:
http://www.php-fig.org/psr/psr-1/ru/
http://www.php-fig.org/psr/psr-2/ru/
Порядок там вроде не определен
> phpDOC. What is this? How to use it?
phpDoc - это стандарт комментариев для описания функций, классов. Комментарии в формате phpDoc понимают IDE и извлекают из них информацию (например показывают описание функции при наведении мыши на ее имя), а также из phpDoc комментариев можно автоматически сгенерировать документацию. Вот пример сгенерированной документации: http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Request.html а вот исходный файл с phpDoc-комментариями: https://github.com/symfony/http-foundation/blob/master/Request.php
phpDoc комментарии отличаются от обычных наличием 2 звездочек в начале.
Описание формата:
кратко https://ru.wikipedia.org/wiki/PhpDocumentor
статья в тему http://habrahabr.ru/post/162535/
и как всегда, подробно на английском: http://www.phpdoc.org/docs/latest/references/phpdoc/index.html
В 99% случаев достаточно знать только следующие теги: @author, @var, @param, @return, @see, @since, @deprecated и изучить как описываются сложные типы, например int[] или SomeClass|null.
Чтобы проверить что ты понял phpDoc, попробуй поставить комментарии в свои классы.
Сразу же замечу что @return mixed ( https://github.com/p-5ch/vector/blob/master/app/Model/Department.php#L19 ) это бессмысленный комментарий так как не несет никакой информации. Очевидно, название депатамента это строка и должно стоять @return string
Этот комментарий «Department constructor.» тоже не нужен так как и без него очевидно что перед нами конструктор.
> @return array of Employee
Не по стандарту написан тип
> @param Employee $employee
Комментарий дублирует тайп-хинт, не дает новой информации и потому не несет особой ценности.
Также, чтобы ты лучше разобрался в ООП, держи дополнительное задание к этой задаче (вообще, я его всем даю):
---------
Задание: напиши программу для учета расходов и результатов работы всего дружного коддектива компании «Вектор».
Пока ты решал задачу по выводу отчета о сотрудниках и департаментах, разразился мировой экономический кризис. Доходы компании начали снижаться, и совет директоров поставил перед руководством задачу принять меры. Менеджеры 3-го ранга, блестящие выпускники топовых экономических вузов столицы, быстро смогли разработать три альтернативных антикризисных решения:
1. Сократить в каждом департаменте 40% (округляя в большую сторону) инженеров, преимущественно самого низкого ранга. Если инженер является боссом, вместо него надо уволить другого инженера, не босса.
2. Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков, а количество выпиваемого им кофе с 50 до 75 литров. В тех департаментах, где руководитель не является аналитиком, заменить его на аналитика самого высшего ранга из этого департамента (а бывшего руководителя вернуть к обычной работе)
3. В каждом департаменте повысить 50% (округляя в большую сторону) менеджеров 1-го и 2-го ранга на один ранг с целью расширить их полномочия.
Совет директоров в затруднении: какой путь выбрать? Помоги им с этим, распечатав прогноз по потреблению и расходам (аналогичный тому что требуется в задаче) после принятия каждой из мер.
----------
И еще:
> echo "Can't load $class from: $classFile \n. Script halted";
Если файл не найден, ты не должен ничего делать. PHP сам обнаружит что класс не загрузаился и сам выведет подробное сообщение об ошибке с номером строки, чего в твоем случае нет.
К тому же если ты будешь подключать библиотеки, у них может быть свой автозагрузчик и этим кодом ты не даешь ему шанса запуститься.
Ок, давай разберемся с твоим решением заадчи про Вектор. Надеюсь все это время ты не ждал меня, а изучал что-то полезное или решал другие задачи.
Для начала, стандартная паста на тему как надо решать ООП-задачки:
-----------
Когда ты решаешь задачу на ООП, ты должен ответить на вопросы:
— какие есть сущности, для которых мы сделаем классы? (Сотрудник и Департамент)
— какие у них есть свойства (у Сотрудника есть ранг, базовая ставка, профессия, является ли боссом). Потребление кофе или зарплата не являются свойствами так как они вычисляются из других свойств и хранить их не надо.
— что мы хотим от них получить (какие у них должны быть методы). Например мы хотим узнать сколько сотрудник заработал или сколько он пьет кофе. От департамента мы наверно хотим получить сколько всего выпито кофе и заплачено денег.
— как сущности связаны? Очевидно, Сотрудник работает в каком-то Департаменте.
Также, сразу скажу еще один совет: гораздо удобнее сделать не один класс Сотрудник, а 4 класса: Инженер, Менеджер, и т.д. Тогда мы можем легко менять например правила расчет зарплаты или кофе для каждой профессии. Естественно, копипастить одинаковый код в 4 класса не надо — создай базовый абстрактный класс Сотрудник и унаследуй от него 4 класса-профессии.
Наследование позволяет создавать класс не с нуля. а расширяя сущетсвующий класс: http://php.net/manual/ru/language.oop5.inheritance.php
«Абстрактный» — это класс, объект которого нельзя создать. Он предназначен для наследования от него других классов: http://php.net/manual/ru/language.oop5.abstract.php
------------
Так вот, у тебя код, который должен быть помещен внутри класса, вынесен из него в index.php, например расчет расходов на зарплату по департаменту должен вычисляться в соответствующем методе департамента.
Также, у тебя там используется лишний массив. Вместо того, чтобы передать удобные объекты с методами в шаблон, ты зачем-то берешь из них данные и переносишь в массив сложной структуры. Массив однозначно хуже чем объект: у него нет методов, у него не определена структура, в отличие от класса. Думаю, массив стоит убрать, и передавать в шаблон сами объекты.
Это тоже неправильно, на мой взгляд:
> $salary = array("ме" => "500",
> "ин" => "200",
Если ты хочешь обозначать профессии, надо завести для них константы: http://php.net/manual/ru/language.oop5.constants.php вроде Employee::JOB_ENGINEER. Или, если ты решишь сделать по классу для каждой профессии, то можно использовать в качестве обозначения их имена: 'Manager' (а с версии 5.5 можно писать Manager::class - мануал: http://php.net/manual/ru/language.oop5.basic.php#language.oop5.basic.class.class )
> Order of properties and methods in class (best practices)
Порядок обычно такой: константы, поля, конструктор, публичные методы, непубличные методы.
> Specified in PSR?
А PSR доступен даже на русском, почитай:
http://www.php-fig.org/psr/psr-1/ru/
http://www.php-fig.org/psr/psr-2/ru/
Порядок там вроде не определен
> phpDOC. What is this? How to use it?
phpDoc - это стандарт комментариев для описания функций, классов. Комментарии в формате phpDoc понимают IDE и извлекают из них информацию (например показывают описание функции при наведении мыши на ее имя), а также из phpDoc комментариев можно автоматически сгенерировать документацию. Вот пример сгенерированной документации: http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Request.html а вот исходный файл с phpDoc-комментариями: https://github.com/symfony/http-foundation/blob/master/Request.php
phpDoc комментарии отличаются от обычных наличием 2 звездочек в начале.
Описание формата:
кратко https://ru.wikipedia.org/wiki/PhpDocumentor
статья в тему http://habrahabr.ru/post/162535/
и как всегда, подробно на английском: http://www.phpdoc.org/docs/latest/references/phpdoc/index.html
В 99% случаев достаточно знать только следующие теги: @author, @var, @param, @return, @see, @since, @deprecated и изучить как описываются сложные типы, например int[] или SomeClass|null.
Чтобы проверить что ты понял phpDoc, попробуй поставить комментарии в свои классы.
Сразу же замечу что @return mixed ( https://github.com/p-5ch/vector/blob/master/app/Model/Department.php#L19 ) это бессмысленный комментарий так как не несет никакой информации. Очевидно, название депатамента это строка и должно стоять @return string
Этот комментарий «Department constructor.» тоже не нужен так как и без него очевидно что перед нами конструктор.
> @return array of Employee
Не по стандарту написан тип
> @param Employee $employee
Комментарий дублирует тайп-хинт, не дает новой информации и потому не несет особой ценности.
Также, чтобы ты лучше разобрался в ООП, держи дополнительное задание к этой задаче (вообще, я его всем даю):
---------
Задание: напиши программу для учета расходов и результатов работы всего дружного коддектива компании «Вектор».
Пока ты решал задачу по выводу отчета о сотрудниках и департаментах, разразился мировой экономический кризис. Доходы компании начали снижаться, и совет директоров поставил перед руководством задачу принять меры. Менеджеры 3-го ранга, блестящие выпускники топовых экономических вузов столицы, быстро смогли разработать три альтернативных антикризисных решения:
1. Сократить в каждом департаменте 40% (округляя в большую сторону) инженеров, преимущественно самого низкого ранга. Если инженер является боссом, вместо него надо уволить другого инженера, не босса.
2. Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков, а количество выпиваемого им кофе с 50 до 75 литров. В тех департаментах, где руководитель не является аналитиком, заменить его на аналитика самого высшего ранга из этого департамента (а бывшего руководителя вернуть к обычной работе)
3. В каждом департаменте повысить 50% (округляя в большую сторону) менеджеров 1-го и 2-го ранга на один ранг с целью расширить их полномочия.
Совет директоров в затруднении: какой путь выбрать? Помоги им с этим, распечатав прогноз по потреблению и расходам (аналогичный тому что требуется в задаче) после принятия каждой из мер.
----------
И еще:
> echo "Can't load $class from: $classFile \n. Script halted";
Если файл не найден, ты не должен ничего делать. PHP сам обнаружит что класс не загрузаился и сам выведет подробное сообщение об ошибке с номером строки, чего в твоем случае нет.
К тому же если ты будешь подключать библиотеки, у них может быть свой автозагрузчик и этим кодом ты не даешь ему шанса запуститься.
Получил какие-то цифры, но пока не знаю, хороши ли они, или нужно стремиться их улучшать (как?).
ab -c 5 -n 50
Document Path: /
Document Length: 68791 bytes
Concurrency Level: 5
Time taken for tests: 14.308 seconds
Complete requests: 50
Failed requests: 0
Total transferred: 3458850 bytes
HTML transferred: 3439550 bytes
Requests per second: 3.49 [#/sec] (mean)
Time per request: 1430.804 [ms] (mean)
Time per request: 286.161 [ms] (mean, across all concurrent requests)
Transfer rate: 236.08 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 1.6 0 6
Processing: 803 1400 192.5 1417 1685
Waiting: 788 1360 184.5 1365 1617
Total: 803 1400 192.7 1423 1685
Percentage of the requests served within a certain time (ms)
50% 1423
66% 1535
75% 1554
80% 1575
90% 1618
95% 1649
98% 1685
99% 1685
100% 1685 (longest request)
Единственный запрос, который производится на странице, оптимизирован (вроде бы) и закеширован.
Не знаю, о чем говорят тесты, но судя по логам yii, страница генерировалась 1.4 секунды, а по моему глазомеру даже дольше. Многовато что-то.
Что еще можно оптимизировать или закешировать?
Что я сделал: поставил индекс на колонках таблицы, по которым идет условие выборки; включил оп-кешер, дал ему памяти (128 должно хватить), увеличил mysql cache и innodb pool по 256 мб.
В самом скрипте кое-что переделал, например убрал кое-где актив-рекорды, заменил дао.
Ну в общем, что делать дальше с цифрами бенчмарка и что еще можно придумать для ускорения полета? Профайлер смотрел, но там вроде особо ничего уже не выдавишь. Приложу на всякий случай. http://rghost.ru/695mdbz8Q
Получил какие-то цифры, но пока не знаю, хороши ли они, или нужно стремиться их улучшать (как?).
ab -c 5 -n 50
Document Path: /
Document Length: 68791 bytes
Concurrency Level: 5
Time taken for tests: 14.308 seconds
Complete requests: 50
Failed requests: 0
Total transferred: 3458850 bytes
HTML transferred: 3439550 bytes
Requests per second: 3.49 [#/sec] (mean)
Time per request: 1430.804 [ms] (mean)
Time per request: 286.161 [ms] (mean, across all concurrent requests)
Transfer rate: 236.08 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 1.6 0 6
Processing: 803 1400 192.5 1417 1685
Waiting: 788 1360 184.5 1365 1617
Total: 803 1400 192.7 1423 1685
Percentage of the requests served within a certain time (ms)
50% 1423
66% 1535
75% 1554
80% 1575
90% 1618
95% 1649
98% 1685
99% 1685
100% 1685 (longest request)
Единственный запрос, который производится на странице, оптимизирован (вроде бы) и закеширован.
Не знаю, о чем говорят тесты, но судя по логам yii, страница генерировалась 1.4 секунды, а по моему глазомеру даже дольше. Многовато что-то.
Что еще можно оптимизировать или закешировать?
Что я сделал: поставил индекс на колонках таблицы, по которым идет условие выборки; включил оп-кешер, дал ему памяти (128 должно хватить), увеличил mysql cache и innodb pool по 256 мб.
В самом скрипте кое-что переделал, например убрал кое-где актив-рекорды, заменил дао.
Ну в общем, что делать дальше с цифрами бенчмарка и что еще можно придумать для ускорения полета? Профайлер смотрел, но там вроде особо ничего уже не выдавишь. Приложу на всякий случай. http://rghost.ru/695mdbz8Q
Вы можете перекатываться, но я сначала должен ответить всем, кого не проверил. А потом перейду в тот тред.
>>576577
Вообще, это не лучшая идея.
Во-первых, интернет-магазин это в общем сложная штука и писать ее долго. Гораздо выгоднее взять и допилить один из готовых движков.
Во-вторых,если уж писать на фреймворке, то Silex плохо подходит. Это микрофреймворк, он рассчитан на маленькие проекты, а магазин маленьким не будет.
>>576584
Может быть у вас что-то плохо настроено и кеши какие-нибудь не подключены. А может конечно и медленная, не знаю.
> preg_split("/([^!\?\.?]+[!\?\.?]+)/"
Почему такое сложно выражение? Ты хочешь получить предложение со знаком препинания? Тогда лучше использовать утверждения - они не захватывают символы при совпадении и они не теряются: http://php.net/manual/ru/regexp.reference.assertions.php
Также, не забывай ставить флаг u. Тут в регулярке нет кириллицы, но лучше привыкать ставить всегда, чтобы не ошибиться потом.
> [!\?\.?]
Непонятно зачем тут 2 раза знак вопроса. В квадратных скобках большинство символов не имеют специального значения (кроме 5 символов: ^ - [ ] \ ).
> function correctSpacesAroundChar($text, $char) {
Эту функцию, по моему, можно легко заменить 1 регулярным выражением и preg_replace. Будет и короче и понятнее.
>>576621
> preg_replace('/,/', '', $text);
Тут не нужна вся мощь регулярок и хватит str_replace
> implode(' ', explode(' ', $sentence));
Непонятен смысл этой операции
> $words[0] = makeFirstletterLowercase($words[0]);
Мне кажется тут можно не первую букву, а все слово сразу переводить в нижний регистр.
> $words[count($words)-1] = makeFirstletterUppercase($words[count($words)-1]);
Вот это не очень красиво смотрится. Лучше взять последний элемент через array_pop и потом положить обратно через $x[] = ...
>>576714
> А это нормально запихивать все в классы и объекты классов в классы и повторяющийся код в классы, пока в контроллере не останется пара строчек?
Зависит от ситуации. Так трудно сказать.
Ну и если ты передаешь $app в класс, то с вероятностью 99% ты делаешь что-то не так. $app это большой сложный объект и классу весь он целиком точно не нужен.
> preg_split("/([^!\?\.?]+[!\?\.?]+)/"
Почему такое сложно выражение? Ты хочешь получить предложение со знаком препинания? Тогда лучше использовать утверждения - они не захватывают символы при совпадении и они не теряются: http://php.net/manual/ru/regexp.reference.assertions.php
Также, не забывай ставить флаг u. Тут в регулярке нет кириллицы, но лучше привыкать ставить всегда, чтобы не ошибиться потом.
> [!\?\.?]
Непонятно зачем тут 2 раза знак вопроса. В квадратных скобках большинство символов не имеют специального значения (кроме 5 символов: ^ - [ ] \ ).
> function correctSpacesAroundChar($text, $char) {
Эту функцию, по моему, можно легко заменить 1 регулярным выражением и preg_replace. Будет и короче и понятнее.
>>576621
> preg_replace('/,/', '', $text);
Тут не нужна вся мощь регулярок и хватит str_replace
> implode(' ', explode(' ', $sentence));
Непонятен смысл этой операции
> $words[0] = makeFirstletterLowercase($words[0]);
Мне кажется тут можно не первую букву, а все слово сразу переводить в нижний регистр.
> $words[count($words)-1] = makeFirstletterUppercase($words[count($words)-1]);
Вот это не очень красиво смотрится. Лучше взять последний элемент через array_pop и потом положить обратно через $x[] = ...
>>576714
> А это нормально запихивать все в классы и объекты классов в классы и повторяющийся код в классы, пока в контроллере не останется пара строчек?
Зависит от ситуации. Так трудно сказать.
Ну и если ты передаешь $app в класс, то с вероятностью 99% ты делаешь что-то не так. $app это большой сложный объект и классу весь он целиком точно не нужен.
> Думаю нет, в нашем случае координаты равные максимумам ширины и длины мира все-таки находится в его границах. По крайней мере я не замечал, чтобы у меня звери пропадали с карты без причины.
По моему это типичная ошибка off-by-1. Если в мире ширина = 5 клеток и отсчет координат идет с нуля, то в нем X может принимать значения 0, 1, 2, 3, 4, но не 5.
> Я могу браться за студентов?
ты можешь браться в любом случае не дожидаясь пока я проверю код. Если что, вернешься к этой задаче.
Все животные могут (скорее обязаны) делать ход. Значит в классе Animal стоит объявить этот метод абстрактным, чтобы все наследники обязаны были его реализовать (если же ты не хочешь обязывать, то можно предоставить неабстрактный метод с базовой реализацией, которая например ничего не делает, кому надо, тот переопределит).
Мне кажется не очень правильным что у кошки и мышки по-разному считается лучший ход (где-то лучшим считается с минимальным числом очков, а где-то с максимальным). Это приводит к путанице, по моему. И затрудняет добавление каких-то других факторов.
> https://github.com/never3ver/catsandmice/blob/master/classes/Cat.php#L101
Здесь у тебя стоит условие: если на клетке не кошка, считаем ее свободной. Но правильнее писать «если на клетке мышка или если она пустая», так как могут появиться новые виды животных, например Собака, и на ее клетку нельзя ходить.
Тут https://github.com/never3ver/catsandmice/blob/master/classes/Mouse.php#L80 не учитывается квадрат зрения мышки.
В остальном, код вполне нормальный.
Не знаю, погугли. Есть наверно сервисы которые позволяют через API рассылать уведомления и решают проблемы с попаданием в спам.
>>576962
Если ты знаешь как сказать A и как сказать Б, что тебе мешает сказать обе буквы по очереди? Если нет, то возможно стоит подучить язык PHP.
>>577209
зря, это довольно неудобно и неэффективно. В базе стоит хранить разве что информацию об изображениях, но не сам файл.
>>577400
Паста:
----------
Я тут не буду писать очевидные вещи что ты должен знать что такое циклы, функции, классы, и уметь написать простую программу на уровне задач из моего учебника или SQL-запрос. Я напишу про вещи которые любят спрашивать сверх этого.
PHP: перечитай мануал, любят спрашивать тонкости языки типа преобразования одного типа в другой или правил сравнения типов (сработает ли if ("foo" == 0) { } или if ("1 person" == true) ? ). По ООП любят вопросы из серии «что выведет эта дебильная программа где мы переопределяем в наследнике приватный метод публичным с тем же именем», опять же спасает чтение мануала.
По теории могут спросить чем отличается абстрактный класс от интерфейса или как переопределить приватный метод в наследнике (правильный ответ: никак).
Если требуют знания фреймворков, могут спросить как реализовать штуку X (например древовидные категории, хотя это продвинутый вопрос) в фрейморке.
MySQL и SQL вообще: любят вопросы по теории баз данных и SQL: транзакции, внешние ключи, нормализация, виды джойнов, индексы (это у более продвинутых). Могут попросить сделать хитрый SQL запрос типа найти в таблице записи которые встречаются больше N раз или найти записи которые есть в одной таблице и нет в другой.
JS: тонкости вроде преобразования типов, чему равно [] + {} + "hello", вопросы по прототипам, вопросы по замыканиям, вопросы по передаче значений по ссылке (объекты всегда передаются по ссылке, примитивы копируются). Некоторые подвохи освещены в моих задачах по JS, некоторые в learn.javascript.ru
HTML/CSS: тут подвохи вряд ли будут, разве что могут спросить как реализовать ту или иную вещь (например вертикальное выравнивание или прибитый к низу футер) и обычно там много вариантов.
Наконец у нас есть набор задач с собеседований самого разного уровня: https://github.com/codedokode/pasta/blob/master/interview-tasks.md
----------
>>577683
В Оп посте есть задания на HTML/CSS которые хорошо помогают изучить их на базовом уровне, включая позиционирование и полноценную верстку макета. Если ты после этого захочешь знать их еще лучше, я могу придумать специальные задания повышенной сложности.
Не знаю, погугли. Есть наверно сервисы которые позволяют через API рассылать уведомления и решают проблемы с попаданием в спам.
>>576962
Если ты знаешь как сказать A и как сказать Б, что тебе мешает сказать обе буквы по очереди? Если нет, то возможно стоит подучить язык PHP.
>>577209
зря, это довольно неудобно и неэффективно. В базе стоит хранить разве что информацию об изображениях, но не сам файл.
>>577400
Паста:
----------
Я тут не буду писать очевидные вещи что ты должен знать что такое циклы, функции, классы, и уметь написать простую программу на уровне задач из моего учебника или SQL-запрос. Я напишу про вещи которые любят спрашивать сверх этого.
PHP: перечитай мануал, любят спрашивать тонкости языки типа преобразования одного типа в другой или правил сравнения типов (сработает ли if ("foo" == 0) { } или if ("1 person" == true) ? ). По ООП любят вопросы из серии «что выведет эта дебильная программа где мы переопределяем в наследнике приватный метод публичным с тем же именем», опять же спасает чтение мануала.
По теории могут спросить чем отличается абстрактный класс от интерфейса или как переопределить приватный метод в наследнике (правильный ответ: никак).
Если требуют знания фреймворков, могут спросить как реализовать штуку X (например древовидные категории, хотя это продвинутый вопрос) в фрейморке.
MySQL и SQL вообще: любят вопросы по теории баз данных и SQL: транзакции, внешние ключи, нормализация, виды джойнов, индексы (это у более продвинутых). Могут попросить сделать хитрый SQL запрос типа найти в таблице записи которые встречаются больше N раз или найти записи которые есть в одной таблице и нет в другой.
JS: тонкости вроде преобразования типов, чему равно [] + {} + "hello", вопросы по прототипам, вопросы по замыканиям, вопросы по передаче значений по ссылке (объекты всегда передаются по ссылке, примитивы копируются). Некоторые подвохи освещены в моих задачах по JS, некоторые в learn.javascript.ru
HTML/CSS: тут подвохи вряд ли будут, разве что могут спросить как реализовать ту или иную вещь (например вертикальное выравнивание или прибитый к низу футер) и обычно там много вариантов.
Наконец у нас есть набор задач с собеседований самого разного уровня: https://github.com/codedokode/pasta/blob/master/interview-tasks.md
----------
>>577683
В Оп посте есть задания на HTML/CSS которые хорошо помогают изучить их на базовом уровне, включая позиционирование и полноценную верстку макета. Если ты после этого захочешь знать их еще лучше, я могу придумать специальные задания повышенной сложности.
> Теперь мне нужно оживить каждое из этих полей, то есть добавить к ним ajax обрабочик. Продавец вводит информацию в поле, ввел первые пару символов, а дальше уже выбирает из появившегося блока что это за дверь.
Не нужно писать велосипед, есть готовые решения: jQuery UI Autocomplete, Chosen, Select2
> Если делать отдельное поле на странице, то есть не создавать его через jquery, а сразу прописать в html, то форма ajax поиск работает, все хорошо. Однако если создавать такое же поле оно не хочет ну накак работать через ajax.
А почему он должен обрабатывать? Ты понимаешь, как работают события в браузере, что такое обработчик событий, как он ставится, что такое addEventListener, всплытие событий? Если нет то стоит почитать теорию на learn.javascript.ru
Если ты думаешь что команда $('.some-class').click(...) говорит что при клике по some-class должна вызываться функция, то это не так. Она лишь находит на странице все элементы указанного класса и неэффективно в цикле ставит на них обработчики. Если ты не понимаешь этого, тебе надо начать с изуения DOM, событий и jQuery.
Я не вижу смысла решать за тебя твою задачу, так как это не даст пользы. Ты застрянешь на следующей проблеме. Единственный способ - тебе самому разобраться в коде который ты нашел или написал и понимать как работает каждая строчка в нем. Другого варианта нет.
Также, я вижу в твоем коде другие ошибки и хочу написать о них, чтобы ты знал над чем тебе еще стоит поработать:
> $("<div class='field'>Номенклатура" + i +"<input name='search' id='search'
На странице не может быть 2 элементов с одинаковым id.
> $('.field:last').remove();
Незачем делать поиск, достаточно сохранить jQuery объект с добавленной строчкой в переменной
У тебя слишком много отступов и вложенности в JS коде. Функции надо писать друг над другом, а не устраивать callback hell.
> <a href='#'
Кнопки верстаются тегом button, тег a предназначен для ссылки. Ссылка должна куда-то вести и у нее не может быть прописано # в href
> cache: false,
Непонятно зачем отключать здесь кеширование, оно полезно. И так как POST запросы не кешируются, логично заменить его на GET. Он по смыслу больше подходит для автокомплита.
> $query = mysqli_query
Нет проверки и обработки ошибок. После почти каждой mysqli функции ты обязан писать if с проверкой ошибок
> otdel_name LIKE '%". $search
Нельзя вставлять переменные в запрос это SQL инъекция. Надо использовать плейсходеры и prepared statements.
> echo "<table style='width: 100%;'>";
Не надо смешивать HTML и PHP логику, HTML надо выносить в шаблоны.
> }while($sql = mysqli_fetch_array($query));
Есть функция, которая выбирает все строки сразу. Незачем писать do/while.
В SQL запросе нет ограничения на число результатов и сортировки результатов.
В общем, по моим ощущениям тебе стоило бы получше изучить все веб-техногии: HTML, CSS, JS, PHP, SQL, делать рабочие проекты с твоим уровнем рановато.
Кстати задачи из ОП поста вполне помогли бы закрыть тебе пробелы, если конечно у тебя достаточно свободного времени.
> Теперь мне нужно оживить каждое из этих полей, то есть добавить к ним ajax обрабочик. Продавец вводит информацию в поле, ввел первые пару символов, а дальше уже выбирает из появившегося блока что это за дверь.
Не нужно писать велосипед, есть готовые решения: jQuery UI Autocomplete, Chosen, Select2
> Если делать отдельное поле на странице, то есть не создавать его через jquery, а сразу прописать в html, то форма ajax поиск работает, все хорошо. Однако если создавать такое же поле оно не хочет ну накак работать через ajax.
А почему он должен обрабатывать? Ты понимаешь, как работают события в браузере, что такое обработчик событий, как он ставится, что такое addEventListener, всплытие событий? Если нет то стоит почитать теорию на learn.javascript.ru
Если ты думаешь что команда $('.some-class').click(...) говорит что при клике по some-class должна вызываться функция, то это не так. Она лишь находит на странице все элементы указанного класса и неэффективно в цикле ставит на них обработчики. Если ты не понимаешь этого, тебе надо начать с изуения DOM, событий и jQuery.
Я не вижу смысла решать за тебя твою задачу, так как это не даст пользы. Ты застрянешь на следующей проблеме. Единственный способ - тебе самому разобраться в коде который ты нашел или написал и понимать как работает каждая строчка в нем. Другого варианта нет.
Также, я вижу в твоем коде другие ошибки и хочу написать о них, чтобы ты знал над чем тебе еще стоит поработать:
> $("<div class='field'>Номенклатура" + i +"<input name='search' id='search'
На странице не может быть 2 элементов с одинаковым id.
> $('.field:last').remove();
Незачем делать поиск, достаточно сохранить jQuery объект с добавленной строчкой в переменной
У тебя слишком много отступов и вложенности в JS коде. Функции надо писать друг над другом, а не устраивать callback hell.
> <a href='#'
Кнопки верстаются тегом button, тег a предназначен для ссылки. Ссылка должна куда-то вести и у нее не может быть прописано # в href
> cache: false,
Непонятно зачем отключать здесь кеширование, оно полезно. И так как POST запросы не кешируются, логично заменить его на GET. Он по смыслу больше подходит для автокомплита.
> $query = mysqli_query
Нет проверки и обработки ошибок. После почти каждой mysqli функции ты обязан писать if с проверкой ошибок
> otdel_name LIKE '%". $search
Нельзя вставлять переменные в запрос это SQL инъекция. Надо использовать плейсходеры и prepared statements.
> echo "<table style='width: 100%;'>";
Не надо смешивать HTML и PHP логику, HTML надо выносить в шаблоны.
> }while($sql = mysqli_fetch_array($query));
Есть функция, которая выбирает все строки сразу. Незачем писать do/while.
В SQL запросе нет ограничения на число результатов и сортировки результатов.
В общем, по моим ощущениям тебе стоило бы получше изучить все веб-техногии: HTML, CSS, JS, PHP, SQL, делать рабочие проекты с твоим уровнем рановато.
Кстати задачи из ОП поста вполне помогли бы закрыть тебе пробелы, если конечно у тебя достаточно свободного времени.
> $app->request->get->cookie()
Это никогда не было методом получения кук
>>577821
> "Сперва добейся", я понял. Только у тебя привилегия
Нет, ты не понял. Я хотел сказать что поддерживать хорошую архитектуру и порядок в стремительно растущем проекте, над которым работает куча людей, очень сложно. Потому мы должны оценивать код с учетом этого.
Ну и оценивать надо более глобальные вещи, не то, что там константа из 2 букв, а общую архитектуру в первую очередь.
> Поискового бота, конечно. Как часто они ходят по страницам, кстати? Помечают ли себя как-то, чтобы их заходы на страницу можно было игнорировать?
Обычно (гарантий нет) они не поддерживают куки и не выполняют JS код. Они себя идентифицируют 2 способами: User-Agent, у каждого бота свой, я тебе дал ссылку на сайт где собрана куча UA, в том числе поисковых ботов. Также, информацию можно найти в самой поисковой системе, например яндекс: https://yandex.ru/support/webmaster/robot-workings/check-yandex-robots.xml
Заметь что кто угодно может прикинуться ботом и по идее надо проверять еще IP-адрес на принадлежность яндексу. Но в твоем случае это не требуется, хочет человек притвориться яндексом, пусть притворяется.
В общем универсального способа нет, из практических есть проверка кук/JS (например сбор статистики аякс запросом) либо взять 5-10 популярных систем и проверять UA на соответсвие списку.
Подумай еще как это можно сделать максимально просто и с минимумом ложных срабатываний.
А, вспомнил! Есть еще так назваемая договоренность про robots.txt.Ты можешь поместить на сервер этот файл и описать в нем адреса, на которые боты заходить не имеют права. И вписать туда скрипт сбора статистики. Заметь, что robots это именно договоренность. Порядочный бот идентифицирует себя с помощью UA, подчиняется robots и взамен за это просит его не банить. Непорядочный бот может делать что угодно, кто ему запретит, но если он попадется, то пощады пусть не ждет.
Подробнее:
- http://www.robotstxt.org/
- http://robotstxt.org.ru/
- https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82_%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B9_%D0%B4%D0%BB%D1%8F_%D1%80%D0%BE%D0%B1%D0%BE%D1%82%D0%BE%D0%B2
- https://yandex.ru/support/webmaster/controlling-robot/robots-txt.xml
Вообще, это важная тема, изучить ее недолго, так что почитай. Заметь, что поисковики обычно расширяют список директив своими.
Также, поисковые роботы гугла и яндекса подчиняются атрибуту rel="nofollow" на ссылке и некоторым метатегам: https://yandex.ru/support/webmaster/controlling-robot/html.xml
> Сделал класс редис-адаптера более высокоуровневым, как ты советовал. Тут минус в том, что нужно писать подробную инструкцию для человека, который вздумает написать адаптер для своего клиента. Раньше мне достаточно было заставить его реализовать несколько низкоуровневых методов типа lrange или setnx, а их вызовы я зашил в классе VisitCounter, то есть четко зафиксировал алгоритм работы библиотеки.
Мне нравится низкоуровневый адаптер. Он проще в реализации и логика работы с редисом остается в 1 экземпляре в VC, а не копируется в каждый адаптер. А так логика будет многократно дублироваться, и сейчас в адаптере у тебя перемешаны высокоуровневые и низкуровневые методы (addUniqueVisit и getFromQueue).
Для composer.json есть несколько полей, которые надо заполнять чтобы например опубликовать где-то библиотеку, думаю стоит их заполнить:
https://getcomposer.org/doc/04-schema.md#description
https://getcomposer.org/doc/04-schema.md#license
> https://github.com/nsdvw/visit-counter/blob/master/VisitCounter.php#L17
> public function setDb(Db\DbAdapter $dbAdapter)
DbAdapter необязательный? Странно конечно. Ведь вызов moveToDb может упасть с ошибкой.
> considerVisit
Придирка: consider значит «рассматривать, иметь в виду», лучше наверно написать countVisit, saveVisit, recordVisit, я обычно не придираюсь к английскому, но тут уж слишком в глаза бросается.
> $batchCount = intval(floor($queueLen/$this->perTransaction));
Почему окугление вниз? Что если длина очередь меньше размера транзакции?
> public function moveBatch($count)
С какой целью публичная?
> https://github.com/nsdvw/visit-counter/blob/master/Db/DbAdapter.php#L9
> public function __construct($connection)
Непонятно зачем тут этот конструктор. Какую он ценность дает? По моему он лишнее ограничение вводит, что должен передаваться объект соединения, не давая особой выгоды. Я думаю, конструктор у каждого адаптера должен быть свой так как они скорее всего все будут разыне. как минимум тайп-хинты будут разные.
> https://github.com/nsdvw/visit-counter/blob/master/Db/PdoAdapter.php
> $tblName;
Эта настройка не сделана обязательной и не имеет значения по умолчанию. Значит если мы простосоздадим объект адаптера то произойдет ошибка при попытке использования. Это неправильно. Если система позволила тебе создать объект то он должен быть работоспособен. Так гораздо проще.
> IN ({$pageList})";
Нужно экранирование или проверка на то что это числа.
> Ну и твое задание про сайт с тестами тоже собираюсь сделать, мне нравится идея. На нем буду учить yii2. Такой план до конца этого года.
Я бы не советовал. Юи 2 это улучшенная и осовремененная версия Юи 1. Мне кажется его не стоит рассматривать как отдельный фреймворк и делать на нем длинный проект. Мне кажется достаточно почитать мануал и может быть портировать сайт объявлений (у тебя думаю уйдет неделька если конечно все сторонние библиотеки или альтернативы им найдутся), если тебе хочется изучить тонкости миграции с одного на другой.
Если ты хочешь изучать более сложный фреймворк, я бы посоветовал Симфони 2. Но опять же, ты ведь хотел искать работу, так можно вечно учиться. Я советую привести существующие проекты в более-менее рабочее состояние и искать работу, а параллельно, если пока нет хороших вариантов, можно почитывать про Симфони 2 или что ты хочешь изучать.
Просто по моему, если ты знаешь Юи 1, а тем более Юи 2, понимаешь некоторые паттерны, нормализацию, индексы, знаешь ООП, умеешь работать с редисом и сфинксом, то у тебя более чем достаточно знаний для того чтобы быть джуниором (не в любой компании конечно, где-то планка высокая, но там видимо ищут людей с опытом). Ты уже можешь работать и приносить пользу в реальном проекте.
> $app->request->get->cookie()
Это никогда не было методом получения кук
>>577821
> "Сперва добейся", я понял. Только у тебя привилегия
Нет, ты не понял. Я хотел сказать что поддерживать хорошую архитектуру и порядок в стремительно растущем проекте, над которым работает куча людей, очень сложно. Потому мы должны оценивать код с учетом этого.
Ну и оценивать надо более глобальные вещи, не то, что там константа из 2 букв, а общую архитектуру в первую очередь.
> Поискового бота, конечно. Как часто они ходят по страницам, кстати? Помечают ли себя как-то, чтобы их заходы на страницу можно было игнорировать?
Обычно (гарантий нет) они не поддерживают куки и не выполняют JS код. Они себя идентифицируют 2 способами: User-Agent, у каждого бота свой, я тебе дал ссылку на сайт где собрана куча UA, в том числе поисковых ботов. Также, информацию можно найти в самой поисковой системе, например яндекс: https://yandex.ru/support/webmaster/robot-workings/check-yandex-robots.xml
Заметь что кто угодно может прикинуться ботом и по идее надо проверять еще IP-адрес на принадлежность яндексу. Но в твоем случае это не требуется, хочет человек притвориться яндексом, пусть притворяется.
В общем универсального способа нет, из практических есть проверка кук/JS (например сбор статистики аякс запросом) либо взять 5-10 популярных систем и проверять UA на соответсвие списку.
Подумай еще как это можно сделать максимально просто и с минимумом ложных срабатываний.
А, вспомнил! Есть еще так назваемая договоренность про robots.txt.Ты можешь поместить на сервер этот файл и описать в нем адреса, на которые боты заходить не имеют права. И вписать туда скрипт сбора статистики. Заметь, что robots это именно договоренность. Порядочный бот идентифицирует себя с помощью UA, подчиняется robots и взамен за это просит его не банить. Непорядочный бот может делать что угодно, кто ему запретит, но если он попадется, то пощады пусть не ждет.
Подробнее:
- http://www.robotstxt.org/
- http://robotstxt.org.ru/
- https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82_%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B9_%D0%B4%D0%BB%D1%8F_%D1%80%D0%BE%D0%B1%D0%BE%D1%82%D0%BE%D0%B2
- https://yandex.ru/support/webmaster/controlling-robot/robots-txt.xml
Вообще, это важная тема, изучить ее недолго, так что почитай. Заметь, что поисковики обычно расширяют список директив своими.
Также, поисковые роботы гугла и яндекса подчиняются атрибуту rel="nofollow" на ссылке и некоторым метатегам: https://yandex.ru/support/webmaster/controlling-robot/html.xml
> Сделал класс редис-адаптера более высокоуровневым, как ты советовал. Тут минус в том, что нужно писать подробную инструкцию для человека, который вздумает написать адаптер для своего клиента. Раньше мне достаточно было заставить его реализовать несколько низкоуровневых методов типа lrange или setnx, а их вызовы я зашил в классе VisitCounter, то есть четко зафиксировал алгоритм работы библиотеки.
Мне нравится низкоуровневый адаптер. Он проще в реализации и логика работы с редисом остается в 1 экземпляре в VC, а не копируется в каждый адаптер. А так логика будет многократно дублироваться, и сейчас в адаптере у тебя перемешаны высокоуровневые и низкуровневые методы (addUniqueVisit и getFromQueue).
Для composer.json есть несколько полей, которые надо заполнять чтобы например опубликовать где-то библиотеку, думаю стоит их заполнить:
https://getcomposer.org/doc/04-schema.md#description
https://getcomposer.org/doc/04-schema.md#license
> https://github.com/nsdvw/visit-counter/blob/master/VisitCounter.php#L17
> public function setDb(Db\DbAdapter $dbAdapter)
DbAdapter необязательный? Странно конечно. Ведь вызов moveToDb может упасть с ошибкой.
> considerVisit
Придирка: consider значит «рассматривать, иметь в виду», лучше наверно написать countVisit, saveVisit, recordVisit, я обычно не придираюсь к английскому, но тут уж слишком в глаза бросается.
> $batchCount = intval(floor($queueLen/$this->perTransaction));
Почему окугление вниз? Что если длина очередь меньше размера транзакции?
> public function moveBatch($count)
С какой целью публичная?
> https://github.com/nsdvw/visit-counter/blob/master/Db/DbAdapter.php#L9
> public function __construct($connection)
Непонятно зачем тут этот конструктор. Какую он ценность дает? По моему он лишнее ограничение вводит, что должен передаваться объект соединения, не давая особой выгоды. Я думаю, конструктор у каждого адаптера должен быть свой так как они скорее всего все будут разыне. как минимум тайп-хинты будут разные.
> https://github.com/nsdvw/visit-counter/blob/master/Db/PdoAdapter.php
> $tblName;
Эта настройка не сделана обязательной и не имеет значения по умолчанию. Значит если мы простосоздадим объект адаптера то произойдет ошибка при попытке использования. Это неправильно. Если система позволила тебе создать объект то он должен быть работоспособен. Так гораздо проще.
> IN ({$pageList})";
Нужно экранирование или проверка на то что это числа.
> Ну и твое задание про сайт с тестами тоже собираюсь сделать, мне нравится идея. На нем буду учить yii2. Такой план до конца этого года.
Я бы не советовал. Юи 2 это улучшенная и осовремененная версия Юи 1. Мне кажется его не стоит рассматривать как отдельный фреймворк и делать на нем длинный проект. Мне кажется достаточно почитать мануал и может быть портировать сайт объявлений (у тебя думаю уйдет неделька если конечно все сторонние библиотеки или альтернативы им найдутся), если тебе хочется изучить тонкости миграции с одного на другой.
Если ты хочешь изучать более сложный фреймворк, я бы посоветовал Симфони 2. Но опять же, ты ведь хотел искать работу, так можно вечно учиться. Я советую привести существующие проекты в более-менее рабочее состояние и искать работу, а параллельно, если пока нет хороших вариантов, можно почитывать про Симфони 2 или что ты хочешь изучать.
Просто по моему, если ты знаешь Юи 1, а тем более Юи 2, понимаешь некоторые паттерны, нормализацию, индексы, знаешь ООП, умеешь работать с редисом и сфинксом, то у тебя более чем достаточно знаний для того чтобы быть джуниором (не в любой компании конечно, где-то планка высокая, но там видимо ищут людей с опытом). Ты уже можешь работать и приносить пользу в реальном проекте.
> Можно ли вызвать метод класса foo?
Для этого класс bar должен где-то иметь ссылку на класс foo (что за привычка писать классы с маленькой буквы? сбивает с толку). Такое часто бывает, что 2 класса содержат ссылки друг на друга (например начальник на подчиненных, а подчиненные на начальника), но система получается сложнее.
Как передать ссылку? Ну можно например в конструкторе foo внедрить ее в $a через какой-то метод $a->setFoo($this);
>>577946
У тебя 7 языков программирования или 7 языков народов мира? В первом случае, думаю, все будет в порядке. Спрос на грамотных разработчиков высок. Поиски можешь начать с geekjob, moikrug, hh.ru
>>578117
Слушай, тут слишком много кода чтобы разбираться. Упрости пример до минимума, чтобы там было не больше 3-4 тегов и залей на jsfiddle или подобный сайт, чтобы сразу можно было посмотреть. Также напиши свое видение ситуации: какие методы позиционирования тут можно бы применить и что в них не устраивает тебя.
Ну и держи ссылку на хороший учебник по позиционированию, читать снизу вверх: http://softwaremaniacs.org/blog/category/primer/
И у нас в ОП посте есть хорошие задания на HTML/CSS которые помогут тебе разобраться с версткой. Обрати на них внимание.
>>578125
Ну ты торопыга. Тред создан всего 10 дней назад и в нем даже 650 постов не набили еще.
Это хорошо, что ты занимаешься тестами производитльности. ab это конечно самый простой и примитивный инструмент, но для начала сгодится. Например его недостаток в том что он долбит одну страницу, данные могут закешироваться, в то время как реальные посетители будут слать разные запросы к разным старницам.
Смотри, на что надо смотреть:
> Requests per second: 3.49 [#/sec] (mean)
3 запроса в секуду обрабатывается. Это очень мало. Смотри, допустим 1 человек просматривает в среднем 15 динамических страниц (это зависит от специфики сайта, на форуме например эта цифра значительно больше, на новостном сайте может быть меньше). Твой сайт сможет обработать в сутки 86400 x 3.5 = 300 000 запросов или 20 000 посетителей. Это очень мало.
> Failed requests: 0
Это хорошо, что не было ни одной ошибки. Скорость не имеет смысла если ответ отдается с ошибкой.
> Time per request: 1430.804 [ms] (mean)
Время обработки запроса: слишком долго. Надо стремится к 50-200 (в зависимости от сложности сайта).
> Transfer rate: 236.08 [Kbytes/sec] received
Скорость передачи данных - очень маленькая из-за маленького числа обработанных запросов
> Connection Times (ms)
по этим данным можно попробовать понять какой этап обработки запроса долгий, например установка соединения или ожидание ответа. Установка соединения у тебя быстрая, а вот обработка и ожидание ответа очень медленны.
> Percentage of the requests served within a certain time (ms)
Сколько процентов запросов в какое время укладывается
Надо искать узкие места. Почему так долго обрабатывется запрос? Используется ли например ресурс процессора на полную мощность или процессор простаивает и ждет медленного диска? Если у тебя Линукс то для него есть куча полезных утилит: http://habrahabr.ru/post/114082/
Я советую применить htop, iotop, mytop. Если у тебя Windows, то попробуй диспетчер задач, начиная с семерки он неплох.
Замерь потребление процессора, используется ли он на 100% или простаивает? Какие процессы его используют и насколько? Достаточно ли памяти? Не идет ли своп? Сколько памяти занимают разные процессы? Как нагружен диск? Сколько трафика/IOPS? Не нагружена ли сеть, не идет ли обращение к внешним медленным ресурсам?
И кстати какое у тебя железо? Ты случайно не в вирутальной машине на китайском андроидофоне сервер поднял?
Для ОП-кешера стоит смотреть статистику его исплоьзования, много ли hits/misses, сколько занято памяти.
После этого можно спускаться на уровень приложения и смотреть запросы (часто неэффективный запрос может тормозить весь скрипт), и профайлер.
Я вижу в профайлере вызов CActiveRecord->findAll() который занял 74 мс, что-то много. Сколько ты там записей выбираешь? Эффективно ли? И еще вижу EAVACtiveRecord->findAll, который занимает около 40 мс внутри CListView->init
И еще у тебя 3600 вызовов Twig_template->getAttribute, не многовато ли? Это большая страница?
В общем, у меня ощущение, что кроме описанных выше нескольких функций, все остальное время размазано по куче мелких функций. И может быть система в общем медленно работает, что-то я там не вижу ничего особо длинного.
> Не знаю, о чем говорят тесты, но судя по логам yii, страница генерировалась 1.4 секунды, а по моему глазомеру даже дольше. Многовато что-то.
По глазомеру может быть больше, если браузер ждет пока загрузятся какие-то скрипты. График загрузки есть на вкладке Network инспектора.
> Что еще можно оптимизировать или закешировать?
Нужно для начала понять на что тратятся ресурсы системы, как загружен процессор. Кеширование это больше костыль, там и так много уровней кеширования (кеширование в БД например) и лишние слои могут только усложнить систему.
Это хорошо, что ты занимаешься тестами производитльности. ab это конечно самый простой и примитивный инструмент, но для начала сгодится. Например его недостаток в том что он долбит одну страницу, данные могут закешироваться, в то время как реальные посетители будут слать разные запросы к разным старницам.
Смотри, на что надо смотреть:
> Requests per second: 3.49 [#/sec] (mean)
3 запроса в секуду обрабатывается. Это очень мало. Смотри, допустим 1 человек просматривает в среднем 15 динамических страниц (это зависит от специфики сайта, на форуме например эта цифра значительно больше, на новостном сайте может быть меньше). Твой сайт сможет обработать в сутки 86400 x 3.5 = 300 000 запросов или 20 000 посетителей. Это очень мало.
> Failed requests: 0
Это хорошо, что не было ни одной ошибки. Скорость не имеет смысла если ответ отдается с ошибкой.
> Time per request: 1430.804 [ms] (mean)
Время обработки запроса: слишком долго. Надо стремится к 50-200 (в зависимости от сложности сайта).
> Transfer rate: 236.08 [Kbytes/sec] received
Скорость передачи данных - очень маленькая из-за маленького числа обработанных запросов
> Connection Times (ms)
по этим данным можно попробовать понять какой этап обработки запроса долгий, например установка соединения или ожидание ответа. Установка соединения у тебя быстрая, а вот обработка и ожидание ответа очень медленны.
> Percentage of the requests served within a certain time (ms)
Сколько процентов запросов в какое время укладывается
Надо искать узкие места. Почему так долго обрабатывется запрос? Используется ли например ресурс процессора на полную мощность или процессор простаивает и ждет медленного диска? Если у тебя Линукс то для него есть куча полезных утилит: http://habrahabr.ru/post/114082/
Я советую применить htop, iotop, mytop. Если у тебя Windows, то попробуй диспетчер задач, начиная с семерки он неплох.
Замерь потребление процессора, используется ли он на 100% или простаивает? Какие процессы его используют и насколько? Достаточно ли памяти? Не идет ли своп? Сколько памяти занимают разные процессы? Как нагружен диск? Сколько трафика/IOPS? Не нагружена ли сеть, не идет ли обращение к внешним медленным ресурсам?
И кстати какое у тебя железо? Ты случайно не в вирутальной машине на китайском андроидофоне сервер поднял?
Для ОП-кешера стоит смотреть статистику его исплоьзования, много ли hits/misses, сколько занято памяти.
После этого можно спускаться на уровень приложения и смотреть запросы (часто неэффективный запрос может тормозить весь скрипт), и профайлер.
Я вижу в профайлере вызов CActiveRecord->findAll() который занял 74 мс, что-то много. Сколько ты там записей выбираешь? Эффективно ли? И еще вижу EAVACtiveRecord->findAll, который занимает около 40 мс внутри CListView->init
И еще у тебя 3600 вызовов Twig_template->getAttribute, не многовато ли? Это большая страница?
В общем, у меня ощущение, что кроме описанных выше нескольких функций, все остальное время размазано по куче мелких функций. И может быть система в общем медленно работает, что-то я там не вижу ничего особо длинного.
> Не знаю, о чем говорят тесты, но судя по логам yii, страница генерировалась 1.4 секунды, а по моему глазомеру даже дольше. Многовато что-то.
По глазомеру может быть больше, если браузер ждет пока загрузятся какие-то скрипты. График загрузки есть на вкладке Network инспектора.
> Что еще можно оптимизировать или закешировать?
Нужно для начала понять на что тратятся ресурсы системы, как загружен процессор. Кеширование это больше костыль, там и так много уровней кеширования (кеширование в БД например) и лишние слои могут только усложнить систему.
Этот тред закрыт.
Если я кому-то не ответил, напомните о себе в новом треде.
Это копия, сохраненная 1 декабря 2015 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.