Это копия, сохраненная 1 ноября 2015 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко.
Это тред для начинающих. Не написал за свою жизнь ни одной программы? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Netbeans PHP или PhpStorm (с ним будет удобнее).
Предыдущий тред был тут: >>543720 (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.github.io/ru-php-the-right-way/ )
- По PHP: Профессиональное программирование на PHP Джордж Шлосснейгл
- По PHP: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- JS: learn.javascript.ru
- Про Git:
Подскажи сайты для поиска работы, я не умею гуглить? brainstorage.me, geekjob.ru, hh.ru
Нужен ли ООП, фреймворки, MVC? — Да, однозначно. Посмотри любую вакансию.
Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.net/45000175
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет.
Где искать работу и заказы — hh.ru, geekjob.ru, brainstorage.me, fl.ru, odesk.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, то рассказать об этом стоит в каком-нибудь другом треде.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
Напомню сделать задачу на SPA, анон просит >>546355
Напомню проверить >>550257
https://github.com/nsdvw/file-sharing
> Джаваскрипт действительно лучше больше не смотри, мне лучше сначала основательно подучить теорию.
https://github.com/nsdvw/classifieds
>Буэээ: https://jsfiddle.net/wv2L7oog/1/
Надо чтобы твои примечания работали в любом тексте а не только содержащем p. Также не очень понял зачем margin-top на первом элементе? Ты пытаешься компенсировать margin-top у p? А что если после примечания идет не p, а h1-h6? Элемент без маргин-топа? Гм, хороший вопрос я придумал, как же это сделать правильно.
Прочитай также комментарий к задаче (или может он в другой задаче) про маргины и паддинги.
Куда должно прилипать примечание? Я не хуя не понял. Прилипать к списку, картинке, заголовку, абзацу?
Список, картинка, заголовок не должны съезжать влево.
Несколько идущих подряд примечаний не должны выстраиваться горизонтально.
>не задавай поля с помощью margin на <p>. Поля задаются с помощью padding на родительском элементе
если задать паддинг, то как добиться чтобы <aside> находились в зоне паддинга, они остаются внутри родителя.
https://jsfiddle.net/wv2L7oog/3/
Маргин может быть отрицательным (не только для флоатов): https://gist.github.com/codedokode/3f6063edf0a2227eb313
foreach ($obj->getArray() as $item)
Есть смысл вынести результат работы метода в отдельную переменную?
Помогите с литературой по SQL. Дело в том, что решил задрочиться на эту тему буквально с нуля. После окончания универа (инженер-программист), работал не по специальности. В SQL'е в студенческие годы я немного шарил, но естественно все забыл к хуям. Так вот, кроме тех книг, которые указаны в шапке (закрепленного треда), есть ли еще какая-либо литература, способная обучить меня, полного нуба в этом деле?
сделай новый класс и пройдись по объекту массивом в нем.
Один раз. Также, foreach делает для себя приватную копию и итерирует по ней, даже если мы меняем исходный массив, пример:
$a = [1, 2, 3];
foreach ($a as $b) {
$a[1] = 100;
echo $b; // 123
}
http://ideone.com/PWYlAd
> Есть смысл
Если выражение длинное то да, ради читабельности.
>>551690
У нас есть задания на MySQL в ОП посте, и там какие-то ссылки по теории. Или тебе важен именно SQL а не его конкретная реализация?
Какой еще график работы и опыт? В задании об этом ничего не сказано.
Ладно, я просто не понял условие. Переделал.
http://ideone.com/PaVVXL
Объекты-работников добавил в цикле, к ним можно обращаться по индексу в массиве (будем считать, что это у нас такая бд).
Создавать каждого сотрудника отдельно, чтобы дать ему имя, возраст и прочие характеристики, это уже нереально. Надо тогда уже делать админку, чтобы заполнять эти поля.
Эм, бака, в различных БД используються различный синтаксис, какой из тебя инжер если ты не в курсе?
> public function fireEmployee($index)
Это неправильно и нелогично. Как этим пользоваться? Где брать этот индекс?
Это явное нарушение инкпусуляции, то есть ты внутренние знания Department (под каким индексом в нем работник) вытаскиваешь наружу.
Логично в качестве идентификатора работника использовать самого работника:
function fireEmployee(Employee $e)
> public function hireCrowd
Незачем это класть в департамент, это не его функция создавать работников. Это нужно разместить отдельно.
> if ($this->departments[$i]->name != $name) continue;
if надо писать в 3 строчки и со скобками ради качества кода, а не заставлять твоих коллег потом ставить за тебя эти скобки при правке.
> for ($i = 0; $i < count($this->departments); $i++) {
Для обхода массива логичнее и удобнее использовать foreach
Опять же, я бы сделал аргументом для удаления департамента не название, а сам объект.
Функцию printReport я бы вынес из класса Company. Она не часть компании, а работает над ее данными и его логично вынести наружу. К сожалению, это плохое объяснение.
>>551713
Почитать можно, но туториал который есть в заданиях на MySQL, гораздо ближе к практике.
> public function fireEmployee($index)
Это неправильно и нелогично. Как этим пользоваться? Где брать этот индекс?
Это явное нарушение инкпусуляции, то есть ты внутренние знания Department (под каким индексом в нем работник) вытаскиваешь наружу.
Логично в качестве идентификатора работника использовать самого работника:
function fireEmployee(Employee $e)
> public function hireCrowd
Незачем это класть в департамент, это не его функция создавать работников. Это нужно разместить отдельно.
> if ($this->departments[$i]->name != $name) continue;
if надо писать в 3 строчки и со скобками ради качества кода, а не заставлять твоих коллег потом ставить за тебя эти скобки при правке.
> for ($i = 0; $i < count($this->departments); $i++) {
Для обхода массива логичнее и удобнее использовать foreach
Опять же, я бы сделал аргументом для удаления департамента не название, а сам объект.
Функцию printReport я бы вынес из класса Company. Она не часть компании, а работает над ее данными и его логично вынести наружу. К сожалению, это плохое объяснение.
>>551713
Почитать можно, но туториал который есть в заданиях на MySQL, гораздо ближе к практике.
И что насчет антикризисных мер? Не вижу пока что функций которые бы брали компанию и осуществляли над ней антикризисные действия.
> $profDependencies = $this->getProfDependencies($this->profession);
зачем передавать $this->profession? По моему это излишне.
> return ($this->isBoss) ? self::BOSS_PREMIUM $profDependencies['wageRate'] $this->rank
: $profDependencies['wageRate'] * $this->rank;
Это надо упростить и разбить на 2-3 команды. Ибо читаемость очень плохая получается, и рекомендуемый лимит это 80 символов на строку.
А, я придумал почему printReport надо вынести наружу. Потому что ты смешиваешь код отвечающий за логику (модель компании) и код отвечающий за вывод этих данных. Традиционно во всех системах отделяют модель от кода представления.
> while (strlen($string) < $columnSize) {
> $string .= ' ';
1) почему strlen? Не читал мой урок по строковым функциям и utf-8?
2) почему не str_repeat вместо цикла?
1) Первая задача про номера - переделал, чтобы всё проверялось регуляркой. Не нравится то, что пришлось во всех местах, где может быть что-то из "-()" их перечислять снова и снова, громоздко выглядит http://ideone.com/7gz4e5
2) Вторая на проверку текста на ошибки - там ты указал на небольшие косяки, вроде поправил. http://ideone.com/y858QL Кстати, почему нужно было вынести счётких замен ($count) в отдельную строку, снаружи if?
3) Третья - убрать лишние символы из номера, вроде тут всё несложно - http://ideone.com/ynYv7Q
4) Последняя - автоисправление ошибок. Тут я разошёлся, решил сделать регистронезависимыми эти замены, с помощью коллбека, и не пойму, нормально это или нет. Проверял, в каком регистре буква, и заменял соответственно. Кстати, почему-то не заработало ctype_lower() на кириллице, на латинице работает. Пришлось регистр регекспом проверять. http://ideone.com/rwNeES
На mb ругается ideone.
Я же написал в комментариях на 4 строке.
>>551728
Наружу это куда? В отдельную функцию, или писать какой-то класс-хелпер?
>>551720
>Логично в качестве идентификатора работника использовать самого работника
Ну я подразумевал, что программа не одноразовая, и после первого добавления сотрудников они куда-то сохранятся (в базу, файл). Не заполнять же список сотрудников заново при каждом запуске программы?
Вне объекта компании сотрудники долго не живут, я не могу постоянно держать ссылку на объект снаружи.
Вообще логичнее было бы именно дать сотрудникам имена, и искать уже по имени как по ключу в ассоциативном массиве-списке сотрудников.
Но я не буду руками писать код создания 101 объекта, а затем 101 раз вызывать метод добавления сотрудника.
>>551724
Потом, если не надоест.
Объектом, или массивом?
Например, увидел что у arrayhelper есть метод toArray(), может быть так будет быстрее, или наоборот, конвертация замедлит работу.
Не зашквар ли использовать foreach, если есть arrayhelper'ы?
> mb_stlen
черт
> Call to undefined function mb_stlen() - Ideone не подключено расширение?
Опечатка в названии, пруф: http://ideone.com/49VNtK
> В отдельную функцию, или писать какой-то класс-хелпер?
Можно и так и так, тут главное чтобы модель компании была ООП, а сторонние по отношению к ней функции можно оставить как функции.
> и после первого добавления сотрудников они куда-то сохранятся (в базу, файл)
Это тут не при чем. Сохранение в файл обычно просто сериализует данные (например в JSON) и кладет в файл и никаких идентификаторов нам не дает. В базе они есть, да, но тут никто про базу не говорил да и не лучшая идея писать код который без наличия базы не может увольнять сотрудников.
Нет, незачем тут вводить идентификаторы когда у нас объект сам по себе является идентификатором.
> Вне объекта компании сотрудники долго не живут, я не могу постоянно держать ссылку на объект снаружи.
Это какая-то непонятная логика. Вот у нас есть объект Департамент, в который можно добавлять сотрудников. Где они будут храниться это не его дело, он не должен об этом знать. Каждый должен заниматься своим делом. Соответственно никакой привязки к базе когда мы хотим просто удалить сотрудника из департамента, быть не может.
Антикризисные меры важная часть задачи, так как они помогают увидеть неправильно спроектированные места. Если твой код спроектирован правильно то много времени их добавление не займет.
> логичнее было бы именно дать сотрудникам имена, и искать уже по имени как по ключу
Нет. Имена неуникальны, а объекты уникальны (тут конечно можно взять и начать присваивать работникам табельные номера). Ты почему-то пытаешься усложнить задачу. У тебя уже есть уникальный идентификатор — это сам объект, какой смысл придумывать дополнительные идентификаторы?
> Не заполнять же список сотрудников заново при каждом запуске программы?
Тем не менее для целей задачи (сделать прогноз в случае антикризисных мер) этого достаточно.
Я не понял, зачем что-то преобразоывать. Если у тебя есть модель что мешает ее и передать, зачем делать преобразования в массивы у которых нет методов, ничего вообще нет? Если у тебя массив моделей, зачем делать преобразования?
Метод toArray() там для случаев когда надо куда-то экпортировать эту информацию, в JSON например.
ну хз, массивы же вроде как быстрее работают.
Еще хотел спросить:
в виде ставить условия -- плохо?
как быть если нужно отобразить разную информацию в зависимости от чего-то
> массивы же вроде как быстрее работают.
В нашем треде такие утверждения должны подкрепляться тестами. В твоем случае тестом который преобразует объекты в массивы и сравнивает потраченное время со случаем когда ничего ни во что не преобразуется.
> в виде ставить условия -- плохо?
Нет, в представлении можно использовать условия и циклы. к примеру, если мы хотим для пользователя с отрицательной кармой выводить предупреждение, можно использовать if. Если мы хотим вывести таблицу пользователей, можно использовать foreach
Используется синтаксис с двоеточием и endif.
>>551738
Хочу также сказать следующее. Решение задачи на Вектор целиком поможет тебе лучше знать ООП. А ведь на собеседованиях иногда дают задачи на ООП, вот пример:
https://github.com/groov/lightsoft#4-oop
Она сложная на первый взгляд, но если подумать то можно догадаться как это сделать в рамках ООП-подхода (хотя там в примерах все объяснено).
спасибо!
Дауны сначала не хотят скачивать jQuery на свой сервер, а потом изобретают костыли для защиты от подмены. Правильное решение в данном случае разместить код у себя.
Более того, вычислять и подставлять хеши — нетривиальное занятие и требует модификации приложения (просто в гульпе дописать правило не получится). Следовательно никто это использовать не ьудет.
Я не могу придумать ни одного сценария для этого.
Пойду еще на HN отпишусь, пусть минусуют.
> public function hireCrowd
>Незачем это класть в департамент, это не его функция создавать работников. Это нужно разместить отдельно.
Как не его функция? Есть объект департамент, у него должен быть метод по добавлению работников. Или ты сагрился на композицию, что объект сотрудника создается внутри метода класса департамент? Ок, сделал отдельной функцией.
>В тех департаментах, где руководитель не является аналитиком, заменить его на аналитика самого высшего ранга из этого департамента (а бывшего руководителя вернуть к обычной работе)
Как быть, если в департаменте нет ни единого аналитика?
И что это за "антикризисные меры" - повышать зарплату и другие (кофе) расходы на сотрудников?
Сейчас доделаю, чтобы показать, что могу. Но у меня ощущение, что я занимаюсь ненужной херней.
> Как быть, если в департаменте нет ни единого аналитика?
Не менять руководителя.
> И что это за "антикризисные меры" - повышать зарплату и другие (кофе) расходы на сотрудников?
Финансовая стимуляция труда.
> Но у меня ощущение, что я занимаюсь ненужной херней.
Отчего же? Разве новая версия программы не лучше старой? Разве ты ничему совсем не научился?
Можешь подсказать, не сильно ли я наговнокодил? Может есть какие-то случаи дурного тона которые я применил в своем коде?
http://ideone.com/JvdwlP
Делал два дня
И еще вопрос, почему нужно писать в регулярном выражении буквы и маленьким и большим регистром если можно в конце поставь "i" и регистр не будет учитываться?
А где это написано? В древних версиях PHP флаг i не работал с кириллицей но начиная с (вроде) 5.3 он (а также \w) работает нормально и можно его использовать.
Ну немного неправильно выразился, ОП не говорил, что так нужно делать, просто везде в поясненниях он пишет дополнительно буквы верхним регистром и не использует "i".
$i<=$halfLength - определяем сколько будет циклов, т.е. если в слове 12 букв, то будет шесть циклов сравнения: 1-ая буква с 12-ой, 2-я с 11-ой и. т.д.
Если буквы одинаковые то увеличиваем значение переменной $result на один а потом сравниваем $result с $halfLength, если равны значит палиндром.
> foreach делает для себя приватную копию и итерирует по ней
Э, а мне нужно в цикле удалить некоторые элементы массива. Че делать?
хуячь по ссылке, очевидно же, или другой массив создавай.
Если финансовая стимуляция труда, то должен быть ее результат, то есть увеличение производительности, кол-во вырабатываемых клерками страниц бумаги.
А так только повышение расходов, какие же это антикризисные меры.
Ладно, сделал 2 из 3.
http://ideone.com/PaVVXL
Так удаляй, в чем проблема? А foreach будет итерировать по своей сделанной копии.
Или тебе надо чтобы он не обходил удаляемые в ходе цикла элементы? Тогда переделывай алгоритм ибо это быдлокод.
Профессии надо сделать константами. Что там еще за магические строки вроде 'engineer'?
Константы защищают от опечаток и делают код понятнее и надежнее.
> Employee::updateDependencies(array('analyst'=>array('wageRate'=>1100,'coffee'=>75,'pages'=>5)));
Массив на массиве погоняет. Сделай-ка чтобы были методы вроде задать базовую зарплату, задать потребление кофе и тд. Не надо тут городить массиво-ориентированное программирование и нарушать инкапсуляцию данных внутри Employee.
Применение антикризисной меры должно быть функцией которая принимает на вход компанию и применяет к ней меры.
Программа должна соответственно вывести 4 таблицы, начальную и после применения каждой меры.
> if ($department != $dep) continue;
Это надо писать в 3 строчки со скобками
> public function fireEmployee(Employee $e)
> {
> foreach ($this->employees as $employee) {
> $index = array_search($employee, $this->employees);
Почему тут и цикл и array-search одновременно?
> public static function updateDependencies(array $dependencies)
Функция обновляет правила глобально для всех компаний и департаментов что очевидно неверно. Я наверно в каждом треде говорю людям не злоупотреблять статическими методами и свойствами и никто не слушает. Эта задача позволяет наглядно показать почему это плохо.
В общем, пока есть над чем работать.
Профессии надо сделать константами. Что там еще за магические строки вроде 'engineer'?
Константы защищают от опечаток и делают код понятнее и надежнее.
> Employee::updateDependencies(array('analyst'=>array('wageRate'=>1100,'coffee'=>75,'pages'=>5)));
Массив на массиве погоняет. Сделай-ка чтобы были методы вроде задать базовую зарплату, задать потребление кофе и тд. Не надо тут городить массиво-ориентированное программирование и нарушать инкапсуляцию данных внутри Employee.
Применение антикризисной меры должно быть функцией которая принимает на вход компанию и применяет к ней меры.
Программа должна соответственно вывести 4 таблицы, начальную и после применения каждой меры.
> if ($department != $dep) continue;
Это надо писать в 3 строчки со скобками
> public function fireEmployee(Employee $e)
> {
> foreach ($this->employees as $employee) {
> $index = array_search($employee, $this->employees);
Почему тут и цикл и array-search одновременно?
> public static function updateDependencies(array $dependencies)
Функция обновляет правила глобально для всех компаний и департаментов что очевидно неверно. Я наверно в каждом треде говорю людям не злоупотреблять статическими методами и свойствами и никто не слушает. Эта задача позволяет наглядно показать почему это плохо.
В общем, пока есть над чем работать.
>Функция обновляет правила глобально для всех компаний и департаментов что очевидно неверно
Нет верно.
Все служащие одной профессии получают одинаковую зарплату, потребляют одинаковое кол-во кофе и производят одинаковое кол-во страниц. Для зарплаты есть множетель ранга.
Если не так, очевидно ты неправильно сформулировал условие задачи.
Что тебе не нравится в использовании массивов?
Создать класс для зависимостей?
И ответь, строго ли зависят такие характеристики как потребление кофе или производство отчетов от профессии?
Если нет, тогда действительно лучше завести свойства для каждого конкретного объекта, а не использовать статические класса.
В случае антикризисных мер это не так. Требуется индивидуально менять базовую ставку.
>>551936
Тем что это некрасиво и надо знать структуру этого массива, которая к тому же не проверяется (то есть можно передать что угодно и ошибки не будет). И это чрезмерное усложнение. Вот нормальный способ:
работник -> задатьБазовуюСтавку(100);
Зачем возиться с массивами когда можно сделать простой метод? Подумай о людях которым придется с кодом потом работать.
> И ответь, строго ли зависят такие характеристики как потребление кофе или производство отчетов от профессии?
Не строго. По умолчанию они зависят, но руководство может менять их для каждого работника индивидуально.
Вот тут задачка http://archive-ipq-co.narod.ru/l1/functions.html (айпад в кредит)
Тут мое гавно http://ideone.com/vQDc3R
Там нужно количество месяцев искать или как? Я вообще туплю уже
Бля решил походу, больше 2х часов сидел над ней, аж жопа вспотела, но мне почему-то кажется что гораздо проще можно сделать хз http://ideone.com/UJkWzw
Всё делаю как следует, mp3 работает, vaw - нет.
Во всех браузерах
Ну и зачем тогда вообще нужна система рангов служащих, если можно вручную раскидать им какие угодно данные? Менеджеру первого уровня я сейчас возьму и задам зарплату в три раза больше чем начальнику 3 левела.
>>551910
> Сделай-ка чтобы были методы вроде задать базовую зарплату, задать потребление кофе и тд.
Базовое потребление кофе, или общее? У тебя в условии задачи сказано, что начальник потребляет в два раза больше кофе и производит 0 отчетов.
Мне кажется, нужно тогда уже задавать итоговое кол-во, раз уж мы лезем в механизм вычисления характеристик снаружи.
>методы вроде задать базовую зарплату
Базовую или итоговую? Зарплата зависит от ранга и статуса начальник/подчиненный.
Мы задаем базовую ставку, от которой будет вычисляться итоговая зарплата в зависимости от доп.коэффициентов, или итоговую?
Сформулируй нормальное условие.
> Ну и зачем тогда вообще нужна система рангов служащих,
Ну может менеджеру приятно что он менеджер 2 ранга, а не первого. Плюс, ты не знаешь как и где еще используются эти данные. Например они используются для определения зарплаты по умолчанию, если она не была изменена.
А возможность менять базовую ставку индивидуально все же нужна. Как минимум для решения пункта про антикризисные меры.
> Базовое потребление кофе, или общее?
А в условии этого не написано? в случае антикризисных мер мы меняем именно базовые величины, базовую ставку и базовое потребление кофе.
> Мне кажется, нужно тогда уже задавать итоговое кол-во,
Нет, это неправильно. Ну сам подумай, тогда при повышении ранга например зарплата не поменяется.
> Сформулируй нормальное условие.
в случае антикризисных мер мы меняем именно базовые величины, базовую ставку и базовое потребление кофе для части сотрудников.
Я делал так: если стоит кука debugGeoIp или GET-параметр то брать IP для определения города из него. Ну а найти IP для нужного города не проблема.
>>552097
Прежде чем браться встраивать в страницу тег аудио, надо изучить теорию и почитать мануалы. Иначе ничего хорошего не выйдет. Вот что надо помнить:
1) audio воспроизводит не любое аудио а только то что поддерживает браузер
2) в wav данные могут быть в разных форматах. Опять же не все воспроизводятся.
Ну и wav по моему обычно не сжатый и потому много весит, что тебе мешает в mp3 перекодировать?
Информация о поддержке форматов:
https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats (англ)
http://caniuse.com/#feat=audio
http://caniuse.com/#feat=wav
http://caniuse.com/#feat=mp3
http://caniuse.com/#feat=opus
http://caniuse.com/#feat=ogg-vorbis
На стороне JS можно проверить наличие поддержки методом canPlayType(): https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType
Ну и проверь правильно ли ты все подключил, например инспектором на вкладке network посмотри скачивается ли файл.
Я делал так: если стоит кука debugGeoIp или GET-параметр то брать IP для определения города из него. Ну а найти IP для нужного города не проблема.
>>552097
Прежде чем браться встраивать в страницу тег аудио, надо изучить теорию и почитать мануалы. Иначе ничего хорошего не выйдет. Вот что надо помнить:
1) audio воспроизводит не любое аудио а только то что поддерживает браузер
2) в wav данные могут быть в разных форматах. Опять же не все воспроизводятся.
Ну и wav по моему обычно не сжатый и потому много весит, что тебе мешает в mp3 перекодировать?
Информация о поддержке форматов:
https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats (англ)
http://caniuse.com/#feat=audio
http://caniuse.com/#feat=wav
http://caniuse.com/#feat=mp3
http://caniuse.com/#feat=opus
http://caniuse.com/#feat=ogg-vorbis
На стороне JS можно проверить наличие поддержки методом canPlayType(): https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType
Ну и проверь правильно ли ты все подключил, например инспектором на вкладке network посмотри скачивается ли файл.
>Применение антикризисной меры должно быть функцией которая принимает на вход компанию и применяет к ней меры
Объект передается по ссылке.
Как этому воспрепятствовать? Первая антикризисная мера увольняет часть сотрудников, таким образом у нас переписывается объект компании. Но мне нужно, чтобы изменения произошли только внутри этой функции с антикризисными мерами, в глобальной области объект компании не должен меняться, чтобы можно было применять меры не последовательно друг за другом, а независимо.
У тебя непонимание как работают массивы.
Вот что например делает тут последняя команда?
$a = 100;
$b = [];
$b[] = $a;
Она кладет в массив $b переменную $a? Неверно. Она кладет в массив копию значения в переменной $a. Ты теперь можешь поменять значение в переменной или удалить ее, массив от этого никак не изменится.
С объектами ситуация чуть интереснее так как они передаются «как бы» по ссылке, а не копируются.
$a = new Test;
$b = [];
$b[] = $a;
Здесь тоже есть копирование, но копируется не объект, а ссылка на него. То есть ссылка на объект в переменной $a копируется в массив и теперь на этот объект есть 2 ссылки (в переменной и в массиве). Опять же, что бы мы не делали с переменной, из массива ничего не исчезнет. Однако
объект у нас только один:
$a->name = '123';
var_dump($b[0]->name); // 123
Теперь поговрим про удаление элементов из массива. Удалить их можно так:
unset($array[$key]);
или функцией array_splice (она удаляет не по индексу а по номеру, номер не совпадает с индексом так как элементы могут храниться в любом порядке).
От того что ты сделаешь unset($a) с массивом ничего не изменится.
Потому я не очень понял что ты хотел сказать своим кодом. Поясни?
У тебя непонимание как работают массивы.
Вот что например делает тут последняя команда?
$a = 100;
$b = [];
$b[] = $a;
Она кладет в массив $b переменную $a? Неверно. Она кладет в массив копию значения в переменной $a. Ты теперь можешь поменять значение в переменной или удалить ее, массив от этого никак не изменится.
С объектами ситуация чуть интереснее так как они передаются «как бы» по ссылке, а не копируются.
$a = new Test;
$b = [];
$b[] = $a;
Здесь тоже есть копирование, но копируется не объект, а ссылка на него. То есть ссылка на объект в переменной $a копируется в массив и теперь на этот объект есть 2 ссылки (в переменной и в массиве). Опять же, что бы мы не делали с переменной, из массива ничего не исчезнет. Однако
объект у нас только один:
$a->name = '123';
var_dump($b[0]->name); // 123
Теперь поговрим про удаление элементов из массива. Удалить их можно так:
unset($array[$key]);
или функцией array_splice (она удаляет не по индексу а по номеру, номер не совпадает с индексом так как элементы могут храниться в любом порядке).
От того что ты сделаешь unset($a) с массивом ничего не изменится.
Потому я не очень понял что ты хотел сказать своим кодом. Поясни?
Да, объект передается «как бы» по ссылке (мануал http://php.net/manual/ru/language.oop5.references.php ) и в 99% случаев это именно то что мы хотим. Мы не хотим плодить тысячу копий одного объекта.
Но в этой задаче, действительно, нам нужно сделать несколько копий компании чтобы проводить эксперименты. Для клонирования объектов есть оператор clone: http://php.net/manual/ru/language.oop5.cloning.php
Тебе также придется переопределить магический метод __clone для компании и департамента чтобы при клонировании ее содержимое тоже клонировалось, а не копировались ссылки на объекты.
Ну вот видишь, какая полезная задача, сколько ты нового узнаешь. Я тебе настоятельно рекомендую показать на проверку решение, а также решить кошки-мышки — она тоже на ООП, только еще сложнее и ошибок там делают кучу. И также задачу про скидки было бы хорошо порешать.
> foreach ($arr as $test) {
>\tif ($test->name == 'hello') unset($test);
$test это не элемент массива. Это переменная которая хранит копию его значения. Меняя ее ты никак не меняешь исходный массив.
Алсо, в PHP есть 2 способа сравнения объектов: http://php.net/manual/ru/language.oop5.object-comparison.php
В нормальном ООП разумеется используется именно строгое сравнение. Мы не хотим сравнивать содержимое объекта, мы хотим проверить тот же самый это объект или не тот же.
Обрати внимание что функции вроед array_search принимают параметр задающий тип сравнения (хотя по моему так они должны всегда использовать строгое).
В некоторых языках (Питон) чтобы избежать путаницы, строгое сравнение делается отдельным оператором is:
# проверить что обе переменные указывают на один объект
if a is b:
А сравнение через == происходит через вызов метода у одного из объектов и этот метод определяет правила сравнения.
Ты здесь спросил >>551910
>Почему тут и цикл и array-search одновременно?
вот в таком куске кода
foreach ($this->employees as $employee) {
if ($employee != $e) continue;
$index = array_search($employee, $this->employees);
unset($this->employees[$index]);
break;
}
Я тебе ответил, что использовал функцию array_search, чтобы найти позицию элемента в исходном массиве, потому что не могу по другому удалить элемент массива в цикле, поэтому привел этот код, чтобы объяснить, зачем использовал array_search в цикле.
Все что ты написал я знаю.
Можно было впрочем использовать for, но тогда будут длинные некрасивые конструкции во вложенных циклах вида
$vector->departments[$i]->employee[$j]->rate
Вообщем есть задачка, ускорить скрипт к которому подсоединяются клиенты (их много, данных среднее кол-во).
Был предложен вариант: ReactPHP, non blocking I/O, ывент дривен говно и прочая малафья. Не мог ли ты дорогой анон подсказать кейсы где его лучше всего использовать? С меня нихуя конешно же
Этот скрипт, выполняет много разной работы, и это зависит от условий.
1. Берем сеттинги из базы для клиента, без которых никак нельзя, они пригодятся позже.
2. Берем из базы, всякие плюшки, коих много и разные.
3. В зависимости от этих плюшек шлём ответ клиенту
4. + Клиенты сами отправляют разные данные, которые также необходимо записать в базу
Подходит ли сама суть React для этого? Я вот читаю, что все обмазываются этим, а сути истерии понять не могу (ментально ограничен видимо).
Бля, забыл добавить, то что иногда в зависимости от плюшек придется читать файлы, они не большие (макс. 1кб). Но именно это меня и смущает
Зачем цикл если ты можешь найти индекс элемента через array_search и удалить?
Проверить наличие объекта в массиве можно через http://php.net/manual/en/function.in-array.php хотя тут это и не требуется.
>>552152
«Ускорение» скрипта начинают с поиска узких мест, профайлинга и определения причин проблем с производительностью.
Те кто начинают его с предложения перейти на другую технологию просто-напросто некомпетентны.
React это фреймворк для поддеркжи асинхронных однопоточных приложений.
>«Ускорение» скрипта начинают с поиска узких мест, профайлинга и определения причин проблем с производительностью.
Уже было, делалось, просто большие и разные требования. Обычно все ок, но есть клиент у которого овер дохуя клиентов и это будет проблемой
>React это фреймворк для поддеркжи асинхронных однопоточных приложений.
Сможет ли он подойти к тем критериям, которые описал выше?
>>552157
Чат на вебсокетах лучше делать
>но есть клиент у которого овер дохуя клиентов
бля не выспался, вообщем есть кейс, где нужно очень много подключений.
Обожаю эти вопросы на резюме
>Что будет если мы сделаем вот так:
>echo "1" + eval('log(10)') + 1 - '1' + 2.3333333;
Сразу представляю какой говнокод там в проектах
1. Хипсторы, которые маются "нестандартной" хуитой.
2. Дноконторы, которые натягивают вордпресс.
Надеюсь все же есть третий вариант, буду искать место, где не занимаются совсем примитивной работой, но и не морочат голову своей псевдоинтеллектуальностью и показной нестандартностью.
https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L99
Очевидно, здесь чтобы кошка "ловила" мышку нужно указать что-то вроде:
$this->getWorld()->determineTheObject($x, $y) != $track
но здесь встает такая проблема, чтобы получить $track нужно пройтись по массиву $tracks. Если использовать foreach, то не получиться использовать continue чтобы пропустить предполагаемый ход, потому что continue будет заставлять переходит к следующему элементу массива $tracks, а не к следующему предполагаемому ходу. Что я пропустил?
https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/World.php#L86
Почему элементы принтуються по два раза? Попытка написать подобный код на ideone, чтобы более детально рассмотреть код,оказалась неудачной, потому что он не поддерживает html-тэги.
> Почему элементы принтуються по два раза?
Ну а ты на код свой посмотри:
> foreach ($this->getAllAnimals() as $object) {
Для каждой клеточки делается цикл по всем животным и на каждом шаге либо выводится картинка либо минус.
Весь HTML код без исключения надо вынести из классов в отдельный шаблон и подключать его через require как описано тут: http://www.phpinfo.su/articles/practice/shablony_v_php.html
В твоем случае удобно в index.php внутри <body> сделать примерно так:
для i от 0 до N {
делаем ход;
подключаем шаблон выводящий ситуацию на текущем ходе.
}
Для кошек/мышек можно сделать просто массив, хранящий соответствие тип => URL картинки.
http://archive-ipq-co.narod.ru/l1/strings.html - здесь задачу с палиндромом надо делать через два массива, где элементами первого будут все символы строки, а элементами второго - все элементы кроме пробелов и переносов строки?
Бля, я еблан.
Как сделать чтобы оно выдавало не одно значение "$matches[1]", а все http://ideone.com/7pXyzL.
мимо-нуб
Что это за вопрос вообще? В чем конкретно проблема? Руками создавай.
Что там непонятного? Функция отличается от preg_match только тем, что записывает все совпадения с шаблоном в массив. preg_match записывает первый попавшийся, затем возвращает true.
> Никогда не вставляйте фигурные скобки { } в своих блоках if-else, если этого не требует синтаксис. Когда в вашем коде несколько выражений и блоков if-else идут подряд, причем еще и с неправильным выравниванием, то вы можете запутать даже опытного коллегу.
http://m.habrahabr.ru/company/friifond/blog/268063/
preg_match находит только первое совпадение а preg_match_all все. Она описана в учебнике где-то в конце урока по регуляркам.
Вот набросал:
https://jsfiddle.net/wv2L7oog/4/
Итого:
1. проблема с картинкой, два последних aside разве не должны быть на уровне последнего абзаца?
2. как элегантно избавиться от проблемы с верхним отступом в первом абзаце?
Воспользуйся циклом foreach и перебери массив $matches и будет что-то что ты хочешь
мимо
Ну а вообще, ты все сделал через жопу и не так как нужно, ведь если найдется ошибка, то будет показываться только сама ошибка типа "шы", а там насколько я помню, нужно, что было показывалось слово с ошибкой
Аноны, я совсем зеленый, как заставить mb_strlen давать отрицательное значение? Ну, в строке, к примеру, 20 символов, как сделать так чтоб подсчитовалось количество символов и перед числом ставился минус -?
> как элегантно избавиться от проблемы с верхним отступом в первом абзаце?
Обычно текст начинается с заголовка, и можно написать правило для этого заголовка. А иногда меняются стили для тегов. В браузере теги hN, ul, p все имеют маргин сверху и снизу, можно поменять чтобы он был только снизу. Еще как вариант, можно нагородить правило типа p:first-child, h1:first-child { ... }.
Я не знаю какого-о универсального решения.
> проблема с картинкой, два последних aside разве не должны быть на уровне последнего абзаца?
А вот это вопрос к тебе, почему так и как это исправить? (подсказка: замени картинку на слово не заключенное в теги и перечитай как позиционируется флоат, например на softwaremaniacs)
Ну и в общем задача решена, я бы только добавил для aside небольшой маргин в 1 строку чтобы идущие друг за другом примечания не слипались в один блок текста.
Надеюсь ты разобрался как работают флоаты на примере этой задачи.
Мне интересно, как происходит процесс ответа в api anti-captcha
Что находится в файле anti-captcha.com/in.php
И что происходит на странице с распознаванием у китайцев?
Хотелось бы примеры, эти два файла
во я ахуел
Верю в тебя, анон
я просто спрашиваю как это устроено
Анон что за детский сад?
1) Берешь любой нормальный редактор или IDE (а не блокнот, никогда не программируй блокнотом), создаешь файл, сохранить -> «все файлы» -> вписываешь любое имя.
2) Допустим у тебя нет ни денег и интернета чтобы «купить» редактор на ближайшем торренте. Берешь блокнот, сохранить, вписываешь имя в кавычках.
3) Просишь доброанона закачать пустой гитигнор на файлообменник или гитхаб
4) Пишешь в командной строке echo > .hello
Дальше может еще кто дополнит.
Создаёшь скрипт, допустим profile.php, юзер заходит в свой профиль по ссылке profile.php?name=vasya, скрипт делает запрос в базу данных по имени, которое содержится в GET переменной и выводит на экран данные профиля, которые хранятся в бд.
Не могу понять что не так с этим? Мне в каждой клеточке как раз и нужна либо картинка либо минус, а не картинка и минус или минус и минус.
>>552254
http://unicode-table.com/ru/search/?q=%D0%BC%D1%8B%D1%88%D1%8C
Все понял. Хи-хи.
не надо мне советовать готовые библиотеки, я тут пытаюсь изобрести велосипед
Если проблема в том, что теряются закрывающие теги, есть смысл включить "жадность" (флаг U).
http://www.php.su/lessons/?lesson_17
https://gist.github.com/codedokode/10539366
у меня почему-то ругается на оператор match, mysql говорит syntax error.
Я что-то забыл прописать/установить?
Когда читал мануал тоже не мог понять, с какого перепуга mysql вдруг начнет понимать синтаксис запросов сфинкса.
Кстати при запуске демона из консоли он выдает warning о какой-то устаревшей настройке
WARNING: compat_sphinxql_magics=1 is deprecated; please update your application and config
ubuntu 14, mysql 5.5
>Весь HTML код без исключения надо вынести из классов в отдельный шаблон и подключать его через require как описано тут:
Не могу сообразить, это мне надо убрать из класса World метод printMap() и реализовать её, например, в /templates/print.phtml? Причем сделать это даже не процедурно, а просто php-кодом?
Спасибо.
А, блин, это мануал виноват.
https://gist.github.com/codedokode/10539366#file-sql-test-php
'mysql:host=localhost;port=9306' нельзя localhost, нужно писать айпи, теперь заработало
Еще он у меня странно перезапускается с ошибкой
service sphinxsearch restart
FATAL: failed to lock pid file '.../log/searchd.pid': Resource temporarily unavailable (searchd already running?)
У меня две копии что ли запущено, не понимаю?
Там под конец такие же сложные и долгие как по PHP, которые решают по несколько месяцев?
http://codepad.org/oJqM3VUt
Не закрыт блок (фигурная скобка).
Пости лучше на ideone, там автоматическая подсветка ошибок. Или установи простой редактор типа notepad++ или sublime text.
>>552592
Респект, если перешел.
Или только собираешься? С симфони действительно работают только грамотные, квалифицированные программисты, поэтому плохого кода конечно меньше.
По этой же причине вот так просто перейти на него не очень легко и быстро.
Но когда я закрываю оператор "если" он помечает мне что в этой строке ошибка.
http://ideone.com/eUStP7
Точка с запятой после break.
Т.е. например, getrand('sobaka') выдавало бы всегда 1 5 6, например.
А getrand ('kot') всегда выдавало бы 2 4 8
Немножко, просто не спал давно.
Ладно, переформулирую. Хочу функцию, которая выдавала бы рандомные значения, но одинаковые, при передаче этой функции переменной, например мд5 хэша.
Ты плохо объяснил что тебе нужно и для чего все это и потому хороший ответ на свой вопрос ты не получишь.
Есть такая штука. Генератор случайных чисел генерирует не настоящие случайные числа, а псевдослучайную последовательность, причем ты можешь задать ее начальное значение функцией http://php.net/manual/ru/function.mt-srand.php
Если ты поставишь в начале программы напримпер mt_srand(123); то она будет при каждом запуске генерировать одинаковую последовательность случайных чисел. Если к примеру ты делаешь игру на основе случайных чисел ты можешь таким образом сделать ее повторяемой.
Исправьте кто-нибудь плиз, или какую-нибудь наводку http://ideone.com/kuBaQV
Можно.
Если я тебя правильно понял, то вот набросал: http://ideone.com/mtkqBg
Только зачем тебе это?
str_replace
Нет, не критично. Вагрант тебе вряд ли понадобится, это средство для настройки виртуальной машины в виртуалбоксе с помощью конфига.
>>552658
Подходит но он менее популярен чем например Юи.
>>552438
Не такие сложные. Но их по моему никто и не сделал, аноны то ли бросают изучение то ли работу раньше находят.
>>552391
Это наверно ты под линуксом запускаешь, там PDO увидев слово localhost вместо соединения с указанным портом пытается найти юникс-сокет и коннектится к MySQL в итоге.
Я наблюдаю у себя под дебианом такой эффект:
mysql -hlocalhost --port=9000 (порт сфинкса)
коннектится к MySQL несмотря на указанный порт
mysql -h127.0.0.1 --port=9000
коннектится к сфинксу
Это описано в мануале: http://php.net/manual/ru/ref.pdo-mysql.connection.php#refsect1-ref.pdo-mysql.connection-notes
Но спасибо за информацию. Исправил на гитхабе. Можешь сейчас проверить исправленный код, работает ли?
> Еще он у меня странно перезапускается с ошибкой
Проверить лишние копии можно командой
ps lax | grep search
Прибить можно через
sudo service sphinxsearch stop
Либо kill -TERM 1234 если не поможет
1234 заменить на PID процесса. Сфинкс понимает чигнал TERM и корректно завершается.
Ну и не знаю, как ты его запускаешь, по идее если он у тебя установлен в системе глобально то через sudo service sphinxsearch start
Нет, не критично. Вагрант тебе вряд ли понадобится, это средство для настройки виртуальной машины в виртуалбоксе с помощью конфига.
>>552658
Подходит но он менее популярен чем например Юи.
>>552438
Не такие сложные. Но их по моему никто и не сделал, аноны то ли бросают изучение то ли работу раньше находят.
>>552391
Это наверно ты под линуксом запускаешь, там PDO увидев слово localhost вместо соединения с указанным портом пытается найти юникс-сокет и коннектится к MySQL в итоге.
Я наблюдаю у себя под дебианом такой эффект:
mysql -hlocalhost --port=9000 (порт сфинкса)
коннектится к MySQL несмотря на указанный порт
mysql -h127.0.0.1 --port=9000
коннектится к сфинксу
Это описано в мануале: http://php.net/manual/ru/ref.pdo-mysql.connection.php#refsect1-ref.pdo-mysql.connection-notes
Но спасибо за информацию. Исправил на гитхабе. Можешь сейчас проверить исправленный код, работает ли?
> Еще он у меня странно перезапускается с ошибкой
Проверить лишние копии можно командой
ps lax | grep search
Прибить можно через
sudo service sphinxsearch stop
Либо kill -TERM 1234 если не поможет
1234 заменить на PID процесса. Сфинкс понимает чигнал TERM и корректно завершается.
Ну и не знаю, как ты его запускаешь, по идее если он у тебя установлен в системе глобально то через sudo service sphinxsearch start
Открой для себя phpquery или аналогичную библиотеку.
>>552287
Не работает она с utf-8, урок: https://gist.github.com/codedokode/ff99e357e9860ea169b8
>>552281
Можно через mb_substr, можно разбить слово на массив букв и перевернуть.
Работает, но что-то странное с rt-индексом. Он почему-то всегда возвращает все записи, игнорируя match. Пик1.
Если использовать только дисковые индексы, поиск корректный.
Через командную строку корректный поиск (пик2). Правда, он не находит какой-то файл, но это скорее всего связано с правами. Как называется демон сфинкса, чтобы добавить его себе в группу?
Через php тоже некорректно, запрос
$pdo->query("SELECT * FROM index_news, rt_news WHERE MATCH('любая строка')");
возвращает все записи из таблицы rt_news. Если строка содержится в index_news, то возвращает найденный документ из index_news и все документы из rt_news.
>phpquery
Любые php библиотеки читающие ДОМ при большом количестве текста на php необратимо фейлятся по производительности. Нужно либо использовать более низкоуровневые решения на C например или попробывать nodejs, либо придумывать костыли с пошаговых парсингом. Меня как то на собеседование спросили, чем я буду парсить XML в 2гб. Никакая няшная библиотека с этим не справится.
А, через search вообще не находит, returned 0 matches
> Как называется демон сфинкса, чтобы добавить его себе в группу?
cat /etc/pa s s wd | grep sphinx
Нужны права не только на саму папку но и право x для всех папок от корня до нужной папки.
Также это с точки зрения юниксов не очень правильно, системные демоны хранят обычно данные в /var/lib/... а конфиги в /etc. Если у тебя конфиг и данные в папке проекта, тебе лучше запускать сфинкс не через service а от своего пользователя или от пользователя проекта.
Если он не может сохранить данные в rt индексе на диск то индекс вообще не обязан работать. Я советую сначала с этим разобраться.
Также, почитай логи сфинкса где-нибудь в /var/log (может в daemon.log, может в messages). Как альтернатива, можно запускать демон из консоли с флагом -с config --console чтобы он был с твоими правами и писал ошибки в консоль.
Ну и попробуй еще поменять запрос чтобы искать только в rt_news.
>>552784
Встроенный в PHP DOM вроде хорошо большие файлы переваривает.
Для огромных XML файлов надо использовать поточные библиотеки вместо DOM, для PHP это XmlReader и XmlWriter (заметь ты сразу вспоминаешь Си, а о том что есть готовая библиотека не думаешь. это плохо, это значит ты велосипедист). Но в вебе 2гб HTML файлов нет.
Nodejs никак не поможет, это та же динамика что и PHP и память она ест так же.
Ну и ты изначально ведешь не в ту сторону. Я о том что анон быдлокодит и не хочет использовать нормальные библиотеки а ты придумыаешь оправдания и уводишь тему в сторону.
Ох лол! Если писать /etc/pa s s wd слитно то пост не отправляется. Это чудо-система защиты от хакеров на cloudflare как я понимаю - так как хакеры часто тестируют эксплойты через чтение этого файла, она считает слово подозрительным. Ну дауны.
Вот когда люди вместо того чтобы подумать головой используют новомодное машинное обучение, нейросети и прочую новомодную дрянь, всегда так получается.
например:
$class = new Class();
или
$class = new Class;
на форчане сижу. а зачем тебе английский?
Перенес все конфигурационные файлы в /var/lib/sphinxsearch, все равно та же проблема с rt: через клиент mysql возвращает все документы, через команду search выдает ошибку об отсутствии файла
index 'rt_news': search error: failed to open /var/lib/sphinxsearch/data/rt_news.sph: No such file or directory
Мне что, руками этот файл создать? (Папка data присутствует, 755) В той же папке без проблем создаются файлы для дискового индекса, так что вряд ли дело в правах. И rt_news.lock присутствует, правда пустой.
>>552767
Устанавливал апт-гетом.
При входе в систему нет процесса, хотя я прописал запускать автоматически в /etc/default/sphinxsearch
# Should sphinxsearch run automatically on startup? (default: no)
START=yes
Поэтому запускаю руками sudo searchd.
После чего оказывается, что запущено два процесса
1 0 2872 1947 20 0 19368 932 wait S ? 0:00 searchd
5 0 2873 2872 20 0 22284 4192 poll_s Sl ? 0:00 searchd
А при попытке перезапустить через service sphinxsearch restart пишет
FATAL: failed to open log file '/var/lib/sphinxsearch/log/searchd.log': Permission denied
На папку log права 755. Но в ней от рута создан файл searchd.log с правами 600.
Дал права на чтение и запись. Теперь говорит, что не может создать pid файл.
Я уже запутался, там половина папок и файлов создана от рута, половина от sphinxsearch.
Алсо почему про классы пишут дочерние, а не сыновьи, разве тут нет сексизма? И почему матрицу называют так, а не патрицем или отцовцем?
а почему систему подчиненный-ведомый называют master-slave ? Нет ли тут бдсм подтекста?
на самом сдела это пришло из бизнеса.
выражение "Дочерняя компания"
"сыновья компания" это как-то странно и уебищно
да походу без разницы
https://www.google.ru/search?q=php+new+without+parentheses
алсо, учитесь гуглить то что вам нужно на инглише
Я просто помню, что мне еще кто-то сказал за эти скобочки, что не поставил.
Покажи свой конфиг сфинкса.
> Поэтому запускаю руками sudo searchd.
Надо sudo service sphinxsearch start
> При входе в систему нет процесса,
Можно проверить в sysv-rc-conf выставлен ли запуск (ну или в /etc/init.d или что там сейчас используют)
> Я уже запутался, там половина папок и файлов создана от рута, половина от sphinxsearch.
Это плохо.
>>552860
Да.
Ставить надо минимальный уровень доступа, если можно обойтись private то ставить его. Ну а вообще да, ты должен думать о том как будет использоваться поле и решать кто будет иметь доступ.
Не знаю, я пишу классы-наследники.
Сейчас я думаю, что мне нужно взять себя в руки и начать заполнять дыры в своих знаниях. Хоть я и ковырял небольшой проект с MVC - архитектурой, я до сих пор нихуя не понимаю в OOP и прочих паттернах.
Пока что у меня в голове дорешать задачи ОП-а, он кстати еще жив и участвует в треде, или вы тут сами катаетесь, и олдфаги прост обсуждают рабочие проблемы попутно помогая нубам решать задачу на айфоны?
Параллельно хочу добить основы на htmlacademy. Собственно вот. Что дальше делать не знаю. Понимаю полностью сейчас, что на работе я учился в 10 раз быстрее, чем сидя до этого дома занимаясь "самообразованием", ведь дома тупо можно бесконечно ебланить, а там тупо не до этого, просто берешь и делаешь.
Надеюсь найду через пару недель работу на джуно-позицию в одной из тех контор, куда ходил год назад и в которых фейлил тестовые задания.
На ООП есть задачи про Вектор, Кошки-мышки, ты их решал? А так, я советую тогда делать задание на Юи 2, например сайт тестов или может как другой анон, доску объявлений с иерархическими категориями и EAV.
Также обрати внимание что у нас есть задания на HTML/CSS и JS и на SQL, все в ОП посте.
Да, вот в этом было дело.
Вместо sphinxd нужно sphinxsearch.
Если быть точнее, его вообще не нужно запускать. В файле /etc/default/sphinxsearch можно выставить start=yes для автозапуска сфинкса вместе с операционной системой.
А я запустил вторую копию руками, причем от суперпользователя sudo sphinxd, поэтому происходил конфликт: файл search.pid уже существовал, созданный от пользователя sphinxsearch. Но я запускал вторую копию через судо, и не получал определенных прав.
Почистил руками папку data и log, перезапустился.
Через php все работает правильно (мне большего пока и не нужно). Только утилита search продолжает сообщать об отсутствии файла rt_news.sph, его действительно нет, не знаю чем это грозит. При перезагрузке системы данные rt индекса не теряются, видимо они подгружаются из rt_news.ram
Да, еще indexer приходится тоже выполнять от суперпользователя, иначе
FATAL: failed to open /var/lib/sphinxsearch/data/index_news.tmp.spl: Permission denied, will not index. Try --rotate option.
Причем с опцией --rotate та же ошибка, так что это ни при чем. От суперпользователя проиндексировалось успешно, хотя теперь половина файлов индекса принадлежит судо. Впрочем, все работает, кроме упомянутой утилиты search, которая прекрасно ищет по дисковому индексу, но в rt жалуется на отсутствие rt_news.sph.
На всякий случай фрагмент содержимого /etc/sphinxsearch/sphinx.conf (почти такое же, как в твоем примере из гайда, но со своими путями)
index index_news
{
morphology = stem_ru, stem_en
source = src_news
path = /var/lib/sphinxsearch/data/index_news
docinfo = extern
charset_type = utf-8
}
index rt_news
{
charset_type = utf-8
type = rt
path = /var/lib/sphinxsearch/data/rt_news
rt_field = header
rt_field = body
rt_attr_uint = added
rt_attr_uint = topic
}
searchd
{
listen = 9312
listen = 9306:mysql41
log = /var/lib/sphinxsearch/log/searchd.log
query_log = /var/lib/sphinxsearch/log/query.log
read_timeout = 5
max_children = 30
pid_file = /var/lib/sphinxsearch/searchd.pid
max_matches = 1000
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
workers = threads
binlog_path = /var/lib/sphinxsearch/data
compat_sphinxql_magics = 0
}
Права на файлы в папке /var/lig/sphinxsearch/data
drwxr-xr-x 2 sphinxsearch root 4096 Окт 4 02:16 .
drwxr-xr-x 4 sphinxsearch root 4096 Окт 4 02:16 ..
-rw------- 1 sphinxsearch sphinxsearch 8 Окт 4 02:16 binlog.001
-rw------- 1 sphinxsearch sphinxsearch 0 Окт 4 02:16 binlog.lock
-rw------- 1 sphinxsearch sphinxsearch 11 Окт 4 02:16 binlog.meta
-rw-r--r-- 1 root root 128 Окт 4 01:39 index_news.spa
-rw-r--r-- 1 root root 480 Окт 4 01:39 index_news.spd
-rw-r--r-- 1 root root 337 Окт 4 01:39 index_news.sph
-rw-r--r-- 1 root root 1059 Окт 4 01:39 index_news.spi
-rw-r--r-- 1 root root 0 Окт 4 01:39 index_news.spk
-rw------- 1 sphinxsearch sphinxsearch 0 Окт 4 02:16 index_news.spl
-rw-r--r-- 1 root root 0 Окт 4 01:39 index_news.spm
-rw-r--r-- 1 root root 156 Окт 4 01:39 index_news.spp
-rw-r--r-- 1 root root 1 Окт 4 01:39 index_news.sps
-rw------- 1 sphinxsearch sphinxsearch 4 Окт 4 02:06 rt_news.kill
-rw------- 1 sphinxsearch sphinxsearch 0 Окт 4 02:16 rt_news.lock
-rw------- 1 sphinxsearch sphinxsearch 305 Окт 4 02:06 rt_news.meta
-rw------- 1 sphinxsearch sphinxsearch 388 Окт 4 02:06 rt_news.ram
>>552962
Там еще кроме древовидных категорий и eav не помешает полнотекстовый поиск, из-за чего я вожусь со сфинксом, для обновления счетчика просмотра страниц понадобился memcache с кроном, плюс нужно наполнить базу через faker, чтобы потестить производительность при помощи explain и cachegrind.
Куда ни плюнь, новая технология, такое впечатление чем больше учишь, тем больше их появляется на горизонте.
Да, вот в этом было дело.
Вместо sphinxd нужно sphinxsearch.
Если быть точнее, его вообще не нужно запускать. В файле /etc/default/sphinxsearch можно выставить start=yes для автозапуска сфинкса вместе с операционной системой.
А я запустил вторую копию руками, причем от суперпользователя sudo sphinxd, поэтому происходил конфликт: файл search.pid уже существовал, созданный от пользователя sphinxsearch. Но я запускал вторую копию через судо, и не получал определенных прав.
Почистил руками папку data и log, перезапустился.
Через php все работает правильно (мне большего пока и не нужно). Только утилита search продолжает сообщать об отсутствии файла rt_news.sph, его действительно нет, не знаю чем это грозит. При перезагрузке системы данные rt индекса не теряются, видимо они подгружаются из rt_news.ram
Да, еще indexer приходится тоже выполнять от суперпользователя, иначе
FATAL: failed to open /var/lib/sphinxsearch/data/index_news.tmp.spl: Permission denied, will not index. Try --rotate option.
Причем с опцией --rotate та же ошибка, так что это ни при чем. От суперпользователя проиндексировалось успешно, хотя теперь половина файлов индекса принадлежит судо. Впрочем, все работает, кроме упомянутой утилиты search, которая прекрасно ищет по дисковому индексу, но в rt жалуется на отсутствие rt_news.sph.
На всякий случай фрагмент содержимого /etc/sphinxsearch/sphinx.conf (почти такое же, как в твоем примере из гайда, но со своими путями)
index index_news
{
morphology = stem_ru, stem_en
source = src_news
path = /var/lib/sphinxsearch/data/index_news
docinfo = extern
charset_type = utf-8
}
index rt_news
{
charset_type = utf-8
type = rt
path = /var/lib/sphinxsearch/data/rt_news
rt_field = header
rt_field = body
rt_attr_uint = added
rt_attr_uint = topic
}
searchd
{
listen = 9312
listen = 9306:mysql41
log = /var/lib/sphinxsearch/log/searchd.log
query_log = /var/lib/sphinxsearch/log/query.log
read_timeout = 5
max_children = 30
pid_file = /var/lib/sphinxsearch/searchd.pid
max_matches = 1000
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
workers = threads
binlog_path = /var/lib/sphinxsearch/data
compat_sphinxql_magics = 0
}
Права на файлы в папке /var/lig/sphinxsearch/data
drwxr-xr-x 2 sphinxsearch root 4096 Окт 4 02:16 .
drwxr-xr-x 4 sphinxsearch root 4096 Окт 4 02:16 ..
-rw------- 1 sphinxsearch sphinxsearch 8 Окт 4 02:16 binlog.001
-rw------- 1 sphinxsearch sphinxsearch 0 Окт 4 02:16 binlog.lock
-rw------- 1 sphinxsearch sphinxsearch 11 Окт 4 02:16 binlog.meta
-rw-r--r-- 1 root root 128 Окт 4 01:39 index_news.spa
-rw-r--r-- 1 root root 480 Окт 4 01:39 index_news.spd
-rw-r--r-- 1 root root 337 Окт 4 01:39 index_news.sph
-rw-r--r-- 1 root root 1059 Окт 4 01:39 index_news.spi
-rw-r--r-- 1 root root 0 Окт 4 01:39 index_news.spk
-rw------- 1 sphinxsearch sphinxsearch 0 Окт 4 02:16 index_news.spl
-rw-r--r-- 1 root root 0 Окт 4 01:39 index_news.spm
-rw-r--r-- 1 root root 156 Окт 4 01:39 index_news.spp
-rw-r--r-- 1 root root 1 Окт 4 01:39 index_news.sps
-rw------- 1 sphinxsearch sphinxsearch 4 Окт 4 02:06 rt_news.kill
-rw------- 1 sphinxsearch sphinxsearch 0 Окт 4 02:16 rt_news.lock
-rw------- 1 sphinxsearch sphinxsearch 305 Окт 4 02:06 rt_news.meta
-rw------- 1 sphinxsearch sphinxsearch 388 Окт 4 02:06 rt_news.ram
>>552962
Там еще кроме древовидных категорий и eav не помешает полнотекстовый поиск, из-за чего я вожусь со сфинксом, для обновления счетчика просмотра страниц понадобился memcache с кроном, плюс нужно наполнить базу через faker, чтобы потестить производительность при помощи explain и cachegrind.
Куда ни плюнь, новая технология, такое впечатление чем больше учишь, тем больше их появляется на горизонте.
Файлы индексов и PID должны принадлежать пользователю sphinxsearch, соответственно индексер запускается тоже от его имени.
Я бы их поменял: владелец и группа sphinxsearch, права 0664.
Логи для демонов принято вести в /var/log/
Для pid по моему есть /var/run (она часто размещается в RAM), но это можно не менять.
> для обновления счетчика просмотра страниц понадобился memcache с кроном,
О, это интересная штука, покажи потом на проверку. Там много подвохов. Ну и сразу статья: http://habrahabr.ru/company/mailru/blog/206494/
Запускать демоны от рута очень плохо так как для рута не работают многие проверки и можно разломать все в системе при ошибке.
>в Хроме за прокрутку страницы отвечает html, а в других браузерах body
Хром, на $('html') не работает, на $('body') - работает.
Сам код: https://ideone.com/FiIuf0
Исправил большинство замечаний, вывел карту с помощью шаблона.
Остается актуальной проблема >>552184
>https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L99
>Очевидно, здесь чтобы кошка "ловила" мышку нужно указать что-то вроде:
>$this->getWorld()->determineTheObject($x, $y) != $track
>но здесь встает такая проблема, чтобы получить $track нужно пройтись по массиву $tracks. Если использовать foreach, то не получиться использовать continue чтобы пропустить предполагаемый ход, потому что continue будет заставлять переходит к следующему элементу массива $tracks, а не к следующему предполагаемому ходу. Что я пропустил?
Вектор решал в своё время. Засел где-то на дополнительных задачах к нему, типа антикризисные решения.
>>552979
Сначала заболел и пропустил 2 дня, и начальник подгорел. Потом у нас был типа "разговор", хотя на самом деле монолог в одностороннем порядке, и я на следующий день опоздал на час из-за того что вообще не спал ночью из-за всего этого психологического пиздеца на работке, в общем как пришел, он совсем бомбанул и сказал что всё.
Говноконтора, или ты настолько говноспециалист сейчас, что заменить без проблем новым за неделю. Подтягивай уровень и устраивайся в нормальную, я по полмесяца в моей прогуливаю бывает, и ничего.
Или правильнее всё в контроллере распаковывать?
И нет, передаётся не контейнер зависимостей.
Ну это даже не контора, нас было двое. Собственно он типа успешный фрилансер, и не справлялся с потоком клиентов, вот и нанял меня в серую за гроши. Я к тому времени уже искал работу довольно долго, вот и согласился на что угодно. Собственно со всеми задачами я справлялся. Тут скорее дело в том, что он см себе хозяин и ему похуй на конструктив. Подгорел чутка - и распрощался со мной.
>что заменить без проблем новым за неделю.
Это вообще легко. Любого студента со знанем html и основами php наймет и посадит баннеры менять на сайтах, а со временем нагрузит дополнительной хуетой. Собственно я пришел лишь со знанием php в своё время, а потом на меня wp и прочий html скинули.
>Подтягивай уровень и устраивайся в нормальную
Это и собираюсь делать
> я по полмесяца в моей прогуливаю бывает, и ничего.
удаленно таски решаешь в таком случае или как?
http://geekbrains.ru/courses
Они косят под чуваков с хабра.
А ещё я бы не пошёл на эти курсы даже бесплатно.
И сделать качественный образовательный ресурс не так просто, как тебе кажется.
>чем больше учишь, тем больше их появляется на горизонте.
Это еще платон математически обосновал.
Тогда ты должен задать себе вопрос, что у тебя за метод?
- работающий только с загруженным через POST файлами
- работающий с любыми файлами в пределах корня сайта
- работающий в принципе с любыми файлами
Скорее всего второе или третье. Значит ты должен вместо UploadedFile передавать что-то более абстрактное, например Symfony\Component\HttpFoundation\File\File, SplFile или может просто путь.
>>553158
чтобы сделать хорошие уроки надо быть хорошим программистом, что не у всех получается, по PHP и вебу вообще очень много неграмотных статей и учебников.
Алсо я тут посмотрел, оказывается UploadedFile, File и SpliFileInfo наследуются друг от друга. Тогда ты можешь передавать UploadedFile, но должен подумать какой класс указать в тайп хинте аргумента функции.
Да главное, чтобы он систематизированный был. фронтенд джуниор, бэкенд джуниор, фронтенд миддл, бэкенд миддл, отдельный раздел по ООП с паттернами, отдельно по фроеймворком. Потому что вначале на человек наваливается каша из незнакомых не систематизированных слов и он заканчивает тем, что учит только верстку с баннерами и вордпресс.
Метод, работающий только с загруженными через POST файлами. Он возвращает новый объект-модель файла, который я потом передаю уже в сохраняющие методы.
Если вдруг фреймворк поменяется или мы решим написать свои контроллеры, в сервисе придётся переписать только один метод-конвертер.
Да, наследуются. У меня частично используются и унаследованные методы (например SplFileInfo::getRealPath()).
Тогда ок. Учти что плохо когда например функция добавления аватарки пользователю принимает только UploadedFile, часто нужно иметь возможность например поставить аватарку из произвольного файла (наример для теста).
Все-таки я считаю чат должен быть анонимным и без регистрации. Не забудьте шаблоны добавить из книги по ООП. Примеры. чтобы их можно было фиксить, как в хтмл академии и задачи для каждого шаблона с пойнтами и подсказками.
> вентили, регистры, ассемблер
Я бы уже на этом месте закрыл. Они учат не тому что полезнее а тому что им хочется преподавать.
Понял, спасибо.
А книгу, которая идёт в контексте этой темы, советовал бы прочесть? Вот эту: http://www.ozon.ru/context/detail/id/28283366/
1. Устройся на месяц-два на быдло-работу, в макдональдс или типа того.
2. Охуей от мерзости людишек и тупой бессмысленной (но вместе с тем тяжелой) работы.
3. Осознай, что если не выучишь хорошую профессию, будешь жить вот так до самой смерти, пока Пукин (да славится имя Его) не даст пенсию (в 65 лет), которой хватит только на гречку на лопате.
4. ???
5. Перестаешь прокрастинировать быть лентяем и неудачником, начинаешь учить php.
6. Повторять предыдущие 5 пунктов каждый раз когда появляется желание пинать хуи
Я делаю АПИ. По запросу ко мне я должен запустить длинную задачу, которая включает в себя несколько проверок данных в локальной БД, запросы во внешние АПИ и изменение данных в локальной БД по результатам этих запросов. Причем изменения такие, что повторный запрос ко мне с теми же параметрами - не пройдет (ибо проверки данных в БД вначале провалятся)
Как правильно всю эту хрень организовать так, чтобы:
- одновременные запросы ко мне не приводили к "двойным" изменениям в БД и "двойным" запросам во внешине АПИ. Ибо между начальной проверкой условий и изменением данных проходит время
- если скрипт упадет в процессе - чтобы .. ээ.. транзакция.. целиком откатилась. Только вот тут проблема в том, что в транзакцию эту запросы ко внешним АПИ-то не включить, и назад эти запросы не откатить...
Гугление выдает какую то мешанину инфы про такие хрени, как mutexes, two-phase commit protocol, идемпотентность запросов и т.п.
Но, блин, из этой мешанины понять, как именно можно\нужно подобные задачи решать - нихрена не понятно.
Знаю ты умеешь очень хорошо сложные вещи описывать простыми словами. Если распишешь варианты решений подобных задач у себя в гисте - моей благодарности тебе не будет границ.
Заранее спасибо!
Думаешь Путин не доживет до 2057 года? Или еще поднимут пенсионный возраст?
>Opencart это CMS для магазинов, а не фреймворк.
Я не писал что Opencart это фреймворк, я лишь указал, что начал знакомство с МВЦ с опенкарта, так как он интуитивно понятен и хотел бы начать знакомство с фреймворками с чего-то простого/необходимого.
>Yii2 новее и лучше. Zend сейчас не особо используют, только в старых проектах, да и он сложноват может быть для новичка.
То-есть всё же наилучшим вариантом начать будет с yii2.
class Product {
public $id;
public $name;
function __construct($id, $name){
$this->id = $id;
$this->name = $name;
}
}
class ProductFacade {
private $products = array();
function __construct($file){
$this->file = $file;
$this->compile();
}
private function compile(){
function getProductFileLines($file){
return file($file);
}
function getProductObjectfromID($id, $productname){
return new Product($id, $productname);
}
function getNameFromLine($line){
if(preg_match("/.-(.)\s\d+/", $line, $array)){
return str_replace('_', " ", $array[1]);
}
return '';
}
function getIDFromLine($line){
if(preg_match("/^(\d{1,3})-/", $line, $array)){
return $array[1];
}
return -1;
}
$lines = getProductFileLines($this->file);
foreach($lines as $line){
$id = getIDFromLine($line);
$name = getNameFromLine($line);
$this->products[$id]=getProductObjectfromID($id, $name);
}
}
function getProducts(){
return $this->products;
}
function getProduct($id){
return $this->products[$id];
}
}
$facade = new ProductFacade('test.txt');
$facade->getProduct(1);
class Product {
public $id;
public $name;
function __construct($id, $name){
$this->id = $id;
$this->name = $name;
}
}
class ProductFacade {
private $products = array();
function __construct($file){
$this->file = $file;
$this->compile();
}
private function compile(){
function getProductFileLines($file){
return file($file);
}
function getProductObjectfromID($id, $productname){
return new Product($id, $productname);
}
function getNameFromLine($line){
if(preg_match("/.-(.)\s\d+/", $line, $array)){
return str_replace('_', " ", $array[1]);
}
return '';
}
function getIDFromLine($line){
if(preg_match("/^(\d{1,3})-/", $line, $array)){
return $array[1];
}
return -1;
}
$lines = getProductFileLines($this->file);
foreach($lines as $line){
$id = getIDFromLine($line);
$name = getNameFromLine($line);
$this->products[$id]=getProductObjectfromID($id, $name);
}
}
function getProducts(){
return $this->products;
}
function getProduct($id){
return $this->products[$id];
}
}
$facade = new ProductFacade('test.txt');
$facade->getProduct(1);
> mt_rand(0, count($word1));
от 0 до количества, но суть в том, что если у тебя 5 значений в массиве то count покажет 5.
Но ключи идут с нулевого (0), а не с первого, дальше подумай что надо сделать с count($word1)
Извини за сумбурность, не всегда могу выразить так как хочу:
$word1 = array('Чудесных', 'Суровых', 'Занятных', 'Внезапных');
count($word1) покажет 4,
но в массиве нет значения с ключем 4 3 последний
Если коротко, то парсит лист с товарами и записывает в объект. Немного странно написан правда.
Если будешь упорно трудиться и не будешь халтурить, освоишь быстрее. Ничего сверхъестественного тут нет.
После любого адекватного фреймворка, на это говно по имени Bitrix невозможно смотреть, не обблевав клавиатуру. Его же пишут калеки из 1С.
За 2 года ты сможешь без пизды на миддла научиться. Чуть чуть упорства - и через 2 года ты на Сеньора пойдешь
Успешный-менеджер-моде-он
"Но лучше никто ничего не придумал"
"Самая лучшая цмс для махазинов"
"На нее затрачены сотни тысяч часов пронраммистов"
>Любого студента со знанем html и основами php наймет и посадит баннеры менять на сайтах
Не будь таким студентом. Учи test driven development, основы SOLID, архитектуру, оптимизацию БД, паттерны проектирования. Сможешь работать в конторах, где не только по-быстрому навалять сайты в вордпрессиках надо, а где также много внимания уделяется чистом коду, рефакторингу и правильным решениям.
>удаленно таски решаешь в таком случае или как?
Крупные конторы могут себе позволить затягивание сроков по запланированным проектам на 1-2 месяца, это норма. Задачи загонять разработчика до истощения как в мелких говноконторках не стоит.
Все сделано правильно (проверял) до пункта if, там уже начинается какая-то порнография и я абсолютно не понимаю в чем ошибка. http://ideone.com/26lUCF
В ин-те вообще не нашел это задачи решенной, все только через strrev могут
У тебя в условии if идет присвоение, а не сравнение. '='
Я новенький в вашей среде, и я прохожу уроки, но я застрял на уроке про кубик. У меня не получается получить результат, но показывает, что задание выполнено "успешно". Вот как я сделал :
<?php
error_reporting (-1);
$anonDice1 = mt_rand(1,6); / Первый бросок анона /
$anonDice2 = mt_rand(1,6); / Второй бросок анона /
$compDice1 = mt_rand(1,6); / Первый бросок компьтера /
$compDice2 = mt_rand(1,6);
/ \n - это перенос строки
echo "У анона выпало {$anonDice1} и {$anonDice2}\nУ компьютера {$compDice1} и {$compDice2}\n";
/ Находим сумму чисел /
$anonSum = ($anonDice1 + $anonDice2);
$compSum = ($compDice1 + $compDice2);
/ Если выпали даблы - игра заканчивается /
/ Используй 2 знака равно для сравнения /
/ && значит "И" /
if (( $anonDice1 == $anonDice2) && ($compDice1 == $compDice2)) {
\t\techo "2 дабла - тебя ждет большая удача. Запости скриншот!!\n";
\t\texit (); / exit () завершает выполнение скрипта /
}
/ Проверяем, кто победил... */
В примере задания написано, что после "Проверяем, кто победил..." надо что-то дописать, но я не знаю даже что именно. В описание урока я ничего не могу найти подходящего. Кто выполнял это задание, то объясните нубу.
Я новенький в вашей среде, и я прохожу уроки, но я застрял на уроке про кубик. У меня не получается получить результат, но показывает, что задание выполнено "успешно". Вот как я сделал :
<?php
error_reporting (-1);
$anonDice1 = mt_rand(1,6); / Первый бросок анона /
$anonDice2 = mt_rand(1,6); / Второй бросок анона /
$compDice1 = mt_rand(1,6); / Первый бросок компьтера /
$compDice2 = mt_rand(1,6);
/ \n - это перенос строки
echo "У анона выпало {$anonDice1} и {$anonDice2}\nУ компьютера {$compDice1} и {$compDice2}\n";
/ Находим сумму чисел /
$anonSum = ($anonDice1 + $anonDice2);
$compSum = ($compDice1 + $compDice2);
/ Если выпали даблы - игра заканчивается /
/ Используй 2 знака равно для сравнения /
/ && значит "И" /
if (( $anonDice1 == $anonDice2) && ($compDice1 == $compDice2)) {
\t\techo "2 дабла - тебя ждет большая удача. Запости скриншот!!\n";
\t\texit (); / exit () завершает выполнение скрипта /
}
/ Проверяем, кто победил... */
В примере задания написано, что после "Проверяем, кто победил..." надо что-то дописать, но я не знаю даже что именно. В описание урока я ничего не могу найти подходящего. Кто выполнял это задание, то объясните нубу.
так даже легче будет, а то сразу не понял, как тут все у вас принято показывать.
http://ideone.com/z4APFZ
У анона выпало число X, у компа выпало число Y, сравнить и вывести на экран кто победил
Нет.
Допиши вывод на экран чисел, выпавших у анона и компьютера и результат (кто победил). Если ты читал урок, тебе не составит труда это сделать.
"Я бежала за Вами три дня и три ночи, чтобы сказать Вам как Вы мне безразличны."
После работы с многими cms и фреймворками ,1-с Битрикс не плох и не хорош, он просто по своему ебанутый.
Так ты сперва напиши, как ты понял, а мы посмотрим, где ты не прав. Проще пояснить на практике.
утром напишу. Сейчас уже голова не соображает толком.
Какую комбинацию индексов все-таки выбрать? В блоге, на который есть ссылка в оповском гайде,
http://chakrygin.ru/2013/04/sphinx-db-indexing-and-delta-indexes.html
описываются какие-то извращения с дисковыми индексами, один из которых переиндексируется кроном раз в определенный промежуток времени, и маленький дельта-индекс, затем они мержатся.
Почему автор так избегает rt-индексов? Мне кажется, что логичнее было бы как раз сделать дельта-индекс rt, который просто очищать при перестройке главного дискового. Тогда не нужно ничего мержить, создавать доп.таблицу с отслеживанием индексаций и тому подобных телодвижений.
Еще я не совсем понял, а зачем собственно нужно sphinx api для php, если все прекрасно работает через клиент mysql. Это для тех случаев, когда у нас база, отличная от mysql/postgresql? Или когда нет возможности установить клиент mysql?
> Почему автор так избегает rt-индексов?
Может быть rt индексов тогда не было. Так, конечно новые данные надо класть в rt индекс, а раз в сутки переиндексировать и переносить в основной индекс. Я могу ошибаться, но rt индексы рассчитаны на небольшие объемы данных - или это уже исправлено? Сфинкс быстро развивается.
> , который просто очищать при перестройке главного дискового.
Только надо не потерять данные которые писались в промежутке между началом перестройки и очисткой.
> а зачем собственно нужно sphinx api для php
По моему исторически оно появилось первым. Ну и есть функции вроде такой http://sphinxsearch.com/docs/current/api-func-buildexcerpts.html - есть ли аналоги им в MySQL API?
Ну и может кому-то обычное АПИ удобнее и он не хочет запросы городить.
> одновременные запросы ко мне не приводили к "двойным" изменениям в БД и "двойным" запросам во внешине АПИ. Ибо между начальной проверкой условий и изменением данных проходит время
Лучший способ это сделать алгоритм так чтобы его можно было вызывать несколько раз и ничего не ломалось. Это называется
> идемпотентность
А так, надо делать пессимистическую блокировку то есть до выполнения скрипта брать блокировку и блокироваться если кто-то уже обрабатывает этот объект. Это не позволит запустить вторую копию скрипта. Не забудь что у блокировки должен быть таймаут иначе можно получить ситуацию когда куча скриптов висит в ожидании когда же ресурс освободится.
> если скрипт упадет в процессе - чтобы .. ээ.. транзакция.. целиком откатилась. Только вот тут проблема в том, что в транзакцию эту запросы ко внешним АПИ-то не включить, и назад эти запросы не откатить...
Ну смотря какое АПИ. Если это АПИ загрузки файла на сервер, то в принципе его можно удалить при откате.
А так, опять же надо продумать алгоритм. Ну рассмотрим к примеру загрузку файла в облако с записью информации в БД. Тут лучше всего сначала загрузить файл, потом записать информацию. В этом случае при падении не получится такого что в базе файл есть, а на сервере нет. А при удалении, наоборот, надо успешно удалить файл из базы и только после - с удаленного сервера.
Анон, чому цикл не выводимт переменную
Подожди, пока рак на горе свистнет, да пока свиньи полетят. Раньше никак не выведет.
У тебя в условии стоит $divider<10, а сама переменная инициализируется со значением 100.
ОП, я понимаю, что тебя уже доебал, но дальше двигаться не могу, томлюсь ожиданием >>551736
/тред
в меде учишься?
Сука решил наконец-то эту ссаную задачу, в инт-те ни одного готового решения, ссаные пешки только через стрю могут сделать.
да и тебе спасибо, помог очень, кстати в прошлый раз выше это я и накосячил также с этим отсчетом с 0, не могу пока привыкнуть
Молодец что решил таки задачу. Лутай свою экспу:-)
Понравилось:
if ($symbol1 == $symbol2){
\t\t\t} else {
\t\tbreak;
\t}
!= или <> давно не работают ?
http://ideone.com/7ebBq3
$word1 = array('Чудесных', 'Суровых', 'Занятных', 'Внезапных');
$word2=array('слов', 'зим', 'глаз', 'дней', 'лет', 'мир', 'взор');
$word3 =array('прикосновений', 'поползновений', 'судьбы явлений','сухие листья', 'морщины смерти', 'долины края', 'замены нету', 'сухая юность', 'навек исчезнув');
$word4=array('обретаю', 'понимаю', 'начертаю', 'закрываю', 'оставляю', 'вынимаю', 'умираю', 'замерзаю', 'выделяю');
$word5=array('очертания', 'безысходность', 'начертанья', 'смысл жизни','вирус смерти', 'радость мира');
function getPoem($word)
{
$length=count($word)-1;
$rand=rand(0, $length);
foreach($word as $key=>$value){
if($key==$rand && array_key_exists($rand, $word)){
echo $value." ";
}
}
}
getPoem($word1);
getPoem($word2);
getPoem($word3);
getPoem($word4);
getPoem($word5);
http://ideone.com/7ebBq3
$word1 = array('Чудесных', 'Суровых', 'Занятных', 'Внезапных');
$word2=array('слов', 'зим', 'глаз', 'дней', 'лет', 'мир', 'взор');
$word3 =array('прикосновений', 'поползновений', 'судьбы явлений','сухие листья', 'морщины смерти', 'долины края', 'замены нету', 'сухая юность', 'навек исчезнув');
$word4=array('обретаю', 'понимаю', 'начертаю', 'закрываю', 'оставляю', 'вынимаю', 'умираю', 'замерзаю', 'выделяю');
$word5=array('очертания', 'безысходность', 'начертанья', 'смысл жизни','вирус смерти', 'радость мира');
function getPoem($word)
{
$length=count($word)-1;
$rand=rand(0, $length);
foreach($word as $key=>$value){
if($key==$rand && array_key_exists($rand, $word)){
echo $value." ";
}
}
}
getPoem($word1);
getPoem($word2);
getPoem($word3);
getPoem($word4);
getPoem($word5);
>>>553769
Я просто хотел выебнуться, код - это как член, от величины пользы может и нет, зато выглядит как охуенно
Но ведь чем меньше код, тем он круче. Маленький и аккуратный код всегда краше глазу чем большой.
Цикл не нужен. Если (есть элемент в массиве по индексу) то (вывести элемент массива) ,иначе (вывести ''хуй тебе")
Тут не в размере дело, лол. А в балансе подробности / читаемости.
https://github.com/nsdvw/classifieds/blob/master/protected/models/Ad.php#L38
Мне это не нравится. Зачем перехватывать любые вызовы любых методов? Если это решение какой-то проблемы ее явно можно было решить более аккуратно.
> https://github.com/nsdvw/classifieds/blob/master/protected/models/Ad.php#L177
Этот метод никакого отношения к Ad по моему не имеет.
И еще к тебе вопрос, а как насчет тестов? Было бы здорово покрыть все это тестами. Конечно автоматизированные тесты далеко не везде используют, и не всегда затраты на них оправданны, но вещь это очень интересная и она выводит разработку на другой уровень. Потому что ты можешь не бояться что кто-то сломает сделанные тобой фичи, если они покрыты тестами. И можешь не бояться делать большие правки кода.
Так как автоматизированные тесты используют не везде, то вполне возможно что на работе тебя им никто учить не будет. А тут будет как раз хорошая возможность набить шишек.
Вот обзорный урок по тестам: https://gist.github.com/codedokode/a455bde7d0748c0a351a
Так как сайт не сложный, я думаю, там нужны будут в основном E2E тесты. Чтобы не мучаться с селениумом, можно будет использовать codeception + phantomJS (это эмулятор браузера на вебките поддерживающий JS/CSS и контролируемый через selenium-соместимый протокол). Ну к примеру тест будет проходить форму создания объявления и убеждаться что оно добавилось в список.
Также можно будет писать регрессионные тесты, то есть тесты сделанные по мотивам обнаруженных багов, проверяющие что этот баг более не повторится.
Как план-максимум - настроить запуск тестов на travis CI чтобы он сам брал новые коммиты из твоего гитхаба, прогонял тесты и у тебя был бэйджик со статусом их выполнения, вот как тут висит: https://github.com/slimphp/slim
А вот лог выполнения одного из тестов (провален): https://travis-ci.org/slimphp/Slim/jobs/83432661
Один на один с тестами никто тебя не бросит, я подскажу и как codeception настроить и опишу пошагово пример теста какого-нибудь. Ну и конечно придется статьи почитать, на хабре например яндекс и баду на эту тему что-то интересное писали.
Это все можно будет делать неспешно, по наличию свободного времени, как в нашем треде и принято.
Подумай сам, хочешь ты изучить современный и прогрессивный подход или хочешь по старинке вручную все кнопочки протыкивать (конечно вручную протыкивать может оказаться и быстрее но ведь это ручная рутинная работа).
https://github.com/nsdvw/classifieds/blob/master/protected/models/Ad.php#L38
Мне это не нравится. Зачем перехватывать любые вызовы любых методов? Если это решение какой-то проблемы ее явно можно было решить более аккуратно.
> https://github.com/nsdvw/classifieds/blob/master/protected/models/Ad.php#L177
Этот метод никакого отношения к Ad по моему не имеет.
И еще к тебе вопрос, а как насчет тестов? Было бы здорово покрыть все это тестами. Конечно автоматизированные тесты далеко не везде используют, и не всегда затраты на них оправданны, но вещь это очень интересная и она выводит разработку на другой уровень. Потому что ты можешь не бояться что кто-то сломает сделанные тобой фичи, если они покрыты тестами. И можешь не бояться делать большие правки кода.
Так как автоматизированные тесты используют не везде, то вполне возможно что на работе тебя им никто учить не будет. А тут будет как раз хорошая возможность набить шишек.
Вот обзорный урок по тестам: https://gist.github.com/codedokode/a455bde7d0748c0a351a
Так как сайт не сложный, я думаю, там нужны будут в основном E2E тесты. Чтобы не мучаться с селениумом, можно будет использовать codeception + phantomJS (это эмулятор браузера на вебките поддерживающий JS/CSS и контролируемый через selenium-соместимый протокол). Ну к примеру тест будет проходить форму создания объявления и убеждаться что оно добавилось в список.
Также можно будет писать регрессионные тесты, то есть тесты сделанные по мотивам обнаруженных багов, проверяющие что этот баг более не повторится.
Как план-максимум - настроить запуск тестов на travis CI чтобы он сам брал новые коммиты из твоего гитхаба, прогонял тесты и у тебя был бэйджик со статусом их выполнения, вот как тут висит: https://github.com/slimphp/slim
А вот лог выполнения одного из тестов (провален): https://travis-ci.org/slimphp/Slim/jobs/83432661
Один на один с тестами никто тебя не бросит, я подскажу и как codeception настроить и опишу пошагово пример теста какого-нибудь. Ну и конечно придется статьи почитать, на хабре например яндекс и баду на эту тему что-то интересное писали.
Это все можно будет делать неспешно, по наличию свободного времени, как в нашем треде и принято.
Подумай сам, хочешь ты изучить современный и прогрессивный подход или хочешь по старинке вручную все кнопочки протыкивать (конечно вручную протыкивать может оказаться и быстрее но ведь это ручная рутинная работа).
Приходится применять все доступные методы чтобы хоть одного анона уговорить научиться тестированию. А то рубисты, питонисты, даже яваскриптщики - все пишут тесты, одни мы тут как в каменном веке сидим (хотя один анон все же освоил юнит-тесты, но на яваскрипте).
http://ideone.com/wtioNY
http://ideone.com/OL6z7l
Вроде работает, энивей, покажите где я проебался.
Целый день на это проебал, пиздец просто!
Автоматизированные тесты, проверяющие правильность работы твоего кода. Там есть юнит-тесты (вызывают функцию и смотрят что она вернет) и end-to-end тесты (робот открывает сайт, тыкает кнопки и смотрит что получится).
https://gist.github.com/codedokode/a455bde7d0748c0a351a
Это ОП макет так сделал. Ну не стукай!
Кстати, если посмотреть тесты Слима то видно что они еще запускают программу для проверки правильно ли отформатированы файлы с кодом:
https://travis-ci.org/slimphp/Slim/jobs/83432661#L215
> vendor/bin/phpcs --standard=PSR2 --ignore=vendor/* -n -p .
Это хороший способ заставить всех правильно оформлять код.
Так чем же всё-таки отличается абстрактный класс от интерфейса? Помимо их разного назначения.
Точно так же, как и в интерфейсе я могу в абстрактном классе объявить абстрактные методы, которые необходимо реализовать в дочернем классе и получится, что все они будут реализовывать "интерфейс" AbstractClassName. Даже тайп-хинтинг в этом случае работает.
Вопрос не в этом, а в том, какие у них функциональные различия. Неужто только то, что в абстрактном классе можно объявить наследуемые свойства?
>что это за ерунда тут
> __call...
Виноват, причем дважды. Потому что не дал нормальные имена коммитам, теперь сам не могу вспомнить, зачем это сделал.
Кажется, проблема была в том, что при обращении к несуществующему методу естественно выбрасывалась ошибка. Но зачем мне нужно было обращаться к несуществующему методу? Ну ладно, проехали. Убрал этот кусок, вроде все работает, странно.
Аааа... Я вспомнил что писал об этом в прошлом треде.
Проблема >>546980
Тупое решение проблемы >>547026
>>547765
> А в какой ситуации может понадобиться обратиться к несуществующему свойству? У объектов список свойств фиксированный и известный заранее, следовательно в правильном коде обращений к несуществующему свойству быть не может.
В той ситуации, когда модель выполнена по паттерну eav. У одной модели один набор атрибутов, у второй совсем другой. Но в представлении мне приходится обращаться к определенным атрибутам, и я не знаю, есть они или нет.
И снова возвращаемся к начальному вопросу: почему твиг не агрится на заведомую ерунду типа test.hello, но при обращении к свойству реальной модели выбрасывает ошибку?
У объявления есть свойство "цена", причем это необязательный eav атрибут. Цену можно не указывать в определенных рубриках, например "отдам даром" или вакансии с предложением подработки.
В представлении я писал model.price, и твиг (или скорее php) говорил об обращении к несуществующему свойству.
Самое странное, что сейчас убрал этот call, все работает. Не понимаю что такое, сейчас откачу на момент того коммита, и посмотрю в чем дело.
>>553833
Тесты однозначно нужны, даже в таком простом проекте как это сайт объявлений или файлообменник тяжело отслеживать ошибки типа описанной выше. Что уж говорить о серьезном проекте.
Вопрос только во времени.
Я планировал сначала разобрать твои замечания по говнокоду, чтобы привести его в нормальный вид.
Затем заняться оптимизацией кода и бд. В прошлом треде ты мне показал интересный инструмент cachegrind, с помощью которого можно анализировать медленные места в коде, надо этим заняться после чистки грубых ошибок.
Затем уделить время команде explain и посмотреть, что можно сделать с базой.
После этого уже займусь тестами.
>что это за ерунда тут
> __call...
Виноват, причем дважды. Потому что не дал нормальные имена коммитам, теперь сам не могу вспомнить, зачем это сделал.
Кажется, проблема была в том, что при обращении к несуществующему методу естественно выбрасывалась ошибка. Но зачем мне нужно было обращаться к несуществующему методу? Ну ладно, проехали. Убрал этот кусок, вроде все работает, странно.
Аааа... Я вспомнил что писал об этом в прошлом треде.
Проблема >>546980
Тупое решение проблемы >>547026
>>547765
> А в какой ситуации может понадобиться обратиться к несуществующему свойству? У объектов список свойств фиксированный и известный заранее, следовательно в правильном коде обращений к несуществующему свойству быть не может.
В той ситуации, когда модель выполнена по паттерну eav. У одной модели один набор атрибутов, у второй совсем другой. Но в представлении мне приходится обращаться к определенным атрибутам, и я не знаю, есть они или нет.
И снова возвращаемся к начальному вопросу: почему твиг не агрится на заведомую ерунду типа test.hello, но при обращении к свойству реальной модели выбрасывает ошибку?
У объявления есть свойство "цена", причем это необязательный eav атрибут. Цену можно не указывать в определенных рубриках, например "отдам даром" или вакансии с предложением подработки.
В представлении я писал model.price, и твиг (или скорее php) говорил об обращении к несуществующему свойству.
Самое странное, что сейчас убрал этот call, все работает. Не понимаю что такое, сейчас откачу на момент того коммита, и посмотрю в чем дело.
>>553833
Тесты однозначно нужны, даже в таком простом проекте как это сайт объявлений или файлообменник тяжело отслеживать ошибки типа описанной выше. Что уж говорить о серьезном проекте.
Вопрос только во времени.
Я планировал сначала разобрать твои замечания по говнокоду, чтобы привести его в нормальный вид.
Затем заняться оптимизацией кода и бд. В прошлом треде ты мне показал интересный инструмент cachegrind, с помощью которого можно анализировать медленные места в коде, надо этим заняться после чистки грубых ошибок.
Затем уделить время команде explain и посмотреть, что можно сделать с базой.
После этого уже займусь тестами.
Слышал, что в yii2 можно использовать rest так, чтобы он выдирал сходу из базы связанные строки таблиц. К примеру в одной таблице есть поле options_id, а в таблице options поле id, и эти два поля связаны. То есть сделать так, чтобы возвращались соответствующие значения второй таблицы вместе с первой. Что-то говорили про extraFields, но я нихуя не могу нагуглить.
В задаче про простой калькулятор не надо поддерживать дробные числа, только целые. Также не надо соблюдать приоритет операций. В задаче про сложный калькулятор надо, но ты не так понял. Дробь 1⁄3 и деление 1÷3 это одно и то же. В обоих случаях мы делим 1 на 3. Там имеется в виду что сложный калькулятор не должен вычислять выражение вроде 1/3 (округляя до 0.333 и теряя точность), а должен хранить его в памяти и выводить на печать именно как дробь. И соответственно поддерживать операции над дробями. Ну к примеру 1/3 + 1/3 должно давать ответ 2/3 а не 0.6666.
Для сложного калькулятора надо разбить строку на токены (числа и знаки операций), пройтись по массиву токенов и построить дерево выражения (AST), и вычислить результат.
https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7
Для превращения токенов в дерево можно использовать алгоритм рекурсивного спуска.
http://lord-n.narod.ru/download/books/walla/programming/Spr_po_C/24/24.htm
http://habrahabr.ru/post/122397/
https://ru.wikibooks.org/wiki/%D0%A0%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B3%D0%BE_%D1%81%D0%BF%D1%83%D1%81%D0%BA%D0%B0
http://ejudge.btty.su/bmstu/2008-2009/term2/practice/arithm_article.pdf
http://ermak.cs.nstu.ru/cprog/html/076.htm
Скорее всего ты там ничего не поймешь так что задавай вопросы. Ну и если ты пока начинающий и не изучил ООП то тебе наверно лучше ограничиться простым калькулятором. Ну хотя можешь попробовать силы и на сложном.
>>551736
Не жди меня, решай дальше.
> Первая задача про номера
> [-\\(\\)\\s]
[\\-()\\s] - внутри квадратных скобок большинство символов не надо экранировать (кроме - ^ [ ] \ которые имеют особое значение)
В остальном все верно.
> Вторая на проверку текста на ошибки
Видит ошибки там, где их нету: http://ideone.com/652183
Я советую написать такое выражение для а/но: буква пробелы (а или но) граница_слова
> Кстати, почему нужно было вынести счётких замен ($count) в отдельную строку, снаружи if?
Потому что если мы будем объядинять разные действия (поиск совпадений и проверку условия) в одну строку, то будет тяжело читать такой код. Также, в if легко перепутать = и == и можно подумать что там идет сравнение, а не присваивание.
> Третья - убрать лишние символы из номера,
Ок, все верно
> Последняя - автоисправление ошибок.
> (к|К)оординально/ui
Если есть флаг i зачем писать К|к? Забыл поправить наверно.
> $orphPatt2 = '/(с|С)десь/ui';
> $orphPatt3 = '/(?:(з|З)дела)((?:ю|л|н)\\s)/ui';
> $orphPatt4 = '/([а-яё](?:ж|ш))(ы)([а-яё]*)/ui';
Вот это ужасно. Во-первых вместо переменных с цифрами надо использовать массивы. Во-вторых, дальше там идет копипасти из 3 почти одинаковых кусков кода. Объедини-ка их в один универальный код. Ты можешь например вынести анонимную функцию в массив по которому пройтись циклом:
$replace = [
['pattern' => ..., 'callback' => function () { ... }],
....
];
Либо сделать свою функцию, тогда массив будет не нужен:
fixGrammar('/..../', function () {
...
});
Массивы $pattern и $replacement объедини в один чтобы было видно что чему соответствует: ['/координально/' => '$1ардинально', ...]
В задаче про простой калькулятор не надо поддерживать дробные числа, только целые. Также не надо соблюдать приоритет операций. В задаче про сложный калькулятор надо, но ты не так понял. Дробь 1⁄3 и деление 1÷3 это одно и то же. В обоих случаях мы делим 1 на 3. Там имеется в виду что сложный калькулятор не должен вычислять выражение вроде 1/3 (округляя до 0.333 и теряя точность), а должен хранить его в памяти и выводить на печать именно как дробь. И соответственно поддерживать операции над дробями. Ну к примеру 1/3 + 1/3 должно давать ответ 2/3 а не 0.6666.
Для сложного калькулятора надо разбить строку на токены (числа и знаки операций), пройтись по массиву токенов и построить дерево выражения (AST), и вычислить результат.
https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7
Для превращения токенов в дерево можно использовать алгоритм рекурсивного спуска.
http://lord-n.narod.ru/download/books/walla/programming/Spr_po_C/24/24.htm
http://habrahabr.ru/post/122397/
https://ru.wikibooks.org/wiki/%D0%A0%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B3%D0%BE_%D1%81%D0%BF%D1%83%D1%81%D0%BA%D0%B0
http://ejudge.btty.su/bmstu/2008-2009/term2/practice/arithm_article.pdf
http://ermak.cs.nstu.ru/cprog/html/076.htm
Скорее всего ты там ничего не поймешь так что задавай вопросы. Ну и если ты пока начинающий и не изучил ООП то тебе наверно лучше ограничиться простым калькулятором. Ну хотя можешь попробовать силы и на сложном.
>>551736
Не жди меня, решай дальше.
> Первая задача про номера
> [-\\(\\)\\s]
[\\-()\\s] - внутри квадратных скобок большинство символов не надо экранировать (кроме - ^ [ ] \ которые имеют особое значение)
В остальном все верно.
> Вторая на проверку текста на ошибки
Видит ошибки там, где их нету: http://ideone.com/652183
Я советую написать такое выражение для а/но: буква пробелы (а или но) граница_слова
> Кстати, почему нужно было вынести счётких замен ($count) в отдельную строку, снаружи if?
Потому что если мы будем объядинять разные действия (поиск совпадений и проверку условия) в одну строку, то будет тяжело читать такой код. Также, в if легко перепутать = и == и можно подумать что там идет сравнение, а не присваивание.
> Третья - убрать лишние символы из номера,
Ок, все верно
> Последняя - автоисправление ошибок.
> (к|К)оординально/ui
Если есть флаг i зачем писать К|к? Забыл поправить наверно.
> $orphPatt2 = '/(с|С)десь/ui';
> $orphPatt3 = '/(?:(з|З)дела)((?:ю|л|н)\\s)/ui';
> $orphPatt4 = '/([а-яё](?:ж|ш))(ы)([а-яё]*)/ui';
Вот это ужасно. Во-первых вместо переменных с цифрами надо использовать массивы. Во-вторых, дальше там идет копипасти из 3 почти одинаковых кусков кода. Объедини-ка их в один универальный код. Ты можешь например вынести анонимную функцию в массив по которому пройтись циклом:
$replace = [
['pattern' => ..., 'callback' => function () { ... }],
....
];
Либо сделать свою функцию, тогда массив будет не нужен:
fixGrammar('/..../', function () {
...
});
Массивы $pattern и $replacement объедини в один чтобы было видно что чему соответствует: ['/координально/' => '$1ардинально', ...]
> return ($text);
Скобки не нужны (впрочем и вреда от них нет, просто смотрится странно и сбивает с толку).
И давай кое-что переделаем. Смотри, сейчас у тебя код выглядит так:
// Разбиваем текст на буквы
$x = сложный запутанный кусок кода;
Можно ли увеличить читаемость кода? Конечно, можно вынести сложный код в функцию с понятным именем (разбить на буквы = splitToLetters):
$x = splitToLetters($string);
Такой код намного читабельнее, и если имя выбрано удачно, то комментарий может и не понадобиться. Это называется самодокументируюий код — код который сам себя комментирует. сделай эти действия отдельными функциями:
- замена в данной строке первой буквы на заглавную (для 1 предложения)
- разбиение строки текста на массив предложений
- исправление пробелов перед запятыми (вроде у тебя это уже вынесено, тогда надо переименовать функцию)
Далее.Чтобы сделать первую букву заглавной, достаточно отрезать ее mb_substr, перевести в верхний регистр, присоединить хвост строки.
Предложения удобнее может быть склеивать, кладя их в массив и вызывая implode().
$characters[$i] - незачем класть каждую строку в отдельный элемент массива, можно использовать одну и ту же переменную для всех строк по очереди.
> [\\s]+
То же самое что \\s+. Скобки значат «один из символов», и тут у тебя всего один вариант и дан.
> $text = $edit;
Непонятно в чем смысл этого перекладывания из одной переменной в другую.
>>551831
ОПу показалось что так нагляднее в данном примере. Используй что больше по ситуации подходит. Конечно это можно и с флагом i переписать.
>>551859
Сделать форму логина, проверку залогиненности, страницу на которой выводится информация о пользователе. Если это для тебя сложно то тебе наверно надо изучить более простые вещи. Посмотри в ОП посте задание про студентов - оно с комментариями и после его выполнения я думаю твою задачу будет решить несложно.
>>551914
Изучать потихоньку, использовать bing translate (вдруг читабельно окажется). Также ты можешь организовать перевод на русский — я или аноны могу подсказать перевод в сложных случаях. Ну и заодно пока ты будешь переводить, ты наизусть все это выучишь.
Реально перевод заключается в прогоне текста через bing transalator и ручную корректировку проблемных мест (которых там будет много). Я проверил — процентов на 50-80 читабельно.
Правда там остается проблема авторских прав, так что опубликовать перевод ты наверно не сможешь.
>>552037
> Там нужно количество месяцев искать
Надо вывести общую сумму заплаченного по кредиту.
>>552093
Анон, ты перезаписал свой код и сейчас по ссылке http://ideone.com/UJkWzw что-то про правописание.
А программа тут http://ideone.com/vQDc3R не работает. Перезапости. Программа должна писать общую сумму выплаченного, около 61270.
>>552167
> else return $this->wageRate * $this->rank;
Ужасно. Надо выдерживать единый стиль. Ставь скобки и пиши в 3 строчки как положено.
> switch ($profession) {
> case Employee::PROF_MANAGER :
> $this->baseCoffee = 20;
Это надо бы вынести в отдельную функцию из конструктора, и может быть стоит поместить значения в массив вида [ профессия => [ 'coffee' => 20, 'pages' => 200, 'wage' => 500 ]] ?
И у тебя не сделаны отдельные классы для профессий, а только один Employee. Это значит что для добавления новых профессий придется править и исправлять старый код, пытаясь не забыть сделать замену во всех местах где требуется. А в случае с несколькими классами достаточно лишь написать новый класс. Ну это не ошибка, это я просто тебя информирую что ты упускаешь шанс изучить наследование и абстрактные методы.
> class Department
> public function getReport()
Мне кажется, это все же должно быть в отдельной функции а не в Department. Ибо видов отчетов может быть много и если мы каждый начнем добавлять в департамент, он раздуется без надобности. Это так называемый принцип Single Responsibility — класс должен заниматься только своим делом а не всем сразу.
Тут логичнее сделать функцию которая берет Департамент и возвращает Отчет по нему. Аналогично с компанией.
> public $employees = array();
> public $departments = array();
Я бы поменял тип доступа на private, ладно еще обычные свойства с числами, но массив сложной структуры наружу выставлять не стоит чтобы люди не ломали голову, как им пользоваться.
> public function fireEmployee(Employee $e)
> foreach ($this->employees as $employee) {
> $index = array_search($employee, $this->employees);
это неправильное. Зачем здеь цикл? Обяъсни что тебе мешает сразу вызвать array_search? Алсо почему не указан строгий тип сравнения? Ты не прочел статью про вид сравнения объектов? У тебя ищется вообще не тот объект который ты передал а любой похожий с такими же значениями свойств.
> foreach ($this->departments as $department) {
> unset($department);
Это не удаляет департамент из массива.
> for ($i=0; $i < 95; $i++) echo '-';
Во-первых, надо писать в 3 строчки со скобками, во-вторых, использовать str_repeat или даже сделать отдельный метод printLine() чтобы сделать длинную простыню кода короче.
> $paddingSize = $columnSize - mb_strlen($string);
> $string .= str_repeat(' ', $paddingSize);
А что если paddingSize отрицательный?
Антикризисные меры надо сделать функциями, я же говорил. И сделать клонирование компании чтобы вывести все 4 отчета.
В увольнении инженеров надо вроде бы отобрать 40% наиболее низкоранговых и не увольнять боссов. Для этого надо взять список инженеров, убрать из него боссов (array_filter), отсортировать по рангу, взять 40% (array_slice) и уволить. Так код будет последовательным и читаемым, а у тебя как-то все запутанно получилось.
> return ($text);
Скобки не нужны (впрочем и вреда от них нет, просто смотрится странно и сбивает с толку).
И давай кое-что переделаем. Смотри, сейчас у тебя код выглядит так:
// Разбиваем текст на буквы
$x = сложный запутанный кусок кода;
Можно ли увеличить читаемость кода? Конечно, можно вынести сложный код в функцию с понятным именем (разбить на буквы = splitToLetters):
$x = splitToLetters($string);
Такой код намного читабельнее, и если имя выбрано удачно, то комментарий может и не понадобиться. Это называется самодокументируюий код — код который сам себя комментирует. сделай эти действия отдельными функциями:
- замена в данной строке первой буквы на заглавную (для 1 предложения)
- разбиение строки текста на массив предложений
- исправление пробелов перед запятыми (вроде у тебя это уже вынесено, тогда надо переименовать функцию)
Далее.Чтобы сделать первую букву заглавной, достаточно отрезать ее mb_substr, перевести в верхний регистр, присоединить хвост строки.
Предложения удобнее может быть склеивать, кладя их в массив и вызывая implode().
$characters[$i] - незачем класть каждую строку в отдельный элемент массива, можно использовать одну и ту же переменную для всех строк по очереди.
> [\\s]+
То же самое что \\s+. Скобки значат «один из символов», и тут у тебя всего один вариант и дан.
> $text = $edit;
Непонятно в чем смысл этого перекладывания из одной переменной в другую.
>>551831
ОПу показалось что так нагляднее в данном примере. Используй что больше по ситуации подходит. Конечно это можно и с флагом i переписать.
>>551859
Сделать форму логина, проверку залогиненности, страницу на которой выводится информация о пользователе. Если это для тебя сложно то тебе наверно надо изучить более простые вещи. Посмотри в ОП посте задание про студентов - оно с комментариями и после его выполнения я думаю твою задачу будет решить несложно.
>>551914
Изучать потихоньку, использовать bing translate (вдруг читабельно окажется). Также ты можешь организовать перевод на русский — я или аноны могу подсказать перевод в сложных случаях. Ну и заодно пока ты будешь переводить, ты наизусть все это выучишь.
Реально перевод заключается в прогоне текста через bing transalator и ручную корректировку проблемных мест (которых там будет много). Я проверил — процентов на 50-80 читабельно.
Правда там остается проблема авторских прав, так что опубликовать перевод ты наверно не сможешь.
>>552037
> Там нужно количество месяцев искать
Надо вывести общую сумму заплаченного по кредиту.
>>552093
Анон, ты перезаписал свой код и сейчас по ссылке http://ideone.com/UJkWzw что-то про правописание.
А программа тут http://ideone.com/vQDc3R не работает. Перезапости. Программа должна писать общую сумму выплаченного, около 61270.
>>552167
> else return $this->wageRate * $this->rank;
Ужасно. Надо выдерживать единый стиль. Ставь скобки и пиши в 3 строчки как положено.
> switch ($profession) {
> case Employee::PROF_MANAGER :
> $this->baseCoffee = 20;
Это надо бы вынести в отдельную функцию из конструктора, и может быть стоит поместить значения в массив вида [ профессия => [ 'coffee' => 20, 'pages' => 200, 'wage' => 500 ]] ?
И у тебя не сделаны отдельные классы для профессий, а только один Employee. Это значит что для добавления новых профессий придется править и исправлять старый код, пытаясь не забыть сделать замену во всех местах где требуется. А в случае с несколькими классами достаточно лишь написать новый класс. Ну это не ошибка, это я просто тебя информирую что ты упускаешь шанс изучить наследование и абстрактные методы.
> class Department
> public function getReport()
Мне кажется, это все же должно быть в отдельной функции а не в Department. Ибо видов отчетов может быть много и если мы каждый начнем добавлять в департамент, он раздуется без надобности. Это так называемый принцип Single Responsibility — класс должен заниматься только своим делом а не всем сразу.
Тут логичнее сделать функцию которая берет Департамент и возвращает Отчет по нему. Аналогично с компанией.
> public $employees = array();
> public $departments = array();
Я бы поменял тип доступа на private, ладно еще обычные свойства с числами, но массив сложной структуры наружу выставлять не стоит чтобы люди не ломали голову, как им пользоваться.
> public function fireEmployee(Employee $e)
> foreach ($this->employees as $employee) {
> $index = array_search($employee, $this->employees);
это неправильное. Зачем здеь цикл? Обяъсни что тебе мешает сразу вызвать array_search? Алсо почему не указан строгий тип сравнения? Ты не прочел статью про вид сравнения объектов? У тебя ищется вообще не тот объект который ты передал а любой похожий с такими же значениями свойств.
> foreach ($this->departments as $department) {
> unset($department);
Это не удаляет департамент из массива.
> for ($i=0; $i < 95; $i++) echo '-';
Во-первых, надо писать в 3 строчки со скобками, во-вторых, использовать str_repeat или даже сделать отдельный метод printLine() чтобы сделать длинную простыню кода короче.
> $paddingSize = $columnSize - mb_strlen($string);
> $string .= str_repeat(' ', $paddingSize);
А что если paddingSize отрицательный?
Антикризисные меры надо сделать функциями, я же говорил. И сделать клонирование компании чтобы вывести все 4 отчета.
В увольнении инженеров надо вроде бы отобрать 40% наиболее низкоранговых и не увольнять боссов. Для этого надо взять список инженеров, убрать из него боссов (array_filter), отсортировать по рангу, взять 40% (array_slice) и уволить. Так код будет последовательным и читаемым, а у тебя как-то все запутанно получилось.
>>552277
Разбить на массив букв, перевернуть массив array_reverse.
>>552731
нужен preg_replace. Указываешь ему выражение для поиска и на что надо заменить найденный текст (для удаления - на пустую строку).
По ссылке http://ideone.com/kuBaQV задача про кубики и там ошибка - после else нет фигурных скобок.
>>552737
> $getRand = makeRandomizerWithMemory();
Тут лучше объект использовать а не имитировать ООП с помощью замыканий.
>>552860
> И почему матрицу называют так, а не патрицем или отцовцем?
Разрешаю тебе использовать «патрицу» для равновесия.
>>553075
А в фаерфоксе проверял? В браузерах не на вебките используется html по моему. Я помню что элементы разные в разных браузерах.
> var $linkTo = $(e.target).attr('href');
> var $linkToOffsetTop = $($linkTo).offset().top;
Неправильно. Атрибут href может содержать не #abc а например page.html#abc. Ты должен определять указывает ли ссылка на эту страницу или другую, есть ли в ней хеш и указывает ли он на существующий элемент. Для разбора ссылки не надо писать велосипеды а надо использовать свойства которые есть у DOM элемента ссылки:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
Обрати внимание что там доступны host, hash и тд. Также вопрос на проверку, а ты знаешь в чем разница между attr('href') и prop('href')? Если нет то плохо.
Также, давай сделаем задание интереснее. Если ты сравнишь работу страницы без твоего скрипта и с ним, то обнаружишь что без скрипта переходы записываются в историю и можно вернуться назад кнопкой «назад» (а при обновлении страницы или передаче ссылки кому-нибудь страница прокручивается к нужному месту). Со скриптом разумеется ничего не работает. Исправь это, подсказки:
- для решения проблемы обновления страницы и пересылки ссылки достаточно менять хеш в URL. Чтобы страница при этом не прыгала надо либо менять хеш после окончания анимации (не очень хорошо) либо попробовать делать это через History API.
- для решения проблемы кнопки назад на первый взгляд кажется сложно что-то сделать. К счастью, у нас есть Web History API которое позволяет добавлять записи в историю, а также реагировать на переходы по ней:
https://developer.mozilla.org/en-US/docs/Web/API/History_API
http://habrahabr.ru/post/144071/
В старых браузерах нет History API и там остается только применять грязные трюки с
Ну и оформи-ка свой код в виде плагина jQuery, чтобы для его использования достаточно было написать
$.smoothHashScroll();
не забудь защиту от повторного вызова, а при желании можешь добавить возможность отключить обрабботчик вызовом
$.smoothHashScroll('destroy');
>>552277
Разбить на массив букв, перевернуть массив array_reverse.
>>552731
нужен preg_replace. Указываешь ему выражение для поиска и на что надо заменить найденный текст (для удаления - на пустую строку).
По ссылке http://ideone.com/kuBaQV задача про кубики и там ошибка - после else нет фигурных скобок.
>>552737
> $getRand = makeRandomizerWithMemory();
Тут лучше объект использовать а не имитировать ООП с помощью замыканий.
>>552860
> И почему матрицу называют так, а не патрицем или отцовцем?
Разрешаю тебе использовать «патрицу» для равновесия.
>>553075
А в фаерфоксе проверял? В браузерах не на вебките используется html по моему. Я помню что элементы разные в разных браузерах.
> var $linkTo = $(e.target).attr('href');
> var $linkToOffsetTop = $($linkTo).offset().top;
Неправильно. Атрибут href может содержать не #abc а например page.html#abc. Ты должен определять указывает ли ссылка на эту страницу или другую, есть ли в ней хеш и указывает ли он на существующий элемент. Для разбора ссылки не надо писать велосипеды а надо использовать свойства которые есть у DOM элемента ссылки:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
Обрати внимание что там доступны host, hash и тд. Также вопрос на проверку, а ты знаешь в чем разница между attr('href') и prop('href')? Если нет то плохо.
Также, давай сделаем задание интереснее. Если ты сравнишь работу страницы без твоего скрипта и с ним, то обнаружишь что без скрипта переходы записываются в историю и можно вернуться назад кнопкой «назад» (а при обновлении страницы или передаче ссылки кому-нибудь страница прокручивается к нужному месту). Со скриптом разумеется ничего не работает. Исправь это, подсказки:
- для решения проблемы обновления страницы и пересылки ссылки достаточно менять хеш в URL. Чтобы страница при этом не прыгала надо либо менять хеш после окончания анимации (не очень хорошо) либо попробовать делать это через History API.
- для решения проблемы кнопки назад на первый взгляд кажется сложно что-то сделать. К счастью, у нас есть Web History API которое позволяет добавлять записи в историю, а также реагировать на переходы по ней:
https://developer.mozilla.org/en-US/docs/Web/API/History_API
http://habrahabr.ru/post/144071/
В старых браузерах нет History API и там остается только применять грязные трюки с
Ну и оформи-ка свой код в виде плагина jQuery, чтобы для его использования достаточно было написать
$.smoothHashScroll();
не забудь защиту от повторного вызова, а при желании можешь добавить возможность отключить обрабботчик вызовом
$.smoothHashScroll('destroy');
Чем отличается собака от кошки? Очевидно что класс и интерфейс это разные вещи по определению.
Это вопрос на то понимаешь ли ты что такое класс/интерфейс или нет. Класс это такая штука, описывающая однотипные объекты, а интерфейс это набор требований к классу.
Они могут казаться похожими но разница тут смысловая: что ты хочешь сделать, базовый класс для наследования или интерфейс (набор требований которые могут реализовать произвольные классы).
Давай-ка ты сам перечислишь все возможные отличия которые знаешь, а мы проверим.
> я могу в абстрактном классе объявить абстрактные методы, которые необходимо реализовать в дочернем классе и получится, что все они будут реализовывать "интерфейс"
Не будут, они будут наследоваться. Реализуют это когда стоит слово implements.
> Точно так же, как и в интерфейсе я могу в абстрактном классе объявить абстрактные методы, которые необходимо реализовать в дочернем классе и получится, что все они будут реализовывать "интерфейс" AbstractClassName. Даже тайп-хинтинг в этом случае работает.
Ты ищешь отличия в том как это работает. А надо искать отличия в том что это разные вещи имеющие разный смысл. Если ты делаешь абс. класс ты говоришь «делайте наследников этого класса» а если интерфейс то «реализуйте этот интерфейс любыми своими классами какими хотите».
Ну и паста
-----------------------
Интерфейс это набор требований к классу. «требование» здесь значит требование чтобы в классе был определенный метод.
Если класс реализует интерфейс, в нем обязаны быть эти методы.Обычно интерфйес представляет собой какое-то умение: классы, реализующие этот интерфейс, умеют что-то делать.
Допустим мы делаем сайт где можно ставить лайки постам и комментам. Допустим у нас есть классы 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 сможет работать и с ним без переписывания кода.
В общем интерфейс представляет какую-то способность класса и требует от него реализовать определенные методы.
Чем отличается собака от кошки? Очевидно что класс и интерфейс это разные вещи по определению.
Это вопрос на то понимаешь ли ты что такое класс/интерфейс или нет. Класс это такая штука, описывающая однотипные объекты, а интерфейс это набор требований к классу.
Они могут казаться похожими но разница тут смысловая: что ты хочешь сделать, базовый класс для наследования или интерфейс (набор требований которые могут реализовать произвольные классы).
Давай-ка ты сам перечислишь все возможные отличия которые знаешь, а мы проверим.
> я могу в абстрактном классе объявить абстрактные методы, которые необходимо реализовать в дочернем классе и получится, что все они будут реализовывать "интерфейс"
Не будут, они будут наследоваться. Реализуют это когда стоит слово implements.
> Точно так же, как и в интерфейсе я могу в абстрактном классе объявить абстрактные методы, которые необходимо реализовать в дочернем классе и получится, что все они будут реализовывать "интерфейс" AbstractClassName. Даже тайп-хинтинг в этом случае работает.
Ты ищешь отличия в том как это работает. А надо искать отличия в том что это разные вещи имеющие разный смысл. Если ты делаешь абс. класс ты говоришь «делайте наследников этого класса» а если интерфейс то «реализуйте этот интерфейс любыми своими классами какими хотите».
Ну и паста
-----------------------
Интерфейс это набор требований к классу. «требование» здесь значит требование чтобы в классе был определенный метод.
Если класс реализует интерфейс, в нем обязаны быть эти методы.Обычно интерфйес представляет собой какое-то умение: классы, реализующие этот интерфейс, умеют что-то делать.
Допустим мы делаем сайт где можно ставить лайки постам и комментам. Допустим у нас есть классы 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 сможет работать и с ним без переписывания кода.
В общем интерфейс представляет какую-то способность класса и требует от него реализовать определенные методы.
Халп!
> В той ситуации, когда модель выполнена по паттерну eav. У одной модели один набор атрибутов, у второй совсем другой. Но в представлении мне приходится обращаться к определенным атрибутам, и я не знаю, есть они или нет.
Ну так ты и не должен делать все эти динамически добавляемые менеджерами свойства полями класса. да и как ты определишь названия полей если там доступно только название на русском (например «модель машины»)?
Тебе нужен лишь метод возвращающий массив EAV свойств объявления, а у каждого свойства должно быть название, единица измерения, значение и тд.
Поясни примером кода если считаешь что я ошибаюсь.
> проблема с твигом
Ты должен на этапе написания кода знать все свойства и методы которые есть у классов. Соответственно ситуации что ты обращаешься к несуществующему свойству быть не может.
Если ты не знаешь список атрибутов то тебе нужен не класс а массив.
> почему твиг не агрится на заведомую ерунду типа test.hello, но при обращении к свойству реальной модели выбрасывает ошибку?
Не знаю, а ты strict_vars включил? Твиг как-то сложно ищет свойство несколькими способами, пытается найти его как элемент массива, как публичное свойство, как геттер.
Даже если это особенность твига ты не должен обращаться к несуществующим переменным, элементам массива, полям объектов.
> В представлении я писал model.price
Должно быть что-то вроде model.getCustomAttribute('price')
> В прошлом треде ты мне показал интересный инструмент cachegrind, с помощью которого можно анализировать медленные места в коде, надо этим заняться после чистки грубых ошибок.
Производительность надо мерять на больших данных, а не на 10 записях, учти это.
> сейчас откачу на момент того коммита, и посмотрю в чем дело.
Ок только много времени наверно тратить не стоит. Ну а вообще ты можешь залезть внутрь твига и натыкать вар дампов в getAttribute()
https://github.com/twigphp/Twig/blob/master/lib/Twig/Template.php#L431
Он вызывается при обращении через точку.
Вот кстати у кого-то похожая проблема: https://github.com/twigphp/Twig/issues/1557
Там еще это может быть связано с тем что Юи добавляет в модели магические методы __get, __call и другие. Переопределяяя их ты кстати ломаешь работу active record. Вот они видны:
https://github.com/yiisoft/yii/blob/1.1.16/framework/db/ar/CActiveRecord.php#L134
> В той ситуации, когда модель выполнена по паттерну eav. У одной модели один набор атрибутов, у второй совсем другой. Но в представлении мне приходится обращаться к определенным атрибутам, и я не знаю, есть они или нет.
Ну так ты и не должен делать все эти динамически добавляемые менеджерами свойства полями класса. да и как ты определишь названия полей если там доступно только название на русском (например «модель машины»)?
Тебе нужен лишь метод возвращающий массив EAV свойств объявления, а у каждого свойства должно быть название, единица измерения, значение и тд.
Поясни примером кода если считаешь что я ошибаюсь.
> проблема с твигом
Ты должен на этапе написания кода знать все свойства и методы которые есть у классов. Соответственно ситуации что ты обращаешься к несуществующему свойству быть не может.
Если ты не знаешь список атрибутов то тебе нужен не класс а массив.
> почему твиг не агрится на заведомую ерунду типа test.hello, но при обращении к свойству реальной модели выбрасывает ошибку?
Не знаю, а ты strict_vars включил? Твиг как-то сложно ищет свойство несколькими способами, пытается найти его как элемент массива, как публичное свойство, как геттер.
Даже если это особенность твига ты не должен обращаться к несуществующим переменным, элементам массива, полям объектов.
> В представлении я писал model.price
Должно быть что-то вроде model.getCustomAttribute('price')
> В прошлом треде ты мне показал интересный инструмент cachegrind, с помощью которого можно анализировать медленные места в коде, надо этим заняться после чистки грубых ошибок.
Производительность надо мерять на больших данных, а не на 10 записях, учти это.
> сейчас откачу на момент того коммита, и посмотрю в чем дело.
Ок только много времени наверно тратить не стоит. Ну а вообще ты можешь залезть внутрь твига и натыкать вар дампов в getAttribute()
https://github.com/twigphp/Twig/blob/master/lib/Twig/Template.php#L431
Он вызывается при обращении через точку.
Вот кстати у кого-то похожая проблема: https://github.com/twigphp/Twig/issues/1557
Там еще это может быть связано с тем что Юи добавляет в модели магические методы __get, __call и другие. Переопределяяя их ты кстати ломаешь работу active record. Вот они видны:
https://github.com/yiisoft/yii/blob/1.1.16/framework/db/ar/CActiveRecord.php#L134
>Ты ищешь отличия в том как это работает. А надо искать отличия в том что это разные вещи имеющие разный смысл. Если ты делаешь абс. класс ты говоришь «делайте наследников этого класса» а если интерфейс то «реализуйте этот интерфейс любыми своими классами какими хотите».
Смысл и назначение обеих этих вещей мне вроде понятны. Мой вопрос можно переформулировать иначе: "Зачем нужны интерфейсы, если то же самое можно реализовать на абстрактных классах?"
Но теперь я вспомнил, что один класс может реализовывать несколько интерфейсов, что с помощью абстрактных классов сымитировать не получится. Вот и вполне себе функциональное отличие.
А снова задуматься над этим меня заставил Пример #1 по ссылке: http://php.net/manual/ru/language.oop5.abstract.php
Если делаю по примеру на сайте, то получаются числа, которые я пишу в "$random= ~". Я что-то вообще не понимаю, где брать инфу, чтобы я знал в каком порядке что делать.
я не прошу тут никого решать задания за меня. Я всего лишь прошу объяснить, как нужно правильно его выполнять. Как разобраться в тексте перед самим заданием? Делать прям все что в этом тексте или что-то конкретное? Смотрю следующее задание про игрока анона и компьютера, и уже понимаю, что решить эту задачу я смогу через неделю.
Там написано что нужно делать. Делай задание как ты понял и выкладывай в тред, не нужно готовиться неделями к элементарным заданиям, задавая миллион вопросов.
в абстрактном еще функции, у которых есть тело, можно прописывать, а в интерфейсе нельзя, только методы обяъвляешь.
Все плохо? Как у вас было по началу?
Я просто смотрю как некоторые задачи решают, они там такие пируэты выдают, о которых я бы хрен додумался.
не гуманитарий
prosti...
preg_match помещает в массив совпадений matches не только совпадения, но и подмаски, указанные в круглых скобках.
Оператор ?: запрещает класть в $matches подмаску, то есть скобки используются только для группировки.
Запусти код и посмотри
preg_match('/(hell)o(wor)ld/', 'helloworld', $matches);
var_dump($matches);
preg_match('/(?:hell)o(?:wor)ld/', 'helloworld', $matches);
var_dump($matches);
Спасибо за развернутый ответ, понял
Не, он прав. Чтобы получить значение элемент массива $array имея ключ $key ты пишешь
$x = $array[$key];
Это быстрая операция. Чтобы найти элемент имея значение $value придется обходить массив циклом или функцией array_search, это не быстрая операция на больших массивах.
Проверить наличие элемента по ключу можно быстро через if (array-key_exists или if (isset($array[$key])) (второй вариант не сработает если значение элемента null)
Проверить наличие элемента зная значение можно перебором массива функцией
if (in_array($value, $array))
Ну и еще функция count() быстро возвращает число элементов в массиве.
это ты должен знать наизусть без шпаргалок. Если не понимаешь слова ключ, значение, посмотри картинки в уроке.
Для этого нужен веб сервер (Апач или встроенный в PHP), установить его на компьютер, и знание HTML, тогда сможешь делать красиво. Если не терпится, ссылки и задачи по HTML в шапке треда. Но и PHP не бросай.
>>554076
Это нормально. Если что пости вопрос и показывай код.
>>554060
Не захватывать и не запомнинать часть строки попавшую в скобки.
(по умолчанию захватывается)
Если понятны то ждем список отличий.
> "Зачем нужны интерфейсы, если то же самое можно реализовать на абстрактных классах?
А если класс уже унаследован от другого, как ты добавишь еще один? да и это побуждает наследовать что попало от чего попало а наследовать можно только однотипные вещи.
за такое надо бить канделябром.
Ну например унаследовать Танк от СредствоПередвижения можно, а Танк от РучнаяГраната нельзя (с точки зрения логики) так как танк это не улучшенная версия гранаты.
Потому если ты пишешь библиотеку и хочешь чтоюы тебе передавали любой объект с нужными методами используй интерфейс (при этом ты можешь также дать в библиотеке готовый класс который его реализует). Если ты хочешь чтобы пользователь библиотеки обязан был унаследоваться от твоего класса, используй абс класс.
Или рассмотрим случай: у нас в игре есть Танк и Солдат, как сделать функцию которая ими стреляет? Надо сделать интерфейс УмеетСтрелять и пусть оба класса его реализуют не наследуюясь друг от друга.
Дам еще ссылочку поломать голову про разделение интерфейсов: http://m.habrahabr.ru/company/ivi/blog/256517/
Еще можешь попробовать прочитать про SOLID но будь готов что не поймешь.
И держи одну из моих любимых статей про хлеб:
http://habrahabr.ru/post/153225/ (правильного ответа на вопрос нет)
http://habrahabr.ru/post/153845/
Наверно есть еще книги разжвывающие принципы ООП, я увы их не читал в свое время.
Надеюсь на практике они не так часто применяются?
Давай по порядку.
1. Большинство классов в первом yii наследуется от ccomponent, у которого определены геттеры вида
https://books.google.com.ua/books?id=agVGAgAAQBAJ&pg=PA21&lpg=PA21&dq=%D0%BC%D0%B0%D0%BA%D0%B0%D1%80%D0%BE%D0%B2+yii+%D0%B3%D0%B5%D1%82%D1%82%D0%B5%D1%80%D1%8B&source=bl&ots=6K8AN6mYQS&sig=-3NY3FYjYzjU1kF2QxOfeLxM2AM&hl=ru&sa=X&ved=0CDkQ6AEwBGoVChMIyqTOjtiryAIVBTJyCh2XTQvU#v=onepage&q=%D0%BC%D0%B0%D0%BA%D0%B0%D1%80%D0%BE%D0%B2%20yii%20%D0%B3%D0%B5%D1%82%D1%82%D0%B5%D1%80%D1%8B&f=false
2. У CActiveRecord этот метод не переопределяется, а расширяется (или как это называется по-научному, когда используется parent)
https://github.com/yiisoft/yii/blob/1.1.16/framework/db/ar/CActiveRecord.php#L134
3. В библиотеке EavActiveRecord, которую я юзаю, геттеры наследуются(?) следующим образом
https://github.com/iAchilles/eavactiverecord/blob/master/EavActiveRecord.php#L78
То есть если атрибута нет в текущей модели, выбрасывается исключение.
Итак, что мне с этим делать? Еще раз, моя проблема:
в шаблоне я вывожу список объявлений. У большинства из них есть eav-атрибут "цена". Но у некоторых цена не задана, то есть не просто пустая строка или ноль, вообще нет такого свойства (апдейт: у третьей группы свойство есть, но пустое, null; кажется, именно в этом проблема). Что делать?
Попробовал сделать проверку оператором IN твига, который работает как in_array('price', model.eavAttributeNames), не помогает. Ошибка вываливается даже тогда, когда свойство price (вернее геттер для него) доступно, но пустое (null).
Значит все-таки проблема в твиге, его взаимодействии с yii.
https://github.com/twigphp/Twig/issues/1557
Если я правильно понял, то твиг проверяет if ($object instanceof ArrayAccess && isset($object[$arrayItem])). Но в случае если свойство равно null, и иссет пролетает мимо.
Так что делать? Мне унаследовать класс Twig_Template и переопределить этот метод?
>Если понятны то ждем список отличий.
Ну ладно. Вот основное, возможно, есть тонкости, о которых я не знаю.
По сути:
Абс. класс, как я понимаю, - это класс-заготовка, основа для других классов; интерфейс - это "протокол", по которому взаимодействуют разные части программы, гарантия, что класс имеет конкретные методы.
По функционалу:
1. Класс может реализовывать сколько угодно интерфейсов, но унаследовать он может только один абс. класс.
2. Интерфейс не может включать в себя свойства.
3. Абстрактный класс может включать в себя реализацию методов, интерфейс - нет.
4. Методы интерфейса - только публичные.
Прочитал, улыбнулся, добавил в закладки
Ну лучше сделать их наследниками класса Юнит, и в метод Атака пихать разные реализации СтрелятьСКалаша ЕбашитьЛопатой СтрелятьСнарядом
Нууууууу, асинхронные (которые паралельно основному коду выполняются) функции обычно вызывают замыкания.
Ноуп.
"Быстрая разработка программ. Принципы, примеры, практика", чистый код лучше потом читать.
Спасибо.
Вау, спасибо! Я бы не додумался сам лол.
зачем мне писать аррай(кей), чтобы получить валью, если я могу просто написать валью? мне кажется, ты хуйню несешь.
>class Ad ...
>public function getCategories...
>Этот метод никакого отношения к Ad по моему не имеет.
Твиг не понимает статические методы, поэтому мне приходится городить обычные методы объекта.
Не хотелось создавать объект Категория и передавать его в представление только ради того чтобы получить список категорий, поэтому положил метод в класс объявлений.
Больше ничего не могу придумать, кроме как получить этот список в контроллере и передать массивом в виде переменной в представление.
Правильная работа с HTTP запросами, в темные времена все запросы были вида profile.php?user=delete и подобные ужасы.
foreach ($array as $element) {
...
Если (первая буква текущего эл. != первой букве предыдущего) {напечатать первую букву текущего элемента}
...
}
Капитал, чтобы больше не работать на дядю.
Например, франшиза Пятерочки входит лямов в 10 (сама франшиза + аренда + отделочные работы). Доход - 500к-2кк в месяц. Средняя окупаемость - 1.5 года. Процент разорения - 5%.
Новосибирск, а что? Надеюсь не поздно ответил.
http://ideone.com/tI95se
>>554226
https://ideone.com/r8M9rg
Держи маленькая попрошайка :3 Думаю что есть решение и поизящнее, но какой вопрос, такой и ответ.
Очевидно это нужно когда у тебя есть $key и $array а $value надо найти. Если это ты вместо [] испольовал цикл то перечитай урок про массивы ибо тема важная и надо с ней разобраться. Ну и код не забудь показать на проверку.
>>554182
> Больше ничего не могу придумать, кроме как получить этот список в контроллере и передать массивом в виде переменной в представление.
Так и надо. А то что твиг не позволяет, может и хорошо, чтобы ты код из контроллера не тащил во вью.
Если ты продвинутый пользователь, то Arch Linux. Если ты домохозяйка, то Ubuntu.
продвинутый пользователь чего именно?
Самая популярная Ubuntu. Оп рекомендует Debian. У него есть большая паста на эту тему.
У меня только старый версия есть.
https://www.evernote.com/shard/s510/sh/e19231a9-a192-4282-920d-7513d940d5fc/345117e726392e102fe54c5e98354ef5
Eще много хороших гайдов наложено на digitalocean.
https://gist.github.com/codedokode/420c8c12a1edae25f0ec
Ubuntu основана на debian так что многое из пасты и к ней относится.
class Foo{
static $bar1 = 'http://blablabalabla/;
static $bar2 = '¶m=lalalala';
public static function fn1($a){
echo $this->bar1 . $a . $this->bar2;
}
public static function fn2($a,$b){
echo $this->bar1 . ucfirst($a) . $b . $this->bar2;
}
...
};
Стоит ли так делать?
спасибо
>$this->
не будет работать в статик методе, делай self::$bar1, а если эти переменные не будут меняться, то лучше константы:
>const BAR1
Спасибо, а вообще насколько хорошая идея иметь подобный класс для конструирования строк?
> Прикиньте аноны, я то раньше думал, что в объекте можно только редактировать поля, причем которые ты описал в классе А нихуя, оказывается можно как в массиве добавлять туда дополнительные поля по желанию.
Жаль с методами так делать нельзя, исключая костыли типа runkit
Использовать memcached, сохранять туда сериализованный массив вида array('page_id'=>1234, 'ip'=>127.0.0.1). В качестве ключа использовать хеш этой сериализованной строки.
Таким образом, я получаю уникальный ключ (id, ip), и смогу засчитывать только уникальные посещения. Вопрос только в том, сколько времени я смогу хранить историю посещений в memcached, то есть как часто мне его чистить. Наверное придется раз в сутки, чтобы не захламлять память. Обновлять счетчик в базе каждые 5/10/20... минут (И создать доп.таблицу с историей апдейтов счетчика, чтобы удалять из кеша только сохраненные в базу просмотры).
Так что абсолютной уникальности к сожалению таким способом не добиться, ведь я теряю историю просмотров каждые 24 часа. В идеале хотелось бы, чтобы счетчик засчитывал только абсолютно новых посетителей за все время (неделю), чтобы пользователь видел в кабинете объективную статистику, защититься от накрутки. Например гнусные риелтеры, посредники и прочие унтерки будут по много раз просматривать объявление, а их просмотры продавцу вообще-то не уперлись.
Наверное, для реализации абсолютно точного счетчика лучше бы хранить историю через какой-нибудь redis, но я не пока не в теме, и не хочется подключать множество лишних дополнений, если memcached уже есть то предпочтительнее на нем и реализовать.
Еще в memcached мне не понравилось отсутствие возможности хранить сложные структуры данных. (Или я что-то упустил?)
Непонятно, по каким ключам брать из него только действительно необходимые данные. Вижу пока только возможность загрузить все, а потом в цикле php ходить и фильтровать ключи по какому-то префиксу.
Всякие метрики и аналитики прикручивать неохота, сайт всегда должен быть автономным.
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
$obj->callback->__invoke();
http://stackoverflow.com/questions/4535330/calling-closure-assigned-to-object-property-directly
что же выбрать? я иссяк.
пикрандом
я там тред создал, но он взлетит и пиздец
Тут конечно можно ошибки найти в каждом абзаце. ну например мемкеш это кеш, а не постоянное хранилище, и он в принципе не годится для хранения каких-то данных (которых нет в более надежном хранилище). В твоем случае правда как я понимаю теряется только факт посещения пользователем. Но учитывая что это учебная задача, где все должно быть «правильно», это не очень хорошо.
Напишу еще раз: мемкеш это временный кеш, а не постоянное хранилище. Все данные в нем могут в любой момент исчезнуть (так как хранятся в оперативной памяти) и это вполне нормальная для него ситуация.
> сохранять туда сериализованный массив вида array('page_id'=>1234, 'ip'=>127.0.0.1). В качестве ключа использовать хеш этой сериализованной строки.
Нафига что-то сериализовать, если можно сделать ключ вида
visit:1234:127.0.0.1
а класть туда пустую строку или единичку.
> Обновлять счетчик в базе каждые 5/10/20... минут (И создать доп.таблицу с историей апдейтов счетчика, чтобы удалять из кеша только сохраненные в базу просмотры).
А на большом числе объявлений и просмотров этот скрипт не станет медленным? Ты ведь пока не тестировал сколько записей в секунду может переварить база? Это может стать узким местом.
Вообще это число и есть ключевой фактор при выборе решения: если база может пережевать нужное число операций то и заморачиваться с мемкешем смысла нет. По моему, типичные цифры для MySQL это сотни транзакций в секунду и несколько тысячи строк в секунду. Это на магнитных дисках, на SSD я не мерял.
И мне интересен алгоритм обновления данных в БД: вот есть у тебя допустим 100 000 объявлений и 5 000 просмотров за 5 минут, а пок акому алгоритму ты данные будешь переносить в базу? Тут очень много подводных камней.
Читал ли ты статью про подсчет просмотров у мейл ру? Понял ли зачем им были нужны все эти сложности? Если нет, то советую разобраться.
> ведь я теряю историю просмотров каждые 24 часа.
Тогда надо продлить время жизни в кеше.
> Еще в memcached мне не понравилось отсутствие возможности хранить сложные структуры данных.
Да, мемкеш задумывался как максимально простая система и ты должен использовать сериализацию.
Еще стоит помнить что кеш лучше всего работает с данными небольшого объема.
> Всякие метрики и аналитики прикручивать неохота, сайт всегда должен быть автономным
Они и не решат твою задачу так как из них не получится в реальном времени выдирать просмотры по большому числу объявлений.
Насчет автономности это хорошо но иногда цена (на разработку и поддержку) такой автономности очень высока.
Насчет редис, это по сути улучшенный мемкеш. В нем есть персистентность (возможность сохранять данные на диск), есть сложные структуры данных вроде списков, деревьев и тд, и еще много чего.
Но я бы советовал и мемкеш изучить как следует. ну и конечно надо понимать общие концепции: почему мемкеш легко масштабируется а mysql нет, что стоит и что не стоит хранить в кеше и тд.
Тут конечно можно ошибки найти в каждом абзаце. ну например мемкеш это кеш, а не постоянное хранилище, и он в принципе не годится для хранения каких-то данных (которых нет в более надежном хранилище). В твоем случае правда как я понимаю теряется только факт посещения пользователем. Но учитывая что это учебная задача, где все должно быть «правильно», это не очень хорошо.
Напишу еще раз: мемкеш это временный кеш, а не постоянное хранилище. Все данные в нем могут в любой момент исчезнуть (так как хранятся в оперативной памяти) и это вполне нормальная для него ситуация.
> сохранять туда сериализованный массив вида array('page_id'=>1234, 'ip'=>127.0.0.1). В качестве ключа использовать хеш этой сериализованной строки.
Нафига что-то сериализовать, если можно сделать ключ вида
visit:1234:127.0.0.1
а класть туда пустую строку или единичку.
> Обновлять счетчик в базе каждые 5/10/20... минут (И создать доп.таблицу с историей апдейтов счетчика, чтобы удалять из кеша только сохраненные в базу просмотры).
А на большом числе объявлений и просмотров этот скрипт не станет медленным? Ты ведь пока не тестировал сколько записей в секунду может переварить база? Это может стать узким местом.
Вообще это число и есть ключевой фактор при выборе решения: если база может пережевать нужное число операций то и заморачиваться с мемкешем смысла нет. По моему, типичные цифры для MySQL это сотни транзакций в секунду и несколько тысячи строк в секунду. Это на магнитных дисках, на SSD я не мерял.
И мне интересен алгоритм обновления данных в БД: вот есть у тебя допустим 100 000 объявлений и 5 000 просмотров за 5 минут, а пок акому алгоритму ты данные будешь переносить в базу? Тут очень много подводных камней.
Читал ли ты статью про подсчет просмотров у мейл ру? Понял ли зачем им были нужны все эти сложности? Если нет, то советую разобраться.
> ведь я теряю историю просмотров каждые 24 часа.
Тогда надо продлить время жизни в кеше.
> Еще в memcached мне не понравилось отсутствие возможности хранить сложные структуры данных.
Да, мемкеш задумывался как максимально простая система и ты должен использовать сериализацию.
Еще стоит помнить что кеш лучше всего работает с данными небольшого объема.
> Всякие метрики и аналитики прикручивать неохота, сайт всегда должен быть автономным
Они и не решат твою задачу так как из них не получится в реальном времени выдирать просмотры по большому числу объявлений.
Насчет автономности это хорошо но иногда цена (на разработку и поддержку) такой автономности очень высока.
Насчет редис, это по сути улучшенный мемкеш. В нем есть персистентность (возможность сохранять данные на диск), есть сложные структуры данных вроде списков, деревьев и тд, и еще много чего.
Но я бы советовал и мемкеш изучить как следует. ну и конечно надо понимать общие концепции: почему мемкеш легко масштабируется а mysql нет, что стоит и что не стоит хранить в кеше и тд.
Я всё думаю, каким же надо быть тупым, чтобы не понимать, как работает анимация в js и, что самое важное, не найти об этом информацию в сети.
Мамкин строитель лендингов обосрался, да?
Вроде и кусок кода есть и подсказки, а я банально не понимаю с чего начать.
Как я понял, в том коде в самому конце используется только одна функция, та которая первая, а их всего 3. Получается я должен в первой функции заюзать две остальные?
Также обьясни, вот в первой функции нужно подать четыре значения $number, $word1, $word2, $word5, а где я их должен взять если по идее я должен код редактировать только внутри функций? понимаю, что можно самому доделать, но тогда нет смысла показывать где писать код
Да и вообще зачем аж 4 значения? Вроде как на вход функции поступает число, мы находим две последние цифры каким-то образом узнаем, что конкретно подходит "рубля рублей рубль" и возвращаем нужное слово. Не?
А если надо будет сменить количество долларов или курс, то рубли тоже переписывать придётся. Поразмышляй, зачем нужны переменные.
слова это вариант написания например «слон», «слона», «слонов». таким образом функция может склонять любые слова а не только рубли.
Подскажите пожалуйста, в чем ошибка в 3 строке?
>$word1 = array('Чудесных', 'Суровых', 'Занятных', 'Внезапных');
>$word2=array('слов', 'зим', 'глаз', 'дней', 'лет', 'мир', 'взор');
>$word3 =array('прикосновений', 'поползновений', 'судьбы явлений','сухие листья', 'морщины смерти', 'долины края', 'замены нету', 'сухая юность', 'навек исчезнув');
Ставь пробелы одинаково, а не как попало. Используй краткий синтакс для создания массивов, он лучше читаем.
>function getPoem($word)
Ставь PHPDoc @param с типом для принимаемого аргумента:
>echo $value." ";
В функции так не делай. Функция должна возвращать результат, а не заниматься его выводом. Выводом заниматься - задача клиента функции. Таким образом твой код можно будет повторно использовать.
>мемкеш это кеш, а не постоянное хранилище
Я в курсе, но данные по посещениям за 5-20 минут не являются критически важными, поэтому можно пренебречь возможностью потери таких данных.
>учитывая что это учебная задача, где все должно быть «правильно», это не очень хорошо
И к чему это? Не использовать мемкеш, потому что он может потерять данные по просмотрам? Я не понял, к чему ты это написал. Я буду использовать memcached, не вижу альтернатив.
>Нафига что-то сериализовать
Да, протупил.
>почему мемкеш легко масштабируется а mysql нет
Только что посмотрел в википедии, что такое масштабируемость. Надо будет теперь почитать множество мутных статей на хабре.
А пока могу догадаться, что мемкеш это всего лишь куча данных, которые валяются в оперативной памяти. Следовательно чем больше памяти, тем больше можно засунуть в кеш. Тогда как mysql сложная система, использует медленный жесткий диск для хранения данных, плюс потеря времени на всякие соединения, транзакционность (innodb) и наверняка еще множество телодвижений, о которых я даже не слышал. Так что покупка большего кол-ва процессоров и быстрых жестких дисков может чуть и поможет, но не значительно.
>что стоит и что не стоит хранить в кеше
Часто запрашиваемые ресурсы : html, большие картинки, результаты медленных или частых запросов к бд.
Но эту тему я еще не разбирал. Я хочу сделать счетчик, потом буду думать, что еще закешировать.
В нашем случае со счетчиком: временные данные, которые не жалко потерять и невыгодно с точки зрения производительности часто дергать базу для их обновления.
>вот есть у тебя допустим 100 000 объявлений и 5 000 просмотров за 5 минут, а пок акому алгоритму ты данные будешь переносить в базу? Тут очень много подводных камней
Очевидно буду делать мультиинсерт новых объявлений, используя транзакцию, как ты советовал здесь >>550423
Я не пропускаю то что ты пишешь, просто у меня не идеальная память, я не могу запомнить с первого раза, систематизировать и проанализировать огромные объемы новой и сложной информации.
Для инсертов использовать синтаксис через запятую INSERT INTO advertisement (title, description, ...) VALUES (первая запись), (вторая запись), ...
Плохим решением будет инсертить каждую запись в цикле.
Для апдейтов сначала сгруппировать по кол-ву просмотров, и обновлять всю группу одним запросом UPDATE <таблица счетчиков> SET views_count = views_count + <кол-во просмотров> WHERE id IN (<id сущностей с одинаковым кол-вом просмотров>), как написано в статье мейлру.
>Тут очень много подводных камней
Я узнаю об их существовании, только если ты скажешь. Запрос в гугл "подводные камни при создании счетчика посещения на memcached" не помогает. Других источников знаний кроме гугла и этого треда у меня нет.
По статье.
Не знаю, как тебе пруфануть, понял я или не понял. Поэтому кратко перескажу содержание, чтобы если я неправильно понял, ты поправил.
>Сначала попробовали реализовать «в лоб». Мы создали в таблице сущности поле views_count
Я тоже так сделал с самого начала, пока там default 0.
>Подумали-подумали, и решили, что количества просмотров объявлений (views_count) в листингах нам не нужны, а нужны они нам только в админке и в личном кабинете пользователя. А значит и не зачем это поле хранить в таблице сущности
У меня наоборот, никаких "листингов" не предвидится (хотя кто знает). Счетчик будет выводиться именно при просмотре страницы объявления, следовательно принадлежить к сущности объявления.
Ага, впрочем, в рабочем кабинете (который я еще не делал), как раз будет такой список "мои объявления", и там это число обязательно должно выводиться. По моей логике это лишний раз подтверждает, что счетчик принадлежит к сущности объявления, но может я ошибаюсь.
С другой стороны, во время апдейтов будут блокировки таблицы на выборку (пусть только конкретных строк в innodb, все равно это будет тормозить).
Значит да, нелишним будет перенести счетчик в отдельную таблицу. Ок, сделаю миграцию.
>теперь появилась очередь из апдейтов этой новой таблицы
Да, нужно что-то предпринять. Они решили использовать еще одну таблицу типа MEMORY, о которых я слышал, но ничего не знаю, и наверное еще и какой-нибудь триггер с хранимой процедурой, там не сказано. Эти техники тоже темный лес пока. Я могу в лучшем случае запустить скрипт по расписанию, который обновит нужные записи и удалит что нужно.
>Еще один важный трюк, который мы применили — это отказались от транзакций MySQL и реализовали свою альтернативу для работы с таблицей-окном.
Ловко. Возьму на заметку.
>Какие альтернативы мы рассматривали
>Пробовали хранить в memcached и потом оттуда по расписанию выгружать в базу. Но определять, по каким ключам лезть за информацией, оказалось непросто, и бизнес-логика в какой-то момент стала слишком сложной.
И я о том же.
Фух, мне нужно передохнуть и попробовать охватить все вышесказанное, чтобы сделать вывод.
Судя по всему, в данном случае мне тоже придется последовать за майлру и сделать точно так, как у них.
Мемкеш больше подходит в простейшей ситуации, когда нужно хранить только счетчик, не заморачиваясь уникальностью, ролью пользователя, фильтровкой ботов и т.д.
Хорошо, попробую сделать так.
Что можешь сказать о триггерах и процедурах? Стоит ли их изучить, или после вставки в эту временную таблицу делать еще один запрос count, и если кол-во записей окажется больше допустимого, запустить из php запрос на обновление?
Очевидно, нужен еще класс для счетчика. Да, тут есть над чем подумать.
>мемкеш это кеш, а не постоянное хранилище
Я в курсе, но данные по посещениям за 5-20 минут не являются критически важными, поэтому можно пренебречь возможностью потери таких данных.
>учитывая что это учебная задача, где все должно быть «правильно», это не очень хорошо
И к чему это? Не использовать мемкеш, потому что он может потерять данные по просмотрам? Я не понял, к чему ты это написал. Я буду использовать memcached, не вижу альтернатив.
>Нафига что-то сериализовать
Да, протупил.
>почему мемкеш легко масштабируется а mysql нет
Только что посмотрел в википедии, что такое масштабируемость. Надо будет теперь почитать множество мутных статей на хабре.
А пока могу догадаться, что мемкеш это всего лишь куча данных, которые валяются в оперативной памяти. Следовательно чем больше памяти, тем больше можно засунуть в кеш. Тогда как mysql сложная система, использует медленный жесткий диск для хранения данных, плюс потеря времени на всякие соединения, транзакционность (innodb) и наверняка еще множество телодвижений, о которых я даже не слышал. Так что покупка большего кол-ва процессоров и быстрых жестких дисков может чуть и поможет, но не значительно.
>что стоит и что не стоит хранить в кеше
Часто запрашиваемые ресурсы : html, большие картинки, результаты медленных или частых запросов к бд.
Но эту тему я еще не разбирал. Я хочу сделать счетчик, потом буду думать, что еще закешировать.
В нашем случае со счетчиком: временные данные, которые не жалко потерять и невыгодно с точки зрения производительности часто дергать базу для их обновления.
>вот есть у тебя допустим 100 000 объявлений и 5 000 просмотров за 5 минут, а пок акому алгоритму ты данные будешь переносить в базу? Тут очень много подводных камней
Очевидно буду делать мультиинсерт новых объявлений, используя транзакцию, как ты советовал здесь >>550423
Я не пропускаю то что ты пишешь, просто у меня не идеальная память, я не могу запомнить с первого раза, систематизировать и проанализировать огромные объемы новой и сложной информации.
Для инсертов использовать синтаксис через запятую INSERT INTO advertisement (title, description, ...) VALUES (первая запись), (вторая запись), ...
Плохим решением будет инсертить каждую запись в цикле.
Для апдейтов сначала сгруппировать по кол-ву просмотров, и обновлять всю группу одним запросом UPDATE <таблица счетчиков> SET views_count = views_count + <кол-во просмотров> WHERE id IN (<id сущностей с одинаковым кол-вом просмотров>), как написано в статье мейлру.
>Тут очень много подводных камней
Я узнаю об их существовании, только если ты скажешь. Запрос в гугл "подводные камни при создании счетчика посещения на memcached" не помогает. Других источников знаний кроме гугла и этого треда у меня нет.
По статье.
Не знаю, как тебе пруфануть, понял я или не понял. Поэтому кратко перескажу содержание, чтобы если я неправильно понял, ты поправил.
>Сначала попробовали реализовать «в лоб». Мы создали в таблице сущности поле views_count
Я тоже так сделал с самого начала, пока там default 0.
>Подумали-подумали, и решили, что количества просмотров объявлений (views_count) в листингах нам не нужны, а нужны они нам только в админке и в личном кабинете пользователя. А значит и не зачем это поле хранить в таблице сущности
У меня наоборот, никаких "листингов" не предвидится (хотя кто знает). Счетчик будет выводиться именно при просмотре страницы объявления, следовательно принадлежить к сущности объявления.
Ага, впрочем, в рабочем кабинете (который я еще не делал), как раз будет такой список "мои объявления", и там это число обязательно должно выводиться. По моей логике это лишний раз подтверждает, что счетчик принадлежит к сущности объявления, но может я ошибаюсь.
С другой стороны, во время апдейтов будут блокировки таблицы на выборку (пусть только конкретных строк в innodb, все равно это будет тормозить).
Значит да, нелишним будет перенести счетчик в отдельную таблицу. Ок, сделаю миграцию.
>теперь появилась очередь из апдейтов этой новой таблицы
Да, нужно что-то предпринять. Они решили использовать еще одну таблицу типа MEMORY, о которых я слышал, но ничего не знаю, и наверное еще и какой-нибудь триггер с хранимой процедурой, там не сказано. Эти техники тоже темный лес пока. Я могу в лучшем случае запустить скрипт по расписанию, который обновит нужные записи и удалит что нужно.
>Еще один важный трюк, который мы применили — это отказались от транзакций MySQL и реализовали свою альтернативу для работы с таблицей-окном.
Ловко. Возьму на заметку.
>Какие альтернативы мы рассматривали
>Пробовали хранить в memcached и потом оттуда по расписанию выгружать в базу. Но определять, по каким ключам лезть за информацией, оказалось непросто, и бизнес-логика в какой-то момент стала слишком сложной.
И я о том же.
Фух, мне нужно передохнуть и попробовать охватить все вышесказанное, чтобы сделать вывод.
Судя по всему, в данном случае мне тоже придется последовать за майлру и сделать точно так, как у них.
Мемкеш больше подходит в простейшей ситуации, когда нужно хранить только счетчик, не заморачиваясь уникальностью, ролью пользователя, фильтровкой ботов и т.д.
Хорошо, попробую сделать так.
Что можешь сказать о триггерах и процедурах? Стоит ли их изучить, или после вставки в эту временную таблицу делать еще один запрос count, и если кол-во записей окажется больше допустимого, запустить из php запрос на обновление?
Очевидно, нужен еще класс для счетчика. Да, тут есть над чем подумать.
>Я буду использовать memcached, не вижу альтернатив.
Редис же. Он умеет бэкапить на диск.
Или можно просто парсить логи сервера раз в n секунд. Чем вообще будет заниматься отдельный скрипт по крону.
мимо нихуя не специалист
Я ведь правильно мыслю, ОП?
Если метод __set() в классе стоит, то такие трюки у тебя не выйдут. В любом случае напрямую в класс свойства присваивать - плохой стиль программирования.
>>почему мемкеш легко масштабируется а mysql нет
> Только что посмотрел в википедии, что такое масштабируемость.
Ой-ой, а вот это нехорошо что ты это не понимаешь. Масштабируемость в данном случае это возможность использовать ресурсы нескольких машин когда одной перестает хватать.
В случае с мемкешем, это key-value хранилище, по сути огромный одномерный массив. Элементы в нем не связаны друг с другом и их можно разнести на разные сервера (это называется шардинг, а сервера - шарды).
Оно масштабируется примерно так: ты делаешь функцию, которая по ключу генерирует число от 1 до N и сохраняешь/загружаешь данные на сервер с соответствующим номером. То есть условно говоря, ключ «a» хранится на сервере 1, а «b» на сервере 2, и нагрузка
распределяется между ними.
Более того, данные в мемкеше можно терять потому добавление/удаление нового сервера не ломает работу приложения.
В случае с mysql все не так. Данные в таблицах связаны друг с другом, а сами таблицы связаны внешними ключами. Ты можешь делать выборки как по таблице, так и по нескольким (с помощью джойна). И хотя есть решения (вроде slave-master репликации или mysql cluster) но они из-за этих особенностей (необходимость синхронизации данных) не могут получить такой выигрыш от масштабированяи который может иметь мемкеш и не могут масштабироваться больше чем на несколько серверов.
То есть именно простота мемкеша и дает ему возможность масштабирования.
>>что стоит и что не стоит хранить в кеше
> Часто запрашиваемые ресурсы : html, большие картинки,
Нет, так как для этого есть другие кеши (например файловый кеш ОС) и ты будешь зря их дублировать.
Кешируют то, что требует тяжелых вычислений, и то, к чему идут частые обращения. Если тебе интересна тема кеширования в веб-приложениях, могу посоветовать пролистать слайды презентации:
http://lib.custis.ru/images/6/6d/WebAppCache.pdf
(по названию можно наверно найти и виде этого доклада)
Чтобы хорошо разбираться в этом надо в том числе понимать как устроен веб-сервер и операционная система.
>>почему мемкеш легко масштабируется а mysql нет
> Только что посмотрел в википедии, что такое масштабируемость.
Ой-ой, а вот это нехорошо что ты это не понимаешь. Масштабируемость в данном случае это возможность использовать ресурсы нескольких машин когда одной перестает хватать.
В случае с мемкешем, это key-value хранилище, по сути огромный одномерный массив. Элементы в нем не связаны друг с другом и их можно разнести на разные сервера (это называется шардинг, а сервера - шарды).
Оно масштабируется примерно так: ты делаешь функцию, которая по ключу генерирует число от 1 до N и сохраняешь/загружаешь данные на сервер с соответствующим номером. То есть условно говоря, ключ «a» хранится на сервере 1, а «b» на сервере 2, и нагрузка
распределяется между ними.
Более того, данные в мемкеше можно терять потому добавление/удаление нового сервера не ломает работу приложения.
В случае с mysql все не так. Данные в таблицах связаны друг с другом, а сами таблицы связаны внешними ключами. Ты можешь делать выборки как по таблице, так и по нескольким (с помощью джойна). И хотя есть решения (вроде slave-master репликации или mysql cluster) но они из-за этих особенностей (необходимость синхронизации данных) не могут получить такой выигрыш от масштабированяи который может иметь мемкеш и не могут масштабироваться больше чем на несколько серверов.
То есть именно простота мемкеша и дает ему возможность масштабирования.
>>что стоит и что не стоит хранить в кеше
> Часто запрашиваемые ресурсы : html, большие картинки,
Нет, так как для этого есть другие кеши (например файловый кеш ОС) и ты будешь зря их дублировать.
Кешируют то, что требует тяжелых вычислений, и то, к чему идут частые обращения. Если тебе интересна тема кеширования в веб-приложениях, могу посоветовать пролистать слайды презентации:
http://lib.custis.ru/images/6/6d/WebAppCache.pdf
(по названию можно наверно найти и виде этого доклада)
Чтобы хорошо разбираться в этом надо в том числе понимать как устроен веб-сервер и операционная система.
> В нашем случае со счетчиком: временные данные, которые не жалко потерять и невыгодно с точки зрения производительности часто дергать базу для их обновления.
Это в общем правильная мысль.
> Очевидно буду делать мультиинсерт новых объявлений,
А как ты будешь собирать данные из мемкеша? Там ведь нету запроса вроде SELECT FROM table.
> Других источников знаний кроме гугла и этого треда у меня нет.
Я напишу про подводные камни когда увижу алгоритм, абстрактно трудно говорить.
Насчет мейл ру, главное что они сделали — это уменьшили число транзакций за счет сбора (аггрегации) данных и обновления их большими пачками по расписанию. Этот прием можно применить также например при логгировании каких-то данных в базу.
> Они решили использовать еще одну таблицу типа MEMORY, о которых я слышал
Это таблица которая хранится только в памяти и потому быстро работает. Обычные таблицы не так быстры так как при коммите транзакции mysql ждет подтверждения что данные записаны на диск (так как она отвечает за их сохранность).
> Что можешь сказать о триггерах и процедурах? Стоит ли их изучить
Изучить можно, но лучше избегать размещения логики в базе. так как это усложняет систему, размазывает код между приложением и базой. Ну и отлаживать, тестировать ее не так удобно.
То есть лучше не использовать ни триггеры ни тем более процедуры.
> Стоит ли их изучить, или после вставки в эту временную таблицу делать еще один запрос count,
Я думаю если на каждую вставку делать запрос COUNT то мы можем получить не ускорение, а замедление. Точнее можно будет сказать когда увидим алгоритм.
Редис это хорошо, но анону наверно стоит дял начала с мемкешем разобарться. А так да, редис хорошо для этой задачи подходит потому что в нем есть списки которые можно использовать как очередь.
>>554246
Не надо добавлять свойства в объекты. Суть классов как раз в том что список свойств известен. Как можно надежно писать код если непонятно какие свойства есть у объекта, а каких нет?
Если ты хочешь динамически добавлять свойства, просто возьми массив.
Я считаю это ошибка проектирования, что PHP такое разрешает.
>>554437
И не надо.
>>554524
Кошмарный код. Почему ты используешь объект там где лучше подойдет массив?
> С другой стороны, во время апдейтов будут блокировки таблицы на выборку (пусть только конкретных строк в innodb, все равно это будет тормозить).
> Значит да, нелишним будет перенести счетчик в отдельную таблицу.
Я бы не спешил с этим, это еще неизвестно будет ли выгода (надо мерять), а вот усложнение кода будет.
>>Еще один важный трюк, который мы применили — это отказались от транзакций MySQL и реализовали свою альтернативу для работы с таблицей-окном.
> Ловко. Возьму на заметку.
Однако в твоем проекте это будет лишнее усложнение на мой взгляд. Опять же прежде чем такие вещи делать надо как-то проверить и измерить разницу.
> парсить логи сервера раз в n секунд
Вот это как раз неэффективно, на нагруженных сервисах аксесс логи часто отключают. Плюс формат логов может меняться, это какое-то извращение получается. Плюс когда серверов много надо еще как-то собирать с них логи.
какая еще клиента функции? вот так что ли надо было писать? или аж целый отдельный класс делать ради красивости?
function getPoem($word)
{
$length = count($word)-1;
$rand = rand(0, $length);
foreach($word as $key=>$value){
if($key == $rand && array_key_exists($rand, $word)){
return $word[$key];
}
}
}
echo getPoem($word1).' ';
А зачем там цикл? цикл не нужен для получения массива по ключу.
Ну и он прав насчет echo, лучше когда функция возвращает результат и вызывающий ее может сам решить что с ним делать.
> какая еще клиента функции
Имеется в виду тот кто вызывает функцию
Вот аноны, обьясните мне, почему вы учите этот язык? Я один из вас и тоже учу php и каждый раз в какой-то дискуссии php поливают говном.
Каждый раз если я где-то скажу, что я учу php, то мне постоянно советуют учить что-то другое, например сишарп ASP.NET, но я все равно учу php, потому что мне я начитался, что он легкий хотя мне совсем так не кажется, после задачек ОПа, много вакансий, к тому же здесь уютно, понимаешь, что этот язык учат многие и по идее ты не проебешься, ОП помогает, да и четко знаешь, что нужно выучить, а вот другие треды и языки и я совсем не представляю как там все устроено и не было особого желания, но каждый раз когда мой выбор поливают говном, то у меня с каждым разом закладывается вопрос, а правильно ли я все делаю? Сделал ли я правильный выбор?
Вы можете ответить на мои вопросы и обосновать их?
Быдло самоутверждается, очевидно же. Большинство местных петухов не знают ни одного языка, а заходят сюда посраться.
Я понял насчет масштабирования.
>Кешируют то, что требует тяжелых вычислений, и то, к чему идут частые обращения
То есть в первую очередь результат частых/медленных запросов к бд, возможно какие-то тяжелые объекты в скрипте. Но это чуть позже, сначала разбирусь со счетчиком, потом уже будем искать проблемные места в коде/медленные запросы.
>>554659
>как ты будешь собирать данные из мемкеша
Вот я и говорил, что неудобно, и мейлрушники тоже отметили это в той статье.
Учитывая, что мне нужно сохранять достаточно короткую строку, храним все данные в ключе вида
visit|1234|127.0.0.1 то есть префикс|page_id|ip
Значения не нужны, кладем пустую строку.
Из мемкеша берем все ключи getAllKeys, в цикле отсеиваем лишние и парсим эту строку, выбирая только идентификаторы.
Подставляем в запрос в цикле.
Удаляем ключи из мемкеша. Но так теряется возможность отслеживать уникальных посетителей. Если я просто продлю время жизни элемента, то не знаю как отличить те просмотры, что уже были занесены в базу. Ну и мы уже обсудили, что мемкеш не нежно использовать как хранилище.
>главное что они сделали — это уменьшили число транзакций за счет сбора (аггрегации) данных и обновления их большими пачками по расписанию
Попытался это реализовать для скрипта, обновляющего счетчик.
https://gist.github.com/nsdvw/58a7d5385a118a5b4306
Давай уже свои камни.
>запрос COUNT
Это я к тому, что в статье при наполнении таблицы MEMORY до определенного количества записей запускалась скорее всего какая-то процедура.
Я не использую процедуры, значит я должен из php узнавать кол-во записей в этой таблице. Но это в случае, если нам нужно обновление счетчика в режиме реального времени, и при использовании таблицы типа memory.
Поскольку мы вроде бы опять переключились на memcached, то об этом можно забыть, просто запускаем вышеупомянутый скрипт по расписанию.
>>554669
>прежде чем такие вещи делать надо как-то проверить и измерить разницу
Да я уже понял, что ты меня заставишь решить задачу тремя (в лучшем случае) способами.
Придется смириться, тем более что ты прав. Протестовать против такого дотошного изучения заставляет только нехватка времени.
Я понял насчет масштабирования.
>Кешируют то, что требует тяжелых вычислений, и то, к чему идут частые обращения
То есть в первую очередь результат частых/медленных запросов к бд, возможно какие-то тяжелые объекты в скрипте. Но это чуть позже, сначала разбирусь со счетчиком, потом уже будем искать проблемные места в коде/медленные запросы.
>>554659
>как ты будешь собирать данные из мемкеша
Вот я и говорил, что неудобно, и мейлрушники тоже отметили это в той статье.
Учитывая, что мне нужно сохранять достаточно короткую строку, храним все данные в ключе вида
visit|1234|127.0.0.1 то есть префикс|page_id|ip
Значения не нужны, кладем пустую строку.
Из мемкеша берем все ключи getAllKeys, в цикле отсеиваем лишние и парсим эту строку, выбирая только идентификаторы.
Подставляем в запрос в цикле.
Удаляем ключи из мемкеша. Но так теряется возможность отслеживать уникальных посетителей. Если я просто продлю время жизни элемента, то не знаю как отличить те просмотры, что уже были занесены в базу. Ну и мы уже обсудили, что мемкеш не нежно использовать как хранилище.
>главное что они сделали — это уменьшили число транзакций за счет сбора (аггрегации) данных и обновления их большими пачками по расписанию
Попытался это реализовать для скрипта, обновляющего счетчик.
https://gist.github.com/nsdvw/58a7d5385a118a5b4306
Давай уже свои камни.
>запрос COUNT
Это я к тому, что в статье при наполнении таблицы MEMORY до определенного количества записей запускалась скорее всего какая-то процедура.
Я не использую процедуры, значит я должен из php узнавать кол-во записей в этой таблице. Но это в случае, если нам нужно обновление счетчика в режиме реального времени, и при использовании таблицы типа memory.
Поскольку мы вроде бы опять переключились на memcached, то об этом можно забыть, просто запускаем вышеупомянутый скрипт по расписанию.
>>554669
>прежде чем такие вещи делать надо как-то проверить и измерить разницу
Да я уже понял, что ты меня заставишь решить задачу тремя (в лучшем случае) способами.
Придется смириться, тем более что ты прав. Протестовать против такого дотошного изучения заставляет только нехватка времени.
Модно ругать вот и ругают, как ТНН, паркуристы и прочая шелупонь лезет, потому что модно.
Если у тебя в классе множества встроенных методов нет, то пойдет, конфликтов не будет.
Плюс при чтении кода
$functionHolder->run('printNumbers', [12, 'попугай']);
сразу понятно, что функция не встроенная и что-то тут не так, программисту придеться смотреть метод run, и он поймет, что функция создается где-то еще, проследит всю цепочку создания.
$functionHolder->printNumbers(12, 'попугай');
Тут он подумает, что функция встроена в класс и будет на нее полагаться, что может привести к непредсказуемым последствиям в крупных проектах.
Зачем это нужно? Чтобы код было сложнее читать, так как непонятно какие функции в объекте есть, а каких нет? Приведи пример где твой код принесет пользу.
Это ведь код не для реальной задачи, а просто от балды написано.
Конкретно в твоем случае код можно переписать так:
function printNumbers($number, $string) {
echo "Номер $number";
echo "\nСтрока $string";
}
printNumbers(12, 'попугай');
Теперь объясни чем твой код лучше моего если они делают одно и то же, но мой в несколько раз короче и проще читается.
Ну и наконец приведу примеры где магические функции (реализованные через __call) имеют смысл. Обычно это используется в фреймворках. Например в доктрине ты можешь написать так:
$user = $userRepository->findOneByName('Ivan');
И это будет искать в репозитории сущность у которой поле name == 'Ivan'. Тут используется магический метод так как автор доктрины разумеется не знает заранее какие поля будут у твоих сущностей. Аналогично можно писать findOneById, findOneByEmail и тд.
Но такие примеры редкие, если ты не автор фреймворка то тебе скорее всего магические методы не понадобятся. Ну и читабельность и понятность кода очень страдает от них (в случае с доктриной — это описано в документации).
Можно обойтись без файла, почитай про конфигурацию веб-сервера. Сама идея твоя....странновата, только ради самообучения стоит мутить.
Тогда такой вопрос, а кошерно вообще делать маршрутизацию обходным путем?
>почитай про конфигурацию веб-сервера
А где это почитать чтоб не сложно для ньюфага?
>идея твоя....странновата
Почему идея странновата? Ну для обучения пока, потом если что как портфолио показать на собеседовании, найти работку, слезть с мамкиной шеи, прийти к успеху.
Посоны, напишите рабочий полный код пример, как доставать пароль из формы, переводить в md5, добавить соль, положить в бд, достать из бд, бурать соль, опять в мд5 и сверить с данными из формы? плз, именно коротенький код-пример нужен, потому что я читаю и нихера представить не могу, именно вот кодик-код напишите плз с этой солью. можно PDO, mysqli - неважно.
>htaccess
AuthType Basic
AuthName admin
require valid-user
AddDefaultCharset utf-8
RewriteEngine on
RewriteBase /
RewriteRule ^(.*)$ index.php
>конфиг апача
https://www.dropbox.com/s/hrx5acjy1hes6yr/Apache-2.2_server.conf?dl=0
Значит у тебя не совпадает кодировка. Можешь описать свою проблему подробнее, и возможно получишь более подробный ответ.
Не понимаю простой вещи... Как в условии задать промежуток, например нужно узнать ровняется ли $a числам от 5 до 7. Я по всякому уже пробовал прописать, все неверно
if ($a == [5-7]){...
неверно же
пробовал переменную задать в виде
$numbers = '/[5-7]/u';
if ($a == [$numbers]){...
все равно не идет
как быть
ОС какая? Винда? Она принимает имена в кодировке cp1251. соответственно твой код должен выглядеть так:
если (винда) {
перевести имя в cp1251;
сохранить под этим именем;
} иначе {
сохранить под именем в кодировке utf-8 (которую ты надеюсь по умолчанию и используешь)
}
> if ($a == [5-7]){...
С точки зрения PHP квадратные скобки значат создание массива. То есть твой код равносилен
if ($a == array(5-7))
5-7 PHP вычислит как выражение, которое равно -2 и получается в итоге
if ($a == array(-2))
То есть идет сравнение $a с массивом содержащим число -2.
> if ($a == [$numbers]){
Та же ерунда, ты сравниваешь $a и массив из 1 элемента содержащий строку.
Почему ты вообще используешь знак равенства? Он используется для проверки на равенство знаений, а тебе надо использовать больше и меньше.
также рекомендую показать решения своих задач на проверку, может тебе еще дадут советы по улучшению.
Бамп теме обновления счетчика посещений через memcached.
Одобряешь алгоритм?
https://gist.github.com/nsdvw/58a7d5385a118a5b4306
Я пока пойду почитаю про редис, мемкеш конечно не годится для долговременного хранения данных, когда нужно отслеживать уникальных посетителей.
После правок конфига Апач перезапускал? Имя файла htaccess написано правильно? В какой папке он лежит?
Да и то что ты скинул по моему не конфиг а только шаблон когнфига. Openserver из него видимо как-то генерирует настоящий конфиг.
Похоже что это ты что-то в настройках опенсервера напутал либо файл htaccess не там создал или не так назвал.
Вообще лучше бы самому ставить Апач и PHP, это не так сложно, но зато ты будешь иметь стандартную конфигурацию. А что там разработчики опенсервера нахимичили, я не знаю, это у них надо спрашивать.
Нет конечно. У PHP свой синтаксис и он не поддерживает тройные сравнения, только сравнения 2 чисел. И не понимает математическую запись для интервалов.
Тебе надо писать так:
если a большеравно чем X и a меньшеравно чем Y то ....
Про то как объединять условия с помощью И/ИЛИ написано в уроке про if и кубики.
>>555074
А, я вспомнил одну особенность мемкеша, не знаю в курсе ты или нет:
https://www.adayinthelifeof.nl/2011/02/06/memcache-internals/
Пункт «LRU algorithm»
Мемкеш при старте выделяет фиксированный объем памяти, и при ее нехватке удаляет (eviction) ключи к которым давно не обращались. Это тоже стоит помнить.
> $rawVisits = $memcached->getAllKeys();
Так как ты очищаешь мемкеш в итоге то у тебя в мемкеше ничего больше нельзя хранить, тебе нужен отдельный выделенный мемкеш только под посещения. Это недостаток твоего решения.
Это неудобно так как лучше иметь один мемкеш на 2 Гб к примеру чем 2 мемкеша на 1 Гб каждый, где один может быть переполнен а второй лишь частично используется.
Также, выборка всех ключей имеет сложность O(N) и если ключей много, она может быть затянется на несколько секунд. Так как мемкеш однопоточный (надеюсь ты это тоже знаешь) это значит что все другие клиенты мемкеша эти несколько секунд будут ждать, так как он не может делать несколько операций параллельно. Несколько секунд не очень страшно, а что если мы давно не сбрасывали данные в базу и там накопились сотни тысяч или миллионы записей?
Также, между выборкой всех ключей и удалением есть промежуток. Посещения попавшие в этот промежуток, теряются.
> if (substr($v, 0, 5) != 'visit') {
Ненадежно, вдруг там есть еще (или будет) 'visitor' или что-то такое. Хотя если у тебя отдельный мемкеш только под посещения, это не так страшно.
> } catch(Exception $e) {
> $connection->rollback();
В таких случаях надо не терять исключение а прокидывать дальше через throw
> $memcached->deleteMulti($rawVisits);
тут надо проверить есть ли у memcached ограничение на длину команды и у клиентской библиотеки ограничения на число параметров. Возможно что есть.
Наконец еще мелочь: если ты успешно закоммитил данные в базу, и скрипт упал не успев удалить ключи, они учтутся повторно. Это тонкий момент, который сложно исправить, даже с учетом описанного ниже решения с очередью.
Как видишь, недостатков много, разного уровня. В общем алгоритм не особо надежный.
Если ты читал статью мейл ру то заметил что у них система более сложная. Это неспроста. Для надежной реализации нужна очередь. С одной стороны скрипты в нее пишут единичные посещения, с другой стороны скрипт-сборщик снимает и удаляет их поблочно. Такой подход позволяет во-первых не выделять отдельный мемкеш под данные, во-вторых, обрабатывать данные блоками а не все сразу, в-третьих не терять поступающие данные.
При этом для отсева повторных посещений можно использовать ту же самую схему с ключами: перед добавлением в очередь проверять нет ли ключа с данным IP и id.
Увы, мемкеш не даст нам реализовать очередь. для этого нужен редис (или база данных как в примере мейл ру).
Ну и в заключение, для того чтобы как следует изучить мемкеш было бы полезно реализовать свой вариант мемкеш-демона на PHP (совместимного со стандартным протоколом мемкеша). Но я понимаю, что времени у тебя на это нет, так что не буду предлагать.
Нет конечно. У PHP свой синтаксис и он не поддерживает тройные сравнения, только сравнения 2 чисел. И не понимает математическую запись для интервалов.
Тебе надо писать так:
если a большеравно чем X и a меньшеравно чем Y то ....
Про то как объединять условия с помощью И/ИЛИ написано в уроке про if и кубики.
>>555074
А, я вспомнил одну особенность мемкеша, не знаю в курсе ты или нет:
https://www.adayinthelifeof.nl/2011/02/06/memcache-internals/
Пункт «LRU algorithm»
Мемкеш при старте выделяет фиксированный объем памяти, и при ее нехватке удаляет (eviction) ключи к которым давно не обращались. Это тоже стоит помнить.
> $rawVisits = $memcached->getAllKeys();
Так как ты очищаешь мемкеш в итоге то у тебя в мемкеше ничего больше нельзя хранить, тебе нужен отдельный выделенный мемкеш только под посещения. Это недостаток твоего решения.
Это неудобно так как лучше иметь один мемкеш на 2 Гб к примеру чем 2 мемкеша на 1 Гб каждый, где один может быть переполнен а второй лишь частично используется.
Также, выборка всех ключей имеет сложность O(N) и если ключей много, она может быть затянется на несколько секунд. Так как мемкеш однопоточный (надеюсь ты это тоже знаешь) это значит что все другие клиенты мемкеша эти несколько секунд будут ждать, так как он не может делать несколько операций параллельно. Несколько секунд не очень страшно, а что если мы давно не сбрасывали данные в базу и там накопились сотни тысяч или миллионы записей?
Также, между выборкой всех ключей и удалением есть промежуток. Посещения попавшие в этот промежуток, теряются.
> if (substr($v, 0, 5) != 'visit') {
Ненадежно, вдруг там есть еще (или будет) 'visitor' или что-то такое. Хотя если у тебя отдельный мемкеш только под посещения, это не так страшно.
> } catch(Exception $e) {
> $connection->rollback();
В таких случаях надо не терять исключение а прокидывать дальше через throw
> $memcached->deleteMulti($rawVisits);
тут надо проверить есть ли у memcached ограничение на длину команды и у клиентской библиотеки ограничения на число параметров. Возможно что есть.
Наконец еще мелочь: если ты успешно закоммитил данные в базу, и скрипт упал не успев удалить ключи, они учтутся повторно. Это тонкий момент, который сложно исправить, даже с учетом описанного ниже решения с очередью.
Как видишь, недостатков много, разного уровня. В общем алгоритм не особо надежный.
Если ты читал статью мейл ру то заметил что у них система более сложная. Это неспроста. Для надежной реализации нужна очередь. С одной стороны скрипты в нее пишут единичные посещения, с другой стороны скрипт-сборщик снимает и удаляет их поблочно. Такой подход позволяет во-первых не выделять отдельный мемкеш под данные, во-вторых, обрабатывать данные блоками а не все сразу, в-третьих не терять поступающие данные.
При этом для отсева повторных посещений можно использовать ту же самую схему с ключами: перед добавлением в очередь проверять нет ли ключа с данным IP и id.
Увы, мемкеш не даст нам реализовать очередь. для этого нужен редис (или база данных как в примере мейл ру).
Ну и в заключение, для того чтобы как следует изучить мемкеш было бы полезно реализовать свой вариант мемкеш-демона на PHP (совместимного со стандартным протоколом мемкеша). Но я понимаю, что времени у тебя на это нет, так что не буду предлагать.
Ну и вообще, очередь в кеше это стандартный способ снижения нагрузки на запись в базу данных, например ее же можно использовать если какие-то логи пишутся в базу, для защиты от перегрузки запросами. За это мы платим некоторым снижением надежности и задержкой между добавлением в очередь и попаданием в базу.
Наконец еще одна маленькая претензия — просмотры будут увеличиваться на сайте не сразу, а с задержкой. Это можно исправить, заведя в кеше ключ для счетчика который мы копируем из базы в первый раз и увеличиваем при следующих просмотрах, и выводить число просмотров из этого кеша.
Ну то есть ключ вида views:1234 в котором указан id объявления.
Но тут есть риск получить расхождения с реальным числом.
Допустим у нас в базе 20 просмотров, и в очереди на добавление еще 5 (их пока не внесли в базу). Приходит новый пользователь, мы добавляем 6-е посещение в очередь, а также инициализируем счетчик числом из базы (20) и увеличиваем на 1 (21).
Затем запускается скрипт сброса данных в базу, он увеличивает в базе значение до 26 но ключ со счетчиком остается на 21. Вот мы и получили отставание на 5 просмотров.
Для борьбы с этим можно делать такой ключ короткоживущим, но тогда мы получаем второй вариант проблемы, откручивание счетчика назад:
Допустим у нас в базе 20 просмотров, и в очереди на добавление еще 5 (их пока не внесли в базу). Приходит еще 5 пользователей, мы инициализируем счетчик числом из базы (20), 5 раз увеличиваем и имеем на счетчике 25 (а в базе 20, а в очереди 10 просмотров).
Затем ключ умирает, заходит новый пользователь, ключ снова создается на основе данных из базы и счетчик теперь показывает 21.
Такие дела. Ты всегда получаешь такого рода проблемы согласованности когда начинаешь строить распределенную систему из нескольких хранилищ. Лучший способ борьбы с несогласованностью — не использовать распределенные системы а хранить все в одной базе, но не всегда это возможно по соображениям производительности. Приходится страдать.
Ты можешь поломать голову над проблемой отображения актуального числа просмотров но не могу обещать что ты его найдешь.
Привет, ОП, спасибо большое, что посмотрел мои задачки. Вроде поисправлял:
1) Поиск ошибок - сделал как ты написал, слово (так проще увидеть ошибку), до пробела (1 или больше), потом союз, после которого любой символ кроме букв - потому что не придумал, как иначе границу слова обозначить. В твоём хитром примере ошибок не находит теперь. http://ideone.com/qhTqFM
2) Автоисправление ошибок - не знаю, правильно ли я тебя понял, получилось собрать в массив все правила с функциями, и пройтись по ним циклом, кода сильно меньше не стало, зато теперь нет однообразных вызовов preg_replace_callback. Смущает, что коллбеки эти у меня похожи, но отличаются по тому, что они должны возвращать вместо найденного совпадения -
>fixGrammar('/..../', function () {...});
эту подсказку я совсем не понял, как использовать, может как раз ты имел в виду какую-то универсальную функцию замены вместо кучи коллбеков, но я в ступоре, как это сделать. И как без массива обойтись вообще, если у меня на каждое слово разное правило. Вот что получилось http://ideone.com/jtrD4e
Перезапускал, файл лежит в корне сайта, рядом с индексом и он не txt. Да вот он https://www.dropbox.com/s/k873xkuzlc9g3vl/.htaccess?dl=0
А вот похоже то что опенсервер сгенерировал https://www.dropbox.com/s/5vi3tj4roit5bul/httpd.conf?dl=0
Пару лет назад пытался вручную все опставить и ничего не вышло, тогда я забил. Боюсь, что и сейчас меня это дело выбесит
Правильно это когда любая ошибка вызывает исключение, программа падает, программист ее исправляет.
Неправильно это когда PHP пишет запись в лог который никто не читает и продолжает выполнять скрипт дальше, разумеется выводя неправильные результаты. Но это не самое плохое. Некоторые примеры кода вообще ошибок не вызывают:
http://ideone.com/5fopIE
Если мы вместо массива кладем в переменную false то никакой ошибки не происходит. Нормально? Смотрите к чему это приводит на практике: есть неработающий код. Я трачу 10 минут времени на то чтобы найти и исправить баг, попутно удивляясь что никаких ошибок нет, а данные выводятся неверные. А если бы в PHP при такой ситуации вылетало исключение — мне бы не пришлось исправлять этот баг вообще так как тот разработчик что вписал неправильный код увидел бы ошибку и сам ее исправил. Я бы сэкономил 10 минут. И это только сегодня.
Неправильная обработка ошибок съедает ваше время, и я ее вижу во многих проектах на PHP. Вот то, что вы должны использовать:
error_reporting = E_ALL
превращение ошибок в исключения: http://php.net/manual/ru/class.errorexception.php (нормальные фреймворки вроде Slim или Symfony делают это за вас)
fail fast: http://habrahabr.ru/post/218325/
Алсо на сайте PHP этот баг висит с 2006 года, отписался там: https://bugs.php.net/bug.php?id=37676
Алсо, хак: если отключить яваскрипт, то ссылки в гугле открываются в том же окне, а не в новом.
> как иначе границу слова обозначить
Ты про \\b в курсе? Этот символ значит «граница слова», то есть место где с одной стороны стоит буква или цифра, а с другой не буква или цифра.
>многопоточность
Если я правильно понял, это способность программы (процесса) выполнять несколько задач одновременно (при многоядерных процессорах) за счет использования нескольких потоков (наборов инструкций, которые выполняются параллельно). Не знаю как еще сказать своими словами.
А что такое очередь? Попробовал погуглить, но для начинающих ничего нет. Из названия только могу догадаться, что mysql собирает запросы в очередь и выполняет по порядку (mysql однопоточный?).
>реализовать свой вариант мемкеш-демона
А?
Это тред для начинающих, не увлекайся. Если бы я мог написать сервер, я бы тут не сидел.
>\\b
Чукча не читатель, чукча писатель. Спасибо. А то прочитал старую статью про регекспы и успокоился, там такого не было.
Число просмотров 1 переменная. Среднее время между просмотрами сколько переменных?
Если не нужна точность, и просмотры равномерно распределены между сущностями, то почему бы и нет.
Равномерного распределения не бывает.
Ночью число падает почти до нуля, днем поднимается. В случае с объявлением, которое живет неделю там динамика еще сложнее. Теперь опиши алгоритм вычисления этого числа.
Я такой алгоритм где-то видел в дозиметрах где нужно было быстро считать количество попадающих частиц в детектор и БЫСТРО выдавать результат. Он работал лучше чем тупой счет попавших частиц за минуту.
делаю фотогалерею на yii2, прогружаю картинки с помощью pjax.
И вот такая ситуация, например:
Есть экшн, который отображает текущую выбранную картинку и стрелочки для перелистывания, как мне узнать предыдущий и следущий айди, чтобы повесить на эти самые стрелочки?(это чтобы передать айди в контроллер для прогрузки следующей, или предыдущей картинки)
чето не могу сообразить
Классы целесообразно использовать только когда предполагается работа с несколькими объектами этого класса, или даже тогда, когда объект будет всего один?
>скриншот кода на айдеоне
>Правильно ли я действую, или я совсем отшибленный
Я больше склоняюсь ко второму.
Ну и что тебя не устраивает? Если тебе не нравится копипаста, можешь упаковать это в функцию.
Да, кстати, у тебя там ошибка с индексами. В каждом массиве индексов меньше, чем чисел, которые прописаны в mt_rand.
Есть ветка. Шла параллельно мастеру. Мастер обновлялся, обновлялся, решил вмерджить его себе. Т.к. изменений много, без конфликтов не обошлось. я их хуёво порешал, и теперь, когда я хочу влить свою ветку в мастер, я не могу этого сделать из-за криво решенных конфликтов.
Нужно как-то откатить мою ветку в состояние до мерджа, сохранить мои коммиты, и вмерджить в нее мастер еще раз, грамотно решив конфликты. Как?
Аноны, у меня вопрос, проходил первое задание с урока с функциями из шапки. Если у одного из трёх банков, комиссия равна нулю, уместно ли писать просто значение "0" в аргумент? (getCreditTotal($creditSum, 1.03, 0, $payout, 0) Ведь дальше по формуле подсчета общей выплаты, в фунции, будут умножения на этот процент, просто тогда в формуле этой переменной просто не будет?
Ахуенно объяснил, ну и что такое config salt? Это просто рандомно генерированный набор символов, как токен что ли?
Еду узнавать на счёт удалённой работы на симфони. Какие то доработки и парсер ы сделать
Сколько денег просит, что бы не послали и не продешивить?
Сам я хуй учился по тредам, полгода в офисе работал, на симфони 3 месяца.
Ок, паста на тему архитектуры серверов, потоков, процессов. Большая паста получилась, но я думаю, она мне еще не раз пригодится, пусть будет.
==============
Процессор может выполнять параллельно столько потоков инструкций, сколько в нем ядер. На серверах например может стоять два 8-ядерных процессора. В древних компьютерах процессоры были однаядерные, а сейчас даже в смартфонах несколько ядер. Это по той причине, что наращивать скорость работы одного ядра уже не получается, приходится брать количеством.
Но даже на одном ядре операционная система имитирует параллельное выполнение, переключаясь между потоками инструкций. Ну к примеру пока одна программа заблокировалась в ожидании чтения данных с диска или прихода пакета из сети, операционная система начинает выполнять вторую. Ну и даже если программа не хочет отдавать процессор, по истечении выданного ей кванта времени процессор у нее отбирается и отдается другой программе, третьей, а потом может его отдадут назад первой. И так далее.
В общем ОС может выполнять программы как бы параллельно даже на одном ядре.
Теперь про потоки и процессы, что это и в чем разница? Процесс это отдельная программа, выполняющаяся в изолированной (от вмешательства других программ) области памяти. Если запущено 20 процессов значит каждый использует свою область памяти и они никак не влияют друг на друга. Если одна падает, другие продолжают работать.
В процессе может быть 1 или более потоков (threads, нитей). Их можно создавать на ходу и они могут завершаться. Меньше одного быть не может, если последний поток завершается, то и вся программа завершается тоже, и операционная система забирает выделенную память и другие ресурсы (например закрываются созданные программой сетевые соединения, снимаются блокировки с файлов и тд).
Создание нового потока это если проводить аналогии с PHP, вызов какой-то функции или include какого-то скрипта. То есть программа может взять функцию и запустить ее выполнение отдельным потоком параллельно с основным потоком. В PHP так не делают, PHP программы однопоточные, но так делают в других языках.
Если потоков в процессе несколько, то они все имеют доступ к одной и той же области памяти. То есть если это бы была программа на PHP то все потоки видят одни и те же переменные и функции.
Так как PHP программы однопоточные, то когда например несколько пользователей заходят на сайт, скрипты, генерирующие им страницу, работают каждый в своем отдельном процессе и никак не связаны друг с другом.
-------
Соответственно когда мы делаем веб-сервер, например Апач или нгинкс, или другой сервер, например сервер mysql, в общем любой сервер который принимает запросы и дает на них ответы, перед нами встает вопрос, каким способом реализовать обработку запросов: одним потоком синхронно, многими процессами с 1 потоком каждый, многими потоками внутри одного процесса, одним потоком асинхронно.
Одним потоком синхронно — это выглядит так: сервер запускается и ждет запросов от клиентов. Получает запрос, обрабатывает, генерирует ответ, отдает ответ клиенту, отсоединяется, ждет следующего. Заметь что такой сервер может обрабатывать параллельно только 1 запрос, в это время другие клиенты которые пытаются подсоединиться и отправить свой запрос, ждут. Если обработка запроса по каким-то причинам затянется например на минуту, все остальные будут ждать минуту.
Этот сервер не использует полностью ресурсы компьютера. К примеру, в процессе обработки запроса ему понадобилось прочитать файл с диска. Он вызывает соответствующий системный вызов, и блокируется до тех пор пока операционная система не прочитает данные с диска в оперативную память. Все это время процессор не используется (именно для обработки запросов), простаивает в ожидании диска. Аналогично если для обработки запроса нам понадобилось связаться с кем-то по сети (например с базой данных), мы блокируется до получения ответа.
Ну и наконец если программа-сервер падает из-за ошибки, то запросы больше никто не обрабатывает, и ее надо перезапускать (впрочем, это можно автоматизировать).
Такой сервер пишут разве что в учебных целях и он очень медленный. Он не использует процессор на 100%, памяти он тоже не много занимает. Это не преимущество, а недостаток, так как эти ресурсы проставивают, а не используются для обслуживания большего числа клиентов.
Если провести аналогию, то это огромный супермаркет с всего одной работающей кассой и очередью к ней. Согласись, этот супермаркет мог бы продавать больше?
Ок, паста на тему архитектуры серверов, потоков, процессов. Большая паста получилась, но я думаю, она мне еще не раз пригодится, пусть будет.
==============
Процессор может выполнять параллельно столько потоков инструкций, сколько в нем ядер. На серверах например может стоять два 8-ядерных процессора. В древних компьютерах процессоры были однаядерные, а сейчас даже в смартфонах несколько ядер. Это по той причине, что наращивать скорость работы одного ядра уже не получается, приходится брать количеством.
Но даже на одном ядре операционная система имитирует параллельное выполнение, переключаясь между потоками инструкций. Ну к примеру пока одна программа заблокировалась в ожидании чтения данных с диска или прихода пакета из сети, операционная система начинает выполнять вторую. Ну и даже если программа не хочет отдавать процессор, по истечении выданного ей кванта времени процессор у нее отбирается и отдается другой программе, третьей, а потом может его отдадут назад первой. И так далее.
В общем ОС может выполнять программы как бы параллельно даже на одном ядре.
Теперь про потоки и процессы, что это и в чем разница? Процесс это отдельная программа, выполняющаяся в изолированной (от вмешательства других программ) области памяти. Если запущено 20 процессов значит каждый использует свою область памяти и они никак не влияют друг на друга. Если одна падает, другие продолжают работать.
В процессе может быть 1 или более потоков (threads, нитей). Их можно создавать на ходу и они могут завершаться. Меньше одного быть не может, если последний поток завершается, то и вся программа завершается тоже, и операционная система забирает выделенную память и другие ресурсы (например закрываются созданные программой сетевые соединения, снимаются блокировки с файлов и тд).
Создание нового потока это если проводить аналогии с PHP, вызов какой-то функции или include какого-то скрипта. То есть программа может взять функцию и запустить ее выполнение отдельным потоком параллельно с основным потоком. В PHP так не делают, PHP программы однопоточные, но так делают в других языках.
Если потоков в процессе несколько, то они все имеют доступ к одной и той же области памяти. То есть если это бы была программа на PHP то все потоки видят одни и те же переменные и функции.
Так как PHP программы однопоточные, то когда например несколько пользователей заходят на сайт, скрипты, генерирующие им страницу, работают каждый в своем отдельном процессе и никак не связаны друг с другом.
-------
Соответственно когда мы делаем веб-сервер, например Апач или нгинкс, или другой сервер, например сервер mysql, в общем любой сервер который принимает запросы и дает на них ответы, перед нами встает вопрос, каким способом реализовать обработку запросов: одним потоком синхронно, многими процессами с 1 потоком каждый, многими потоками внутри одного процесса, одним потоком асинхронно.
Одним потоком синхронно — это выглядит так: сервер запускается и ждет запросов от клиентов. Получает запрос, обрабатывает, генерирует ответ, отдает ответ клиенту, отсоединяется, ждет следующего. Заметь что такой сервер может обрабатывать параллельно только 1 запрос, в это время другие клиенты которые пытаются подсоединиться и отправить свой запрос, ждут. Если обработка запроса по каким-то причинам затянется например на минуту, все остальные будут ждать минуту.
Этот сервер не использует полностью ресурсы компьютера. К примеру, в процессе обработки запроса ему понадобилось прочитать файл с диска. Он вызывает соответствующий системный вызов, и блокируется до тех пор пока операционная система не прочитает данные с диска в оперативную память. Все это время процессор не используется (именно для обработки запросов), простаивает в ожидании диска. Аналогично если для обработки запроса нам понадобилось связаться с кем-то по сети (например с базой данных), мы блокируется до получения ответа.
Ну и наконец если программа-сервер падает из-за ошибки, то запросы больше никто не обрабатывает, и ее надо перезапускать (впрочем, это можно автоматизировать).
Такой сервер пишут разве что в учебных целях и он очень медленный. Он не использует процессор на 100%, памяти он тоже не много занимает. Это не преимущество, а недостаток, так как эти ресурсы проставивают, а не используются для обслуживания большего числа клиентов.
Если провести аналогию, то это огромный супермаркет с всего одной работающей кассой и очередью к ней. Согласись, этот супермаркет мог бы продавать больше?
Половину задачи сделал. Как сделать условие? Можешь брать случайное число, записывать его в переменную, потом во второй строке генерировать ещё число, сравнивать с первым, чтобы не совпало ( конструкция if (условие) {действие} )...,
Но как то неудобно, правда? Есть функция , возвращающая длину массива, её можно использовать, чтобы знать в каком диапазоне генерировать случайное число.(вдруг в массивах будет сделаешь больше/меньше слов). Есть функция, которая удаляет из массива элемент.
В итоге можно так - выбрали слово из массива, вставили в строку, удалили из массива слово. Потом во второй строке снова выбрали из массива элемент ( старого слова там уже нет, повтора точно не будет) . и так далее.
Наверное можно ещё изящнее, я не знаю, я нубас.
Много однопоточных процессов
Что тут можно улучшить? Очевидно мы можем запустить несколько независимых воркеров - рабочих процессов (каждый из них однопоточен). В зависимости от версии операционной системы, нам может понадобиться также процесс-менеджер-балансировщик, который будет принимать запросы от клиентов, выбирать случайного свободного рабочего и передавать их ему (это называется балансировка запросов. В новых линуксах этим может заниматься ядро ОС и процесс-менеджер не обязателен. Также балансировкой могут заниматься клиенты, например выбирая случайно к кому из рабочих обратиться, но тогда есть риск наткнуться на занятого или упавшего из-за ошибки рабочего).
В такой системе все гораздо лучше. Если один рабочий застрял на обработке запроса, например ждет данных с диска, или из сети, то другие рабочие продолжают обрабатывать запросы и клиенты не страдают. Более того, операционная система передает процессор тем, кому он нужен и если число рабочих больше числа ядер то процессор оказывается задействован почти на 100%. И это отлично: мы для того и покупали процессор чтобы он выполнял вычисления а не простаивал зря.
Если рабочий упал с ошибкой, процесс-менеджер запустит новый рабочий процесс на замену ему.
Тут конечно надо еще подбирать число рабочих процессов: если слишком мало, то они могут не на 100% занимать процессор (например в какой-то момент они все могут ждать чего-то), если слишком много то операционная система будет постоянно переключаться между ними, а это тоже не бесплатно. Десятки процессов это нормально, на тысячах время процессора может начать уходить именно на переключения.
Также, чем больше процессов тем больше памяти надо, ведь каждый использует свою отдельную область.
По такой схеме работает веб-сервер Апач в режиме mpm-prefork. Он создает определенное число рабочих процессов (пул воркеров - worker pool) и даже умеет создавать новые при повышении нагрузки (или при падении рабочего из-за ошибки) и убивать лишние при снижении. Аналогично работает PHP в сочетании с Апачом: каждый скрипт работает в отдельном процессе.
Недостатки: как я написал выше, такая схема подходит для случаев когда параллельно обрабатываются десятки, может сотни запросов, но не более. Это значит что она подходит для случая когда обработать запрос можно быстро, в этом случае за секунду один рабочий успеет обработать много запросов.
Плюс: трудно сломать весь сервер плохо написанным скриптом, так как умрет/зависнет только процесс с этим скриптом.
Допустим PHP-скрипт генерирует главную за 200 мс (вполне обычная цифра).
1 рабочий обслуживает 5 запр/сек.
10 рабочих = 50 запр/сек
100 рабочих = 500 запр/сек.
1000 рабочих - тяжело переключаться между ними, много памяти расходуется, так что 5 000 мы можем и не увидеть, а увидим например 1000-3000.
Ну, 500 запросов в секунду это довольно нагруженный сайт с миллионом посетителей в день. Если один сервер такое тянет то это отличное приложение. На практике 500 может и не выйти, так как 100 параллельно работающих скриптов будут создавать 100 параллельных соединений с базой, делать запросы, она может не успеть им всем ответить, и тд, но это ведь не вина Апача, правда? 500 это скорее высокая планка к которой стоит стремиться.
В общем для веба она подходит, если это не совсем хайлоад.
Она не подходит для случаев когда у нас много параллельных слабоактивных соединений. Например у нас сервер который раздает файлы с диска большому числу клиентов на не очень быстрых каналах. Ну к примеру отдача одной картинки занимает 3 секунды (так как у клиента медленный интернет), картинка весит 100 Кб, это значит что:
1 рабочий может обслужить 1/3 запроса в секунду, трафик 33 кб/с (это значит что ты зря заплатил хостеру за гигабитный канал, он используется менее чем на 0.1% ).
10 рабочих - 10/3 = 3.33 запроса в секунду, трафик 330 кб/с
100 рабочих - 33.3 запроса в секунду, трафик 3.3 Мб/с
1000 рабочих - 333 запр/сек., 33Мбита/с, но они будут есть много памяти и операционная система замучается переключать процессор между ними
10 000 рабочих - 3333 запрс/сек мы не получим, получим условно говоря 1000 и 100 Мбит/с.
Смотри, какие печальные цифры: наш Апач на раздаче картинок загружает гигабитный канал лишь на 10% (значит нам надо купить еще 9 дорогущих серверов чтобы загрузить его полностью), жрет тонны памяти (10 000 рабочих × 10 Мб каждый = 100 Гб ), процессор расходуется в основном на переключения контекста, быстрые клиенты не могут скачивать файл на полной скорости.
Сравнить многопроцессный сервер можно с супермаркетом где много касс: покупателей обслуживают быстро, но расходы на зарплату высокие.
Один многопоточный процесс
Тут идея такая: делаем все так же как и в предыдущей схеме, но запускаем не много процесссов, а много потоков в одном процессе, работающих в общей области памяти.
Плюсы: потребление памяти чуть ниже, так как какие-то общие данные можно хранить в одном экземпляре, а не по копии в каждом процессе. Переключение между потоками в процессе может быть чуть быстрее чем между процессами.
Минусы: если один поток сойдет с ума и что-то повредит в общей памяти, все остальные потоки от этого пострадают. На практике это значит что они тоже падают. ну или хуже, один поток может записать что-то не то в данные другого потока и тот вернет неправильный результат клиенту. То есть код рабочего потока должен быть очень надежным, и его надо тщательно отлаживать.
По такой схеме работает mysql: он создает пул потоков, и когда подсоединяется новый клиент, выделяет ему свободный поток, который принмимает и обрабатывает запросы. Также, многие службы и фоновые программы под Windows используют потоки. Апач под Windows тоже может использовать эту схему.
Проблему раздачи статики, описанную выше, это не решает.
Как это описать аналогией, я не знаю.
Много однопоточных процессов
Что тут можно улучшить? Очевидно мы можем запустить несколько независимых воркеров - рабочих процессов (каждый из них однопоточен). В зависимости от версии операционной системы, нам может понадобиться также процесс-менеджер-балансировщик, который будет принимать запросы от клиентов, выбирать случайного свободного рабочего и передавать их ему (это называется балансировка запросов. В новых линуксах этим может заниматься ядро ОС и процесс-менеджер не обязателен. Также балансировкой могут заниматься клиенты, например выбирая случайно к кому из рабочих обратиться, но тогда есть риск наткнуться на занятого или упавшего из-за ошибки рабочего).
В такой системе все гораздо лучше. Если один рабочий застрял на обработке запроса, например ждет данных с диска, или из сети, то другие рабочие продолжают обрабатывать запросы и клиенты не страдают. Более того, операционная система передает процессор тем, кому он нужен и если число рабочих больше числа ядер то процессор оказывается задействован почти на 100%. И это отлично: мы для того и покупали процессор чтобы он выполнял вычисления а не простаивал зря.
Если рабочий упал с ошибкой, процесс-менеджер запустит новый рабочий процесс на замену ему.
Тут конечно надо еще подбирать число рабочих процессов: если слишком мало, то они могут не на 100% занимать процессор (например в какой-то момент они все могут ждать чего-то), если слишком много то операционная система будет постоянно переключаться между ними, а это тоже не бесплатно. Десятки процессов это нормально, на тысячах время процессора может начать уходить именно на переключения.
Также, чем больше процессов тем больше памяти надо, ведь каждый использует свою отдельную область.
По такой схеме работает веб-сервер Апач в режиме mpm-prefork. Он создает определенное число рабочих процессов (пул воркеров - worker pool) и даже умеет создавать новые при повышении нагрузки (или при падении рабочего из-за ошибки) и убивать лишние при снижении. Аналогично работает PHP в сочетании с Апачом: каждый скрипт работает в отдельном процессе.
Недостатки: как я написал выше, такая схема подходит для случаев когда параллельно обрабатываются десятки, может сотни запросов, но не более. Это значит что она подходит для случая когда обработать запрос можно быстро, в этом случае за секунду один рабочий успеет обработать много запросов.
Плюс: трудно сломать весь сервер плохо написанным скриптом, так как умрет/зависнет только процесс с этим скриптом.
Допустим PHP-скрипт генерирует главную за 200 мс (вполне обычная цифра).
1 рабочий обслуживает 5 запр/сек.
10 рабочих = 50 запр/сек
100 рабочих = 500 запр/сек.
1000 рабочих - тяжело переключаться между ними, много памяти расходуется, так что 5 000 мы можем и не увидеть, а увидим например 1000-3000.
Ну, 500 запросов в секунду это довольно нагруженный сайт с миллионом посетителей в день. Если один сервер такое тянет то это отличное приложение. На практике 500 может и не выйти, так как 100 параллельно работающих скриптов будут создавать 100 параллельных соединений с базой, делать запросы, она может не успеть им всем ответить, и тд, но это ведь не вина Апача, правда? 500 это скорее высокая планка к которой стоит стремиться.
В общем для веба она подходит, если это не совсем хайлоад.
Она не подходит для случаев когда у нас много параллельных слабоактивных соединений. Например у нас сервер который раздает файлы с диска большому числу клиентов на не очень быстрых каналах. Ну к примеру отдача одной картинки занимает 3 секунды (так как у клиента медленный интернет), картинка весит 100 Кб, это значит что:
1 рабочий может обслужить 1/3 запроса в секунду, трафик 33 кб/с (это значит что ты зря заплатил хостеру за гигабитный канал, он используется менее чем на 0.1% ).
10 рабочих - 10/3 = 3.33 запроса в секунду, трафик 330 кб/с
100 рабочих - 33.3 запроса в секунду, трафик 3.3 Мб/с
1000 рабочих - 333 запр/сек., 33Мбита/с, но они будут есть много памяти и операционная система замучается переключать процессор между ними
10 000 рабочих - 3333 запрс/сек мы не получим, получим условно говоря 1000 и 100 Мбит/с.
Смотри, какие печальные цифры: наш Апач на раздаче картинок загружает гигабитный канал лишь на 10% (значит нам надо купить еще 9 дорогущих серверов чтобы загрузить его полностью), жрет тонны памяти (10 000 рабочих × 10 Мб каждый = 100 Гб ), процессор расходуется в основном на переключения контекста, быстрые клиенты не могут скачивать файл на полной скорости.
Сравнить многопроцессный сервер можно с супермаркетом где много касс: покупателей обслуживают быстро, но расходы на зарплату высокие.
Один многопоточный процесс
Тут идея такая: делаем все так же как и в предыдущей схеме, но запускаем не много процесссов, а много потоков в одном процессе, работающих в общей области памяти.
Плюсы: потребление памяти чуть ниже, так как какие-то общие данные можно хранить в одном экземпляре, а не по копии в каждом процессе. Переключение между потоками в процессе может быть чуть быстрее чем между процессами.
Минусы: если один поток сойдет с ума и что-то повредит в общей памяти, все остальные потоки от этого пострадают. На практике это значит что они тоже падают. ну или хуже, один поток может записать что-то не то в данные другого потока и тот вернет неправильный результат клиенту. То есть код рабочего потока должен быть очень надежным, и его надо тщательно отлаживать.
По такой схеме работает mysql: он создает пул потоков, и когда подсоединяется новый клиент, выделяет ему свободный поток, который принмимает и обрабатывает запросы. Также, многие службы и фоновые программы под Windows используют потоки. Апач под Windows тоже может использовать эту схему.
Проблему раздачи статики, описанную выше, это не решает.
Как это описать аналогией, я не знаю.
Один асинхронный процесс с 1 потоком
Есть еще одна интересная архитектура. Здесь используется один поток, но используются неблокирующие (асинхронные) обращения к внешним ресурсам вроде файлов или сети. Все описанные выше архитектуры используют блокирующие вызовы. Это значит что когда поток просит ОС прочитать файл с диска или отправить пакет по сети, или хочет получить пакет из сети, он делает системный вызов и блокируется до тех пор пока не будут получены данные.
Асинхронный вызов работает по другому: поток просит ОС начать операцию ввода-вывода, и не блокируется, а продолжает выполняться, а позже может спрашивать у ОС каков статус операции и пришли ли данные. Очевидно что код для работы с асинхронными вызовами будет сложнее. Обычно он строится на так называемом событийном программировании (event-driven programming). То есть внутри программы ведется список выполняющихся операций и список функций которые надо вызвать по их завершении. Например, когда придет новый запрос от клиента, вызвать функцию processRequest. Когда прочитается в память файл X, вызвать функцию Y.
Затем поток обращается к ОС с просьбой сообщать ему о завершении отсележиваемых операций, блокируется (так как делать больше нечего), как только что-то происходит, ОС его разблокирует, и дает ему информацию, какая именно операция завершилась и с каким результатом. Поток вызывает функцию-обработчик, удаляет эту перацию из списка и снова обращается к ОС в ожидании новых событий. При этом функция-обработчик может запускать новые операции. Ну например функция обработки запроса клиента может запустить чтение файла с диска (или начать отправлять запрос в базу), указав функцию которую надо будет вызвать по завершении и которая продолжит обработку запроса.
Сложно? Наверно. Но по сути это аналог предыдущей схемы, так называемые «зеленые потоки». Только если в предыдущей схеме переключением потоков занималась ОС, тут сама программа имитирует многопоточную работу, вызывая разные функции по очереди. Разница только в том, что с точки зрения ОС тут работает 1 поток, значит никаких переключений контекста делать не надо. На большом числе обрабатываемых запросов выгода получается огромная.
Минусы: обработчики должны работать быстро и не имеют права блокироваться (так как у нас 1 поток и все остальные будут его ждать). Блокироваться имеет право только основная функция, которая получает события от ОС. Так как на высоконагруженном сервере блокировку может вызвать даже операция выделения памяти, часто приходится применять разные трюки, например выделять всю нужную память при старте сервера.
Код надо писать в асинхронной манере.
Если происходит ошибка, то падает весь сервер, так как он весь в одном потоке. Все необработанные запросы теряются.
Если какой-то обработчик выделил память и забыл ее освободить, никто уже ее не освободит. Будет утечка памяти, если они повторяются то процесс рано или поздно займет всю доступную память и будет убит. В случае с процессами-рабочими проблема менее вероятна, так как рабочих можно периодически прибивать, освобождая память.
То есть требуется высокий уровень грамотности и ответственности разработчика.
Плюсы: расходы памяти на один зеленый тред могут быть в сотни раз меньше чем на поток/процесс. Также нет накладных расходов на переключение между ними. Это дает нам возможность иметь сотни тысяч или даже миллион зеленых потоков, хватило бы памяти (естественно при условии что каждый поток не использует много памяти). Используется 100% процессора.
По такой схеме работает веб-сервер nginx, кеш memcached, хранилище redis, платформа Node.JS (реализует веб-сервер на яваскрипте).
За счет такой схемы сервер nginx при раздаче статики способен поддерживать десяток тысяч медленных соединений, эффективно используя процессор, память и загружая вышеупомянутый гигабитный канал.
Заметь что все эти сервера (кроме node.js) не содержат в себе сложной логики, которая может долго выполняться и замедлять обработку запросов. nginx просто раздает файлы с диска по сети или проксирует данные из одного соединения в другое, мемкеш просто кладет небольшие кусочки данных в память или отдает их в сеть. А вот в случае с Node.js все зависит от умения разработчика, и там получить тормоза, утечки памяти довлоьно легко.
Эта же схема будет эффективна, если ты например делаешь чат где огромное число людей подсоединяются к серверу, отсылают сообщения и видят сообщения других. В случае с процессами-рабочими нам бы пришлось либо выделить по одному процессу на каждого пользователя (и мы быстро упремся в ограничения), либо каждому клиенту вместо постоянного соединения с сервером делать периодический опрос, не появилось ли чего нового в чате, что выльется в шквал запросов, на большинство из которых будет ответ «ничего нового не произошло».
Эта архитектура стала ответом на проблему C10K - «как поддерживать 10 000 параллельных соединений».
Для PHP есть фреймворк - ReactPHP http://reactphp.org/ который позволяет на PHP реализовать асинхронную обработку большого числа запросов. Если ты хочешь помучаться с написанием правильного асинхронного кода, не прощающего ошибки.
Ссылочки для чтения:
http://www.moodle.ipm.kstu.ru/mod/page/view.php?id=49
https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_10000_%D1%81%D0%BE%D0%B5%D0%B4%D0%B8%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9
http://uinc.ru/articles/34/ (может быть сложно все понять)
Один асинхронный процесс с 1 потоком
Есть еще одна интересная архитектура. Здесь используется один поток, но используются неблокирующие (асинхронные) обращения к внешним ресурсам вроде файлов или сети. Все описанные выше архитектуры используют блокирующие вызовы. Это значит что когда поток просит ОС прочитать файл с диска или отправить пакет по сети, или хочет получить пакет из сети, он делает системный вызов и блокируется до тех пор пока не будут получены данные.
Асинхронный вызов работает по другому: поток просит ОС начать операцию ввода-вывода, и не блокируется, а продолжает выполняться, а позже может спрашивать у ОС каков статус операции и пришли ли данные. Очевидно что код для работы с асинхронными вызовами будет сложнее. Обычно он строится на так называемом событийном программировании (event-driven programming). То есть внутри программы ведется список выполняющихся операций и список функций которые надо вызвать по их завершении. Например, когда придет новый запрос от клиента, вызвать функцию processRequest. Когда прочитается в память файл X, вызвать функцию Y.
Затем поток обращается к ОС с просьбой сообщать ему о завершении отсележиваемых операций, блокируется (так как делать больше нечего), как только что-то происходит, ОС его разблокирует, и дает ему информацию, какая именно операция завершилась и с каким результатом. Поток вызывает функцию-обработчик, удаляет эту перацию из списка и снова обращается к ОС в ожидании новых событий. При этом функция-обработчик может запускать новые операции. Ну например функция обработки запроса клиента может запустить чтение файла с диска (или начать отправлять запрос в базу), указав функцию которую надо будет вызвать по завершении и которая продолжит обработку запроса.
Сложно? Наверно. Но по сути это аналог предыдущей схемы, так называемые «зеленые потоки». Только если в предыдущей схеме переключением потоков занималась ОС, тут сама программа имитирует многопоточную работу, вызывая разные функции по очереди. Разница только в том, что с точки зрения ОС тут работает 1 поток, значит никаких переключений контекста делать не надо. На большом числе обрабатываемых запросов выгода получается огромная.
Минусы: обработчики должны работать быстро и не имеют права блокироваться (так как у нас 1 поток и все остальные будут его ждать). Блокироваться имеет право только основная функция, которая получает события от ОС. Так как на высоконагруженном сервере блокировку может вызвать даже операция выделения памяти, часто приходится применять разные трюки, например выделять всю нужную память при старте сервера.
Код надо писать в асинхронной манере.
Если происходит ошибка, то падает весь сервер, так как он весь в одном потоке. Все необработанные запросы теряются.
Если какой-то обработчик выделил память и забыл ее освободить, никто уже ее не освободит. Будет утечка памяти, если они повторяются то процесс рано или поздно займет всю доступную память и будет убит. В случае с процессами-рабочими проблема менее вероятна, так как рабочих можно периодически прибивать, освобождая память.
То есть требуется высокий уровень грамотности и ответственности разработчика.
Плюсы: расходы памяти на один зеленый тред могут быть в сотни раз меньше чем на поток/процесс. Также нет накладных расходов на переключение между ними. Это дает нам возможность иметь сотни тысяч или даже миллион зеленых потоков, хватило бы памяти (естественно при условии что каждый поток не использует много памяти). Используется 100% процессора.
По такой схеме работает веб-сервер nginx, кеш memcached, хранилище redis, платформа Node.JS (реализует веб-сервер на яваскрипте).
За счет такой схемы сервер nginx при раздаче статики способен поддерживать десяток тысяч медленных соединений, эффективно используя процессор, память и загружая вышеупомянутый гигабитный канал.
Заметь что все эти сервера (кроме node.js) не содержат в себе сложной логики, которая может долго выполняться и замедлять обработку запросов. nginx просто раздает файлы с диска по сети или проксирует данные из одного соединения в другое, мемкеш просто кладет небольшие кусочки данных в память или отдает их в сеть. А вот в случае с Node.js все зависит от умения разработчика, и там получить тормоза, утечки памяти довлоьно легко.
Эта же схема будет эффективна, если ты например делаешь чат где огромное число людей подсоединяются к серверу, отсылают сообщения и видят сообщения других. В случае с процессами-рабочими нам бы пришлось либо выделить по одному процессу на каждого пользователя (и мы быстро упремся в ограничения), либо каждому клиенту вместо постоянного соединения с сервером делать периодический опрос, не появилось ли чего нового в чате, что выльется в шквал запросов, на большинство из которых будет ответ «ничего нового не произошло».
Эта архитектура стала ответом на проблему C10K - «как поддерживать 10 000 параллельных соединений».
Для PHP есть фреймворк - ReactPHP http://reactphp.org/ который позволяет на PHP реализовать асинхронную обработку большого числа запросов. Если ты хочешь помучаться с написанием правильного асинхронного кода, не прощающего ошибки.
Ссылочки для чтения:
http://www.moodle.ipm.kstu.ru/mod/page/view.php?id=49
https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_10000_%D1%81%D0%BE%D0%B5%D0%B4%D0%B8%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9
http://uinc.ru/articles/34/ (может быть сложно все понять)
Очередь тут это структура данных, в которую с одного конца можно класть данные, а с другого снимать:
https://ru.wikipedia.org/wiki/%D0%9E%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D1%8C_(%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://informatics.mccme.ru/mod/book/view.php?id=580
http://neerc.secna.ru/Algor/algo_base_ds_lists.html
Реализовать очередь можно по-разному: таблица в базе данных (как у мейл ру), список в редисе или даже отдельный сервер очередей вроде ZeroMQ, RabbitMQ.
Соответственно идея в том что новые посещения кладутся с одной стороны очереди, а скрипт-сборщик периодически вынимает их с другой. Скрипты друг другу не мешают, данные не теряются.
Неудобно с планшета глядеть,
if ($sum<0) break;
Не очень полезная конструкция,ничего не делает, а место занимает.
Лучше в своём условии точно задачей границы с помощью && :
if ($sum>0 && $sum <400)
*задай
Спасибо.
Ну тогда соси хуи, зашквар у него,блядь. Тут в соседнем треде задача про матрицы висит, сделай на пхп,
Спасибо.
Вот я уже не первый раз такое слышу. Верстку почему-то очень не любят те, кто HTML/CSS не знает. Естественно не зная этих языков верстать не получится.
Прошел бы наши задачки из ОП поста и уверен таких проблем бы не было. Они сопровождаются в том числе полезными ссылкми.
Ну а те кто смотрят даунские видеоуроки, автор которых сам ничего не знает, или учат CSS на кодеакадеми, такой результат и получают. почему-то распространено убеждение что HTML/CSS это какие-то неполноценные технологии и их изучать не надо, и так все понятно. Нет, надо.
Так что советую открыть ОП-пост и пройти все задачки по HTML там, и показать решения. Может быть узнаешь много нового.
По JS, DOM, jQuery задания у нас тоже есть.
Ты надеюсь в командной строке git pull делал, а не в графическом интерфейсе? ибо ГУИ можно пользоваться только если знаешь гит на 5 с плюсом, иначе через него легко что-нибудь сломать.
Ты ведь не закоммитил пока состояние с конфликтом? Тогда можно все сбросить через
git reset --hard HEAD
После этого снова делаешь merge и вдумчиво решаешь конфликты, вникая в каждый слуай.
Для разрешения конфликтов нужна программа умеющая 3-way merge. Я пользуюсь наверно самой неудобной — KDiff (или как-то так, не помню точно). Она пытается помочь и мерджит код, разрывая функции на куски и склеивая в произвольном порядке.
Но в других GUI программах для гита есть более удобные средства. Главное, что это должно быть 3way merge, то есть отображается 3 версии файла сразу.
Да, разрешение конфликтов это как раз то место где нужен хороший GUI-инструмент, а не обычный текстовый редактор, это путь в гроб.
Ну и надо смотреть на изменения и думать, как их совместить. Не понимая что и зачем там изменно, ничего не получится.
Ну и еще надо чаще мерджиться чтобы такого не было. Это ты наверно уже понял и так.
>>555383
Цыц, не ругайся.
>>555364
Нет требования чтобы слова не повторялись, потому можно не удалять. Но если тебе хочется именно без повторов то обрати внимание на array_rand() - она выбирает из массива указанное число ключей случайно без повторов (она вообще хорошо подходит тут). Подробности в мануале.
>>555354
Не ругайся.
>>555359
Задачи из урока «повторим» в нашем учебнике решал уже? Там почти все на массивы.
>>555350
У меня есть выделить только код копируется только код. Хромиум.
>>555349
> $paymentTotal = $paymentTotal + $creditSum;
> $creditSum = ( $creditSum x $percent )
Это повторяется 2 раза, убери повторы
> if ($creditSum < 0) {
Это вряд ли выполнится. Логично проверять достижение нуля после выплаты части долга, а не после начисления комиссий и процентов.
>>555341
Можно конечно.
> Ведь дальше по формуле подсчета общей выплаты, в фунции, будут умножения на этот процент, просто тогда в формуле этой переменной просто не будет?
Если умножить на ноль то ноль наверно и получится? А вот если прибавлять то другое дело.
>>555340
Коммиты в гите не исчезают, если не лезть руками в папку .git и не делать rebase, так что я бы не паниковал.
>>555323
Вообще мне иногда скриншот удобен так как не надо открывать новую вкладку и ждать ideone.
В идеале конечно лучше иметь и скриншот и ссылку :)
>>555322
> [mt_rand(0,6)]
Надо цифры не считать руками, а автоматически, а то с ума сойти можно. Как это проверять? Я не хочу пересчитывать все.
Ты ж программист, автоматизируй.
чтобы не было повторов, нужен array_rand
>>555383
Цыц, не ругайся.
>>555364
Нет требования чтобы слова не повторялись, потому можно не удалять. Но если тебе хочется именно без повторов то обрати внимание на array_rand() - она выбирает из массива указанное число ключей случайно без повторов (она вообще хорошо подходит тут). Подробности в мануале.
>>555354
Не ругайся.
>>555359
Задачи из урока «повторим» в нашем учебнике решал уже? Там почти все на массивы.
>>555350
У меня есть выделить только код копируется только код. Хромиум.
>>555349
> $paymentTotal = $paymentTotal + $creditSum;
> $creditSum = ( $creditSum x $percent )
Это повторяется 2 раза, убери повторы
> if ($creditSum < 0) {
Это вряд ли выполнится. Логично проверять достижение нуля после выплаты части долга, а не после начисления комиссий и процентов.
>>555341
Можно конечно.
> Ведь дальше по формуле подсчета общей выплаты, в фунции, будут умножения на этот процент, просто тогда в формуле этой переменной просто не будет?
Если умножить на ноль то ноль наверно и получится? А вот если прибавлять то другое дело.
>>555340
Коммиты в гите не исчезают, если не лезть руками в папку .git и не делать rebase, так что я бы не паниковал.
>>555323
Вообще мне иногда скриншот удобен так как не надо открывать новую вкладку и ждать ideone.
В идеале конечно лучше иметь и скриншот и ссылку :)
>>555322
> [mt_rand(0,6)]
Надо цифры не считать руками, а автоматически, а то с ума сойти можно. Как это проверять? Я не хочу пересчитывать все.
Ты ж программист, автоматизируй.
чтобы не было повторов, нужен array_rand
Важно не сколько будет объектов, а является ли что-то отдельной сущностью требующей отдельного класса.
Вот паста (на примере задачи про вектор):
--------------
Когда ты решаешь задачу на ООП, ты должен ответить на вопросы:
— какие есть сущности, для которых мы сделаем классы? (Сотрудник и Департамент)
— какие у них есть свойства (у Сотрудника есть ранг, базовая ставка, профессия, является ли боссом). Потребление кофе или зарплата не являются свойствами так как они вычисляются из других свойств и хранить их не надо.
— что мы хотим от них получить (какие у них должны быть методы). Например мы хотим узнать сколько сотрудник заработал или сколько он пьет кофе. От департамента мы наверно хотим получить сколько всего выпито кофе и заплачено денег.
— как сущности связаны? Очевидно, Сотрудник работает в каком-то Департаменте.
--------------
>>555288
А при чем тут pjax? Как я понимаю он реализует ту же логику что и обычный переход по ссылкам и ссылки без него формируются так же.
Также, по моему он в принципе не подходит для переключения картинок. Во-первых, он не для этого, во-вторых он загрязняет историю навигации, в-третьих, это оверкилл, есть сотни плагинов специально для фотогалерей.
Ну и мое мнение, галереи на яваскрипте дрянь с точки зрения удобства пользования, вот смотри как делают галереи нормальные люди: http://www.tema.ru/travel/spb.2011.10/
Чтобы получить следующую картинку, достаточно сделать запрос
WHERE x > ? ORDER BY x LIMIT 1
Где x это поле по которому отсортированы картинки.
В твоем случае тебе наверно логичнее получить полный список фотграфий в галерее, закодировать в JSON и внедрить в тело страницы. А скрипт на клиенте пусть берет оттуда данные и переключает картинки.
Ну и надеюсь ты выучил JS/DOM/jQery перед тем как за это браться .
Важно не сколько будет объектов, а является ли что-то отдельной сущностью требующей отдельного класса.
Вот паста (на примере задачи про вектор):
--------------
Когда ты решаешь задачу на ООП, ты должен ответить на вопросы:
— какие есть сущности, для которых мы сделаем классы? (Сотрудник и Департамент)
— какие у них есть свойства (у Сотрудника есть ранг, базовая ставка, профессия, является ли боссом). Потребление кофе или зарплата не являются свойствами так как они вычисляются из других свойств и хранить их не надо.
— что мы хотим от них получить (какие у них должны быть методы). Например мы хотим узнать сколько сотрудник заработал или сколько он пьет кофе. От департамента мы наверно хотим получить сколько всего выпито кофе и заплачено денег.
— как сущности связаны? Очевидно, Сотрудник работает в каком-то Департаменте.
--------------
>>555288
А при чем тут pjax? Как я понимаю он реализует ту же логику что и обычный переход по ссылкам и ссылки без него формируются так же.
Также, по моему он в принципе не подходит для переключения картинок. Во-первых, он не для этого, во-вторых он загрязняет историю навигации, в-третьих, это оверкилл, есть сотни плагинов специально для фотогалерей.
Ну и мое мнение, галереи на яваскрипте дрянь с точки зрения удобства пользования, вот смотри как делают галереи нормальные люди: http://www.tema.ru/travel/spb.2011.10/
Чтобы получить следующую картинку, достаточно сделать запрос
WHERE x > ? ORDER BY x LIMIT 1
Где x это поле по которому отсортированы картинки.
В твоем случае тебе наверно логичнее получить полный список фотграфий в галерее, закодировать в JSON и внедрить в тело страницы. А скрипт на клиенте пусть берет оттуда данные и переключает картинки.
Ну и надеюсь ты выучил JS/DOM/jQery перед тем как за это браться .
Тогда надо записывать выпавший рандом в переменную и делать unset для массива с тем же индексом.
А погрешность какая? Это используется только для оценки есть превышение определенного порога (так что тебе не стоит ждать окончания подсчета) или нет.
Анон, не разбираешься в статистике - лучше спроси у тех кто разбиаретя. Или напиши программу имитирующую посещения и посмотри что она насчитает.
>>555265
Если не нужна точность, почему бы сразу не вписать в HTML коде число 100500?
>>555258
Что такое среднее? чтобы его посчитать надо сделать N измерений. Кстати это N ты не написал, по скольки числам среднее?
>>555155
Может тогда в поддержку open server стоит писать? htaccess верный.
Алсо,
> c:/openserver/modules/php/PHP-5.3
Ты представляешь насколько ты древний PHP используешь? Хотя бы до 5.4 или 5.5 надо бы обновиться. Ну спасибо что не 5.2 где анонимных функций не было.
В логах сервера ничего интересного нет?
В общем тебе надо либо разобраться как работает и устроен этот опен сервер либо снести его и все поставить с нуля.
А погрешность какая? Это используется только для оценки есть превышение определенного порога (так что тебе не стоит ждать окончания подсчета) или нет.
Анон, не разбираешься в статистике - лучше спроси у тех кто разбиаретя. Или напиши программу имитирующую посещения и посмотри что она насчитает.
>>555265
Если не нужна точность, почему бы сразу не вписать в HTML коде число 100500?
>>555258
Что такое среднее? чтобы его посчитать надо сделать N измерений. Кстати это N ты не написал, по скольки числам среднее?
>>555155
Может тогда в поддержку open server стоит писать? htaccess верный.
Алсо,
> c:/openserver/modules/php/PHP-5.3
Ты представляешь насколько ты древний PHP используешь? Хотя бы до 5.4 или 5.5 надо бы обновиться. Ну спасибо что не 5.2 где анонимных функций не было.
В логах сервера ничего интересного нет?
В общем тебе надо либо разобраться как работает и устроен этот опен сервер либо снести его и все поставить с нуля.
А чем он не подходит?
Идеально же. А засорение истории это то, что мне нужно, там красивые ссылочки, люди будут иметь возможность нажать кнопочку ПОДЕЛИТЬСЯ именно этой картинкой, на которую приведет ссылка
Спасибо за запрос, это то, что нужно
А почему ты просишь сделать несколько вещей: и работу с базой, и с формами, и хеширование? Ты не умеешь с базой работать? Тогда пример кода тебе не поможет.
Если у тебя нехватка базовых знаний, то почитай сначала про работу с формами, мой урок про соль и хеширование, еще какие-нибудь статьи на тему.
>>555144
> (?:а|но)[^а-яё]
границу слова можно обозначить как \\b
Это описано вроде ведь в моем уроке по регуляркам? посмотри, может ты еще что-то не знал?
В остальном верно.
> Автоисправление ошибок - не знаю, правильно ли я тебя понял
А код где? Не написан? Трудно тогда комментировать.
> эту подсказку я совсем не понял, как использовать,
Массив + цикл можно заменить на несколько вызовов функции, ну например:
$data = [
[1,2],
[3,4]
];
foreach ($data ..)
...
можно заменить на
doSomething(1, 2);
doSomething(3, 4);
Если элементов не много и читабельность получается лучше, то можно заменить. То есть возможно в твоем случае код станет чуть проще так как массив в перемешку с функциями не очень читабелен. Ну я не настаиваю, можно и массив оставить.
У тебя есть слова капслоком писать, регистр искажается: http://ideone.com/p7Ltcz
В остальном, замечаний нет.
А! Я придумал как чуть упростить эти коллбеки и убрать из них if. Смотри, там во всех 3 случаях нам надо заменить одну букву на другую, сохранив регистр:
з -> c, З -> С
Ну так сделай функцию которая возвращает данную букву, копируя ее регистр с другой буквы:
$letter = copyCase('c', $matches[2]); // вернет либо c либо С в зависимости от matches
Хотя бы от этого if избавишься и укоротишь код.
А почему ты просишь сделать несколько вещей: и работу с базой, и с формами, и хеширование? Ты не умеешь с базой работать? Тогда пример кода тебе не поможет.
Если у тебя нехватка базовых знаний, то почитай сначала про работу с формами, мой урок про соль и хеширование, еще какие-нибудь статьи на тему.
>>555144
> (?:а|но)[^а-яё]
границу слова можно обозначить как \\b
Это описано вроде ведь в моем уроке по регуляркам? посмотри, может ты еще что-то не знал?
В остальном верно.
> Автоисправление ошибок - не знаю, правильно ли я тебя понял
А код где? Не написан? Трудно тогда комментировать.
> эту подсказку я совсем не понял, как использовать,
Массив + цикл можно заменить на несколько вызовов функции, ну например:
$data = [
[1,2],
[3,4]
];
foreach ($data ..)
...
можно заменить на
doSomething(1, 2);
doSomething(3, 4);
Если элементов не много и читабельность получается лучше, то можно заменить. То есть возможно в твоем случае код станет чуть проще так как массив в перемешку с функциями не очень читабелен. Ну я не настаиваю, можно и массив оставить.
У тебя есть слова капслоком писать, регистр искажается: http://ideone.com/p7Ltcz
В остальном, замечаний нет.
А! Я придумал как чуть упростить эти коллбеки и убрать из них if. Смотри, там во всех 3 случаях нам надо заменить одну букву на другую, сохранив регистр:
з -> c, З -> С
Ну так сделай функцию которая возвращает данную букву, копируя ее регистр с другой буквы:
$letter = copyCase('c', $matches[2]); // вернет либо c либо С в зависимости от matches
Хотя бы от этого if избавишься и укоротишь код.
Анон, это плохой учюбный пример:
- простыня кода без разбиения на функции с кучей оступов. Так нельзя писать.
- смешана работа с формой (которая тут не нужна) и хеширование
Видимо ты сам еще начинающий. Не хочешь наши задачки из оп-поста про студентов и файлообменник решить?
То, что надо было сделать — это функцию хеширования и функцию проверки паролей. Не работающие ни с формами, ни с базой а с тем что им дали. И все.
ну и если погуглить, то выяснится что в PHP5.5. уже есть такие готовые функции: http://habrahabr.ru/post/194972/
Соль маленькая, пиши 40 символов минимум, включая специсмволы, пусть хакеры процессоры себе сожгут пытаясь разгадать.
Планшет- рандомандроид от DNS, пишу тебе этот пост, сидя на толчке.
Откуда такие стереотипы то, тоже мой.
> А где это почитать чтоб не сложно для ньюфага?
В случае апача надо читать документацию по mod_rewrite (то есть тот же htaccess):
https://beget.ru/articles/htaccess#mod_rewrite
http://habrahabr.ru/company/sprinthost/blog/129560/
+ офиуиальный сайт апача на англ, раздел mod_rewrite
>>554993
У него опен сервер и там конфиг поменять еще сложнее. Ему надо снести этот опен сервер просто.
Боюсь с зарплатой копирайтера мне даже выспаться некогда будет, не то что в тред зайти.
Копирайтинг это очень странная вещь, я всем советую бросать эту дрянь и либо перквалифицтроваться в полноценного журналиста/сценариста/автора статей/книг либо заняться чем-то другим.
Конечно копирайтинг бывает и нормальный, когда действительно человек талантливо пишет и оплата не идет по знакам, но я до такого уровня точно не дотягиваюсь.
>>555410
А если больше одной галереи на странице?
pjax это оверкилл так как ты делаешь лишние запросы чтобы скачать кусок hTML в то время как ссылку на картинку и описание проще заложить в тело страницы сразу.
Если часто пользоваться интернетом такое можно наблюдать у большинства сторонних серверов. Яндекс-карты бывает тупят по 15 секунд, я помню yandex.st падал ненадолго, итд.
Статику надо размещать на своем сервере а не слушать клоунов-хипстеров которые сложнее туду листа ничего не верстали.
Не хочет отправлять файлы.
На предпоследней версии отправляет, но не работает что-то другое. И через некоторое время работы сервера все равно перестает отправлять.
Алсо если ты будешь делать что-то не для себя а для заказчика, то писать вот так вот:
cdn/jquery.min.js
Это верный способ слоамть сайт при каком-нибудь очередном обновлении jQuery. если тебе там принципиально сэкономить 100 кб на жестком диске то хотя бы указывай конкретную версию библиотеки а не «какая выпадет».
Я пока не разбирался. Так, просто, говорю.
Ну $config[] - это массив твоих настроек в который все вот такие вот штуки обычно и выносят, путь к базе, соли, сколько юзеров/предметов выводится на странице, всевозможные другие штуки сразу влияющие на весь скрипт. Хотя соль это конечно более низкоуровневая хуйня чем простые настройки, и после того как твой скрипт уже заработал, то её лучше не править, а то никто не сможет больше залогиниться при вот такой реализации. Но для тестового задания или в учебых целях сойдет.
Ну тип это как бы дополнительное шифрование паролей.
Даже если кто-то вскроет твою базу, то он всё равно не сможет залогиниться под юзверями, не спиздив твой php код. Ведь он не сможет понять каким образом из "qwer123" получается 32-х значная хуита которая там хранится.
Спрашивай еще если что непонятно, постараюсь пояснить.
У тебя общая соль для всех пользователей? Неправильно, соль должна быть индивидуальной иначе пароли можно перебирать параллельно.
Тащемта полная ссылка - ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js
Это какой-то профессиональный юмор жаваскриптеров?
Т.е. нужно еще доп. поле, куда эта соль для каждог юзера записывается? как токен в куки?
так эта соль (рандомно сгенерированная строка) в какое место должна быть записана, если в бд ее хранить нельзя из-за угрозы взлома?
Ну вообще-то не должна. Но этот господин тоже говорит дело. Вместо соли можно использовать email или login самого же юзера. А именно тот параметр, который юзер в своем профиле не может поменять. Пусть это будет даже id его записи в базе.
$login = "vasya"; //неизменяемый, юзаем для соли без страха
$pass = "qwer123"; //
$mail = "[email protected]" //пользователь может заменить, юзать для соли нельзя
$saltyPass = $login . $pass;
$hash = md5($saltyPass); // это идет в базу собственно
Ну и когда юзер пытается залогиниться, ты дергаешь из базы по юзернейму того кто там лежит, и сравниваешь точно так же соленые пароли друг с другом.
да, либо отдельное поле либо в кеш дописывать
>>555458
в БД конечно. В твоем случае можно сделать 2 части соли, 1 постоянная из конфига, вторая из базы. Та что в базе генерируется рандомно для каждого юзера.
>>555460
Слишком короткая и слабая соль выходит, взламывать наверно легче будет. Одна из функций соли - усложнить простые пароли чтобы их нельзя было вытащить из баз заранее вычисленных хешей.
В твоем случае комбинация vasya123456 очень слабая.
Лучше 20-40 рандомных символов.
заебись, мастера кибер безопасности. добавлять логин к паролю, ну надо же, кто еще до этого додумается.
ну блин, ну для объяснения же чисто, и с упором на подводные камни, ну что вы :(
Ну квадрипл же, посоветовали чего
Значение по умолчанию может быть только скалярной величиной (строка, число, boolean, null), а также массив.
http://php.net/manual/ru/functions.arguments.php#functions.arguments.default
И это неправильно, что ты пытаешься намертво зашить соединение только на конкретную конфигурацию.
Класс должен принимать объект соединения в конструкторе, например.
То чувство, когда ты ничего не знал, и спустя год ничего-не-делания твои знания ничуть не преуменьшились.
Нет, ты не понял, как мне выводить их в два столбика. вот если пишешь. foreach ($files as $key =>$value) { print $files[$value];}
А верстка у меня такая
<div style="float:right"></div>
<div style="float:left"></div>
Вот как мне напечатать files[value] поровну в обоих этих дивах с различной версткой, классами и стилями?? Т.е если просто печатать массив, то он печатается В СТОЛБИК БЛЯТЬ. ОН НЕ ПЕЧАТАЕТСЯ В ДВА СТОЛБИКА МНЕ НУЖНО В ДВА.
мне нужно сделать что-то вроде такого:
<?php
echo date('i:s'); sleep(1); echo date('i:s');
echo date('i:s');
?>
И получится:
00:00 00:01
00:00
Что-то вроде многопоточности, помоги мне анон :3
Мускул в utf8_classic_epta символы жирнее 3х байтов не жрет и режет строку к хуям собачим
Как делать без костылей и чтоб сравнивать было норм и кракозябр не было?
Нихуя непонятно, нахуй тебе многопоточность и какая задача? Поможешь с удовольствием епта, только поясни
Ну или перехуячить в какой-нить бинарный формат и в бд. А в пхп перехуячить. Только хз как, в пхп 5.5 нихуя кроме кодинга-енкодинга для утф8 не сделали падлы
да цикл у меня есть, а в нем нужно выполнить задачу с задержкой, а после задержки еще одну задачу, но это должно не влиять на сам цикл
Вот допустим есть сервер и сайт.
На сервере работает скрипт, который должен отправлять на сайт текст и картинку к нему и ждать ответа пользователя на данный текст, скрипт ждет ответа пользователя, на сайте автообновление этого есть
Мне нужен скрипт с сайтом и как это использовать
Спасибо
У меня на такие случаи самые важные моменты во время обучения в файлик записаны, а куски кода со сложными конструкциями по мелким файликам раскиданы. Можно просмотреть быстро и подтянуться.
учи html, css. типограф, блядь.
почитай что чел выше тебе говорил. foreach просто перебирает информацию, он тебе ее не должен выстравивать. Чтобы блоки переносились нужно правильно сверстать.
Гуглить не умеешь? http://stackoverflow.com/questions/6385293/simple-two-column-html-layout-without-using-tables
ок, я подумал и ты прав, pjax не нужен. Допустим, я могу в data-аттрибут положить ссылку на изображение и по клику формировать на его основании тег img, но как быть, если мне нужно большой кусок html кода вхуячить по клику? Например, управление и возможность посмотреть комментарии. В js его сувать некрасиво.
Привет ОП, посмотри мой файлообменник, я туда прикрутил jQuery и поправил твои замечания. С версткой беда, знаю, но сейчас хочу разобраться до конца с бэкендом, а потом уже наводить красоту.
21 в нике это размер члена?
Тут все зависит от того как у тебя в верстке сделано. Если у тебя логика такая что данные идут не слева направо а столбиком то и верстку стоит наверно сделать чтобы они шли столбиком (хотя можно и так и так).
Что касается массива, если ты умеешь работать с массивами то легко разобьешь массив на 2 нужных либо выберешь из него элементы. Если нет то стоит начать с теории и изучить работу с массивами.
>>555501
флексбокс? При чем это тут?
>>555675
Тогда так:
- по простому: грузить с сервера HTML
- по сложному: делать шаблонизацию на стороне клиента, грузить комментарии аяксом и не уверен стоит ли оно того
А какой смысл в комментариях которые спрятаны где-то за фоткй и которые мало кто увидит? Комментарии проще сделать общие, если конечно их у тебя там не сотни будут.
спасибо, нужно отдельно к каждой, скорее всего сделаю, чтобы изначально количество комментариев отображалось, а по клику аяксом подгружу. в догонку вопрос: если я использую pushState, как сделать, чтобы, допустим, айди не добавлялся 2 раза к юрл?
У меня есть 2 класса: CreateAuthorForm и UpdateAuthorForm с практически одинаковыми правилами валидации и сообщениями, например:
['phone', 'filter', 'filter' => 'trim'],
['phone', 'required', "message" => "Номер телефона обязателен."],
['phone', 'unique', 'targetClass' => '\app\models\User', 'message' => '"Этот номер телефона уже используется.'],
['phone', 'string', 'length' => [10, 12], 'tooShort' => '...', 'tooLong' => '...'],
['phone', function ($attribute, $params) {
if (!ctype_digit($this->$attribute)) {
$this->addError($attribute, Только числа.');
}
}]
Эти все правила будут одинаковы как для создания, так и для редактирования, дублировать, конечно, не хочется.
А зачем ты сделал 2 формы когда можно сделать одну форму и 2 сценария? Это и есть решение.
Алсо почитай теорию например https://github.com/yiisoft/yii2/blob/master/docs/guide-ru/structure-models.md#Сценарии-
Алсо
> Только числа
надеюсь это только для примера написано и в реальном коде такого позора не будет.
Телефон должен приниматься в привычном человеку формате.
Есть двунаправленная связь один-ко-многим (юзер => файлы).
Стоит задача: имея на руках id юзера получить все его файлы.
Как сделать это правильно, не заставляя Доктрину генерировать кучу лишних запросов? В гугле советуют поставить либо fetch="EAGER" в аннотациях (нам это не подходит, т. к. далеко не всегда нужен юзер в связке со своими файлами), либо писать запрос ручками.
Я ведь правильно понимаю, что конструкции вида:
$files = $em->getRepository("File")->findBy(array('user' => $user->getId()));
и
$files = $user->getFiles();
загрузят лишь массив прокси-объектов, и последующий проход по нему с вызовами геттеров сгенерирует много однотипных запросов?
Спасибо, почитал, я правильно понимаю, что стоит сделать один класс типа AuthorForm, где будет написано примерно это:
class AuthorForm extends Model{
public $name;
public $email;
public $password;
public $phone;
public function scenarios()
{
return [
'create' => ['username', 'phone'],
'update' => ['username', 'phone', 'password'],
];
}
public function rules()
{
return [
...
]
public function create(){
if($this->validate(){}else{}
}
public function update(){
if($this->validate(){}else{}
}
И в этом случае для $model->update будет вызвана валидация и на телефон? (пример вымышлен, конечно)
>>555792
Надо подтвердить телефон автора, выслав смс, поэтому только числа и будет ещё ограничение на +7/8. Хотя сейчас подумал, что, может быть, лучше сделать свободный формат и потом выделять оттуда числа и уже с этим работать.
Спасибо, почитал, я правильно понимаю, что стоит сделать один класс типа AuthorForm, где будет написано примерно это:
class AuthorForm extends Model{
public $name;
public $email;
public $password;
public $phone;
public function scenarios()
{
return [
'create' => ['username', 'phone'],
'update' => ['username', 'phone', 'password'],
];
}
public function rules()
{
return [
...
]
public function create(){
if($this->validate(){}else{}
}
public function update(){
if($this->validate(){}else{}
}
И в этом случае для $model->update будет вызвана валидация и на телефон? (пример вымышлен, конечно)
>>555792
Надо подтвердить телефон автора, выслав смс, поэтому только числа и будет ещё ограничение на +7/8. Хотя сейчас подумал, что, может быть, лучше сделать свободный формат и потом выделять оттуда числа и уже с этим работать.
456
> имея на руках id юзера получить все его файлы.
1) $fileFepository->findByUser($user); (findByUser это магия доктрины, также там есть методы вида findOneByXXX)
Если использовать findBy(...) то можно также указать сортировку и лимит. Не знаю можно ли указать их с findByUser.
2) $user->getFiles() если у тебя настроена связь между сущностями (если нет, настрой)
Насчет прокси. Доктрина создает для каждого твоего класса сущности прокси-класс который от него наследуется (и потому совместим с ним и содержит все поля и методы). Также есть прокси-коллекции. Прокси подставляются вместо связанных сущностей. Допустим у тебя есть класс User и у юзера есть файлы и комментарии, файлы хранятся в поле $files, комментарии в $comments:
class User
{
...
@ORM\OneToMany(....)
...
private $files;
...
Когда ты загружаешь объект User, допустим через find('User', $id) доктрина должна что-то записать в эти поля, логично? Но выбирать список всех комментариев и файлов для каждого пользователя — накладно, потому она вместо коллекции файлов и комментариев вставляет туда прокси-коллекции. При обращении к этим прокси-коллекциями (например попытке получить элемент из них, или число элементов в них) происходит загрузка сущностей через SQL запрос.
Ну к примеру если мы напишем
foreach ($this->files as $file) {
То в момент начала foreach, когда он попытается взять первый элемент, будет выполнен запрос
SELECT × FROM files WHERE user_id = ?
И коллекция заполнится нужными файлами. И в дальнейшем уже эта коллекция не будет обращаться к базе.
Это называется ленивая загрузка. Ленивая загрузка требует по 1 запросу для каждой коллекции, то есть есть у тебя 5 юзеров и ты хочешь у каждого получить файлы, будет 5 SQL запросов.
Избежать этих запросов можно несколькими способами. Лучший, на мой взгляд, использовать DQL и указать что надо дополнительно выбрать файлы:
SELECT u, f
FROM User u
LEFT JOIN u.files f // обрати внимание, если ты напишешь JOIN File f то в SQL получится JOIN без ON и запрос будет очень медленный
WHERE u.name LIKE '%Ivan%'
Это заставит доктрину сделать запрос вида
SELECT u.×, f.× FROM user u LEFT JOIN file f ON f.user_id = u.id ...
И во всех юзерах заполнить поле $files.
Минус, что такой запрос может быть слишком сложным, например если выбрать юзеров с файлами и комментариями то джойн даст гигантское число строк, и запрос в итоге может оказаться неэффективнее чем ленивая загрузка.
Другой вариант - писать в классе аннотацию fetch="EAGER" ( http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#onetomany ) у связи, которая будет всегда принудительно загружать ее жадно. Не уверен что это хорошая идея так как оно не отключается.
Также, прокси можно запросить у доктрины явно:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/advanced-configuration.html#proxy-objects
$item = $em->getReference('Item', $itemId);
Это либо вернет объект Item если объект с таким id уже ранее загружался и хранится в Identity Map, либо создаст прокси (класса-наследника Item). В прокси хранится только id сущности. При вызове любого метода модели кроме getId произойдет ленивая загрузка и прокси будет заполнен данными из базы.
Создание прокси происходит без обращения к базе, и доктрина не может проверить есть ли сущность с таким id. Если позже при ленивой загрузке выяснится что id неправильный, будет выброшено исключение.
зачем это нужно? Ну например чтобы удалить сущность не загружая ее в память:
$item = $em->getReference('Item', $itemId);
$em->remove($item);
$em->flush();
Или чтобы прописать комментарию автора, не загружая его в память. прокси полностью взаимозаменяемы с моделями и их можно использовать вместо друг друга.
А, и надеюсь ты в курсе других особенностей доктрины вроде Identity Map или Unit Of Work. Это надо знать чтобы эффективно ей пользоваться и уметь разбираться в случае багов.
http://odiszapc.ru/doctrine/working-with-objects/#882
http://odiszapc.ru/doctrine/working-with-objects/#82
Ну и английский мануал тут:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/
Надеюсь что концепция прокси тебе понятна.
Если что-то кажется странными или непонятным, спрашивай. тема важная, надо разобраться.
> Я ведь правильно понимаю, что конструкции вида:
> $files = $em->getRepository("File")->findBy(array('user' => $user->getId()));
> и
> $files = $user->getFiles();
> загрузят лишь массив прокси-объектов
Нет, неправильно.
> имея на руках id юзера получить все его файлы.
1) $fileFepository->findByUser($user); (findByUser это магия доктрины, также там есть методы вида findOneByXXX)
Если использовать findBy(...) то можно также указать сортировку и лимит. Не знаю можно ли указать их с findByUser.
2) $user->getFiles() если у тебя настроена связь между сущностями (если нет, настрой)
Насчет прокси. Доктрина создает для каждого твоего класса сущности прокси-класс который от него наследуется (и потому совместим с ним и содержит все поля и методы). Также есть прокси-коллекции. Прокси подставляются вместо связанных сущностей. Допустим у тебя есть класс User и у юзера есть файлы и комментарии, файлы хранятся в поле $files, комментарии в $comments:
class User
{
...
@ORM\OneToMany(....)
...
private $files;
...
Когда ты загружаешь объект User, допустим через find('User', $id) доктрина должна что-то записать в эти поля, логично? Но выбирать список всех комментариев и файлов для каждого пользователя — накладно, потому она вместо коллекции файлов и комментариев вставляет туда прокси-коллекции. При обращении к этим прокси-коллекциями (например попытке получить элемент из них, или число элементов в них) происходит загрузка сущностей через SQL запрос.
Ну к примеру если мы напишем
foreach ($this->files as $file) {
То в момент начала foreach, когда он попытается взять первый элемент, будет выполнен запрос
SELECT × FROM files WHERE user_id = ?
И коллекция заполнится нужными файлами. И в дальнейшем уже эта коллекция не будет обращаться к базе.
Это называется ленивая загрузка. Ленивая загрузка требует по 1 запросу для каждой коллекции, то есть есть у тебя 5 юзеров и ты хочешь у каждого получить файлы, будет 5 SQL запросов.
Избежать этих запросов можно несколькими способами. Лучший, на мой взгляд, использовать DQL и указать что надо дополнительно выбрать файлы:
SELECT u, f
FROM User u
LEFT JOIN u.files f // обрати внимание, если ты напишешь JOIN File f то в SQL получится JOIN без ON и запрос будет очень медленный
WHERE u.name LIKE '%Ivan%'
Это заставит доктрину сделать запрос вида
SELECT u.×, f.× FROM user u LEFT JOIN file f ON f.user_id = u.id ...
И во всех юзерах заполнить поле $files.
Минус, что такой запрос может быть слишком сложным, например если выбрать юзеров с файлами и комментариями то джойн даст гигантское число строк, и запрос в итоге может оказаться неэффективнее чем ленивая загрузка.
Другой вариант - писать в классе аннотацию fetch="EAGER" ( http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#onetomany ) у связи, которая будет всегда принудительно загружать ее жадно. Не уверен что это хорошая идея так как оно не отключается.
Также, прокси можно запросить у доктрины явно:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/advanced-configuration.html#proxy-objects
$item = $em->getReference('Item', $itemId);
Это либо вернет объект Item если объект с таким id уже ранее загружался и хранится в Identity Map, либо создаст прокси (класса-наследника Item). В прокси хранится только id сущности. При вызове любого метода модели кроме getId произойдет ленивая загрузка и прокси будет заполнен данными из базы.
Создание прокси происходит без обращения к базе, и доктрина не может проверить есть ли сущность с таким id. Если позже при ленивой загрузке выяснится что id неправильный, будет выброшено исключение.
зачем это нужно? Ну например чтобы удалить сущность не загружая ее в память:
$item = $em->getReference('Item', $itemId);
$em->remove($item);
$em->flush();
Или чтобы прописать комментарию автора, не загружая его в память. прокси полностью взаимозаменяемы с моделями и их можно использовать вместо друг друга.
А, и надеюсь ты в курсе других особенностей доктрины вроде Identity Map или Unit Of Work. Это надо знать чтобы эффективно ей пользоваться и уметь разбираться в случае багов.
http://odiszapc.ru/doctrine/working-with-objects/#882
http://odiszapc.ru/doctrine/working-with-objects/#82
Ну и английский мануал тут:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/
Надеюсь что концепция прокси тебе понятна.
Если что-то кажется странными или непонятным, спрашивай. тема важная, надо разобраться.
> Я ведь правильно понимаю, что конструкции вида:
> $files = $em->getRepository("File")->findBy(array('user' => $user->getId()));
> и
> $files = $user->getFiles();
> загрузят лишь массив прокси-объектов
Нет, неправильно.
Прокси нужны так как доктрина пытается изобразить видимость что все объекты связаны и ты всегда можешь двигаться по цепочке связей, например от комментария к автору, от автора к файлам, от файлов к их комментариям:
$firstFileComments = $comment->getAuthor()->getFiles()->first()->getComments();
заметь что код не очень правильный: если у автора нет файлов то один из этапов вернет null. Исправить это предалгаю тебе самостоятельно.
Если тебе интересно, откуда я взял метод first(), то отсюда, из интерфейса который реализуют коллекции доктрины (а список файлов пользователя это коллекция): http://www.doctrine-project.org/api/common/2.1/class-Doctrine.Common.Collections.Collection.html
(рекомендую тебе иногда смотреть описание классов и какие у них есть методы, полезно).
Цепочка, описанная выше, довлоьно длинная. Потенциально по ней можно двигаться дальше, и чтобы реализовать ее без ленивой загрузки, нам бы в худшем случае пришлось в память загрузить базу целиком.
Потому доктрина при загрузке объекта вставляет прокси-объекты и прокси-коллекции вместо связанных сущностей. А при обращении к ним лениво грузит данные для них.
А что происходит когда мы создаем объект, например User, что впишется в поля files и comments? В этом случае доктрина сделать ничего не может и заполнить их должен ты. У только что созданной сущности нет ни файлов ни комментариев потому просто впиши туда пустые коллекции:
use ...ArrayCollection;
class User
{
...
public function __construct()
{
$this->files = new ArrayCollection();
$this->comments = new ArrayCollection();
}
....
Таким образом если мы создаем объект, мы вписываем туда пустую ArrayCollection. Если мы загружаем объект, доктрина вписывает туда прокси-коллекции. Если мы загружаем юзера + его файлы, доктрина вписывает туда коллекцию с файлами. Таким образом в поле files всегда будет что-то что реализует интерфейс Collection и мы можем смело обращаться к этому полю не боясь что там окажется null или что-то еще.
И таким образом создается ощущение что все объекты находятся у нас в памяти и мы можем двигаться по их связям.
Прокси нужны так как доктрина пытается изобразить видимость что все объекты связаны и ты всегда можешь двигаться по цепочке связей, например от комментария к автору, от автора к файлам, от файлов к их комментариям:
$firstFileComments = $comment->getAuthor()->getFiles()->first()->getComments();
заметь что код не очень правильный: если у автора нет файлов то один из этапов вернет null. Исправить это предалгаю тебе самостоятельно.
Если тебе интересно, откуда я взял метод first(), то отсюда, из интерфейса который реализуют коллекции доктрины (а список файлов пользователя это коллекция): http://www.doctrine-project.org/api/common/2.1/class-Doctrine.Common.Collections.Collection.html
(рекомендую тебе иногда смотреть описание классов и какие у них есть методы, полезно).
Цепочка, описанная выше, довлоьно длинная. Потенциально по ней можно двигаться дальше, и чтобы реализовать ее без ленивой загрузки, нам бы в худшем случае пришлось в память загрузить базу целиком.
Потому доктрина при загрузке объекта вставляет прокси-объекты и прокси-коллекции вместо связанных сущностей. А при обращении к ним лениво грузит данные для них.
А что происходит когда мы создаем объект, например User, что впишется в поля files и comments? В этом случае доктрина сделать ничего не может и заполнить их должен ты. У только что созданной сущности нет ни файлов ни комментариев потому просто впиши туда пустые коллекции:
use ...ArrayCollection;
class User
{
...
public function __construct()
{
$this->files = new ArrayCollection();
$this->comments = new ArrayCollection();
}
....
Таким образом если мы создаем объект, мы вписываем туда пустую ArrayCollection. Если мы загружаем объект, доктрина вписывает туда прокси-коллекции. Если мы загружаем юзера + его файлы, доктрина вписывает туда коллекцию с файлами. Таким образом в поле files всегда будет что-то что реализует интерфейс Collection и мы можем смело обращаться к этому полю не боясь что там окажется null или что-то еще.
И таким образом создается ощущение что все объекты находятся у нас в памяти и мы можем двигаться по их связям.
109 я думаю это число уникальных IP. А ведь есть еще стесняши, которые только читают но не пишут.
Алсо 100 людям я и отвечать не буду успевать, пока уже доделывать робота для проверки. Вот надо с работой только разобраться.
> cascade = persist
Это не с другой стороны писать надо? Персистить юзера который привязан к файлу? У тебя же наверно файлы можно загружать только для существующих пользователей?
persist если что надо вызывать только для созданных через new, то есть пока еще не управляемых доктриной, сущностей, чтобы она начала за ними следить.
Ну и помни что эти cascade не бесплатные, если для связи прописано persist, доктрина рекурсивно обходит связанные сущности в поисках не отслеживаемых, если связей с cascade=persist много то это потребует допольнительного времени.
Ну в твоем случае вряд ли, но просто имей в виду.
http://redis.io/clients#php
Еще проблема с faker: не могу понять в чем дело, но генерируемые им realText и password приводят к ошибке
Invalid parameter number: no parameters were bound
Вероятно дело в экранировании спецсимволов. Использую dao yii, странно что там не экранируется запрос.
Пришлось использовать скучный lorem text.
А жаль, realText выдает цитаты из "Мертвых душ".
Советы опа по оптимизации говнокода тоже не помешают.
https://github.com/nsdvw/classifieds/blob/master/protected/controllers/FakerController.php
Не могу сгенерировать больше 200000 объявлений, занимает почти час. Хотелось бы 10-20 миллионов.
Как бы ускорить? Могу выложить файл cachegrind, но там не очень понятно что можно сделать, видно только что 80% времени занимают обращения к faker.
http://rghost.ru/6n6FZlMVw
>>555363
Хорошая паста про многопоточность, понятно объяснил.
Ой, что-то я про тебя подзабыл, надеюсь ты не тратил зря время в ожидании.
https://github.com/someApprentice/Cat-and-Mouse/blob/master/index.php#L21
> <?php $cat->move(); ?>
> <?php $mouse->move(); ?>
Лучше вынести это в отдеьную функцию и сделать так:
foreach ($world->getAllAnimals() as $animal) {
$animal->move();
}
Ну только добавь тут еще проверку живое ли животное (оно могло быть съедено кем-то на одном из предыдущих шагов). Если список животных это массив то foreach делает копию и итерирует по ней и удаление животных по ходу дела не повлияет на цикл. Если же спсиок животных это SplObjectStorage то копии не делается, и удаленные животные не попадают в цикл.
Ну то есть надежнее сделать проверку живое ли оно еще.
Теперь по поводу алгоритма выбора хода. Он состоит из нескольких частей:
1. Получить список возможных ходов
2. Оценить каждый с помощью оценочной функции
3. Выбрать лучший
4. Выполнить его
Тут есть общие для всех животных, а есть разные части. Что у каждого животного свое? Очевидно, оценочная функция (каждое животное имеет свои интересы), получение списка ходов (так как ходят они по-разному). Также в 4-м пункте может быть разница в том, что кошки едят мышек, а мышки только ходят.
Ну а сама последовательность этих 4 действий общая для всех.
Потому очевидно функции должны быть размещены по классам так:
- алгоритм выполнения хода из 4 шагов, функция выбора лучшего хода - можно в классе Animal
- получение списка ходов, оценочная функция, выполнение хода - в классах соответствующих животных.
У тебя как-то все не так. Например функция оценки одного хода, у тебя не вынесена отдельно, а намертво впаяна в цикл оценки ходов, и она почему-то в базовом классе: https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L94
Из-за этого метод получился немного запутанным, он мог бы быть чище.
По самой оценке замечания такие:
Ты странно учитываешь сумму расстояний до кошек. В чем смысл именно суммирования? получаются странные результаты:
- рядом 1 кошка до которой 3 клетки: 3 балла
- рядом 4 кошки до которых 1 клетка: 4 балла
- вдали 2 кошки до которых 10 клеток: 20 баллов
Самый плохой ход тут второй — фактически в лапы кошек. Но по баллам это не очевидно. Если уж и складывать расстояния, то надо складывать обратные расстояния, 1/d, чтобы близкие кошки (как более опасные) давали большой вклад в сумму, а далекие — маленький.
Далее, само расстояние считается неверно: sqrt. Это декартово расстояние применимо в нашем мире где кошки ходят во все стороны с одинаковой скоростю. Но тут кошка по горизонтали ходит на расстояние 1, а по диагонали на sqrt(2) = 1.41, то есть игровой мир не изотропный. Тут надо считать не декартово расстояние а за сколько ходов до нас доберется кошка.
Наконец одного фактора маловато. Предлагаю взять три, с разными весами (то есть некоторые факторы вносят больший вклад в сумму):
- расстояние до ближайшей кошки в ходах
- число выходов с клетки (клетка в углу хуже клетки в центре)
- сумма обратных расстояний (оценивает число и дальность до кошек)
Также, ты в базовый класс поместил свойства:
> protected $fears = array();
> protected $tracks = array();
То есть ты изначально подразумеваешь что все животные на кого-то охотятся и кого-то боятся. Мне кажется это слишком сильное обобщение, ведь у нас есть собака которой вообще все безразличны и которая ходит случайнр. Я бы убрал это из базового класса.
> public $symbol;
Я бы сделал абстрактный метод getSymbol. Это позволяет легко менять вид животного в зависимости от состояния, а также заставляет всех потомков класса реализовать этот метод. В твоем случае, что если кто-то унаследует класс и не переопределит поле symbol, что будет? Ведь в коде нет никаких намеков что его надо переопределить.
> abstract public function chooseTheMovement($move);
Очень странный метод так как реализация у него по идее должна быть одинаковой для животных: выбрать лучший ход (например с макс. числом баллов). Также, делая метод абстрактным ты вводишь допущение что все животные действуют по алгоритму с оценкой. Подумай, так ли это или нет? Должен ли этот метод быть обязательным?
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L97
> for ($x = $this->getX() - $this->getSpeed(); $x <= $this->getX() + $this->getSpeed(); $x++) {
Ты слишком много команд засунул в строку и ее тяжело читать. Тут надо вынести выражения врожде $this->getX() - $this->getSpeed() в переменные.
> if ($this->getWorld()->validateCoordinates($x, $y) or $this->getWorld()->determineTheObject($x, $y)) {
> continue
Во-первых странно что при ошибке validate вернет true, обычно true возвращают когда все ок. Далее, я бы назвал функцию более конкретно: isInsideMap, находится ли точка внутри карты. Вторая часть мне непонятна, она не позволяет кошке рассматривать занятые мышкой клетки. С другой стороны мышка не может ходить на занятые клетки. Как тут поступить? Есть 2 варианта:
- сделать отдельную, свою для каждого животного функцию, определяющую может ли оно сходить на данную клетку или нет. Плохо, так как добавляется еще одна функция вдобавок к оценочной.
- перенести проверку в метод определения возможных ходов
- перенести проверку в оценочную функцию. Сделать специальную оценку, например const CANNOT_MOVE = -1000; которая говорит что ход на эту клетку запрещен и его не надо рассматривать.
Также у тебя не учтено что мышка не ходит по диагонали.
Наверно я тебя запутал, потому задавай вопросы, думаю тут есть о чем поговорить.
Ой, что-то я про тебя подзабыл, надеюсь ты не тратил зря время в ожидании.
https://github.com/someApprentice/Cat-and-Mouse/blob/master/index.php#L21
> <?php $cat->move(); ?>
> <?php $mouse->move(); ?>
Лучше вынести это в отдеьную функцию и сделать так:
foreach ($world->getAllAnimals() as $animal) {
$animal->move();
}
Ну только добавь тут еще проверку живое ли животное (оно могло быть съедено кем-то на одном из предыдущих шагов). Если список животных это массив то foreach делает копию и итерирует по ней и удаление животных по ходу дела не повлияет на цикл. Если же спсиок животных это SplObjectStorage то копии не делается, и удаленные животные не попадают в цикл.
Ну то есть надежнее сделать проверку живое ли оно еще.
Теперь по поводу алгоритма выбора хода. Он состоит из нескольких частей:
1. Получить список возможных ходов
2. Оценить каждый с помощью оценочной функции
3. Выбрать лучший
4. Выполнить его
Тут есть общие для всех животных, а есть разные части. Что у каждого животного свое? Очевидно, оценочная функция (каждое животное имеет свои интересы), получение списка ходов (так как ходят они по-разному). Также в 4-м пункте может быть разница в том, что кошки едят мышек, а мышки только ходят.
Ну а сама последовательность этих 4 действий общая для всех.
Потому очевидно функции должны быть размещены по классам так:
- алгоритм выполнения хода из 4 шагов, функция выбора лучшего хода - можно в классе Animal
- получение списка ходов, оценочная функция, выполнение хода - в классах соответствующих животных.
У тебя как-то все не так. Например функция оценки одного хода, у тебя не вынесена отдельно, а намертво впаяна в цикл оценки ходов, и она почему-то в базовом классе: https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L94
Из-за этого метод получился немного запутанным, он мог бы быть чище.
По самой оценке замечания такие:
Ты странно учитываешь сумму расстояний до кошек. В чем смысл именно суммирования? получаются странные результаты:
- рядом 1 кошка до которой 3 клетки: 3 балла
- рядом 4 кошки до которых 1 клетка: 4 балла
- вдали 2 кошки до которых 10 клеток: 20 баллов
Самый плохой ход тут второй — фактически в лапы кошек. Но по баллам это не очевидно. Если уж и складывать расстояния, то надо складывать обратные расстояния, 1/d, чтобы близкие кошки (как более опасные) давали большой вклад в сумму, а далекие — маленький.
Далее, само расстояние считается неверно: sqrt. Это декартово расстояние применимо в нашем мире где кошки ходят во все стороны с одинаковой скоростю. Но тут кошка по горизонтали ходит на расстояние 1, а по диагонали на sqrt(2) = 1.41, то есть игровой мир не изотропный. Тут надо считать не декартово расстояние а за сколько ходов до нас доберется кошка.
Наконец одного фактора маловато. Предлагаю взять три, с разными весами (то есть некоторые факторы вносят больший вклад в сумму):
- расстояние до ближайшей кошки в ходах
- число выходов с клетки (клетка в углу хуже клетки в центре)
- сумма обратных расстояний (оценивает число и дальность до кошек)
Также, ты в базовый класс поместил свойства:
> protected $fears = array();
> protected $tracks = array();
То есть ты изначально подразумеваешь что все животные на кого-то охотятся и кого-то боятся. Мне кажется это слишком сильное обобщение, ведь у нас есть собака которой вообще все безразличны и которая ходит случайнр. Я бы убрал это из базового класса.
> public $symbol;
Я бы сделал абстрактный метод getSymbol. Это позволяет легко менять вид животного в зависимости от состояния, а также заставляет всех потомков класса реализовать этот метод. В твоем случае, что если кто-то унаследует класс и не переопределит поле symbol, что будет? Ведь в коде нет никаких намеков что его надо переопределить.
> abstract public function chooseTheMovement($move);
Очень странный метод так как реализация у него по идее должна быть одинаковой для животных: выбрать лучший ход (например с макс. числом баллов). Также, делая метод абстрактным ты вводишь допущение что все животные действуют по алгоритму с оценкой. Подумай, так ли это или нет? Должен ли этот метод быть обязательным?
> https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L97
> for ($x = $this->getX() - $this->getSpeed(); $x <= $this->getX() + $this->getSpeed(); $x++) {
Ты слишком много команд засунул в строку и ее тяжело читать. Тут надо вынести выражения врожде $this->getX() - $this->getSpeed() в переменные.
> if ($this->getWorld()->validateCoordinates($x, $y) or $this->getWorld()->determineTheObject($x, $y)) {
> continue
Во-первых странно что при ошибке validate вернет true, обычно true возвращают когда все ок. Далее, я бы назвал функцию более конкретно: isInsideMap, находится ли точка внутри карты. Вторая часть мне непонятна, она не позволяет кошке рассматривать занятые мышкой клетки. С другой стороны мышка не может ходить на занятые клетки. Как тут поступить? Есть 2 варианта:
- сделать отдельную, свою для каждого животного функцию, определяющую может ли оно сходить на данную клетку или нет. Плохо, так как добавляется еще одна функция вдобавок к оценочной.
- перенести проверку в метод определения возможных ходов
- перенести проверку в оценочную функцию. Сделать специальную оценку, например const CANNOT_MOVE = -1000; которая говорит что ход на эту клетку запрещен и его не надо рассматривать.
Также у тебя не учтено что мышка не ходит по диагонали.
Наверно я тебя запутал, потому задавай вопросы, думаю тут есть о чем поговорить.
> Остается актуальной проблема
Я там написал 3 варианта решения. Насчет tracks — я предлагаю убрать ее из базового класса.
Мне нравится идея поместить проверку занятости клетки либо в метод получения списка ходов, который уникален для животного (хорошо), либо в оценочную функцию (не так хорошо, это же не ее дело, но на практике не всегда все гладко выходит).
Спасибо за подробное объяснение. То, что коллекции грузятся целиком, стоит лишь запросить хотя бы 1 её элемент, я упустил. А в целом как работают прокси, зачем нужны Identity Map и Unit of Work, какие возможности они дают и какие ограничения накладывают вроде понимаю.
>>556071
>У тебя же наверно файлы можно загружать только для существующих пользователей?
Теперь уже да. Но когда я начинал делать фалообменник, у меня при загрузке на сервер к файлу цеплялся объект-юзер созданный на основе кук, которого могло и не быть в базе (новый пользователь = объект с пустыми полями, если кук нет).
Теперь cascade=persist не нужен, т. к. теперь первое, что я делаю при заходе пользователя на сайт - это идентифицирую его по кукам, а если они пустые - то создаю запись в базе с cookie-токеном.
Вообще, наверно выложу-ка я его уже на гитхаб. Основной функционал в принципе готов.
Вот. Файлообменник. ОП, посмотри пожалуйста.
Так-то ещё сыровато. На некоторые действия ответы пока не в виде html-страничек, а просто текста (а кое-где и вовсе редирект).
Что есть: регистрация, простой личный кабинет с файлами пользователя и возможностью отредактировать личную информацию, возможность загружать и удалять (если ты владелец) файлы, загрузка файлов драг-н-дропом (можно пачками).
Чего ещё нет: комментариев и превьюшек к изображениям.
>Процессор может выполнять параллельно столько потоков инструкций, сколько в нем ядер.
Опять тупой оп обосрался и не знает про Hyper-threading. Дальше даже не читал этот нубский высер.
Аноны, валите из треда, у этого школьника куча ошибок в пастах и заданиях.
А чо ты тута делаешь, м?
> ни одного готового решения
хе-хе-хе это хорошо, пусть люди сами думают
По твоему решению: немного запутанно получилось. Ты собираешь 2 строки, буквы слева и буквы справа, но можно ведь сделать проще. Заводишь переменную, кладешь в нее изначально допустим, 1, а если встречаешь 2 несовпадающие буквы, кладешь 0 и выходишь из цикла.
В конце проверяешь, 0 там или 1.
Ну и тут еще можно исправить:
> if ($symbol1 == $symbol2){
> } else {
> break;
> }
if ($symbol1 != $symbol2)
!= значит «не равно»
>>553815
Неплохо для начала, но структура стиха не та, и вот тут вот:
> foreach($word as $key=>$value){
Вместо цикла надо брать элемент массива с помощью [$key]
Ну и еще, есть функция array_rand() с которой будет проще.
> ни одного готового решения
хе-хе-хе это хорошо, пусть люди сами думают
По твоему решению: немного запутанно получилось. Ты собираешь 2 строки, буквы слева и буквы справа, но можно ведь сделать проще. Заводишь переменную, кладешь в нее изначально допустим, 1, а если встречаешь 2 несовпадающие буквы, кладешь 0 и выходишь из цикла.
В конце проверяешь, 0 там или 1.
Ну и тут еще можно исправить:
> if ($symbol1 == $symbol2){
> } else {
> break;
> }
if ($symbol1 != $symbol2)
!= значит «не равно»
>>553815
Неплохо для начала, но структура стиха не та, и вот тут вот:
> foreach($word as $key=>$value){
Вместо цикла надо брать элемент массива с помощью [$key]
Ну и еще, есть функция array_rand() с которой будет проще.
> foreach($splitText as $key => $value){
Что-то у тебя там аж 3 цикла, не многовато ли? Предлагаю оставить 1 цикл, а функции переделать чтобы они принимали не массив предложений, а одно предложение за раз. Тогда например функция makeFirstLetterUppercase сократится до пары строчек.
> $encoding = 'utf-8'
Я думаю, возможность указать кодировку не требуется, так как внутри скрипта все данные обычно в одной кодировке (а если не так то это серьезная проблема и постоянный источник багов).
> /[,]/
То же самое что /,/ - запятая не спецсимвол (их список по моему перечислен в конце урока а также есть тут: http://php.net/manual/ru/regexp.reference.meta.php (проверь себя: все ли ты знаешь?) )
Также, привыкай ставить в регулярках флаг u иначе регулярки с русскими символами будут работать неправильно. То есть ставь его везде.
В остальном, верно.
> $splitText
Лучше назвать $sentences - предложения.
> foreach ($splitText as $key => $value){
$key не используется и потому не нужно его писать
$value ничего не значит. Нужно давать нормальные имена, например $sentence, иначе тяжело понять код.
> preg_split('/[,?!;]?\\s/'
По идее ?! там встретиться не может, мы же по ним разбиваем на предложения
> $value = preg_split('/[,?!;]?\\s/', $value, 0, PREG_SPLIT_NO_EMPTY);
Надо называть переменные нормально, например $words = preg_split(... $sentence) - смотри, насколько понятнее.
> return implode('', makeFirstletterUppercase($splitText));
Это лучше разбить на 2 строчки, и сделать чтобы makeFirstletterUppercase принимала одно предложение а не массив.
В общем, код работает верно, но читать его тяжело, переменные названы бессмысленно, в некоторых строках слишком много всего намешано (например длинное выражение в makeFirstletterUppercase надо разбить на 2-3 строки). Если ты пишешь текст прямо в ideone, может стоит перейти на текстовый редактор или IDE - будет удобнее писать.
> }
> elseif
мелкая придирка: else/elseif пишутся на одной строке со скобками, то есть
} elseif (...) {
> else { return $word5; }
Пиши в 3 строчки, не жалей место.
> $number = floor($number/1000%100);
Порядок действий неправильный. Ты делишь $number на 1000, получаешь дробь и дальше пытаешься найти остаток от деления. А надо сначала округлять а потом брать остаток.
> function checkFemale($number) {
Эта функция не нужна. В женском роде пишутся тысячи (1 тысяча), а рубли и миллионы всегда в мужском. Тебе просто надо, если дали значение $isFemale == 1 заменять в массиве $spelling значения для чисел 1 и 2. А далее уже работать с этим массивом как обычно.
> return preg_replace('/ноль/','',$spelling[floor($number/100)100]." ".$spelling[floor($number%100/10)10]." ".$femaleSpelling[$number%10]);
Так писать нельзя. Это же невозможно прочесть, и ошибку тут допустить, например перепутав скобку, очень легко. Ну и preg_replace выглядит как костыль. Это надо переделать.
Вообще, есть мягкое ограничение в 80 символов на длину строки - придерживайся его.
Ну и как я выше написал, для женского рода надо просто заменить 2 элемента в $spelling и все.
> smallNumberToText($number, $isFemale) {
Эта функция слишком запутанная и в ней слишком много условий, надо упростить. Например, так:
список слов = [];
Если (в числе есть сотни) {
добавить в список число сотен;
}
...
Костыли с preg_replace надо везде убрать, они затрудняют понимание кода.
> elseif($number == 1000) {
> return "одня тысяча рублей";
Тут незачем делать отдельный случай.
> numberToText($number) {
тут очень много копипасты, надо всю ее убрать и сделать общий алгоритм, по типу:
если (есть миллионы) {
добавить в список число миллионов;
}
В общем, то, что есть, никуда не годится. Код тяжело понять и потому я не могу даже сказать правильно он работает или нет.
> foreach($splitText as $key => $value){
Что-то у тебя там аж 3 цикла, не многовато ли? Предлагаю оставить 1 цикл, а функции переделать чтобы они принимали не массив предложений, а одно предложение за раз. Тогда например функция makeFirstLetterUppercase сократится до пары строчек.
> $encoding = 'utf-8'
Я думаю, возможность указать кодировку не требуется, так как внутри скрипта все данные обычно в одной кодировке (а если не так то это серьезная проблема и постоянный источник багов).
> /[,]/
То же самое что /,/ - запятая не спецсимвол (их список по моему перечислен в конце урока а также есть тут: http://php.net/manual/ru/regexp.reference.meta.php (проверь себя: все ли ты знаешь?) )
Также, привыкай ставить в регулярках флаг u иначе регулярки с русскими символами будут работать неправильно. То есть ставь его везде.
В остальном, верно.
> $splitText
Лучше назвать $sentences - предложения.
> foreach ($splitText as $key => $value){
$key не используется и потому не нужно его писать
$value ничего не значит. Нужно давать нормальные имена, например $sentence, иначе тяжело понять код.
> preg_split('/[,?!;]?\\s/'
По идее ?! там встретиться не может, мы же по ним разбиваем на предложения
> $value = preg_split('/[,?!;]?\\s/', $value, 0, PREG_SPLIT_NO_EMPTY);
Надо называть переменные нормально, например $words = preg_split(... $sentence) - смотри, насколько понятнее.
> return implode('', makeFirstletterUppercase($splitText));
Это лучше разбить на 2 строчки, и сделать чтобы makeFirstletterUppercase принимала одно предложение а не массив.
В общем, код работает верно, но читать его тяжело, переменные названы бессмысленно, в некоторых строках слишком много всего намешано (например длинное выражение в makeFirstletterUppercase надо разбить на 2-3 строки). Если ты пишешь текст прямо в ideone, может стоит перейти на текстовый редактор или IDE - будет удобнее писать.
> }
> elseif
мелкая придирка: else/elseif пишутся на одной строке со скобками, то есть
} elseif (...) {
> else { return $word5; }
Пиши в 3 строчки, не жалей место.
> $number = floor($number/1000%100);
Порядок действий неправильный. Ты делишь $number на 1000, получаешь дробь и дальше пытаешься найти остаток от деления. А надо сначала округлять а потом брать остаток.
> function checkFemale($number) {
Эта функция не нужна. В женском роде пишутся тысячи (1 тысяча), а рубли и миллионы всегда в мужском. Тебе просто надо, если дали значение $isFemale == 1 заменять в массиве $spelling значения для чисел 1 и 2. А далее уже работать с этим массивом как обычно.
> return preg_replace('/ноль/','',$spelling[floor($number/100)100]." ".$spelling[floor($number%100/10)10]." ".$femaleSpelling[$number%10]);
Так писать нельзя. Это же невозможно прочесть, и ошибку тут допустить, например перепутав скобку, очень легко. Ну и preg_replace выглядит как костыль. Это надо переделать.
Вообще, есть мягкое ограничение в 80 символов на длину строки - придерживайся его.
Ну и как я выше написал, для женского рода надо просто заменить 2 элемента в $spelling и все.
> smallNumberToText($number, $isFemale) {
Эта функция слишком запутанная и в ней слишком много условий, надо упростить. Например, так:
список слов = [];
Если (в числе есть сотни) {
добавить в список число сотен;
}
...
Костыли с preg_replace надо везде убрать, они затрудняют понимание кода.
> elseif($number == 1000) {
> return "одня тысяча рублей";
Тут незачем делать отдельный случай.
> numberToText($number) {
тут очень много копипасты, надо всю ее убрать и сделать общий алгоритм, по типу:
если (есть миллионы) {
добавить в список число миллионов;
}
В общем, то, что есть, никуда не годится. Код тяжело понять и потому я не могу даже сказать правильно он работает или нет.
http://namefuctionstar.esy.es/AmericaVip/
Алсо, наткнулся на статью на хабре про авто отправку смс клиенту при обработке заявки. Каким способом можно автоматом смски на номера слать, есть какой-то сайт, к апи которого нужно подключиться?
Я не знаю, не делал такое.
>>553993
Сделай свою компанию и работай так как нравится. Ну или просто найди другую работу.
Работа «делать клоны-сайты на битриксе» — это лишь временная подработка для молодежи. Я не думаю, что на ней кто-то долго задерживается.
>>554107
В каждом первом скрипте. Так что лучше отмучайся сейчас, няша (и реши наши задачки на JS), потом легче будет.
>>554111
Немного слоупочное дополнение, но если посмотреть код вот тут:
https://github.com/iAchilles/eavactiverecord/blob/master/EavActiveRecord.php#L78
Ты увидишь что там есть методы
> if ($this->hasEavAttribute($name))
> return $this->getEavAttribute($name);
То есть делать свои методы, как я посоветовал, не надо.
Соответственно пиши в шаблоне так:
{% if model.hasEavAttribute('price') %}
Price: {{ model.getEavAttribute('price') }}
...
> Но в случае если свойство равно null, и иссет пролетает мимо.
Тогда надо писать
{% if model.hasEavAttribute('price') and model.getEavAttribute('price') %}
...
Вообще, я тебе советую меньше полагаться на такую вот магию, которую дает твиг, и больше на явные вызовы, так как магия твига, как мы видим, не очень совмещается с магическими методами.
Я не знаю, не делал такое.
>>553993
Сделай свою компанию и работай так как нравится. Ну или просто найди другую работу.
Работа «делать клоны-сайты на битриксе» — это лишь временная подработка для молодежи. Я не думаю, что на ней кто-то долго задерживается.
>>554107
В каждом первом скрипте. Так что лучше отмучайся сейчас, няша (и реши наши задачки на JS), потом легче будет.
>>554111
Немного слоупочное дополнение, но если посмотреть код вот тут:
https://github.com/iAchilles/eavactiverecord/blob/master/EavActiveRecord.php#L78
Ты увидишь что там есть методы
> if ($this->hasEavAttribute($name))
> return $this->getEavAttribute($name);
То есть делать свои методы, как я посоветовал, не надо.
Соответственно пиши в шаблоне так:
{% if model.hasEavAttribute('price') %}
Price: {{ model.getEavAttribute('price') }}
...
> Но в случае если свойство равно null, и иссет пролетает мимо.
Тогда надо писать
{% if model.hasEavAttribute('price') and model.getEavAttribute('price') %}
...
Вообще, я тебе советую меньше полагаться на такую вот магию, которую дает твиг, и больше на явные вызовы, так как магия твига, как мы видим, не очень совмещается с магическими методами.
В общем верно.
>>554131
В JS любая функция при создании захватывает переменные вокруг и таким образом становится замыканием (замыкание = функция + захваченные ей внешние переменные). Может тебе тоже стоит наши задачки из ОП-поста на JS порешать?
>>554134
У меня есть получше ссылки:
http://habrahabr.ru/post/208442/
http://blog.byndyu.ru/2009/10/solid.html (хорошо и подробно описано)
Имей в виду что паттерны и SOLID это для тех у кого уже есть опыт работы с кодом, особенно с большим. Такой человек увидит в них ответы на вопросы «как улучшить этот код», а вот начинающему будет сложновато. НО ты попробуй, что-нибудь да усвоишь. Принцип подстановки Лисков например вполне понятный.
>>554362
Это штука типа генератора URL?
public function getNewsUrl($newsId)
{
return "/news/{$newsId}";
}
Если так то сделай нормальные методы, а не статические. Статические методы почти всегда это результат плохого проектирования, и с ними код только станет спутаннее.
>>554445
Ты прав.
>>554532
На CSS
>>554560
> Клуб изучающих PHP
>>554682
А зачем ты перепечатываешь код? Шаблоны проектирования не совсем для начинающих, а для тех кто работал с кодом большого объема. Тебе надо сначала сам ООП нормально выучить (сделать наши задачи на кошки-мышки и вектор), проекты какие-то поделать, например наших студентов и файлообменник, а на них уже можно шаблоны применять.
Заучивать шаблоны бессмысленно, если ты не сталкивался с ситуацияим где они нужны, ты скорее всего их просто не поймешь и тем более не научишься правильно применять. Ты занимаешься глупостью на мой взгляд.
>>554765
Безработные студентики самоутверждаются, чтобы хотя бы в /pr/ поучвствовать себя успешными.
>>554782
> паблики
> программисты
Там наверно программисты уровня «вчера установил новую тему на винду». Попроси их своими словами описать что им не нравится в PHP.
>>555217
Первый раз слышу про этот сайт. У нас пока были версии про доброчан и конфа в слаке (но там нужна регистрация).
>>555353
Не знаю. Поищи похожие вакансии и сравни.
В общем верно.
>>554131
В JS любая функция при создании захватывает переменные вокруг и таким образом становится замыканием (замыкание = функция + захваченные ей внешние переменные). Может тебе тоже стоит наши задачки из ОП-поста на JS порешать?
>>554134
У меня есть получше ссылки:
http://habrahabr.ru/post/208442/
http://blog.byndyu.ru/2009/10/solid.html (хорошо и подробно описано)
Имей в виду что паттерны и SOLID это для тех у кого уже есть опыт работы с кодом, особенно с большим. Такой человек увидит в них ответы на вопросы «как улучшить этот код», а вот начинающему будет сложновато. НО ты попробуй, что-нибудь да усвоишь. Принцип подстановки Лисков например вполне понятный.
>>554362
Это штука типа генератора URL?
public function getNewsUrl($newsId)
{
return "/news/{$newsId}";
}
Если так то сделай нормальные методы, а не статические. Статические методы почти всегда это результат плохого проектирования, и с ними код только станет спутаннее.
>>554445
Ты прав.
>>554532
На CSS
>>554560
> Клуб изучающих PHP
>>554682
А зачем ты перепечатываешь код? Шаблоны проектирования не совсем для начинающих, а для тех кто работал с кодом большого объема. Тебе надо сначала сам ООП нормально выучить (сделать наши задачи на кошки-мышки и вектор), проекты какие-то поделать, например наших студентов и файлообменник, а на них уже можно шаблоны применять.
Заучивать шаблоны бессмысленно, если ты не сталкивался с ситуацияим где они нужны, ты скорее всего их просто не поймешь и тем более не научишься правильно применять. Ты занимаешься глупостью на мой взгляд.
>>554765
Безработные студентики самоутверждаются, чтобы хотя бы в /pr/ поучвствовать себя успешными.
>>554782
> паблики
> программисты
Там наверно программисты уровня «вчера установил новую тему на винду». Попроси их своими словами описать что им не нравится в PHP.
>>555217
Первый раз слышу про этот сайт. У нас пока были версии про доброчан и конфа в слаке (но там нужна регистрация).
>>555353
Не знаю. Поищи похожие вакансии и сравни.
my bad
>>555523
Пиши код.
>>555561
Режим работы себе установи. Иначе у тебя будет постоянно режим студента накануне экзамена.
>>555593
Банально перекодировать между UTF-32 и UTF-8.
>>555621
> Мне нужен скрипт с сайтом и как это использовать
Это долго объяснять, так как непонятно что ты знаешь, а что нет, если ничего то тут на целый учебник выйдет.
>>555733
> если я использую pushState, как сделать, чтобы, допустим, айди не добавлялся 2 раза к юрл?
пушить абсолютный, а не относительный URL (работают телепаты угадывающие суть вопроса по смутному описанию).
> чтобы изначально количество комментариев отображалось, а по клику аяксом подгружу
Вот это плохая идея: ты прячешь комментарии за 2 клика, ты как бы говоришь этим пользователям что их мнению тут не особо и рады. Я тебе советую сделать общие комментарии на галерею, если только у тебя их не сотни.
>>555739
Ты знаешь что такое Active Record? Ты читал документацию Юи 2?
> но никак не могу найти метода для базы данных
У модели есть метод save но ты должен сначала прочесть документацию. Не только по ACtive Record, а в принципе весь официальный туториал.
>>555848
> И в этом случае для $model->update будет вызвана валидация и на телефон? (пример вымышлен, конечно)
Наверно да
> Надо подтвердить телефон автора,
Я бы не стал вводить телефон на левом сайте.
Почитай еще и про формы: https://github.com/yiisoft/yii2/blob/master/docs/guide-ru/input-forms.md
И вообще, найди время весь этот гайд пролистать.
my bad
>>555523
Пиши код.
>>555561
Режим работы себе установи. Иначе у тебя будет постоянно режим студента накануне экзамена.
>>555593
Банально перекодировать между UTF-32 и UTF-8.
>>555621
> Мне нужен скрипт с сайтом и как это использовать
Это долго объяснять, так как непонятно что ты знаешь, а что нет, если ничего то тут на целый учебник выйдет.
>>555733
> если я использую pushState, как сделать, чтобы, допустим, айди не добавлялся 2 раза к юрл?
пушить абсолютный, а не относительный URL (работают телепаты угадывающие суть вопроса по смутному описанию).
> чтобы изначально количество комментариев отображалось, а по клику аяксом подгружу
Вот это плохая идея: ты прячешь комментарии за 2 клика, ты как бы говоришь этим пользователям что их мнению тут не особо и рады. Я тебе советую сделать общие комментарии на галерею, если только у тебя их не сотни.
>>555739
Ты знаешь что такое Active Record? Ты читал документацию Юи 2?
> но никак не могу найти метода для базы данных
У модели есть метод save но ты должен сначала прочесть документацию. Не только по ACtive Record, а в принципе весь официальный туториал.
>>555848
> И в этом случае для $model->update будет вызвана валидация и на телефон? (пример вымышлен, конечно)
Наверно да
> Надо подтвердить телефон автора,
Я бы не стал вводить телефон на левом сайте.
Почитай еще и про формы: https://github.com/yiisoft/yii2/blob/master/docs/guide-ru/input-forms.md
И вообще, найди время весь этот гайд пролистать.
> Какой клиент выбрать для редиса?
Rediska - хороший, весь на ООП и паттернах (делают разработчики одной питерской сомнительной соцсети в которую приличный человек и носа не сунет)
phpredis - на Си, нацелен на макс. производительность, но не умеет нормально обрабатывать ошибки от редиса (тупо игнорирует их по умолчанию), и были какие-то косяки (например был баг что он числа искажал), не советую
С остальными не работал. Ты бы посмотрел, мои знания может уже устарели.
> Еще проблема с faker: не могу понять в чем дело, но генерируемые им realText и password приводят к ошибке
Сдампь генерируемые данные и попробуй заменить их просто на строку. Я уверен, дело не в нем. Не должно быть такого, явно ошибка на твоей стороне.
> Вероятно дело в экранировании спецсимволов. Использую dao yii, странно что там не экранируется запрос.
Ты просто непраивльно их используешь. Никогда не вставляй переменные в SQL код, используй плейсхолждеры. Ну и подготовленные запросы могут дать небольшой плюс в производительности.
> Не могу сгенерировать больше 200000 объявлений, занимает почти час.
Что именно тормозит? Профайлинг делал? mysql вставит 200 000 записей за минут 5-15.
Алсо почитал бы: https://dev.mysql.com/doc/refman/5.5/en/optimizing-innodb-bulk-data-loading.html (англ)
Данные лучше вставлять большими транзакциями, а то у тебя каждый запрос отдельной транзакцией идет.
Алсо проверь не в генераторе ли случайных чисел (или array_rand) проблема. Проверь объем текстов, советую делать поменьше, ибо база забивается, а разницы особой нет.
> видно только что 80% времени занимают обращения к faker.
Если так то можно сделать заранее пул например из 1000 значений, и добивать их номерами ддля уникальности.
> Delete this file on production!
Это значит что файл там будет аж в нескольких экземплярах. Лучше сделать отдельную папку для таких файлов либо поставить там проверку что окркжение имеет тип dev а не production.
> set_time_limit(3600 * 3);
В CLI режиме ограничения нет, а через браузер никто не запускает скрипты работающие больше 10 секунд. Пиши CLI-скрипт: http://www.yiiframework.com/doc/guide/1.1/ru/topics.console
Гайд по ком. строке: http://www.yiiframework.com/doc/guide/1.1/ru/topics.console
Еще больше: https://gist.github.com/codedokode/420c8c12a1edae25f0ec#file-linux1-md
Методы великоваты, посмотри что можно вынести в отдельные функции.
> https://github.com/nsdvw/classifieds/blob/master/protected/controllers/FakerController.php#L41
Что это за фигня? Где плейсхолдеры? Ну ад же, запрос раскидан по всему методу кусками, так что его надо собирать в уме, плюс возможны ошибки из-за экранирования.
> echo date('H:i:s') . ' Finish!<br>';
Там наверно что-то готовое есть для логгирования в командах.
> $user['password'] = $faker->word . $faker->word;
им всем можно ставить один пароль, тебе же логиниться проще будет
> createLegion(Faker\Generator $faker,
$faker лучше сделать полем класса
> ошибка при использовании realText:
Подготовленные запросы используй с плейсхолдерами. Никогда не вставляй переменные в запрос, ты наверно наши задачи не решал, я всех за это ругаю.
Подготовленные запросы кстати позволяют экономить время базы на парсинг SQL кода, так как он парсится только первый раз.
> private function attachEavAttributes()
тут надо разбить на более мелкие методы
Вместо того чтобы добавлять EAV отдельным блоком, лучше добавлять их при создании объявления, тогда не нужна будет куча селектов.
Насчет профайлинга: если у тебя функция работает очень быстро, но вызывается очень много раз, то профайлер может не дать тебе точных чисел. Придется делать «профайлер бедного человека». Посмотри, из каких шагов состоит создание записей (генерация случайных данных, создание команды, выполнение), и протестируй каждй шаг отдельно, просто вызывая его в цикле N раз и меряя общее время. Таким образом попробуй понять сколько времени на что уходит.
Простор для разгона однозначно есть.
Проверь не включен ли логгер запросов Юи/отладчик Юи и не логгирует ли он все тысячи запросов. Проверь на сколько загружен процессор во время работы скрипта. Проверь не съел ли твой скрипт всю память (в линуксе это команда top, а лучше установить htop, он цветной). Проверь сильно ли нагружен диск программой iostat и iotop: http://serverfault.com/questions/9428/how-can-i-monitor-hard-disk-load-on-linux
Алсо держи мой генератор (тоже лапша та еще так как писалось на 1 раз): https://github.com/codedokode/board-test-scripts (inb4: где faker? я тогда не знал про него)
> Какой клиент выбрать для редиса?
Rediska - хороший, весь на ООП и паттернах (делают разработчики одной питерской сомнительной соцсети в которую приличный человек и носа не сунет)
phpredis - на Си, нацелен на макс. производительность, но не умеет нормально обрабатывать ошибки от редиса (тупо игнорирует их по умолчанию), и были какие-то косяки (например был баг что он числа искажал), не советую
С остальными не работал. Ты бы посмотрел, мои знания может уже устарели.
> Еще проблема с faker: не могу понять в чем дело, но генерируемые им realText и password приводят к ошибке
Сдампь генерируемые данные и попробуй заменить их просто на строку. Я уверен, дело не в нем. Не должно быть такого, явно ошибка на твоей стороне.
> Вероятно дело в экранировании спецсимволов. Использую dao yii, странно что там не экранируется запрос.
Ты просто непраивльно их используешь. Никогда не вставляй переменные в SQL код, используй плейсхолждеры. Ну и подготовленные запросы могут дать небольшой плюс в производительности.
> Не могу сгенерировать больше 200000 объявлений, занимает почти час.
Что именно тормозит? Профайлинг делал? mysql вставит 200 000 записей за минут 5-15.
Алсо почитал бы: https://dev.mysql.com/doc/refman/5.5/en/optimizing-innodb-bulk-data-loading.html (англ)
Данные лучше вставлять большими транзакциями, а то у тебя каждый запрос отдельной транзакцией идет.
Алсо проверь не в генераторе ли случайных чисел (или array_rand) проблема. Проверь объем текстов, советую делать поменьше, ибо база забивается, а разницы особой нет.
> видно только что 80% времени занимают обращения к faker.
Если так то можно сделать заранее пул например из 1000 значений, и добивать их номерами ддля уникальности.
> Delete this file on production!
Это значит что файл там будет аж в нескольких экземплярах. Лучше сделать отдельную папку для таких файлов либо поставить там проверку что окркжение имеет тип dev а не production.
> set_time_limit(3600 * 3);
В CLI режиме ограничения нет, а через браузер никто не запускает скрипты работающие больше 10 секунд. Пиши CLI-скрипт: http://www.yiiframework.com/doc/guide/1.1/ru/topics.console
Гайд по ком. строке: http://www.yiiframework.com/doc/guide/1.1/ru/topics.console
Еще больше: https://gist.github.com/codedokode/420c8c12a1edae25f0ec#file-linux1-md
Методы великоваты, посмотри что можно вынести в отдельные функции.
> https://github.com/nsdvw/classifieds/blob/master/protected/controllers/FakerController.php#L41
Что это за фигня? Где плейсхолдеры? Ну ад же, запрос раскидан по всему методу кусками, так что его надо собирать в уме, плюс возможны ошибки из-за экранирования.
> echo date('H:i:s') . ' Finish!<br>';
Там наверно что-то готовое есть для логгирования в командах.
> $user['password'] = $faker->word . $faker->word;
им всем можно ставить один пароль, тебе же логиниться проще будет
> createLegion(Faker\Generator $faker,
$faker лучше сделать полем класса
> ошибка при использовании realText:
Подготовленные запросы используй с плейсхолдерами. Никогда не вставляй переменные в запрос, ты наверно наши задачи не решал, я всех за это ругаю.
Подготовленные запросы кстати позволяют экономить время базы на парсинг SQL кода, так как он парсится только первый раз.
> private function attachEavAttributes()
тут надо разбить на более мелкие методы
Вместо того чтобы добавлять EAV отдельным блоком, лучше добавлять их при создании объявления, тогда не нужна будет куча селектов.
Насчет профайлинга: если у тебя функция работает очень быстро, но вызывается очень много раз, то профайлер может не дать тебе точных чисел. Придется делать «профайлер бедного человека». Посмотри, из каких шагов состоит создание записей (генерация случайных данных, создание команды, выполнение), и протестируй каждй шаг отдельно, просто вызывая его в цикле N раз и меряя общее время. Таким образом попробуй понять сколько времени на что уходит.
Простор для разгона однозначно есть.
Проверь не включен ли логгер запросов Юи/отладчик Юи и не логгирует ли он все тысячи запросов. Проверь на сколько загружен процессор во время работы скрипта. Проверь не съел ли твой скрипт всю память (в линуксе это команда top, а лучше установить htop, он цветной). Проверь сильно ли нагружен диск программой iostat и iotop: http://serverfault.com/questions/9428/how-can-i-monitor-hard-disk-load-on-linux
Алсо держи мой генератор (тоже лапша та еще так как писалось на 1 раз): https://github.com/codedokode/board-test-scripts (inb4: где faker? я тогда не знал про него)
> теперь первое, что я делаю при заходе пользователя на сайт - это идентифицирую его по кукам, а если они пустые - то создаю запись в базе с cookie-токеном.
Советую создавать только при первом действии (файл/комментарий), чтобы не забивать базу бесполезными пользователями-пустышками.
Я проверю, но попозже, так что не теряй время, занимайся чем-нибудь полезным.
>>556168
http://namefuctionstar.esy.es/AmericaVip/
> Каким способом можно автоматом смски на номера слать, есть какой-то сайт, к апи которого нужно подключиться?
Гугли «API SMS шлюз». Там есть куча своих аспектов, например не у всех СМС хорошо доходят на все операторы и в разные страны, не все дают указать произвольный номер отправителя, итд. Все это платно разумеется, но не дорого.
Имей в виду что некоторые ориентированы на спам и с такими лучше не работать: можно попасть под фильтры операторов и сообщения будут теряться. Лучше брать тех что поизвестнее и покрупнее, давно на рынке и не ориентированы на спам рассылки (это часто по цене видно). Стоит также протестировать отправку самому.
По верстке:
- Стрелки на слайдере не по центру вертикально
- почта не кликабельна
- картинка с корзиной пикселизованная (дно тележки)
- в главном меню кликабельна только надпись, а черный косой блок на фоне - нет
- на втором слайдере зона для клика маленькая, да и по моему 2 слайдера перебор (по мне так и один это перебор, я их не люблю)
- в блоке «Голливудская улыбка» слева кликабельна только крошечная ссылка в которую еще попасть надо, а должен быть весь блок. Ты знаешь как неудобно в такие маленьие ссылки тыкать с планшета, на тачпаде, с заевшей мышкой? А ведь это все твои клиенты.
- блок «Голливудская улыбка» должен быть не только кликаьельным но и выделяться при наведении, давая обратную связь и намекая что можно на него нажать
- иконки соцсетей не становятся цветными при наведении
- некоторые тексты нереально мелкие (претензия к дизайнеру, вообще дизайн слабоват)
- сумма в корзине подчеркнута как будто это ссылка, но она не кликабельна (претензия к дизайну)
- на карточке товара некликабельна ни картинка, ни заголовок. Нет ссылки для перехода на страницу товара. Зато есть кнопка добавления, но ведь никто не добавит товар в корзину не почитав подробно о нем. Бред же. Открой например http://www.lamoda.ru/c/369/clothes-platiya/?genders=women и поищи здесь кнопку добавления в корзину. Так же обрати внимание на кликабельные зоны на карточке товара. Дизайнер и верстальщик оба не имеют понятия о поведении пользователя на сайте. Интерфейс я бы оценил на тройку с минусом, такие огрехи приведут к потере покупателей.
- сайт помечен как адаптивный в коде, но если посмотреть его в режиме эмуляции смартфона или планшета, никакой адаптивности нет, все разъезжается. Не копипасть тег meta viewport если ты не сделал адаптивную верстку.
- //maxcdn.bootstrapcdn.com/ - надежнее размещать библиотеки у себя, не надо подключать внещние скрипты, а то твой сайт ляжет вместе с CDN
> @font-face {
Сделано по моему неправильно, там нет разных форматов, только один формат.
HTML код это кошмар. Я ждал аккуратную лаконичную семантичную разметку. Но ощущение что откуда-то накопировали тегов и свалили в кучу.
> <img class="flag" src="bootstrap/images/flag.png" alt="flag">
Элементы оформления (в отличие от контента) надо делать через background-image.
> <p class="sliderTitle">
Надо hN использовать
> <h1><span class="sideYellow">/</span> Fitness одежда молодежной американской марки </h1>
Спан можно заменить на псевдоэлемент
> <div class="border"> </div>
Этого не должно быть в HTML коде, должно быть в CSS
Разметка карточки товара замусорена тегами, надо выкинуть оттуда все что относится к оформлению.
На странице может быть только один тег h1
> <div class="sticker small teeth">
> <div class="sticker small hair">
При натяжке на CMS будут проблемы если категории берутся из базы
> Голливудская <br>улыбка
br тут признак неквалифицированности верстальщика.
jQuery зачем-то подключен 2 раза.
Тег </html> потеряли.
CSS и JS я не смотрел, нет смысла тратить мое время на это. Это некачественная верстка на тройку с минусом, за которую тебе, как человеку который у нас учился (так ведь?) должно быть стыдно.
> теперь первое, что я делаю при заходе пользователя на сайт - это идентифицирую его по кукам, а если они пустые - то создаю запись в базе с cookie-токеном.
Советую создавать только при первом действии (файл/комментарий), чтобы не забивать базу бесполезными пользователями-пустышками.
Я проверю, но попозже, так что не теряй время, занимайся чем-нибудь полезным.
>>556168
http://namefuctionstar.esy.es/AmericaVip/
> Каким способом можно автоматом смски на номера слать, есть какой-то сайт, к апи которого нужно подключиться?
Гугли «API SMS шлюз». Там есть куча своих аспектов, например не у всех СМС хорошо доходят на все операторы и в разные страны, не все дают указать произвольный номер отправителя, итд. Все это платно разумеется, но не дорого.
Имей в виду что некоторые ориентированы на спам и с такими лучше не работать: можно попасть под фильтры операторов и сообщения будут теряться. Лучше брать тех что поизвестнее и покрупнее, давно на рынке и не ориентированы на спам рассылки (это часто по цене видно). Стоит также протестировать отправку самому.
По верстке:
- Стрелки на слайдере не по центру вертикально
- почта не кликабельна
- картинка с корзиной пикселизованная (дно тележки)
- в главном меню кликабельна только надпись, а черный косой блок на фоне - нет
- на втором слайдере зона для клика маленькая, да и по моему 2 слайдера перебор (по мне так и один это перебор, я их не люблю)
- в блоке «Голливудская улыбка» слева кликабельна только крошечная ссылка в которую еще попасть надо, а должен быть весь блок. Ты знаешь как неудобно в такие маленьие ссылки тыкать с планшета, на тачпаде, с заевшей мышкой? А ведь это все твои клиенты.
- блок «Голливудская улыбка» должен быть не только кликаьельным но и выделяться при наведении, давая обратную связь и намекая что можно на него нажать
- иконки соцсетей не становятся цветными при наведении
- некоторые тексты нереально мелкие (претензия к дизайнеру, вообще дизайн слабоват)
- сумма в корзине подчеркнута как будто это ссылка, но она не кликабельна (претензия к дизайну)
- на карточке товара некликабельна ни картинка, ни заголовок. Нет ссылки для перехода на страницу товара. Зато есть кнопка добавления, но ведь никто не добавит товар в корзину не почитав подробно о нем. Бред же. Открой например http://www.lamoda.ru/c/369/clothes-platiya/?genders=women и поищи здесь кнопку добавления в корзину. Так же обрати внимание на кликабельные зоны на карточке товара. Дизайнер и верстальщик оба не имеют понятия о поведении пользователя на сайте. Интерфейс я бы оценил на тройку с минусом, такие огрехи приведут к потере покупателей.
- сайт помечен как адаптивный в коде, но если посмотреть его в режиме эмуляции смартфона или планшета, никакой адаптивности нет, все разъезжается. Не копипасть тег meta viewport если ты не сделал адаптивную верстку.
- //maxcdn.bootstrapcdn.com/ - надежнее размещать библиотеки у себя, не надо подключать внещние скрипты, а то твой сайт ляжет вместе с CDN
> @font-face {
Сделано по моему неправильно, там нет разных форматов, только один формат.
HTML код это кошмар. Я ждал аккуратную лаконичную семантичную разметку. Но ощущение что откуда-то накопировали тегов и свалили в кучу.
> <img class="flag" src="bootstrap/images/flag.png" alt="flag">
Элементы оформления (в отличие от контента) надо делать через background-image.
> <p class="sliderTitle">
Надо hN использовать
> <h1><span class="sideYellow">/</span> Fitness одежда молодежной американской марки </h1>
Спан можно заменить на псевдоэлемент
> <div class="border"> </div>
Этого не должно быть в HTML коде, должно быть в CSS
Разметка карточки товара замусорена тегами, надо выкинуть оттуда все что относится к оформлению.
На странице может быть только один тег h1
> <div class="sticker small teeth">
> <div class="sticker small hair">
При натяжке на CMS будут проблемы если категории берутся из базы
> Голливудская <br>улыбка
br тут признак неквалифицированности верстальщика.
jQuery зачем-то подключен 2 раза.
Тег </html> потеряли.
CSS и JS я не смотрел, нет смысла тратить мое время на это. Это некачественная верстка на тройку с минусом, за которую тебе, как человеку который у нас учился (так ведь?) должно быть стыдно.
Кнопка «все спецпредложения» при первом наведении на нее мигает так как нужно время на загрузку второй картинки. такие вещи надо делать через CSS спрайты.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L11
> define('UPLOAD_DIR', 'upload');
Вместо констант лучше сделать методы для получения нужных путей, так как константы провоцируют размазывать код получения путей по всему приложению. Не одобряю константы тут.
> define('BASE_DIR', '..');
Лучше использовать абсолтный путь например с использованием dirname(_ _ DIR _ _)
> return new PDO(
Где строгий режим? Где ERRMODE_EXCEPTION? По умолчанию PDO по моему ничего не делает при ошибках от базы и ты о них не узнаешь.
> if (!Token::issetToken()) $token = Token::generateToken();
Не экономь строки и не пиши if в 1 строку
https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L46
Этот код стоит куда-нибудь вынести, хотя бы в функцию в классе Token.
JSON надо отдавать с правильным типом и с помощью метода-хелпера как тут: http://stackoverflow.com/questions/6807404/slim-json-outputs/18070349#18070349
>https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L72
Логично отдавать либо ошибку 404 либо JSON {"error": "File not found"}, а твой вариант ничего не говорит о причине проблемы.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L80
> if ($app->request->isGet()) {
> $app->render('upload_form.tpl');
Где тут логика? Почему в методе login мы выводим форму загрузки файла?
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L85
> 'email'=>$_POST['login']['email'],
Надо использовать $app->request. Можно сразу в форме сделать метод принимающий Request.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L90
> if ($user = $app->userMapper->findByEmail($loginForm->email)) {
Проверку логина/пароля надо сделать либо частью валидации либо методом в loginManager.
Алсо не вижу где у тебя выводится сообщение о том что пароль введен неправильно.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L105
Эту простыню надо разбить на методы и вынести в какой-нибудь FileUploader. Метода должно быть минимум 2: валидация и загрузка. Ну как и в любой другой форме.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L165
> $app->get('/reg'
Надо сделать по стандартному алгоритму обработки форм.
Валидацию CSRF можно тоже поместить в формы.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L252
> foreach ($comments as $comment) {
> $comment->level = Comment::getLevel($comment->materialized_path);
Это должно быть в маппере.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L254
> $comment->author_id = $app->userMapper->findById($comment->author_id);
Вообще, если тебе нужны комменты + авторы, а доктрины у нас нет, можно попробовать вернуть массив пар вида
[
['comment' => ..., 'author' => ....],
....
]
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L262
> $captcha = new Storage\Model\FormWithCaptcha(
Капча должна быть частью формы коммента. Надо просто у класса CommentForm сделать метод setCaptchaRequired(true|false) который отключает ее проверку и вывод.
Хуже, ты там зачем-то CommentForm аж 2 раза создаешь. такого быть не должно.
Ты для каждой формы придумываешь свой алгоритм (точнее кучу кода в index.php), хотя им всем подошел бы один и тот же подход.
> https://github.com/nsdvw/file-sharing/blob/master/app/Auth/LoginManager.php#L11
> public $loggedUser = null;
Это лчше сделать методом
logout/login должны менять состояние loggedUser.
> https://github.com/nsdvw/file-sharing/blob/master/app/Auth/LoginManager.php#L38
> public function authorizeUser(User $user)
Где гарантия что нам передадут пользователя у которого непустой id и hash? Я бы поставил ассерты или if + throw:
assert(!!$user->id);
....
Модели форм логично бы вынести в неймспейс Form.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/Comment.php#L17
> protected $mapper;
В модели не должно быть ссылок на маппер.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/Comment.php#L19
> public function fromForm(CommentForm $form, CommentMapper $mapper)
Это в форме должен быть метод getModel(), причем логичнее всего чтобы форма содержала модель внутри себя.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/Comment.php#L44
> protected static function incrementPath
Это должно быть наверно в маппере.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/File.php#L103
> if ($propertyValue instanceof MediaInfo) {
Нарушение принципа единой ответственности: MediaInfo сам должен уметь превращать себя в массив, ты не должен лезть внутрь него.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/File.php#L16
> protected function getSafeFields()
название неудачное, надо называть getPublicFields()
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/CommentForm.php
Логичнее внутри CommetnForm хранить сущность Comment и в нее и прописывать данные, чем заводить параллельный набор полей.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/FormWithCaptcha.php
капчу логичнее сделать элементом формы, а не типом формы
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/FormWithCaptcha.php#L19
> session_start();
Лучше использовать сессии Слима
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/Form.php#L8
> public function __construct(array $fields)
Не, это плохая идея передавать значения в конструктор. Лучше передавать их в метод handle(Request/array $request) который проставялет их и вызвыает validate().
> https://github.com/nsdvw/file-sharing/blob/master/app/Helper/HashGenerator.php
Для соли надо испльзовать еще и спецсимволы. Иначе она мало добавляет защиты от предвыисленных таблиц хешей.
> https://github.com/nsdvw/file-sharing/blob/master/app/Helper/Pager.php
Тут надо избавиться от зависимости в виде маппера, передавая вместо него число страниц снаружи. Незачем пагинатору обращаться к базе, он должен показыать те числа что ему дают.
> https://github.com/nsdvw/file-sharing/blob/master/app/Mapper/FileMapper.php#L84
> $sth->fetch(\PDO::FETCH_ASSOC);
есть fetchColumn() или как-то так
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L11
> define('UPLOAD_DIR', 'upload');
Вместо констант лучше сделать методы для получения нужных путей, так как константы провоцируют размазывать код получения путей по всему приложению. Не одобряю константы тут.
> define('BASE_DIR', '..');
Лучше использовать абсолтный путь например с использованием dirname(_ _ DIR _ _)
> return new PDO(
Где строгий режим? Где ERRMODE_EXCEPTION? По умолчанию PDO по моему ничего не делает при ошибках от базы и ты о них не узнаешь.
> if (!Token::issetToken()) $token = Token::generateToken();
Не экономь строки и не пиши if в 1 строку
https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L46
Этот код стоит куда-нибудь вынести, хотя бы в функцию в классе Token.
JSON надо отдавать с правильным типом и с помощью метода-хелпера как тут: http://stackoverflow.com/questions/6807404/slim-json-outputs/18070349#18070349
>https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L72
Логично отдавать либо ошибку 404 либо JSON {"error": "File not found"}, а твой вариант ничего не говорит о причине проблемы.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L80
> if ($app->request->isGet()) {
> $app->render('upload_form.tpl');
Где тут логика? Почему в методе login мы выводим форму загрузки файла?
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L85
> 'email'=>$_POST['login']['email'],
Надо использовать $app->request. Можно сразу в форме сделать метод принимающий Request.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L90
> if ($user = $app->userMapper->findByEmail($loginForm->email)) {
Проверку логина/пароля надо сделать либо частью валидации либо методом в loginManager.
Алсо не вижу где у тебя выводится сообщение о том что пароль введен неправильно.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L105
Эту простыню надо разбить на методы и вынести в какой-нибудь FileUploader. Метода должно быть минимум 2: валидация и загрузка. Ну как и в любой другой форме.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L165
> $app->get('/reg'
Надо сделать по стандартному алгоритму обработки форм.
Валидацию CSRF можно тоже поместить в формы.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L252
> foreach ($comments as $comment) {
> $comment->level = Comment::getLevel($comment->materialized_path);
Это должно быть в маппере.
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L254
> $comment->author_id = $app->userMapper->findById($comment->author_id);
Вообще, если тебе нужны комменты + авторы, а доктрины у нас нет, можно попробовать вернуть массив пар вида
[
['comment' => ..., 'author' => ....],
....
]
> https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L262
> $captcha = new Storage\Model\FormWithCaptcha(
Капча должна быть частью формы коммента. Надо просто у класса CommentForm сделать метод setCaptchaRequired(true|false) который отключает ее проверку и вывод.
Хуже, ты там зачем-то CommentForm аж 2 раза создаешь. такого быть не должно.
Ты для каждой формы придумываешь свой алгоритм (точнее кучу кода в index.php), хотя им всем подошел бы один и тот же подход.
> https://github.com/nsdvw/file-sharing/blob/master/app/Auth/LoginManager.php#L11
> public $loggedUser = null;
Это лчше сделать методом
logout/login должны менять состояние loggedUser.
> https://github.com/nsdvw/file-sharing/blob/master/app/Auth/LoginManager.php#L38
> public function authorizeUser(User $user)
Где гарантия что нам передадут пользователя у которого непустой id и hash? Я бы поставил ассерты или if + throw:
assert(!!$user->id);
....
Модели форм логично бы вынести в неймспейс Form.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/Comment.php#L17
> protected $mapper;
В модели не должно быть ссылок на маппер.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/Comment.php#L19
> public function fromForm(CommentForm $form, CommentMapper $mapper)
Это в форме должен быть метод getModel(), причем логичнее всего чтобы форма содержала модель внутри себя.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/Comment.php#L44
> protected static function incrementPath
Это должно быть наверно в маппере.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/File.php#L103
> if ($propertyValue instanceof MediaInfo) {
Нарушение принципа единой ответственности: MediaInfo сам должен уметь превращать себя в массив, ты не должен лезть внутрь него.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/File.php#L16
> protected function getSafeFields()
название неудачное, надо называть getPublicFields()
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/CommentForm.php
Логичнее внутри CommetnForm хранить сущность Comment и в нее и прописывать данные, чем заводить параллельный набор полей.
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/FormWithCaptcha.php
капчу логичнее сделать элементом формы, а не типом формы
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/FormWithCaptcha.php#L19
> session_start();
Лучше использовать сессии Слима
> https://github.com/nsdvw/file-sharing/blob/master/app/Model/Form.php#L8
> public function __construct(array $fields)
Не, это плохая идея передавать значения в конструктор. Лучше передавать их в метод handle(Request/array $request) который проставялет их и вызвыает validate().
> https://github.com/nsdvw/file-sharing/blob/master/app/Helper/HashGenerator.php
Для соли надо испльзовать еще и спецсимволы. Иначе она мало добавляет защиты от предвыисленных таблиц хешей.
> https://github.com/nsdvw/file-sharing/blob/master/app/Helper/Pager.php
Тут надо избавиться от зависимости в виде маппера, передавая вместо него число страниц снаружи. Незачем пагинатору обращаться к базе, он должен показыать те числа что ему дают.
> https://github.com/nsdvw/file-sharing/blob/master/app/Mapper/FileMapper.php#L84
> $sth->fetch(\PDO::FETCH_ASSOC);
есть fetchColumn() или как-то так
Если проблема в Faker то можно сгенерировать пул случайных значений, а затем брать из оттуда, дописывая цифры.
Это довольно изветсная в США доска объявлений.
Ну а у нас крупнейший авито, это ты наверно и сам знаешь.
Немного поздно наверно, но все же.
> Если я правильно понял, то твиг проверяет if ($object instanceof ArrayAccess && isset($object[$arrayItem])). Но в случае если свойство равно null, и иссет пролетает мимо.
Баг https://github.com/twigphp/Twig/issues/1557 не имеет отношения к твоей проблеме. Он о баге в каком-то стороннем фреймворке.
http://ideone.com/g9CD7o
Для объектов ArrayAccess там используется isset, который вызвыает offsetExists. null на это не влияет. И строчка которую ты процитировал, она корректная.
Другой вопрос что в Юи offsetExists вызывает __isset и там возможно что-то криво реализовано. А возможно и нет.
Мораль в том что при реализации магических методов легко сделать ошибку. Также мораль в том что явное лучше неявного и стоит поменьше полагаться на магию по поиску полей в твиге.
>Ой, что-то я про тебя подзабыл, надеюсь ты не тратил зря время в ожидании.
Да, эта неделя была для меня кошмарной, но я знал что ты наверно очень устаешь помогая нам и то что чем меньше помогаешь мне тем больше отдыхаешь сам. Я понимаю какой титаническими вещами ты здесь занимаешься поэтому считаю что ты заслуживаешь как можно больше отдыха. Так что это даже к лучшему произошло, потому что за эту неделю я осознал как сильно я хочу писать код и как я люблю это дело. Может быть было бы лучше если бы ты мне подкидывал какой-нибудь информации, которая расширяла мой кругозор, на самостоятельное изучение, чтобы мне было чем заняться в свободное время? Что-нибудь по информационной безопасности или по администрированию систем (я с линуксом плохо знаком, стоит ли мне сначала начать с самостоятельного изучения каких-то азов?).
>Ну только добавь тут еще проверку живое ли животное (оно могло быть съедено кем-то на одном из предыдущих шагов). Если список животных это массив то foreach делает копию и итерирует по ней и удаление животных по ходу дела не повлияет на цикл. Если же спсиок животных это SplObjectStorage то копии не делается, и удаленные животные не попадают в цикл.
А где лучше делать эту проверку? В функции оценки ходов или в функции получение всех возможных ходов?
По моему если животное мертво, то его вообще не должно быть на карте (в смысле мире), и из этого бы вытекало все остальное.
>- получение списка ходов, оценочная функция, выполнение хода - в классах соответствующих животных.
Получение списка ходов можно тоже в Animal сделать. Просто взять все возможные ходы вокруг и потом работать с ними с помощью оценочной функции. Мне кажется так не придется в будущем не придется писать больше кода. Что скажешь?
>>556079
>Ты странно учитываешь сумму расстояний до кошек. В чем смысл именно суммирования?
Сложно объяснить, попробую показать наглядно. На первом пике мышка типо такая "ОМГ! Мне нужно сматываться как можно дальше" и выбирает самую дальнюю точку от всех кошек. На втором пике происходит тоже самое и самое главное по тому же самому алгоритму. Я писал программу которая могла бы доказать то что происходит на пиках, но идеоне не открыл страницу при субмите и код не сохранился. Однако, там было тоже самое что и в моем коде, и со временем, когда код будет работать исправно, ты сможешь убедиться в этом своими глазами. Если я прав насчет этой функции. Я просто не знаю как еще проще можно сделать в случае если кошек будет несколько.
>Ой, что-то я про тебя подзабыл, надеюсь ты не тратил зря время в ожидании.
Да, эта неделя была для меня кошмарной, но я знал что ты наверно очень устаешь помогая нам и то что чем меньше помогаешь мне тем больше отдыхаешь сам. Я понимаю какой титаническими вещами ты здесь занимаешься поэтому считаю что ты заслуживаешь как можно больше отдыха. Так что это даже к лучшему произошло, потому что за эту неделю я осознал как сильно я хочу писать код и как я люблю это дело. Может быть было бы лучше если бы ты мне подкидывал какой-нибудь информации, которая расширяла мой кругозор, на самостоятельное изучение, чтобы мне было чем заняться в свободное время? Что-нибудь по информационной безопасности или по администрированию систем (я с линуксом плохо знаком, стоит ли мне сначала начать с самостоятельного изучения каких-то азов?).
>Ну только добавь тут еще проверку живое ли животное (оно могло быть съедено кем-то на одном из предыдущих шагов). Если список животных это массив то foreach делает копию и итерирует по ней и удаление животных по ходу дела не повлияет на цикл. Если же спсиок животных это SplObjectStorage то копии не делается, и удаленные животные не попадают в цикл.
А где лучше делать эту проверку? В функции оценки ходов или в функции получение всех возможных ходов?
По моему если животное мертво, то его вообще не должно быть на карте (в смысле мире), и из этого бы вытекало все остальное.
>- получение списка ходов, оценочная функция, выполнение хода - в классах соответствующих животных.
Получение списка ходов можно тоже в Animal сделать. Просто взять все возможные ходы вокруг и потом работать с ними с помощью оценочной функции. Мне кажется так не придется в будущем не придется писать больше кода. Что скажешь?
>>556079
>Ты странно учитываешь сумму расстояний до кошек. В чем смысл именно суммирования?
Сложно объяснить, попробую показать наглядно. На первом пике мышка типо такая "ОМГ! Мне нужно сматываться как можно дальше" и выбирает самую дальнюю точку от всех кошек. На втором пике происходит тоже самое и самое главное по тому же самому алгоритму. Я писал программу которая могла бы доказать то что происходит на пиках, но идеоне не открыл страницу при субмите и код не сохранился. Однако, там было тоже самое что и в моем коде, и со временем, когда код будет работать исправно, ты сможешь убедиться в этом своими глазами. Если я прав насчет этой функции. Я просто не знаю как еще проще можно сделать в случае если кошек будет несколько.
>>Ох уж эти замыкания в JS. Голова трещит от них.
Надеюсь на практике они не так часто применяются?
>В каждом первом скрипте. Так что лучше отмучайся сейчас, няша (и реши наши задачки на JS), потом легче будет.
Да, собственно, подходя к концу главы про замыкания и поняв, что разбираюсь в них я очень плохо, решил заново все те уроки на learn.javascript.ru перепройти и как-то даже за один вечер все их осилил и разобрался нормально, хотя перед этим не один день на них потратил.
>и реши наши задачки на JS
Знания первой части учебника достаточно для решения этих задач? Или стоит и вторую часть перед ними тоже пройти?
>и реши наши задачки на JS
>Знания первой части учебника достаточно для решения этих задач? Или стоит и вторую часть перед ними тоже пройти?
Хотя, глупый вопрос. Лучше не разрываться, а весь учебник закончить, тем более там и свои задачи имеются.
>Где плейсхолдеры?
Мне казалось, что подготовленные запросы нужно использовать только если есть опасность инъекции от пользователя. Зачем мне здесь плейсхолдеры, если нужно только заэкранировать?
>Подготовленные запросы кстати позволяют экономить время базы на парсинг SQL кода, так как он парсится только первый раз.
Не понимаю, как это работает. Мне казалось наоборот, они должны замедлять выполнение, поскольку выполняется лишняя операция "подготовки", именно поэтому и не использовал их в этом случае.
И как я буду биндить параметры, если у меня мультиинсерт на 10000 записей, который я формирую в цикле?
>запрос раскидан по всему методу кусками, так что его надо собирать в уме
Так я же добивался мультиинсерта, он вроде работает быстрее, чем если каждую запись вставлять по отдельности.
Или сделать одной транзакцией все 100 000 - 1 000 000 вставок? Это будет быстро?
Как вообще работает транзакция? Можно ли вставить миллион записей одной транзакцией, или оно пишется куда-то (в память, на диск, во временную таблицу), где есть лимит?
Черт, ничего не понимаю, за что хоть хвататься. Пойду поем.
>В пхп вообще есть функции в духе "кликнуть на кнопку", "заполнить форму", итд?
Это есть в js.
Это я знаю. С нодой ковырялся, там такое и увидел. В пхп как подобное сделать?
> Мне казалось, что подготовленные запросы нужно использовать только если есть опасность инъекции от пользователя. Зачем мне здесь плейсхолдеры, если нужно только заэкранировать?
во-первых если в тексте содержится одиначная кавычка то она закрывает строку и запрос становится невалидным:
$x = "test'test";
$sql = "SELECT '$x'"; // ошибка синтаксиса
Чтобы этого не было, ты например в случае с PDO должен вызывать функцию $pdo->quote
во-вторых, твой код без плейсхолдеров выглядит, как написали выше «как земля». Мне надо по функции глазами бегать и в голове собирать этот запрос по частям. Да и куча кавычек, скобочек лишних.
Если ты считаешь что биндинг это медленно (и готов подтвердить это тестом) то ты можешь написать свою функцию замены плейсхолдеров, но превращать код в кашу не надо.
> Не понимаю, как это работает. Мне казалось наоборот, они должны замедлять выполнение,
Подготовку можно делать один раз, а выполнение с разными параметрами - много. Если используются настоящие плейсхолдеры то запрос парсится и анализируется на стороне базы один раз, и потом в него многократно подставляются переданные с клиента параметры (впрочем последний раз когда я мерял разницу, выигрыш там копеечный). Если используется эмуляция плейсхолдеров (то есть PDO сам их подставляет и отправляет в базу уже собранный запрос) то выигрыш конечно будет меньше.
Но код может опять же стать чище, так как будет видно что выполняется один запрос, просто с разными данными.
> И как я буду биндить параметры, если у меня мультиинсерт на 10000 записей
Тогда никак, но ты должен сделать свою отдельную функцию мультиинсерта (или посмотреть может в DAO такая уже есть) которая все правильно экранирует. А не смешивать в одной функции и подготовку данных и сборку запроса по частям.
> Или сделать одной транзакцией все 100 000 - 1 000 000 вставок? Это будет быстро?
Не факт
> Как вообще работает транзакция? Можно ли вставить миллион записей одной транзакцией, или оно пишется куда-то (в память, на диск, во временную таблицу), где есть лимит?
Транзакция это изолированный набор изменений, соответствующий принципам ACID:
https://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D1%8F_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0)
https://ru.wikipedia.org/wiki/ACID
Транзакции это важно. В идеале ты должен в своем приложении делать изменения, группируя запросы в транзакции по смыслу (чтобы не могло получиться так что часть изменений запишется, а часть нет). К примеру при вставке объявления и увеличения счетчика объявлений в категории, ты должен объединить это в одну транзакцию. Найди потом время пройтись по своему коду и проверить правильно ли расставлены границы транзакций.
Если этим пренебречь, со временем в базе будут накапливаться несогласованные данные. Например скрипт успел записать часть данных и упал, или сервер перезагрузили, или питание пропало.
В реальном мире частью требований можно жертвовать (менять уровень изоляции транзакций) в пользу производительности: https://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D1%8F_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0)
https://ru.wikipedia.org/wiki/%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B8%D0%B7%D0%BE%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%82%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D0%B9
http://habrahabr.ru/post/135217/
Вопросы по транзакциям любят задавать на собеседованиях. Я считаю это правильно.
Как реализовать изоляцию транзакций и параллельную их работу с минимумом задержек?
В MySQL в InnoDB используется так назваемая мультиверсионность:
https://en.wikipedia.org/wiki/Multiversion_concurrency_control
https://dev.mysql.com/doc/refman/5.0/en/innodb-multi-versioning.html
Там сложная система, но она позволяет получить высокую производительность при параллельной работе (когда несколько пользователей пишут и несколько читают данные), и за счет этой системы потоки, которые читают данные, почти никогда не блокируются. Если делать все «по-простому» (то есть данные транзакции накапливаются в буфере, а при коммите вносятся в таблицу), то было бы много блокировок в момент коммита и при частой записи в базу все бы лежало.
Также, еще одна вещь, которую надо понимать, это то, что MySQL гарантирует сохранность данных после успешного коммита. То есть перед тем как отчитаться что команда COMMIT завершена успешно, mysql сбрасывает данные (данные таблицы, а также изменившиеся индексы) на диск и дожидается от ОС подтверждения что они физически сохранены. Даже если питание выключится, коммит не пропадет (если оно выключится до коммита, то он пропадет, но база останется в согласованном состоянии которое было до коммита, а такого что половина данных записалась, а половина нет, быть не может. Это называется атомарность транзакции).
Соответственно если ты выполняешь запросы по одному, каждый идет отдельной транзакцией и mysql после каждого должна ждать диск. Это неэффективно. Если ты делаешь 100 запросов одной транзакцией, то mysql имеет право накапливать данные в памяти (или писать на диск без подтверждения и без ожидания), а сбросить на диск только в конце транзакции, и это работает быстрее.
Магнитные диски не могут делать больше пары сотен операций случайной записи в секунду (так как диск крутится с конечной скоростью и ты должен ждать пока сектор окажется под головокой). Потому на магнитных дисках как ты не крутись, появляется ограничение на пару сотен транзакций в секунду. На SSD вроде с этим получше, но там есть свои особенности (там чтобы перезаписать блок данных, надо сначала очистить большой кусок диска, а потом восстановить старые данные).
Возвращаясь к MVCC - там для каждой строчки таблицы добавляется несколько скрытых полей, вроде таймстампа (время добавления) и номера транзакции. При изменениях в таблице (update/delete/insert) исходные данные не удаляются, а просто добавляются новые строчки. При выборке если есть несколько версий строки, по id транзакции и таймстампу выбирается самая новая видимая данному пользователю версия. При коммите новая версия становится видна всем, старая становится неактуальной и специальный фоновый тред очищает такие строки и помечает занимаемое ими место как свободное.
Условно говоря, у нас есть таблица такого вида:
id | text
1 | hello
В InnoDB она хранится с id транзакции которая ее вставила и таймстампом:
txn | timestamp | id | text
1000 | 12:00:00 | 1 | hello
Как мы видим эта строчка вставлена транзакцией 1000, которая (допустим) давно закоммичена.
(далее....)
> Мне казалось, что подготовленные запросы нужно использовать только если есть опасность инъекции от пользователя. Зачем мне здесь плейсхолдеры, если нужно только заэкранировать?
во-первых если в тексте содержится одиначная кавычка то она закрывает строку и запрос становится невалидным:
$x = "test'test";
$sql = "SELECT '$x'"; // ошибка синтаксиса
Чтобы этого не было, ты например в случае с PDO должен вызывать функцию $pdo->quote
во-вторых, твой код без плейсхолдеров выглядит, как написали выше «как земля». Мне надо по функции глазами бегать и в голове собирать этот запрос по частям. Да и куча кавычек, скобочек лишних.
Если ты считаешь что биндинг это медленно (и готов подтвердить это тестом) то ты можешь написать свою функцию замены плейсхолдеров, но превращать код в кашу не надо.
> Не понимаю, как это работает. Мне казалось наоборот, они должны замедлять выполнение,
Подготовку можно делать один раз, а выполнение с разными параметрами - много. Если используются настоящие плейсхолдеры то запрос парсится и анализируется на стороне базы один раз, и потом в него многократно подставляются переданные с клиента параметры (впрочем последний раз когда я мерял разницу, выигрыш там копеечный). Если используется эмуляция плейсхолдеров (то есть PDO сам их подставляет и отправляет в базу уже собранный запрос) то выигрыш конечно будет меньше.
Но код может опять же стать чище, так как будет видно что выполняется один запрос, просто с разными данными.
> И как я буду биндить параметры, если у меня мультиинсерт на 10000 записей
Тогда никак, но ты должен сделать свою отдельную функцию мультиинсерта (или посмотреть может в DAO такая уже есть) которая все правильно экранирует. А не смешивать в одной функции и подготовку данных и сборку запроса по частям.
> Или сделать одной транзакцией все 100 000 - 1 000 000 вставок? Это будет быстро?
Не факт
> Как вообще работает транзакция? Можно ли вставить миллион записей одной транзакцией, или оно пишется куда-то (в память, на диск, во временную таблицу), где есть лимит?
Транзакция это изолированный набор изменений, соответствующий принципам ACID:
https://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D1%8F_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0)
https://ru.wikipedia.org/wiki/ACID
Транзакции это важно. В идеале ты должен в своем приложении делать изменения, группируя запросы в транзакции по смыслу (чтобы не могло получиться так что часть изменений запишется, а часть нет). К примеру при вставке объявления и увеличения счетчика объявлений в категории, ты должен объединить это в одну транзакцию. Найди потом время пройтись по своему коду и проверить правильно ли расставлены границы транзакций.
Если этим пренебречь, со временем в базе будут накапливаться несогласованные данные. Например скрипт успел записать часть данных и упал, или сервер перезагрузили, или питание пропало.
В реальном мире частью требований можно жертвовать (менять уровень изоляции транзакций) в пользу производительности: https://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D1%8F_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0)
https://ru.wikipedia.org/wiki/%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B8%D0%B7%D0%BE%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%82%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D0%B9
http://habrahabr.ru/post/135217/
Вопросы по транзакциям любят задавать на собеседованиях. Я считаю это правильно.
Как реализовать изоляцию транзакций и параллельную их работу с минимумом задержек?
В MySQL в InnoDB используется так назваемая мультиверсионность:
https://en.wikipedia.org/wiki/Multiversion_concurrency_control
https://dev.mysql.com/doc/refman/5.0/en/innodb-multi-versioning.html
Там сложная система, но она позволяет получить высокую производительность при параллельной работе (когда несколько пользователей пишут и несколько читают данные), и за счет этой системы потоки, которые читают данные, почти никогда не блокируются. Если делать все «по-простому» (то есть данные транзакции накапливаются в буфере, а при коммите вносятся в таблицу), то было бы много блокировок в момент коммита и при частой записи в базу все бы лежало.
Также, еще одна вещь, которую надо понимать, это то, что MySQL гарантирует сохранность данных после успешного коммита. То есть перед тем как отчитаться что команда COMMIT завершена успешно, mysql сбрасывает данные (данные таблицы, а также изменившиеся индексы) на диск и дожидается от ОС подтверждения что они физически сохранены. Даже если питание выключится, коммит не пропадет (если оно выключится до коммита, то он пропадет, но база останется в согласованном состоянии которое было до коммита, а такого что половина данных записалась, а половина нет, быть не может. Это называется атомарность транзакции).
Соответственно если ты выполняешь запросы по одному, каждый идет отдельной транзакцией и mysql после каждого должна ждать диск. Это неэффективно. Если ты делаешь 100 запросов одной транзакцией, то mysql имеет право накапливать данные в памяти (или писать на диск без подтверждения и без ожидания), а сбросить на диск только в конце транзакции, и это работает быстрее.
Магнитные диски не могут делать больше пары сотен операций случайной записи в секунду (так как диск крутится с конечной скоростью и ты должен ждать пока сектор окажется под головокой). Потому на магнитных дисках как ты не крутись, появляется ограничение на пару сотен транзакций в секунду. На SSD вроде с этим получше, но там есть свои особенности (там чтобы перезаписать блок данных, надо сначала очистить большой кусок диска, а потом восстановить старые данные).
Возвращаясь к MVCC - там для каждой строчки таблицы добавляется несколько скрытых полей, вроде таймстампа (время добавления) и номера транзакции. При изменениях в таблице (update/delete/insert) исходные данные не удаляются, а просто добавляются новые строчки. При выборке если есть несколько версий строки, по id транзакции и таймстампу выбирается самая новая видимая данному пользователю версия. При коммите новая версия становится видна всем, старая становится неактуальной и специальный фоновый тред очищает такие строки и помечает занимаемое ими место как свободное.
Условно говоря, у нас есть таблица такого вида:
id | text
1 | hello
В InnoDB она хранится с id транзакции которая ее вставила и таймстампом:
txn | timestamp | id | text
1000 | 12:00:00 | 1 | hello
Как мы видим эта строчка вставлена транзакцией 1000, которая (допустим) давно закоммичена.
(далее....)
Допустим 3 пользователя подключенных к базе параллельно открывают 3 транзакции. Первый (транзакция 1001) удаляет строчку (и пока не закоммитил транзакцию), второй (1002) и третий (1003) добавляют вторую строчку.Таблица выглядит так:
txn | timestamp | id | text
1000 | 12:00:00 | 1 | hello
1001 | 13:00:00 | 1 | (deleted)
1002 | 13:00:00 | 2 | world1
1003 | 13:00:00| 3 | world2
Пользователь внутри транзакции видит только закоммиченные чужие изменения, а также все свои. То есть транзакция 1001 видит изменения из закомиченной транзакции 1000 и незакомиченной 1001. Если пользователь сделает SELECT из этой транзакции, то база будет брать только строки транзакций 1000 и 1001, причем если эти строки соответствуют одному id, то база берет самую новую версию.
Транзакция 1001 видит только эти строки:
txn | timestamp | id | text
1000 | 12:00:00 | 1 | hello
1001 | 13:00:00 | 1 | (deleted)
Так как id одинаковый то она берет последнюю версию, которая помечена как удаленная. Больше строк нет потому SELECT вернет 0 строк.
Транзакция 1002 видит только строки от транзакций 1000 и свои, 1002. Транзакция 1003 видит строки от транзакций 1000 и 1003. Что они получат при выборке всех строк, подумай сам.
Допустим теперь транзакции 1002 и 1003 отменяются, а 1001 коммитится. Строки для отмененных транзакций стали неактуальными, и их позже удалит сборщик мусора. А так как 1001 теперь закоммичена то любой пользователь видит эту строку. При выборке он получит такие строки:
txn | timestamp | id | text
1000 | 12:00:00 | 1 | hello
1001 | 13:00:00 | 1 | (deleted)
Так как строки имеют общий id, из них выбирается новейшая, а она удалена, потому пользователь видит пустую таблицу.
Строка транзакции 1000 после коммита тран. 1001 стала неактуальной, и ее позже удалит сборщик мусора. А также строку 1001 которую после этого нет смысла хранить.
На первый взгляд сложно, но смотри какое преимущество имеет эта система: транзакции не меняют существующие данные, а только добавляют новые. Получается нет такой ситуации, что 2 транзакции могут писать в одну строку (или одна пишет, а другая читает). У нас есть либо закомиченные ранее строки, из которых идет только чтение, либо видимые только одной транзакции незакомиченные строки, которые принадлежат только ей. Раз так, нам не надо делать блокировки на эти строки и не надо ждать например пока блокировка освободится (а блокировки нужны именно когда есть 2 писателя или писатель + читатель).
Это позволяет выжать максимум производительности при параллельных операциях с таблицой. А ведь веб приложения как правило много процессные/многопоточные (если ты помнишь), и соответственно параллельно выполняется несколько SQL запросов от разных воркеров. Да и сервера как правило содержат много ядер и чтобы их загрузить нужно много потоков.
В MyISAM например нет мультиверсионности. Любая запись в таблицу блокирует ее так, что работать с ней может только писатель, а все остальные ждут. В myISAM писать в таблицу может только один поток, а читать можно только когда никто в нее не пишет. Какая тут параллельность?
В redis тоже нет, но там однопоточное приложение (асинхронное) и параллельный доступ к данным невозможен. В MongoDB нету, там блокируется вся коллекция (до версии 3 - блокировалась вообще вся база) на время записи, но там и транзакций нет.
Обрати внимание что речь тут шла о блокировках для модификации данных. MVCC (почти) не требует их наличия. Но MySQL все равно блокирует строки, которые изменяются или удаляются. Это делается для того чтобы 2 транзакции не могли сделать противоречащие изменения (например записать разные значения в одну и ту же строку) и нарушить ACID. MySQL делает блокировки как минимум в таких случаях:
- есть 2 писателя (UPDATE/DELETE) для 1 и той же строки
- есть писатель + читатель (SELECT) для 1 и той же строки
В этих случаях доступ к строке получает только первый захвативший блокировку, а второй будет ждать пока она освободится. Блокировки снимаются при коммите транзакции. Если их не делать то нарушается изоляция транзакций.
https://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-model.html
http://webew.ru/articles/1383.webew
В особо тяжелых слуаях можно словить взаимную блокировку - deadlock:
https://ru.wikipedia.org/wiki/%D0%92%D0%B7%D0%B0%D0%B8%D0%BC%D0%BD%D0%B0%D1%8F_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0
Например транзакция A хочет апдейтить строку 1, за ней строку 2. Транзакия B хочет апдейтить сначала 2, потом 1. Каждая их них захватит блокировку по одной строке и никогда не получит блокировку по второй. MySQL обнаруживает дедлоки и решает их принудительным откатом одной из транзакций.
Выше я написал что MVCC почти не требует блокировок. Но как написал один опытный анон, зашедший к нам в тред, небольшие блокировки там есть. Например при вставке записи надо выделить новый id и на время выделение счетчик автоинкремента блокируется. В момент коммита надо пометить транзакцию как закоммиченную и какая-то внутренняя структура блокируется.
Подробности о работе и состоянии движка InnoDB можно получить запросом (попробуй это сделать):
SHOW ENGINE InnoDB STATUS\G
Увидеть список соединений и выполняющихся запросов можно командой SHOW FULL PROCESSLIST\G. Убить поток-воркер можно командой KILL 123; (принципы ACID и целостность данных это не нарушит).
Наконец, вернемся к этому вопросу:
> Можно ли вставить миллион записей одной транзакцией, или оно пишется куда-то (в память, на диск, во временную таблицу), где есть лимит?
В InnoDB есть пул страниц (InnoDB buffer pool) в памяти: https://dev.mysql.com/doc/refman/5.5/en/innodb-buffer-pool.html
Размер пула задается в конфиге mysql. В нем хранятся страницы (строки таблиц и куски индексов). При выборке данных mysql читает данные с диска в пул (если их там нет) и в нем уже делает выборку. При записи данные пишутся в пул, а при нехватке места сбрасываются на диск, также они надежно сбрасываются на диск при коммите транзакции.
Потому ответ на твой вопрос: данные пишутся в память пока есть место и на диск. Размер базы ограничен местом на диске, настройками конфига и встроенными ограничениями (вроде того что размер строки ограничен 65536 байтами).
Как ты понимаешь, от объема пула зависит часто ли базе придется обращаться к диску, а диск очень медленный. На серверах БД под пул выделяют 80-90% свободной памяти, также есть мнение что размер пула должен быть не меньше чем размер индексов (и «горячих» данных). Не редкость иметь например 100 Гб пула если на сервере много памяти.
А теперь вопросы для проверки.
1) Выше я написал что когда кто-то записывает данные (например изменяет строку) он должен брать эксклюзивную блокировку на эти данные, и другие не могут к ним обращаться, то есть читать или писать. Почему? Почему MyISAM блокирует таблицу на время апдейта одной строки?
Если это важно, то в MyISAM данные хранятся так: для каждой таблицы и каждого индекса делается отдельный файл и в нем хранятся строки таблиц или записи индекса.
2) Почему MVCC позволяет это не делать?
3) Более сложный вопрос, почему InnoDB блокирует строки, хотя MVCC позволяет это не делать? Почему строки блокируются на все время транзакции (а не на время выполнения запроса)? Что будет если не блокировать?
4) И еще, если программа выбирает какие-то данные (SELECT), меняет их и записывает назад в базу (UPDATE), как запретить другим в это время их менять?
Допустим 3 пользователя подключенных к базе параллельно открывают 3 транзакции. Первый (транзакция 1001) удаляет строчку (и пока не закоммитил транзакцию), второй (1002) и третий (1003) добавляют вторую строчку.Таблица выглядит так:
txn | timestamp | id | text
1000 | 12:00:00 | 1 | hello
1001 | 13:00:00 | 1 | (deleted)
1002 | 13:00:00 | 2 | world1
1003 | 13:00:00| 3 | world2
Пользователь внутри транзакции видит только закоммиченные чужие изменения, а также все свои. То есть транзакция 1001 видит изменения из закомиченной транзакции 1000 и незакомиченной 1001. Если пользователь сделает SELECT из этой транзакции, то база будет брать только строки транзакций 1000 и 1001, причем если эти строки соответствуют одному id, то база берет самую новую версию.
Транзакция 1001 видит только эти строки:
txn | timestamp | id | text
1000 | 12:00:00 | 1 | hello
1001 | 13:00:00 | 1 | (deleted)
Так как id одинаковый то она берет последнюю версию, которая помечена как удаленная. Больше строк нет потому SELECT вернет 0 строк.
Транзакция 1002 видит только строки от транзакций 1000 и свои, 1002. Транзакция 1003 видит строки от транзакций 1000 и 1003. Что они получат при выборке всех строк, подумай сам.
Допустим теперь транзакции 1002 и 1003 отменяются, а 1001 коммитится. Строки для отмененных транзакций стали неактуальными, и их позже удалит сборщик мусора. А так как 1001 теперь закоммичена то любой пользователь видит эту строку. При выборке он получит такие строки:
txn | timestamp | id | text
1000 | 12:00:00 | 1 | hello
1001 | 13:00:00 | 1 | (deleted)
Так как строки имеют общий id, из них выбирается новейшая, а она удалена, потому пользователь видит пустую таблицу.
Строка транзакции 1000 после коммита тран. 1001 стала неактуальной, и ее позже удалит сборщик мусора. А также строку 1001 которую после этого нет смысла хранить.
На первый взгляд сложно, но смотри какое преимущество имеет эта система: транзакции не меняют существующие данные, а только добавляют новые. Получается нет такой ситуации, что 2 транзакции могут писать в одну строку (или одна пишет, а другая читает). У нас есть либо закомиченные ранее строки, из которых идет только чтение, либо видимые только одной транзакции незакомиченные строки, которые принадлежат только ей. Раз так, нам не надо делать блокировки на эти строки и не надо ждать например пока блокировка освободится (а блокировки нужны именно когда есть 2 писателя или писатель + читатель).
Это позволяет выжать максимум производительности при параллельных операциях с таблицой. А ведь веб приложения как правило много процессные/многопоточные (если ты помнишь), и соответственно параллельно выполняется несколько SQL запросов от разных воркеров. Да и сервера как правило содержат много ядер и чтобы их загрузить нужно много потоков.
В MyISAM например нет мультиверсионности. Любая запись в таблицу блокирует ее так, что работать с ней может только писатель, а все остальные ждут. В myISAM писать в таблицу может только один поток, а читать можно только когда никто в нее не пишет. Какая тут параллельность?
В redis тоже нет, но там однопоточное приложение (асинхронное) и параллельный доступ к данным невозможен. В MongoDB нету, там блокируется вся коллекция (до версии 3 - блокировалась вообще вся база) на время записи, но там и транзакций нет.
Обрати внимание что речь тут шла о блокировках для модификации данных. MVCC (почти) не требует их наличия. Но MySQL все равно блокирует строки, которые изменяются или удаляются. Это делается для того чтобы 2 транзакции не могли сделать противоречащие изменения (например записать разные значения в одну и ту же строку) и нарушить ACID. MySQL делает блокировки как минимум в таких случаях:
- есть 2 писателя (UPDATE/DELETE) для 1 и той же строки
- есть писатель + читатель (SELECT) для 1 и той же строки
В этих случаях доступ к строке получает только первый захвативший блокировку, а второй будет ждать пока она освободится. Блокировки снимаются при коммите транзакции. Если их не делать то нарушается изоляция транзакций.
https://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-model.html
http://webew.ru/articles/1383.webew
В особо тяжелых слуаях можно словить взаимную блокировку - deadlock:
https://ru.wikipedia.org/wiki/%D0%92%D0%B7%D0%B0%D0%B8%D0%BC%D0%BD%D0%B0%D1%8F_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0
Например транзакция A хочет апдейтить строку 1, за ней строку 2. Транзакия B хочет апдейтить сначала 2, потом 1. Каждая их них захватит блокировку по одной строке и никогда не получит блокировку по второй. MySQL обнаруживает дедлоки и решает их принудительным откатом одной из транзакций.
Выше я написал что MVCC почти не требует блокировок. Но как написал один опытный анон, зашедший к нам в тред, небольшие блокировки там есть. Например при вставке записи надо выделить новый id и на время выделение счетчик автоинкремента блокируется. В момент коммита надо пометить транзакцию как закоммиченную и какая-то внутренняя структура блокируется.
Подробности о работе и состоянии движка InnoDB можно получить запросом (попробуй это сделать):
SHOW ENGINE InnoDB STATUS\G
Увидеть список соединений и выполняющихся запросов можно командой SHOW FULL PROCESSLIST\G. Убить поток-воркер можно командой KILL 123; (принципы ACID и целостность данных это не нарушит).
Наконец, вернемся к этому вопросу:
> Можно ли вставить миллион записей одной транзакцией, или оно пишется куда-то (в память, на диск, во временную таблицу), где есть лимит?
В InnoDB есть пул страниц (InnoDB buffer pool) в памяти: https://dev.mysql.com/doc/refman/5.5/en/innodb-buffer-pool.html
Размер пула задается в конфиге mysql. В нем хранятся страницы (строки таблиц и куски индексов). При выборке данных mysql читает данные с диска в пул (если их там нет) и в нем уже делает выборку. При записи данные пишутся в пул, а при нехватке места сбрасываются на диск, также они надежно сбрасываются на диск при коммите транзакции.
Потому ответ на твой вопрос: данные пишутся в память пока есть место и на диск. Размер базы ограничен местом на диске, настройками конфига и встроенными ограничениями (вроде того что размер строки ограничен 65536 байтами).
Как ты понимаешь, от объема пула зависит часто ли базе придется обращаться к диску, а диск очень медленный. На серверах БД под пул выделяют 80-90% свободной памяти, также есть мнение что размер пула должен быть не меньше чем размер индексов (и «горячих» данных). Не редкость иметь например 100 Гб пула если на сервере много памяти.
А теперь вопросы для проверки.
1) Выше я написал что когда кто-то записывает данные (например изменяет строку) он должен брать эксклюзивную блокировку на эти данные, и другие не могут к ним обращаться, то есть читать или писать. Почему? Почему MyISAM блокирует таблицу на время апдейта одной строки?
Если это важно, то в MyISAM данные хранятся так: для каждой таблицы и каждого индекса делается отдельный файл и в нем хранятся строки таблиц или записи индекса.
2) Почему MVCC позволяет это не делать?
3) Более сложный вопрос, почему InnoDB блокирует строки, хотя MVCC позволяет это не делать? Почему строки блокируются на все время транзакции (а не на время выполнения запроса)? Что будет если не блокировать?
4) И еще, если программа выбирает какие-то данные (SELECT), меняет их и записывает назад в базу (UPDATE), как запретить другим в это время их менять?
> Знания первой части учебника достаточно для решения этих задач? Или стоит и вторую часть перед ними тоже пройти?
А ты посмотри сам. Первые задачи очень простые, и еще у нас
для первых 10 задач есть робот-проверяльщик!
Я бы советовал параллельно с учебником их смотреть, в первой части задачи простые и много времени не требуют. Первые задачи как раз про замыкания.
Задачи в конце на DOM и jQuery требуют побольше знаний, но зато они уже ближе к жизни.
И еще большая задача на SPA которую я хочу написать уже 3 недели.
Надо в них разобраться и тогда бесить не будет. Теорию почитай, наши задачки на HTML/CSS порешай (там кстати и ссылочка хорошая есть).
>>556568
А ты не решал наши задачи на верстку? Адаптивность обычно заключается в небольших изменениях, чтобы сайтом было удобно поьзоваться с учетом ограничений мобильных платформ.
Например чуть перестроить шапку, сделать вывод товаров одной ветикальной колонкой, сделать меню вертикальными и/или скрыть его за кнопкой. Убедиться что области для клика достаточно крупные и заметные.
Обычно выбирается 1 или 2 порога ширины (например 480 и 900 пикс), при переходе через которые верстка меняется. Реализуется это правилом @media
Между порогами верстка должна быть резиновой.
Вот справочник который поможет тебе выбрать пороги:
http://mydevice.io/devices/
Если у тебя нет адаптивной версии ты должен убрать метатег (viewport) который заявляет о ее наличии. Либо сделать.
Вот смотри как с тобой все плохо: ты скопировал тег не зная что он значит, а оказалось он значит «этот сайт адаптирован и сверстан под мобильные платформы» что не соответствует действительности.
Примеры адаптивных сайтов: http://habrahabr.ru/post/141059/
Если у тебя есть уточняющие вопросы то задавай.
ТЕстировать адаптивность можно открыв инспектор Хрома (Ctrl + Shift + I ) и нажав иконку смартфона. Ну и если у тебя есть смартфон, проверь еще и на нем .
Также можно сделать скриншоты сайта на реальных устройствах тут:
https://www.browserstack.com/screenshots
https://www.browserstack.com/responsive
(это коммерческий сайт но там есть бесплатные возможности).
Надо в них разобраться и тогда бесить не будет. Теорию почитай, наши задачки на HTML/CSS порешай (там кстати и ссылочка хорошая есть).
>>556568
А ты не решал наши задачи на верстку? Адаптивность обычно заключается в небольших изменениях, чтобы сайтом было удобно поьзоваться с учетом ограничений мобильных платформ.
Например чуть перестроить шапку, сделать вывод товаров одной ветикальной колонкой, сделать меню вертикальными и/или скрыть его за кнопкой. Убедиться что области для клика достаточно крупные и заметные.
Обычно выбирается 1 или 2 порога ширины (например 480 и 900 пикс), при переходе через которые верстка меняется. Реализуется это правилом @media
Между порогами верстка должна быть резиновой.
Вот справочник который поможет тебе выбрать пороги:
http://mydevice.io/devices/
Если у тебя нет адаптивной версии ты должен убрать метатег (viewport) который заявляет о ее наличии. Либо сделать.
Вот смотри как с тобой все плохо: ты скопировал тег не зная что он значит, а оказалось он значит «этот сайт адаптирован и сверстан под мобильные платформы» что не соответствует действительности.
Примеры адаптивных сайтов: http://habrahabr.ru/post/141059/
Если у тебя есть уточняющие вопросы то задавай.
ТЕстировать адаптивность можно открыв инспектор Хрома (Ctrl + Shift + I ) и нажав иконку смартфона. Ну и если у тебя есть смартфон, проверь еще и на нем .
Также можно сделать скриншоты сайта на реальных устройствах тут:
https://www.browserstack.com/screenshots
https://www.browserstack.com/responsive
(это коммерческий сайт но там есть бесплатные возможности).
Есть библиотеки. Регулярки не нужны, надо использовать DOM + XPath или библиотеки вроде phpquery.
Ну и надо знать хотя бы основы протокола HTTP.
Мне не нравится твоя идея потому посоветую лучше выучиться и стать нормальным разработчиком, а парсеры оставить школьникам с фриланса.
Какая такая моя идея тебе не нравится? Я просто хочу сделать пару интересных сайтов для портфолио так сказать, не наполнять же их лоремиспумом. Ну да ладно, я уже догадался что нужно сначала взять все ссылки из меню допустим, потом собрать с каждой такой ссылки все ссылки на сами элементы, чем бы они ни были, а потом уже проходить сами элементы. Да и библиотеки уже нагуглил. Черт знает зачем спрашивал, нужно было просто подумать. Скучно поди.
А что если пагинация? Что если сайт с аяксом, и там просто кнопка "загрузить еще", или что еще хуже - элементы добавляются по мере скролла?
Кстати, оп, давно хочу спросить у тебя. А где ты сам-то работаешь? Сколько получаешь сейчас \ получал больше всего? Сколько лет разработкой занимаешься? Над чем сейчас работаешь?
> Может быть было бы лучше если бы ты мне подкидывал какой-нибудь информации, которая расширяла мой кругозор, на самостоятельное изучение,
По линуксу - прежде чем браться за администрирование, надо поставить себе линукс хотя бы в виртуалку и освоиться в командной строке. Паста по установке
https://gist.github.com/codedokode/420c8c12a1edae25f0ec
После того как установишь, надо разобраться с bash и сопустствующими утилитами (cat, grep, find), чтобы научиться делать простые вещи вроде «взять лог веб-сервера и посчитать сколько процентов обращений в нем идет к картинкам» или «удалить все временные файлы старше недели» или «найти все измененные с момента последнего бекапа файлы, заархивировать, зашифровать (по желанию) и закачать на сервер».
Ну и освоить на примере дебиана:
- пакетный менеджер, установка программ (apt-get и apt-cache)
- общую структуру папок в линкусе, где например находятся логи и что такое /dev/sda1
- пользователи, группы и права на файлы. Создание и управление пользователями и правами
- процессы и сигналы, просмотр процессов (top, htop), корректное прибивание (kill, killall)
- управление сервисами: запуск, останов, включение запуска при загрузке. Раньше там были команды типа service start/stop, а сейчас
- блочные устройства, файловые системы и монтирование дисков: mount, /etc/fstab, fsck, mkfs
- мониторинг диска, сети, процессора: http://habrahabr.ru/post/114082/
- сеть, общие понятия и простая диагностика: netstat, ping, traceroute, dig, host , файл hosts
Я к сожалению не дам тебе ссылки или учебника по всем этим темам. Но они есть в сети, погугли по всем этим словам, если сомневаешься, напиши ссылки, я гляну и скажу стоит читать или нет.
После этого ты будешь готов к тому чтобы учиться администрированию, это отдельная наука. Но даже то что написано выше тебе наверняка не раз пригодится, да и ты будешь увереннее себя чувствовать в командной строке.
>>Ну только добавь тут еще проверку живое ли животное
> А где лучше делать эту проверку?
Не там. В коде который берет всех животных на карте и вызывает у них move() надо добавить проверку, что животное еще живо.
> По моему если животное мертво, то его вообще не должно быть на карте (в смысле мире),
Верно. Но ведь мы получаем список всех животных в начале хода, а затем проходимся по нему и вызываем move(). Если животное умерло после того как мы получили список, оно все еще остается в нашей копии.
У тебя может такого кода сейчас нет, значит надо его сделать. Я же просил сделать чтобы было любое число животных, а не только два.
> Получение списка ходов можно тоже в Animal сделать.
Нет, разные животные ходят по разному. Мышь например ходит только по горизонтали и вертикали. Можно сделать обобщенную функцию, которую будут вызывать кошка и мышка, но с разными параметрами.
>>Ты странно учитываешь сумму расстояний до кошек. В чем смысл именно суммирования?
> Сложно объяснить, попробую показать наглядно.
Твоя формула работает в описанных тобой примерах, но будет ли она работать при другом числе и расположении кошек? Всегда ли она выберет лучшую клеточку?
Так как для каждой клеточки мы суммируем расстояния до одних и тем же кошек то фактически мы сравниваем среднее расстояние от клеточки до кошек.
Но всегда ли такой подход будет работать правильно? Мне например кажется что надо брать расстояние до ближайшей кошки. Разве это не логичнее?
А затем уже, если есть несколько клеточек с равным расстояним, добавлять им очки с учетом других факторов, например числа выходов с клеточки или среднего расстояния до кошек.
>>Ну только добавь тут еще проверку живое ли животное
> А где лучше делать эту проверку?
Не там. В коде который берет всех животных на карте и вызывает у них move() надо добавить проверку, что животное еще живо.
> По моему если животное мертво, то его вообще не должно быть на карте (в смысле мире),
Верно. Но ведь мы получаем список всех животных в начале хода, а затем проходимся по нему и вызываем move(). Если животное умерло после того как мы получили список, оно все еще остается в нашей копии.
У тебя может такого кода сейчас нет, значит надо его сделать. Я же просил сделать чтобы было любое число животных, а не только два.
> Получение списка ходов можно тоже в Animal сделать.
Нет, разные животные ходят по разному. Мышь например ходит только по горизонтали и вертикали. Можно сделать обобщенную функцию, которую будут вызывать кошка и мышка, но с разными параметрами.
>>Ты странно учитываешь сумму расстояний до кошек. В чем смысл именно суммирования?
> Сложно объяснить, попробую показать наглядно.
Твоя формула работает в описанных тобой примерах, но будет ли она работать при другом числе и расположении кошек? Всегда ли она выберет лучшую клеточку?
Так как для каждой клеточки мы суммируем расстояния до одних и тем же кошек то фактически мы сравниваем среднее расстояние от клеточки до кошек.
Но всегда ли такой подход будет работать правильно? Мне например кажется что надо брать расстояние до ближайшей кошки. Разве это не логичнее?
А затем уже, если есть несколько клеточек с равным расстояним, добавлять им очки с учетом других факторов, например числа выходов с клеточки или среднего расстояния до кошек.
разбираться в хтмл цсс
Не хояу я на это говно даже время свое тратить. вот есть фронтенд макаки - пусть и разгребают, я бэкендщик и хочу им быть.
Скажи проще ты хочешь быть быдлокодером и не хочшь изучать технологии которые используешь. Никому бенедщики в вакууме не нужны, ладно бы ты был экспертом по блокировкам в mysql или на Си писал демоны держащие миллион соединений, а если ты быдлокодишь на PHP, то это слишком просто, будь любезен и фронтенд освоить.
>>556786
Статью читать не пробовал?
>>556783
Шапку своего макета посмотри на маленькой ширине.
>https://github.com/someApprentice/Cat-and-Mouse/blob/master/index.php#L21
>> <?php $cat->move(); ?>
>> <?php $mouse->move(); ?>
>Лучше вынести это в отдеьную функцию и сделать так:
>
>foreach ($world->getAllAnimals() as $animal) {
>$animal->move();
>}
После этого перестает двигаться кошка. Странно, но если поменять местами добавление животных на карту, и добавлять кошку первой, то перестает двигаться мышка. Почему?
https://github.com/someApprentice/Cat-and-Mouse/blob/master/index.php#L22
https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/World.php#L96
>У тебя как-то все не так. Например функция оценки одного хода, у тебя не вынесена отдельно, а намертво впаяна в цикл оценки ходов
Не могу понять почему плохо использовать цикл тут? Может быть ты имел ввиду что плохо объединять функцию получения всех ходов и функцию оценки этих ходов? Именно ходов. Мне сразу в голову пришло что в эту функцию нужно передавать массив со всеми возможными ходами и проверять каждый ход, а потом возвращать массив с балами.
>У тебя как-то все не так. Например функция оценки одного хода, у тебя не вынесена отдельно, а намертво впаяна в цикл оценки ходов, и она почему-то в базовом классе: https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Animal.php#L94
Не могу понять почему плохо использовать цикл здесь? Может быть ты имел ввиду что нельзя объединять функцию получения всех возможных ходов и оценку ходов?
Именно ходов. Мне сразу пришло в голову что нужно в эту функцию передавать массив со всеми возможными ходами и проверять каждый, а затем возвращать массив с балами.
Упс, макаба выдала ошибку при постинге и затем не обновила тред.
>Далее, само расстояние считается неверно: sqrt. Это декартово расстояние применимо в нашем мире где кошки ходят во все стороны с одинаковой скоростю. Но тут кошка по горизонтали ходит на расстояние 1, а по диагонали на sqrt(2) = 1.41, то есть игровой мир не изотропный. Тут надо считать не декартово расстояние а за сколько ходов до нас доберется кошка
А как посчитать это? Решиться ли это простым округлением?
Кстати, мне кажется что выбирать количество ходов будет не лучшем вариантом, потому что количество ходов у разных животных может быть разным. По моему лучше брать что-то менее относительное, например количество клеток. Или ты это и имел ввиду?
>>556079
>Самый плохой ход тут второй — фактически в лапы кошек. Но по баллам это не очевидно. Если уж и складывать расстояния, то надо складывать обратные расстояния, 1/d, чтобы близкие кошки (как более опасные) давали большой вклад в сумму, а далекие — маленький.
>>556781
>Но всегда ли такой подход будет работать правильно? Мне например кажется что надо брать расстояние до ближайшей кошки. Разве это не логичнее?>>556079
>Наконец одного фактора маловато. Предлагаю взять три, с разными весами (то есть некоторые факторы вносят больший вклад в сумму):
>
>- расстояние до ближайшей кошки в ходах
>- число выходов с клетки (клетка в углу хуже клетки в центре)
>- сумма обратных расстояний (оценивает число и дальность до кошек)
>
>А затем уже, если есть несколько клеточек с равным расстояним, добавлять им очки с учетом других факторов, например числа выходов с клеточки или среднего расстояния до кошек.
Ну можно и это допилить. Только сначала исправлю ошибки чтобы кот работал >>556808
>Я же просил сделать чтобы было любое число животных, а не только два.
Dont worry я сделаю это когда код будет работать хотя бы с двумя. Чтобы несколько животных не создавали хаос, а то я глупенький не разберусь со всеми ошибками сразу.
> бы сделал абстрактный метод getSymbol. Это позволяет легко менять вид животного в зависимости от состояния, а также заставляет всех потомков класса реализовать этот метод.
>менять
А не setSymbol случайно?
И еще, могут ли быть абстрактными свойства? А то я что-то не вижу чтобы на php.net было написано об этом.
Хотели перейти с PHP на Яву и не смогли.
Я не думаю, что Ява такая плохая. Ява в общем-то быстрее PHP при правильном использовании, так что скорее всего они просто с архитектурой накосячили. Наделали модных микросервисов, как я понял из поста.
Ну устроиться за еду в мухосрани это одно. А хочется историй типа: вот учился тут год, а потом переехал в Питер/Москву и устроился сеньором за 10к долларов пассивного дохода.
Что ты хочешь от нас? Тебе уже ответили что на работу после уроков ОПа устроиться реально, дальше дела опыта.
>>556597
> Подготовку запроса можно делать один раз, а выполнение с разными параметрами - много.
Не знал этого, сейчас погуглил, действительно можно сначала сделать prepare одного выражения, а потом в цикле bind и execute.
Но меня смущает количество инсертов.
Это ведь каждый раз новая команда для субд, даже при использовании транзакции?
Мне кажется, что мультиинсерт гораздо эффективнее, то есть собрать длинный запрос в цикле. Ну извините, что выглядит нечитабельно, но производительность ведь важнее?
>код может опять же стать чище, так как будет видно что выполняется один запрос, просто с разными данными
Плевать на чистоту, меня интересует скорость, я не буду 36 часов ждать, пока база наполнится фейковыми данными.
Насчет плейсхолдеров принимаю твои замечания, но что насчет мультиинсерта? Я так и не понял, одобряешь или нет.
Короче, мне делать так:
$command->prepare($sql);
foreach(...) {$command->bindValue(); $command->execute();}
или так как я сделал раньше, то есть в цикле собрать длинный запрос на 10000 вставок (или сколько позволит max_allowed_packet), и один раз execute?
>ты должен сделать свою отдельную функцию мультиинсерта (или посмотреть может в DAO такая уже есть)
Вроде есть. Член знает, как учить фреймворк. То ли мне выучить наизусть десять тысяч свойств и методов, то ли каждый раз гуглить.
Нашел сначала issue по запросу в гугл "yii dao multiple insert"
https://github.com/yiisoft/yii/issues/1255
Затем сам метод
https://github.com/yiisoft/yii/blob/master/framework/db/schema/CDbCommandBuilder.php#L262
Ладно, я так понял, делаем мультиинсерт при помощи метода фреймворка и заключаем все это в транзакцию.
>Если используются настоящие плейсхолдеры...выигрыш там копеечный
>Если используется эмуляция плейсхолдеров...выигрыш конечно будет меньше
Так не понял, использовать эмуляцию или нет? Думаю, ты опечатался, хотел сказать, что с эмуляцией быстрее.
>К примеру при вставке объявления и увеличения счетчика объявлений в категории, ты должен объединить это в одну транзакцию.
Какого счетчика? Разве не проще сделать select count(x) where category_id in ...
У меня счетчик только для просмотров конкретного объявления, вроде бы остановились на редисе для этих целей.
В одну транзакцию должны входить объявление и фото, которые юзер к нему прикрепляет, это да, сделал так с самого начала.
По статье в википедии:
чем отличается механизм теневых страниц от mvcc?
В википедии сказано:
1. "теневые страницы содержат копии тех страниц базы данных на начало транзакции, в которых происходят изменения"
2. "заключающийся в предоставлении каждому пользователю т. н. «снимка» БД, обладающего тем свойством, что вносимые пользователем изменения в БД невидимы другим пользователям до момента фиксации транзакции"
Чем "снимок" (mvcc) отличается от "копии" (теневые страницы)?
Или речь о том, что в механизме теневых страниц копируется только тот фрагмент данных, те строки (так я интерпретирую термин "страницы", который они не объясняют), над которыми производятся изменения? В случае с mvcc происходит копирование всей таблицы(?), благодаря чему можно избежать блокировки. Если я правильно понял.
acid вроде тоже понятно: атомарность (должны быть выполнены все команды в транзакции, иначе откат), изолированность (параллельные модификации тех же данных не должны влиять на текущую транзакцию), надежность (при коммите данные не должны теряться даже при катастрофе). Не совсем понятна идея "согласованности", судя по всему идет речь о целостности данных, плюс соблюдение бизнес-логики: там приводится пример с банковской системой, где в случае перевода денег с одного счета на другой должно быть произведено и списание с одного счета, и начисление на другой. Я правда не понял, причем тут бизнес-логика, соблюдение корректности работы программы это не дело транзакции, а дело программиста. Транзакция со своей стороны гарантирует атомарность, а уж правильные ли данные для нее подготовил программист, это не забота субд, я так считаю. Ну не суть важно.
По поводу реализации в innodb mysql мультиверсионности, то кажется они сохраняют данные по транзакции прямо в модифицируемой таблице в специальных столбиках. (rollback segment).
>Internally, InnoDB adds three fields to each row stored in the database
Непонятно, прямо в таблице создаются эти поля? Нет, думаю скорее всего создаются временные(?) таблицы, куда складываются данные каждой транзакции, мы же выше читали о том, что mvcc реализуется в виде неких "снимков".
Таким образом, если над одними и теми же записями несколько соединений одновременно пытаются выполнить транзакционные изменения, то будут созданы соответствующие записи в этой спец.таблице, и после коммита эти данные уже будут перенесены в главную таблицу.
Или нет?
>Если делать все «по-простому» (то есть данные транзакции накапливаются в буфере, а при коммите вносятся в таблицу), то было бы много блокировок в момент коммита и при частой записи в базу все бы лежало
Значит я понял не совсем верно.
>При изменениях в таблице (update/delete/insert) исходные данные не удаляются, а просто добавляются новые строчки
Эвона как.
>Большая паста 2/2
Пример понятен. Но что будет, если например транзакция #1001 удаляет данные, а #1002 обновляет те же самые данные (с id=1 и text='hello')?
Причем коммитится сначала 1001, а потом попытается закоммититься 1002? Mysql достаточно умный, чтобы при таком казусе превратить апдейт в инсерт?
>Но MySQL все равно блокирует строки, которые изменяются или удаляются.
А, ну да, блокировка (на уровне строк в innodb). Блокировка означает, что конкурирующий запрос будет ждать, пока не выполнится (не закоммитится) текущая транзакция, я так понимаю.
> вопросы для проверки
Э, ты че, это ты должен отвечать, а я задавать тупые вопросы. Ну ладно.
1. Почему MyISAM блокирует таблицу на время апдейта одной строки?
Вероятно, это связано со способом хранения: если каждая таблица хранится в отдельном файле, то наверное есть ограничение от файловой системы на одновременный доступ/модификацию этого файла. А innodb тогда как хранится? Не знаю, короче.
2. Почему MVCC позволяет не блокировать таблицу?
Каждая транзакция работает с отдельным "снимком" базы. Вероятно, эти "снимки" хранятся отдельно от основной таблицы, поэтому с одной таблицей (и с одной строкой) могут работать разные транзакции.
3. почему InnoDB блокирует строки, хотя MVCC позволяет это не делать?
Чтобы избежать нарушения согласованности(?) данных.
Почему строки блокируются на все время транзакции (а не на время выполнения запроса)?
Не понимаю, о чем речь. Какого запроса? Имеешь ввиду, что если транзакция состоит из нескольких запросов, один из которых на модификацию данных, то блокировать модифицируемую строку только на время этого запроса, но не на всю транзакцию? Не знаю.
4. Не знаю.
>>556597
> Подготовку запроса можно делать один раз, а выполнение с разными параметрами - много.
Не знал этого, сейчас погуглил, действительно можно сначала сделать prepare одного выражения, а потом в цикле bind и execute.
Но меня смущает количество инсертов.
Это ведь каждый раз новая команда для субд, даже при использовании транзакции?
Мне кажется, что мультиинсерт гораздо эффективнее, то есть собрать длинный запрос в цикле. Ну извините, что выглядит нечитабельно, но производительность ведь важнее?
>код может опять же стать чище, так как будет видно что выполняется один запрос, просто с разными данными
Плевать на чистоту, меня интересует скорость, я не буду 36 часов ждать, пока база наполнится фейковыми данными.
Насчет плейсхолдеров принимаю твои замечания, но что насчет мультиинсерта? Я так и не понял, одобряешь или нет.
Короче, мне делать так:
$command->prepare($sql);
foreach(...) {$command->bindValue(); $command->execute();}
или так как я сделал раньше, то есть в цикле собрать длинный запрос на 10000 вставок (или сколько позволит max_allowed_packet), и один раз execute?
>ты должен сделать свою отдельную функцию мультиинсерта (или посмотреть может в DAO такая уже есть)
Вроде есть. Член знает, как учить фреймворк. То ли мне выучить наизусть десять тысяч свойств и методов, то ли каждый раз гуглить.
Нашел сначала issue по запросу в гугл "yii dao multiple insert"
https://github.com/yiisoft/yii/issues/1255
Затем сам метод
https://github.com/yiisoft/yii/blob/master/framework/db/schema/CDbCommandBuilder.php#L262
Ладно, я так понял, делаем мультиинсерт при помощи метода фреймворка и заключаем все это в транзакцию.
>Если используются настоящие плейсхолдеры...выигрыш там копеечный
>Если используется эмуляция плейсхолдеров...выигрыш конечно будет меньше
Так не понял, использовать эмуляцию или нет? Думаю, ты опечатался, хотел сказать, что с эмуляцией быстрее.
>К примеру при вставке объявления и увеличения счетчика объявлений в категории, ты должен объединить это в одну транзакцию.
Какого счетчика? Разве не проще сделать select count(x) where category_id in ...
У меня счетчик только для просмотров конкретного объявления, вроде бы остановились на редисе для этих целей.
В одну транзакцию должны входить объявление и фото, которые юзер к нему прикрепляет, это да, сделал так с самого начала.
По статье в википедии:
чем отличается механизм теневых страниц от mvcc?
В википедии сказано:
1. "теневые страницы содержат копии тех страниц базы данных на начало транзакции, в которых происходят изменения"
2. "заключающийся в предоставлении каждому пользователю т. н. «снимка» БД, обладающего тем свойством, что вносимые пользователем изменения в БД невидимы другим пользователям до момента фиксации транзакции"
Чем "снимок" (mvcc) отличается от "копии" (теневые страницы)?
Или речь о том, что в механизме теневых страниц копируется только тот фрагмент данных, те строки (так я интерпретирую термин "страницы", который они не объясняют), над которыми производятся изменения? В случае с mvcc происходит копирование всей таблицы(?), благодаря чему можно избежать блокировки. Если я правильно понял.
acid вроде тоже понятно: атомарность (должны быть выполнены все команды в транзакции, иначе откат), изолированность (параллельные модификации тех же данных не должны влиять на текущую транзакцию), надежность (при коммите данные не должны теряться даже при катастрофе). Не совсем понятна идея "согласованности", судя по всему идет речь о целостности данных, плюс соблюдение бизнес-логики: там приводится пример с банковской системой, где в случае перевода денег с одного счета на другой должно быть произведено и списание с одного счета, и начисление на другой. Я правда не понял, причем тут бизнес-логика, соблюдение корректности работы программы это не дело транзакции, а дело программиста. Транзакция со своей стороны гарантирует атомарность, а уж правильные ли данные для нее подготовил программист, это не забота субд, я так считаю. Ну не суть важно.
По поводу реализации в innodb mysql мультиверсионности, то кажется они сохраняют данные по транзакции прямо в модифицируемой таблице в специальных столбиках. (rollback segment).
>Internally, InnoDB adds three fields to each row stored in the database
Непонятно, прямо в таблице создаются эти поля? Нет, думаю скорее всего создаются временные(?) таблицы, куда складываются данные каждой транзакции, мы же выше читали о том, что mvcc реализуется в виде неких "снимков".
Таким образом, если над одними и теми же записями несколько соединений одновременно пытаются выполнить транзакционные изменения, то будут созданы соответствующие записи в этой спец.таблице, и после коммита эти данные уже будут перенесены в главную таблицу.
Или нет?
>Если делать все «по-простому» (то есть данные транзакции накапливаются в буфере, а при коммите вносятся в таблицу), то было бы много блокировок в момент коммита и при частой записи в базу все бы лежало
Значит я понял не совсем верно.
>При изменениях в таблице (update/delete/insert) исходные данные не удаляются, а просто добавляются новые строчки
Эвона как.
>Большая паста 2/2
Пример понятен. Но что будет, если например транзакция #1001 удаляет данные, а #1002 обновляет те же самые данные (с id=1 и text='hello')?
Причем коммитится сначала 1001, а потом попытается закоммититься 1002? Mysql достаточно умный, чтобы при таком казусе превратить апдейт в инсерт?
>Но MySQL все равно блокирует строки, которые изменяются или удаляются.
А, ну да, блокировка (на уровне строк в innodb). Блокировка означает, что конкурирующий запрос будет ждать, пока не выполнится (не закоммитится) текущая транзакция, я так понимаю.
> вопросы для проверки
Э, ты че, это ты должен отвечать, а я задавать тупые вопросы. Ну ладно.
1. Почему MyISAM блокирует таблицу на время апдейта одной строки?
Вероятно, это связано со способом хранения: если каждая таблица хранится в отдельном файле, то наверное есть ограничение от файловой системы на одновременный доступ/модификацию этого файла. А innodb тогда как хранится? Не знаю, короче.
2. Почему MVCC позволяет не блокировать таблицу?
Каждая транзакция работает с отдельным "снимком" базы. Вероятно, эти "снимки" хранятся отдельно от основной таблицы, поэтому с одной таблицей (и с одной строкой) могут работать разные транзакции.
3. почему InnoDB блокирует строки, хотя MVCC позволяет это не делать?
Чтобы избежать нарушения согласованности(?) данных.
Почему строки блокируются на все время транзакции (а не на время выполнения запроса)?
Не понимаю, о чем речь. Какого запроса? Имеешь ввиду, что если транзакция состоит из нескольких запросов, один из которых на модификацию данных, то блокировать модифицируемую строку только на время этого запроса, но не на всю транзакцию? Не знаю.
4. Не знаю.
> После этого перестает двигаться кошка. Странно, но если поменять местами добавление животных на карту, и добавлять кошку первой, то перестает двигаться мышка.
Разберись, натыкай echo и var_dump, посмотри какие условия выполняются какие нет.
>>556856
Потому что код будет чище если будет отдельно функция оценки варианта хода и отдельно функция оценки всех вариантов, а у тебя все смешано.
Функция оценки хода у каждого животного своя. Функция которая перебирает варианты ходов и вызывает функцию оценки, общая для всех. Соответственно раз она общая ее можно поместить в Animal, а вот функцию оценки нет.
> Мне сразу в голову пришло что в эту функцию нужно передавать массив со всеми возможными ходами и проверять каждый ход, а потом возвращать массив с балами.
Ты смешиваешь в одной функции 2 действия.
>>556859
> почему плохо использовать цикл здесь?
Потому что функция оценки хода должна быть у каждого животного своя. А цикл для всех общий. Потому они должны быть отдельными функциями.
> А как посчитать это? Решиться ли это простым округлением?
Надо брать минимальное из расстояние по горизонтали и вертикали.
Но я не уверен что тогда сложение будет давать правильные цифры.
> количество ходов будет не лучшем вариантом, потому что количество ходов у разных животных может быть разным.
Надо это учитывать, надо считать именно за сколько ходов кошка доберется до клетки.
>>556889
> А не setSymbol случайно?
setSymbol это когда ты хочешь его поменять сам, а getSymbol — возвращает символ животного.
$animal->setSymbol('c');
$x = $animal->getSymbol();
> И еще, могут ли быть абстрактными свойства?
нет
>>556925
Через csv например
>>557032
Посмотри куда форма отправляет данные и посмотри что там.
>>557239
> Мне кажется, что мультиинсерт гораздо эффективнее,
Скорее всего так. Но во-первых мультиинсерты тоже можно объединять, а можно и не объединять, в транзакции, во-вторых у меня не было претензий к мультиинсертам.
У меня претензия что ты вместо того чтобы разбить код на несколько функций набыдлокодил все одной простыней. И что не экранируешь данные при вставке в запрос. И что не нашел готовую функцию мультиинсера в Юи.
Я не против мультиинсертов никоим образом. Но необходимость экранировать данные это не отменяет. и не обходимость сделать отдельную функцию для мультиинсерта тоже (или поискать вдруг она уже есть в Юи - я проверил, есть).
> Плевать на чистоту, меня интересует скорость,
В данном случае большой разницы скорее всего не будет. А код твой читать невозможно, какой смысл если его никто понять не может и проверить.
> но что насчет мультиинсерта? Я так и не понял, одобряешь или нет.
Одобряю но код надо сделать нормально.
> или так как я сделал раньше, то есть в цикле собрать длинный запрос
Использовать функцию мультиинсерта в Юи
> как учить фреймворк
Прежде чем писать свое, погуглить. Также, прочесть весь туториал на сайте:
http://www.yiiframework.com/doc/guide/1.1/ru
http://www.yiiframework.com/doc/guide/1.1/en/index
Ну, я видел другие фреймворки так что примерно знаю что надо искать, ты со временем тоже научишься я думаю.
> Ладно, я так понял, делаем мультиинсерт при помощи метода фреймворка и заключаем все это в транзакцию.
Да. Помни что размер мультиинсерта ограничен размером пакета на сервере mysql и он то ли 1Мб то ли 16 Мб по умолчанию, тебе сообщат если ты его превысишь.
> Так не понял, использовать эмуляцию или нет? Думаю, ты опечатался, хотел сказать, что с эмуляцией быстрее.
С чего быстрее-то? Эмуляция это для тех баз которые не поддерживают плейсхолдеры и при ее использовании PDO сам подставляет данные и шлет каждый раз новый запрос. А без эмуляции он делает prepare/execute на стороне базы что и дает микроэкономию.
>>К примеру при вставке объявления и увеличения счетчика объявлений в категории, ты должен объединить это в одну транзакцию.
> Какого счетчика?
Это к примеру. Нет, SELECT COUNT не всегда проще хотя это правильее с точки зрения нормализации.
> А как посчитать это? Решиться ли это простым округлением?
Надо брать минимальное из расстояние по горизонтали и вертикали.
Но я не уверен что тогда сложение будет давать правильные цифры.
> количество ходов будет не лучшем вариантом, потому что количество ходов у разных животных может быть разным.
Надо это учитывать, надо считать именно за сколько ходов кошка доберется до клетки.
>>556889
> А не setSymbol случайно?
setSymbol это когда ты хочешь его поменять сам, а getSymbol — возвращает символ животного.
$animal->setSymbol('c');
$x = $animal->getSymbol();
> И еще, могут ли быть абстрактными свойства?
нет
>>556925
Через csv например
>>557032
Посмотри куда форма отправляет данные и посмотри что там.
>>557239
> Мне кажется, что мультиинсерт гораздо эффективнее,
Скорее всего так. Но во-первых мультиинсерты тоже можно объединять, а можно и не объединять, в транзакции, во-вторых у меня не было претензий к мультиинсертам.
У меня претензия что ты вместо того чтобы разбить код на несколько функций набыдлокодил все одной простыней. И что не экранируешь данные при вставке в запрос. И что не нашел готовую функцию мультиинсера в Юи.
Я не против мультиинсертов никоим образом. Но необходимость экранировать данные это не отменяет. и не обходимость сделать отдельную функцию для мультиинсерта тоже (или поискать вдруг она уже есть в Юи - я проверил, есть).
> Плевать на чистоту, меня интересует скорость,
В данном случае большой разницы скорее всего не будет. А код твой читать невозможно, какой смысл если его никто понять не может и проверить.
> но что насчет мультиинсерта? Я так и не понял, одобряешь или нет.
Одобряю но код надо сделать нормально.
> или так как я сделал раньше, то есть в цикле собрать длинный запрос
Использовать функцию мультиинсерта в Юи
> как учить фреймворк
Прежде чем писать свое, погуглить. Также, прочесть весь туториал на сайте:
http://www.yiiframework.com/doc/guide/1.1/ru
http://www.yiiframework.com/doc/guide/1.1/en/index
Ну, я видел другие фреймворки так что примерно знаю что надо искать, ты со временем тоже научишься я думаю.
> Ладно, я так понял, делаем мультиинсерт при помощи метода фреймворка и заключаем все это в транзакцию.
Да. Помни что размер мультиинсерта ограничен размером пакета на сервере mysql и он то ли 1Мб то ли 16 Мб по умолчанию, тебе сообщат если ты его превысишь.
> Так не понял, использовать эмуляцию или нет? Думаю, ты опечатался, хотел сказать, что с эмуляцией быстрее.
С чего быстрее-то? Эмуляция это для тех баз которые не поддерживают плейсхолдеры и при ее использовании PDO сам подставляет данные и шлет каждый раз новый запрос. А без эмуляции он делает prepare/execute на стороне базы что и дает микроэкономию.
>>К примеру при вставке объявления и увеличения счетчика объявлений в категории, ты должен объединить это в одну транзакцию.
> Какого счетчика?
Это к примеру. Нет, SELECT COUNT не всегда проще хотя это правильее с точки зрения нормализации.
по теневым старницам:
https://en.wikipedia.org/wiki/Shadow_paging
Я понял это так: на диске лежит файл с таблицей. Транзакция, меняя данные, считывает части этого файла (страницы) в свою приватную область памяти и меняет там данные, но не сбрасывает обратно на диск до коммита. Таким образом эта транзакция видит свои незакомиченные изменения, а другие видят исходную неизмененную таблицу на диске.
После коммита измененные страницы сбрасываются на диск. Этот сброс требует блокировки либо всей таблицы либо как минимум этих страниц с частью таблицы, как я понимаю.
То есть механизм другой. Идея MVCC именно в том что мы никогда не правим записанные данные, мы только можем дописывать новые строки (ну и собирать устаревшие строки сборщиком мусора). И потому нам не нужны блокировки.
> "заключающийся в предоставлении каждому пользователю т. н. «снимка» БД, обладающего тем свойством, что вносимые пользователем изменения в БД невидимы другим пользователям до момента фиксации транзакции"
Ну вот данные на диске + измененные страницы в памяти это и есть снимок (снапшот) базы, который видит данная транзакция. Я думаю, автор хочет сказать что транзакция не делает себе полную копию базы (которая может в принципе и не влезть туда), а делает копии только тех частей (страницы) которые она меняет, ради экономии памяти. А те страницы которые она не меняла, она всегда может взять с диска если понадобятся.
> Или речь о том, что в механизме теневых страниц копируется только тот фрагмент данных, те строки (так я интерпретирую термин "страницы", который они не объясняют), над которыми производятся изменения?
Да
> В случае с mvcc происходит копирование всей таблицы(?), благодаря чему можно избежать блокировки. Если я правильно понял.
Неправильно. В случае MVCC приватные копии данных вообще не нужны, так как все транзакции работают (и дописывают данные) с общей большой таблицей (которая хранится в файле + частично в памяти), но не трогают чужие строки (те которые ринадлежат чужим незакомиченным пока транзакциям).
Блокировок и проблем нет так как в MVCC транзакции не меняют и не удаляют существующие строки (которые в данный момент может читать кто-то еще) а только дописывают новые.
В случае теневых страниц транзакция делает приватные копии частей таблицы которые она меняет. Чтобы прочитать страницу с диска, нужно взять на нее блокировку (иначе может эту страницу переписывает в этот момент другая транзакция). Чтобы записать при коммите измененные страницы. нужно заблокировать их (иначе может их сейчас пытается прочесть другая транзакция).
MVCC не требует таких блокировок так как ничего не перезапиывает. а только дописывает новые строки в конец.
по теневым старницам:
https://en.wikipedia.org/wiki/Shadow_paging
Я понял это так: на диске лежит файл с таблицей. Транзакция, меняя данные, считывает части этого файла (страницы) в свою приватную область памяти и меняет там данные, но не сбрасывает обратно на диск до коммита. Таким образом эта транзакция видит свои незакомиченные изменения, а другие видят исходную неизмененную таблицу на диске.
После коммита измененные страницы сбрасываются на диск. Этот сброс требует блокировки либо всей таблицы либо как минимум этих страниц с частью таблицы, как я понимаю.
То есть механизм другой. Идея MVCC именно в том что мы никогда не правим записанные данные, мы только можем дописывать новые строки (ну и собирать устаревшие строки сборщиком мусора). И потому нам не нужны блокировки.
> "заключающийся в предоставлении каждому пользователю т. н. «снимка» БД, обладающего тем свойством, что вносимые пользователем изменения в БД невидимы другим пользователям до момента фиксации транзакции"
Ну вот данные на диске + измененные страницы в памяти это и есть снимок (снапшот) базы, который видит данная транзакция. Я думаю, автор хочет сказать что транзакция не делает себе полную копию базы (которая может в принципе и не влезть туда), а делает копии только тех частей (страницы) которые она меняет, ради экономии памяти. А те страницы которые она не меняла, она всегда может взять с диска если понадобятся.
> Или речь о том, что в механизме теневых страниц копируется только тот фрагмент данных, те строки (так я интерпретирую термин "страницы", который они не объясняют), над которыми производятся изменения?
Да
> В случае с mvcc происходит копирование всей таблицы(?), благодаря чему можно избежать блокировки. Если я правильно понял.
Неправильно. В случае MVCC приватные копии данных вообще не нужны, так как все транзакции работают (и дописывают данные) с общей большой таблицей (которая хранится в файле + частично в памяти), но не трогают чужие строки (те которые ринадлежат чужим незакомиченным пока транзакциям).
Блокировок и проблем нет так как в MVCC транзакции не меняют и не удаляют существующие строки (которые в данный момент может читать кто-то еще) а только дописывают новые.
В случае теневых страниц транзакция делает приватные копии частей таблицы которые она меняет. Чтобы прочитать страницу с диска, нужно взять на нее блокировку (иначе может эту страницу переписывает в этот момент другая транзакция). Чтобы записать при коммите измененные страницы. нужно заблокировать их (иначе может их сейчас пытается прочесть другая транзакция).
MVCC не требует таких блокировок так как ничего не перезапиывает. а только дописывает новые строки в конец.
Дополню еще по теневым страницам: я немного не так описал процесс сброса данных при коммите:
> Shadow paging is a copy-on-write technique for avoiding in-place updates of pages. Instead, when a page is to be modified, a shadow page is allocated. Since the shadow page has no references (from other pages on disk), it can be modified liberally, without concern for consistency constraints, etc. When the page is ready to become durable, all pages that referred to the original are updated to refer to the new replacement page instead. Because the page is "activated" only when it is ready, it is atomic.
Транзакция добавляет в файл (в конец или в свободное место) новую страницу, копирует в нее данные из исходной и делает с ними что хочет (конфликтов быть не может так как на новую страницу нет указателей и с ней работает только эта транзакция). При коммите транзакция меняет указатели (я не знаю, где они, может там есть что-то вроде оглавления например) которые ссылались на старую страницу, вписывая в них адрес новой страницы.
Ну и далее в вики описаны варианты оптимизаций этой техники.
> атомарность (должны быть выполнены все команды в транзакции, иначе откат)
Лучше так: либо применяются все указанные изменения, либо ни одного. То есть не может быть что мы у одного человека деньги со счета сняли, а другому не добавили.
Замечу что в mysql не все запросы подчинаются этому правилу, некоторые виды запросов вызывают неявный коммит (например запросы создания и модификации тадблиц ALTER TABLE, также по моему TRUNCATE).
Это не очень удобно, если у тебя есть миграция из нескольких ALTER TABLE и один из запросов падает, миграция оказывается выполненной наполовину. И нужно ручное вмешательство.
> изолированность
Это то, что незакомиченные изменения видит только транзакция которая их сделала. А закомиченные — все.
> Не совсем понятна идея "согласованности"
Пример с банком относится к базам которые позволяют указывать условия которые должны всегда выполняться (вроде сумма всех денег на счетах равна X или число строк в таблице равно Y, в mysql такого нет).
В Mysql таким условием является например то что внешний ключ должен указывать на существующую запись. Или что в колонке с уникальным индексом нет повторяющихся значений. Согласованность значит что до и после транзакции эти условия должны соблюдаться (все внешние ключи должны быть верные). А вот должна ли соблюдаться внутри транзакции — этого ACID не требует.
В mysql впрочем внешние и уникальные ключи проверяются не на границах транзакций а на границе запроса. То есть временно их нарушить не получится (недостаток это или преимущество?). И соответственно для mysql согласованность следует из атомарности.
Впрочем, пожалуюсь еще, в mysql можно временно отключить проверку внешних ключей, и после включения никто их не перепроверяет, то есть при желании нарушить целостность можно. Это плохо.
> Транзакция со своей стороны гарантирует атомарность, а уж правильные ли данные для нее подготовил программист, это не забота субд
нет. соблюдение наложенных на базу ограничений как раз забота БД.
К примеру в некоторых базах ты можешь указать для колонки выражение с помощью слова CHECK и база будет проверять что оно выполняется для всех вставляемых значений:
CREATE TABLE ...
level INT NOT NULL CHECK (level BETWEEN 1 AND 5),
...
В mysql слово CHECK игнорируется и условие не проверяется.
Оно работает в postgres например: http://www.postgresql.org/docs/8.1/static/ddl-constraints.html
Дополню еще по теневым страницам: я немного не так описал процесс сброса данных при коммите:
> Shadow paging is a copy-on-write technique for avoiding in-place updates of pages. Instead, when a page is to be modified, a shadow page is allocated. Since the shadow page has no references (from other pages on disk), it can be modified liberally, without concern for consistency constraints, etc. When the page is ready to become durable, all pages that referred to the original are updated to refer to the new replacement page instead. Because the page is "activated" only when it is ready, it is atomic.
Транзакция добавляет в файл (в конец или в свободное место) новую страницу, копирует в нее данные из исходной и делает с ними что хочет (конфликтов быть не может так как на новую страницу нет указателей и с ней работает только эта транзакция). При коммите транзакция меняет указатели (я не знаю, где они, может там есть что-то вроде оглавления например) которые ссылались на старую страницу, вписывая в них адрес новой страницы.
Ну и далее в вики описаны варианты оптимизаций этой техники.
> атомарность (должны быть выполнены все команды в транзакции, иначе откат)
Лучше так: либо применяются все указанные изменения, либо ни одного. То есть не может быть что мы у одного человека деньги со счета сняли, а другому не добавили.
Замечу что в mysql не все запросы подчинаются этому правилу, некоторые виды запросов вызывают неявный коммит (например запросы создания и модификации тадблиц ALTER TABLE, также по моему TRUNCATE).
Это не очень удобно, если у тебя есть миграция из нескольких ALTER TABLE и один из запросов падает, миграция оказывается выполненной наполовину. И нужно ручное вмешательство.
> изолированность
Это то, что незакомиченные изменения видит только транзакция которая их сделала. А закомиченные — все.
> Не совсем понятна идея "согласованности"
Пример с банком относится к базам которые позволяют указывать условия которые должны всегда выполняться (вроде сумма всех денег на счетах равна X или число строк в таблице равно Y, в mysql такого нет).
В Mysql таким условием является например то что внешний ключ должен указывать на существующую запись. Или что в колонке с уникальным индексом нет повторяющихся значений. Согласованность значит что до и после транзакции эти условия должны соблюдаться (все внешние ключи должны быть верные). А вот должна ли соблюдаться внутри транзакции — этого ACID не требует.
В mysql впрочем внешние и уникальные ключи проверяются не на границах транзакций а на границе запроса. То есть временно их нарушить не получится (недостаток это или преимущество?). И соответственно для mysql согласованность следует из атомарности.
Впрочем, пожалуюсь еще, в mysql можно временно отключить проверку внешних ключей, и после включения никто их не перепроверяет, то есть при желании нарушить целостность можно. Это плохо.
> Транзакция со своей стороны гарантирует атомарность, а уж правильные ли данные для нее подготовил программист, это не забота субд
нет. соблюдение наложенных на базу ограничений как раз забота БД.
К примеру в некоторых базах ты можешь указать для колонки выражение с помощью слова CHECK и база будет проверять что оно выполняется для всех вставляемых значений:
CREATE TABLE ...
level INT NOT NULL CHECK (level BETWEEN 1 AND 5),
...
В mysql слово CHECK игнорируется и условие не проверяется.
Оно работает в postgres например: http://www.postgresql.org/docs/8.1/static/ddl-constraints.html
>>Internally, InnoDB adds three fields to each row stored in the database
> Непонятно, прямо в таблице создаются эти поля?
Да. Посмотри на мой пример с 2 дописанными полями (я не стал дописвыать третье так как у меня есть первичный ключ по которому можно найти одинаковые записи).
> в innodb mysql мультиверсионности, то кажется они сохраняют данные по транзакции прямо в модифицируемой таблице в специальных столбиках. (rollback segment).
Нет, это другое.
> ? Нет, думаю скорее всего создаются временные(?) таблицы, куда складываются данные каждой транзакции, мы же выше читали о том, что mvcc реализуется в виде неких "снимков".
Нету никаких снимков. снимок получится если ты прочитаешь таблицу, пропуская принадлежащие незакоммиченным транзакциям строки и выбирая самую новую версию для каждой строки.
> Таким образом, если над одними и теми же записями несколько соединений одновременно пытаются выполнить транзакционные изменения, то будут созданы соответствующие записи в этой спец.таблице, и после коммита эти данные уже будут перенесены в главную таблицу.
Нет, MVCC подразумевает что таблица одна, все с ней работают, и не мешают друг другу благодаря этим скрытым полям (транзакция не читает строки которые принадлежат незакомиченным транзакциям и как бы «не замечает» их). Благодаря тому что никто не правит закоммиченные данные, а только дописывает новые.
> Пример понятен. Но что будет, если например транзакция #1001 удаляет данные, а #1002 обновляет те же самые данные (с id=1 и text='hello')?
Это нарушает правила ACID (или какие-то еще), потому как я написал, mysql вводит дополнительные блокировки. Если ты читаешь строку, ты блокируешь ее разделяемой блокировкой (другие могут читать, писать никто не может) на время выполнения запроса. Если ты пишешь данные, то берешь эксклюзивную блокировку (никто не имеет права к ним обращаться) до коммита.
Потому та транзакция которая будет первой, возьмет эксклюзивную блокировку, а вторая при попытке ее взять заблокируется до момента коммита первой. Но эти блокировки не из-за MVCC а ради предотвращения конфликта.
> Mysql достаточно умный, чтобы при таком казусе превратить апдейт в инсерт?
Нет, и я не уверен что это равнозначная замена.
> Вероятно, это связано со способом хранения: если каждая таблица хранится в отдельном файле, то наверное есть ограничение от файловой системы на одновременный доступ/модификацию этого файла.
Нет, ограничений нет. Блокировка нужна так как изменения вносятся не атомарно. Представь что у нас есть строчка с текстом hello и мы меняем его на world. Ты не можешь поменять ее атомарно, ты идешь и меняешь по одной букве.
Если в это время второй поток попробует прочитать строку, он может получить полуперезаписанную строку:
wo|llo
А если в это время третий поток пытается заменить hello на goodbuy, мы вообще можем сохранить в итоге смесь goodbuy и world:
wor|dbuy
Это мы меняем строку. А в файле гораздо более сложная структура, там есть разные указатели, также например мы должны поменять значение в строке, а также перестроить и пересортировать части индексов, в которых было старое значение. И все это атомарно чтобы никто не увидел несогласованные полузаписанные данные.
Потому если есть больше 2 параллельных потоков и один из них хочет писать данные, он должен брать эксклюзивную блокировку (чтобы никто не видел полузаписанные данные). Если есть больше 2 потоков и они читают данные, они должны брать разделяемую блокировку. Зачем? Чтобы когда придет поток-писатель, он знал что данные сейчас кто-то читает и нельзя их сейчас перезаписывать, надо подождать.
Если есть время, ты можешь сделать самодельную «базу» на основе текстового файла и допустим менять в ней строки (с помощью fseek и fwrite). Если запустить 2-3 скрипта параллельно, со временем файл превратится в мусор.
Блокировки решают проблему, но на них при большом числе потоков приходится много ждать, теряется вся выгода от многопоточности.
В MVCC обходятся без блокировок за счет того что никто не перезаписывает строки. В MyISAM блокировки обязательны, на всю таблицу (причем блокируется таблица + ее индексы чтобы никто не увидел несогласованные данные между ними).
> 2. Почему MVCC позволяет не блокировать таблицу?
> Каждая транзакция работает с отдельным "снимком" базы. Вероятно, эти "снимки" хранятся отдельно от основной таблицы, поэтому с одной таблицей (и с одной строкой) могут работать разные транзакции.
Неверно, попробуй еще.
> Почему строки блокируются на все время транзакции (а не на время выполнения запроса)?
> Не понимаю, о чем речь. Какого запроса? Имеешь ввиду, что если транзакция состоит из нескольких запросов, один из которых на модификацию данных, то блокировать модифицируемую строку только на время этого запроса, но не на всю транзакцию? Не знаю.
расмотри пример в википедии: https://ru.wikipedia.org/wiki/%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B8%D0%B7%D0%BE%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%82%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D0%B9#.D0.9F.D0.BE.D1.82.D0.B5.D1.80.D1.8F.D0.BD.D0.BD.D0.BE.D0.B5_.D0.BE.D0.B1.D0.BD.D0.BE.D0.B2.D0.BB.D0.B5.D0.BD.D0.B8.D0.B5
представь что 2 транзакции выполняют эти 2 запроса, по шагам (причем с разной скоростью и непредсказуемыми паузами). Попробуй рассмотреть что может получиться.
Если там не делать блокировку строчки до конца транзакции, возможны потери одного из обновлений. Обновляемые данные блокируются до конца транзакции, читаемые по умолчанию до конца запроса (чтобы заблокировать их до конца транзакции надо попросить это явно).
> 4. Не знаю.
Погугли SELECT FOR UPDATE и SELECT .. SHARED LOCK
>>Internally, InnoDB adds three fields to each row stored in the database
> Непонятно, прямо в таблице создаются эти поля?
Да. Посмотри на мой пример с 2 дописанными полями (я не стал дописвыать третье так как у меня есть первичный ключ по которому можно найти одинаковые записи).
> в innodb mysql мультиверсионности, то кажется они сохраняют данные по транзакции прямо в модифицируемой таблице в специальных столбиках. (rollback segment).
Нет, это другое.
> ? Нет, думаю скорее всего создаются временные(?) таблицы, куда складываются данные каждой транзакции, мы же выше читали о том, что mvcc реализуется в виде неких "снимков".
Нету никаких снимков. снимок получится если ты прочитаешь таблицу, пропуская принадлежащие незакоммиченным транзакциям строки и выбирая самую новую версию для каждой строки.
> Таким образом, если над одними и теми же записями несколько соединений одновременно пытаются выполнить транзакционные изменения, то будут созданы соответствующие записи в этой спец.таблице, и после коммита эти данные уже будут перенесены в главную таблицу.
Нет, MVCC подразумевает что таблица одна, все с ней работают, и не мешают друг другу благодаря этим скрытым полям (транзакция не читает строки которые принадлежат незакомиченным транзакциям и как бы «не замечает» их). Благодаря тому что никто не правит закоммиченные данные, а только дописывает новые.
> Пример понятен. Но что будет, если например транзакция #1001 удаляет данные, а #1002 обновляет те же самые данные (с id=1 и text='hello')?
Это нарушает правила ACID (или какие-то еще), потому как я написал, mysql вводит дополнительные блокировки. Если ты читаешь строку, ты блокируешь ее разделяемой блокировкой (другие могут читать, писать никто не может) на время выполнения запроса. Если ты пишешь данные, то берешь эксклюзивную блокировку (никто не имеет права к ним обращаться) до коммита.
Потому та транзакция которая будет первой, возьмет эксклюзивную блокировку, а вторая при попытке ее взять заблокируется до момента коммита первой. Но эти блокировки не из-за MVCC а ради предотвращения конфликта.
> Mysql достаточно умный, чтобы при таком казусе превратить апдейт в инсерт?
Нет, и я не уверен что это равнозначная замена.
> Вероятно, это связано со способом хранения: если каждая таблица хранится в отдельном файле, то наверное есть ограничение от файловой системы на одновременный доступ/модификацию этого файла.
Нет, ограничений нет. Блокировка нужна так как изменения вносятся не атомарно. Представь что у нас есть строчка с текстом hello и мы меняем его на world. Ты не можешь поменять ее атомарно, ты идешь и меняешь по одной букве.
Если в это время второй поток попробует прочитать строку, он может получить полуперезаписанную строку:
wo|llo
А если в это время третий поток пытается заменить hello на goodbuy, мы вообще можем сохранить в итоге смесь goodbuy и world:
wor|dbuy
Это мы меняем строку. А в файле гораздо более сложная структура, там есть разные указатели, также например мы должны поменять значение в строке, а также перестроить и пересортировать части индексов, в которых было старое значение. И все это атомарно чтобы никто не увидел несогласованные полузаписанные данные.
Потому если есть больше 2 параллельных потоков и один из них хочет писать данные, он должен брать эксклюзивную блокировку (чтобы никто не видел полузаписанные данные). Если есть больше 2 потоков и они читают данные, они должны брать разделяемую блокировку. Зачем? Чтобы когда придет поток-писатель, он знал что данные сейчас кто-то читает и нельзя их сейчас перезаписывать, надо подождать.
Если есть время, ты можешь сделать самодельную «базу» на основе текстового файла и допустим менять в ней строки (с помощью fseek и fwrite). Если запустить 2-3 скрипта параллельно, со временем файл превратится в мусор.
Блокировки решают проблему, но на них при большом числе потоков приходится много ждать, теряется вся выгода от многопоточности.
В MVCC обходятся без блокировок за счет того что никто не перезаписывает строки. В MyISAM блокировки обязательны, на всю таблицу (причем блокируется таблица + ее индексы чтобы никто не увидел несогласованные данные между ними).
> 2. Почему MVCC позволяет не блокировать таблицу?
> Каждая транзакция работает с отдельным "снимком" базы. Вероятно, эти "снимки" хранятся отдельно от основной таблицы, поэтому с одной таблицей (и с одной строкой) могут работать разные транзакции.
Неверно, попробуй еще.
> Почему строки блокируются на все время транзакции (а не на время выполнения запроса)?
> Не понимаю, о чем речь. Какого запроса? Имеешь ввиду, что если транзакция состоит из нескольких запросов, один из которых на модификацию данных, то блокировать модифицируемую строку только на время этого запроса, но не на всю транзакцию? Не знаю.
расмотри пример в википедии: https://ru.wikipedia.org/wiki/%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B8%D0%B7%D0%BE%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%82%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D0%B9#.D0.9F.D0.BE.D1.82.D0.B5.D1.80.D1.8F.D0.BD.D0.BD.D0.BE.D0.B5_.D0.BE.D0.B1.D0.BD.D0.BE.D0.B2.D0.BB.D0.B5.D0.BD.D0.B8.D0.B5
представь что 2 транзакции выполняют эти 2 запроса, по шагам (причем с разной скоростью и непредсказуемыми паузами). Попробуй рассмотреть что может получиться.
Если там не делать блокировку строчки до конца транзакции, возможны потери одного из обновлений. Обновляемые данные блокируются до конца транзакции, читаемые по умолчанию до конца запроса (чтобы заблокировать их до конца транзакции надо попросить это явно).
> 4. Не знаю.
Погугли SELECT FOR UPDATE и SELECT .. SHARED LOCK
>Через csv например
Пробовал через csv импортировать в pma, но он ругается на пустые значения в столбцах.
кто ругается? mysql? Значит сделай что бы значения в столбцах могли быть null
Ну ты понял. У тебя логически все должно совпадать. Если ты импортируешь лист с пустыми значениями в базу, в которой везде стоит not null, то ты его либо заполняешь сначала какими-нибудь нулями, ну либо меняешь устройство базы.
Как правильно разворачивать проекты на yii2, если у тебя они в репозитории на гите?
Допустим работал я на одном пк, потом перекатился на другой, естественно гит не подхватывает все файлы, да и не нужно. Папка vendor заполняется с помощью композера, но как быть с другими файлами, которые попадают в gitignore? Просто убрать их оттуда, или же ебаться в ручную? Может еще есть какие варианты?
>Также, мне не нравится идея генерировать имя функции так:
> $imgCreateFunc = "imagecreatefrom".$this->extension;
>Как-то это не очень надежно, ведь определение расширения у тебя в обном месте, а эта строчка в другом. Вдруг какой-нибудь программист добавит новое расширение, а на то как оно используется, не обратит внимание? Я думаю, тогда в классе можно сделать поле imgCreateFunc и присваиваеть ему значение в том же switch где и определяется расширение. Аналогично сделать поле для функции сохранения файла.
Сделал $imgCreateFunc полем класса, но проблема в том, что когда я обращаюсь к этому полю как к функции $this->imgCreateFunc(аргумент), то скрипт ищет метод с таким именем,а не поле, в связи с чем возникает ошибка Call to undefined method Thumbnail::imgCreateFunc()
это потому что я гетар.
С третьего раза вроде дошло по поводу подготовленных выражений и эмуляции pdo.
При использовании подготовленного выражения (при отключенной эмуляции) php отправляет на сервер шаблон запроса, например
$sql = "INSERT INTO user (name, password, email) VALUES (:name, :password, :email)";
$sth = $dbh->prepare($sql);
На сервере строится план выполнения запроса, и прочие внутренние дела, в которые я не вникаю. Но суть в том, что пока открыто соединение, от pdo требуется только присылать данные для подстановки плейсхолдеров.
foreach ($users as $user) {
$sth->bindValue(':name', $user->name);
$sth->bindValue(':password', $user->password);
$sth->bindValue(':email', $user->email);
$sth->execute();
}
Execute здесь только отсылает данные для привязки, а операции подстановки выполняются на сервере.
Если включить эмуляцию, то pdo при execute каждый раз шлет новый запрос. При этом сначала экранируются и подставляются плейсхолдеры на стороне php.
Значит, эмуляцию вообще не нужно использовать, только в случае, когда база не поддерживает плейсхолдеры.
А как ее отключить? Вроде бы через setAttribute, но что-то я не уверен, что это корректно работает.
Пробовал наоборот включить ради теста
$pdo->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );
$res = $pdo->getAttribute( PDO::ATTR_EMULATE_PREPARES );
var_dump($res);
Выдает false.
По умолчанию эмуляция включена или нет? В мануале php не сказано.
http://php.net/manual/ru/pdo.setattribute.php
>Если драйвер не сможет подготовить запрос, эта настройка сбросится в режим эмуляции.
Судя по этой строке, по умолчанию эмуляция выключена, но если база не сможет подготовить запрос, только тогда включится эмуляция.
В yii кстати по умолчанию эта настройка в конфиге выставлена в true (emulatePrepare=>on)
Вероятно это сделано для старых версий mysql (или других баз, не поддерживающих подтготовленные выражения).
Пост на stackoverflow http://stackoverflow.com/questions/10113562/pdo-mysql-use-pdoattr-emulate-prepares-or-not/10455228#10455228
>прочесть весь туториал на сайте
А теперь покажи мне то место в туториале, где говорится о методе для мультиинсерта или например про эмуляцию подготовленных выражений.
Туториал отстой, читал я его несколько раз, там только беглый обзор возможностей фреймворка и особенности реализации тех или иных фич.
С другой стороны, для большого фреймворка действительно непонятно как организовать мануал: если перечислить в нем вообще все методы, то он разрастется до бесконечности, и все равно ничего нельзя будет найти.
С третьего раза вроде дошло по поводу подготовленных выражений и эмуляции pdo.
При использовании подготовленного выражения (при отключенной эмуляции) php отправляет на сервер шаблон запроса, например
$sql = "INSERT INTO user (name, password, email) VALUES (:name, :password, :email)";
$sth = $dbh->prepare($sql);
На сервере строится план выполнения запроса, и прочие внутренние дела, в которые я не вникаю. Но суть в том, что пока открыто соединение, от pdo требуется только присылать данные для подстановки плейсхолдеров.
foreach ($users as $user) {
$sth->bindValue(':name', $user->name);
$sth->bindValue(':password', $user->password);
$sth->bindValue(':email', $user->email);
$sth->execute();
}
Execute здесь только отсылает данные для привязки, а операции подстановки выполняются на сервере.
Если включить эмуляцию, то pdo при execute каждый раз шлет новый запрос. При этом сначала экранируются и подставляются плейсхолдеры на стороне php.
Значит, эмуляцию вообще не нужно использовать, только в случае, когда база не поддерживает плейсхолдеры.
А как ее отключить? Вроде бы через setAttribute, но что-то я не уверен, что это корректно работает.
Пробовал наоборот включить ради теста
$pdo->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );
$res = $pdo->getAttribute( PDO::ATTR_EMULATE_PREPARES );
var_dump($res);
Выдает false.
По умолчанию эмуляция включена или нет? В мануале php не сказано.
http://php.net/manual/ru/pdo.setattribute.php
>Если драйвер не сможет подготовить запрос, эта настройка сбросится в режим эмуляции.
Судя по этой строке, по умолчанию эмуляция выключена, но если база не сможет подготовить запрос, только тогда включится эмуляция.
В yii кстати по умолчанию эта настройка в конфиге выставлена в true (emulatePrepare=>on)
Вероятно это сделано для старых версий mysql (или других баз, не поддерживающих подтготовленные выражения).
Пост на stackoverflow http://stackoverflow.com/questions/10113562/pdo-mysql-use-pdoattr-emulate-prepares-or-not/10455228#10455228
>прочесть весь туториал на сайте
А теперь покажи мне то место в туториале, где говорится о методе для мультиинсерта или например про эмуляцию подготовленных выражений.
Туториал отстой, читал я его несколько раз, там только беглый обзор возможностей фреймворка и особенности реализации тех или иных фич.
С другой стороны, для большого фреймворка действительно непонятно как организовать мануал: если перечислить в нем вообще все методы, то он разрастется до бесконечности, и все равно ничего нельзя будет найти.
по вебсокетам на хабре годно и просто читал, погугли
>Блокировка нужна так как изменения вносятся не атомарно. Представь что у нас есть строчка с текстом hello и мы меняем его на world. Ты не можешь поменять ее атомарно, ты идешь и меняешь по одной букве.
Я не в курсе нюансов работы файловой системы, поэтому кажется странным, что замена производится побуквенно. Почему так?
Но теперь понятно, зачем нужна блокировка на уровне таблицы в myisam.
> 2. Почему MVCC позволяет не блокировать таблицу?
>Неверно, попробуй еще.
Ну ты уже объяснил, поэтому я только перефразирую своими словами, чтобы показать, насколько понял.
В myisam изменения вносятся прямо в запись, поэтому два процесса могут внести противоречивые модификации.
В innodb, где используется mvcc, строки не перезаписываются, а добавляются новые, поэтому такого конфликта не возникает. Поэтому в блокировке на уровне таблицы нет необходимости. На уровне строки блокировки нужны во избежание нарушения согласованности.
> 3. Почему строки блокируются на все время транзакции (а не на время выполнения запроса)?
Я не понимаю вопроса. Суть транзакций в том, чтобы внести атомарное изменение. Естественно блокировать нужно на все время транзакции, иначе возникнет несогласованность, когда выполнена только часть запросов от первой транзакции, и после этого будут внесены модификации от другой транзакции.
Какой смысл в транзакции, если строки будут блокироваться только на время выполнения запроса?
> 4. Чтение с блокировкой.
Погуглил, ок. Когда возникает нужда в выборке с последующей модификацией (например выбрать всех пользователей с таким-то статусом и дать им другой статус), имеет смысл поставить блокировку LOCK IN SHARE MODE, чтобы пока работает наш скрипт, над выбранными пользователями не была произведена какая-то посторонняя модификация.
Эта конструкция блокирует данные только на запись, не на чтение.
SELECT FOR ... UPDATE выставляет также блокировку на чтение.
Покажи файл гитигнор.
Там должны быть только временные файлы типа ассетов, кеши и тому подобный мусор, но никак не модели и контроллеры.
> Я не в курсе нюансов работы файловой системы, поэтому кажется странным, что замена производится побуквенно. Почему так?
Файловая система тут не при чем. Механизм в mysql примерно такой: когда потоку надо что-то сделать с фрагментом таблицы, он грузит его в память и правит там. Так как все потоки-рабочие работают с общей областью памяти то они видят тот же кусок файла.
Конечно они могли бы грузить кусок файла для редактирования в свою приватную область, но тогда потребление памяти возрастет и каждому потоку придется грузить повторно те же данные. И даже в этом случае ты не можешь атомарно поменять данные в файле так как на уровне ОС чтение/запись идет блоками по 4Кб. Если тебе надо записать более одного блока, запись будет не атомарная.
Конечно, один файл можно поменять атомарно, создав рядом новый и переименовав его чтобы он заменил старый. Но в случае с базой это очень неэффективно так как таблица может быть огромная, например гигабайт размером и если мы ради изменения одного поля будем пересоздавать гигабайтный файл то это очень дорого. И то, те, кто открыл старый файл, будут по прежнему работать с ним, а не с новым.
А в случае с MyISAM мы должны менять не один файл, а несколько (таблица + индексы), причем заранее неизвестно какой именно объем данных надо поменять.
То есть ты не можешь атомарно вносить изменения в файловую систему, запись идет блоками по 4Кб, и тебе надо либо применять трюки с переименованием либо делать блокировки.
Пример с буквами это лишь пример. Если тебе надо поменять не буквы, а нескоько 4 Кб блоков в файле, ты получаешь ту же проблему не атомарной записи.
> В innodb, где используется mvcc, строки не перезаписываются, а добавляются новые, поэтому такого конфликта не возникает.
Верно
> Естественно блокировать нужно на все время транзакции, иначе возникнет несогласованность, когда выполнена только часть запросов от первой транзакции, и после этого будут внесены модификации от другой транзакции.
Ну да, иначе может быть ситуация что 2 транзакции внесут противоречивые изменения в одну и ту же строку.
> LOCK IN SHARE MODE
Да, все верно.
Ну, надеюсь ты теперь хорошо понимаешь что такое транзакции.
> Я не в курсе нюансов работы файловой системы, поэтому кажется странным, что замена производится побуквенно. Почему так?
Файловая система тут не при чем. Механизм в mysql примерно такой: когда потоку надо что-то сделать с фрагментом таблицы, он грузит его в память и правит там. Так как все потоки-рабочие работают с общей областью памяти то они видят тот же кусок файла.
Конечно они могли бы грузить кусок файла для редактирования в свою приватную область, но тогда потребление памяти возрастет и каждому потоку придется грузить повторно те же данные. И даже в этом случае ты не можешь атомарно поменять данные в файле так как на уровне ОС чтение/запись идет блоками по 4Кб. Если тебе надо записать более одного блока, запись будет не атомарная.
Конечно, один файл можно поменять атомарно, создав рядом новый и переименовав его чтобы он заменил старый. Но в случае с базой это очень неэффективно так как таблица может быть огромная, например гигабайт размером и если мы ради изменения одного поля будем пересоздавать гигабайтный файл то это очень дорого. И то, те, кто открыл старый файл, будут по прежнему работать с ним, а не с новым.
А в случае с MyISAM мы должны менять не один файл, а несколько (таблица + индексы), причем заранее неизвестно какой именно объем данных надо поменять.
То есть ты не можешь атомарно вносить изменения в файловую систему, запись идет блоками по 4Кб, и тебе надо либо применять трюки с переименованием либо делать блокировки.
Пример с буквами это лишь пример. Если тебе надо поменять не буквы, а нескоько 4 Кб блоков в файле, ты получаешь ту же проблему не атомарной записи.
> В innodb, где используется mvcc, строки не перезаписываются, а добавляются новые, поэтому такого конфликта не возникает.
Верно
> Естественно блокировать нужно на все время транзакции, иначе возникнет несогласованность, когда выполнена только часть запросов от первой транзакции, и после этого будут внесены модификации от другой транзакции.
Ну да, иначе может быть ситуация что 2 транзакции внесут противоречивые изменения в одну и ту же строку.
> LOCK IN SHARE MODE
Да, все верно.
Ну, надеюсь ты теперь хорошо понимаешь что такое транзакции.
Кстати Sublime Text применяет именно трюк с переименованием чтобы атомарно сохранить редактируемый файл.
>Потому что код будет чище если будет отдельно функция оценки варианта хода и отдельно функция оценки всех вариантов, а у тебя все смешано.
>
>Функция оценки хода у каждого животного своя. Функция которая перебирает варианты ходов и вызывает функцию оценки, общая для всех. Соответственно раз она общая ее можно поместить в Animal, а вот функцию оценки нет.
А как я буду оценивать вариант хода не имея возможности сравнивать его с другими ходами? Я вижу тут только такой вариант и тут без цикла, который будет проходиться по каждому ходу и сравнивать его с другими, не обойтись. Как понять вообще функция оценки хода? Что-то ты меня совсем запутал.
>>557262
>> Мне сразу в голову пришло что в эту функцию нужно передавать массив со всеми возможными ходами и проверять каждый ход, а потом возвращать массив с балами.
>Ты смешиваешь в одной функции 2 действия.
Нет же, будет отдельно функция получения списка всех ходов, и функция сравнивания их с друг с другом.
>>557273
>> А как посчитать это? Решиться ли это простым округлением?
>Надо брать минимальное из расстояние по горизонтали и вертикали.
А можно подсказку как это сделать?
>>557273
>> А не setSymbol случайно?
>setSymbol это когда ты хочешь его поменять сам, а getSymbol — возвращает символ животного.
>
>$animal->setSymbol('c');
>$x = $animal->getSymbol();
>
Но ты же сказал чтобы он >менял вид животного в зависимости от состояния.
> Как понять вообще функция оценки хода?
Функция оценки хода это функция получающая на вход координаты клеточки и выдающая оценку хода на эту клеточку.
> Но ты же сказал чтобы он >менял вид животного в зависимости от состояния.
set это когда знаечние меняют извне. get возвращает значение. У нас никто символ для кошки менять не будет извне, мы прсто вызваем getSymbol а она вернет текущую иконку.
>сомнительной соцсети в которую приличный человек и носа не сунет
Прямо заинтриговал.
>>556188
Написал консольный скрипт для faker (пока только создание пользователей).
https://github.com/nsdvw/classifieds/blob/master/protected/commands/FakerCommand.php
100 тысяч за 7 минут, для начала неплохо наверное.
Вопросы:
Не получается переопределить методы beforeAction и afterAction.
http://www.yiiframework.com/doc/api/1.1/CConsoleCommand#beforeAction-detail
Что с ними не так? Я хотел перед началом выполнения и после выполнения выдать сообщение о времени, например.
Если прописать что-то типа
protected function beforeAction(string $action, array $params)
{
echo date('H:i:s') . " - start $action generation";
parent::beforeAction($action, array $params);
}
то скрипт выдает это сообщение и подыхает, дальше не выполняется.
И еще, что делать с фейкерскими имейлами? Их мало, а у меня уникальный индекс на имейле.
Ради фейкера грохнуть индекс? Или написать костыль, который будет добавлять случайную строку к имейлу?
Наверное ни то ни другое не очень правильно.
>Там наверно что-то готовое есть для логгирования в командах.
Я ничего не нашел в классе CConsoleCommand, чтобы выводило информационные сообщения.
Только beforeAction и afterAction, но не понимаю, как их корректно переопределить.
>сомнительной соцсети в которую приличный человек и носа не сунет
Прямо заинтриговал.
>>556188
Написал консольный скрипт для faker (пока только создание пользователей).
https://github.com/nsdvw/classifieds/blob/master/protected/commands/FakerCommand.php
100 тысяч за 7 минут, для начала неплохо наверное.
Вопросы:
Не получается переопределить методы beforeAction и afterAction.
http://www.yiiframework.com/doc/api/1.1/CConsoleCommand#beforeAction-detail
Что с ними не так? Я хотел перед началом выполнения и после выполнения выдать сообщение о времени, например.
Если прописать что-то типа
protected function beforeAction(string $action, array $params)
{
echo date('H:i:s') . " - start $action generation";
parent::beforeAction($action, array $params);
}
то скрипт выдает это сообщение и подыхает, дальше не выполняется.
И еще, что делать с фейкерскими имейлами? Их мало, а у меня уникальный индекс на имейле.
Ради фейкера грохнуть индекс? Или написать костыль, который будет добавлять случайную строку к имейлу?
Наверное ни то ни другое не очень правильно.
>Там наверно что-то готовое есть для логгирования в командах.
Я ничего не нашел в классе CConsoleCommand, чтобы выводило информационные сообщения.
Только beforeAction и afterAction, но не понимаю, как их корректно переопределить.
> parent::beforeAction($action, array $params);
Опечатка? что за array?
> И еще, что делать с фейкерскими имейлами?
генерировать шаблоном [email protected]
пароли прописать всем простые и одинаковые, чтобы ты мог под ними залогиниться
заодно нет риска отправить письмо реальным людям.
Обрати внимание, в faker это учтено и там есть свойство safeEmail: https://github.com/fzaninotto/Faker#fakerproviderinternet
Обрати внимание также, если бы ты использовал Доктрину, то у faker есть какая-то интеграция, чтобы автоматически находить нужные поля в моделях и заполнять: https://github.com/fzaninotto/Faker#populating-entities-using-an-orm-or-an-odm (хотя это наверно было бы не так быстро но зато кода меньше писать).
> логи
Есть такая штука: http://www.yiiframework.com/doc/guide/1.1/en/topics.logging
Надо посмотреть можно ли это перенаправить в консоль, желательно с метками времени, и стоит ли это использовать.
Вот еще список плагинов по теме: http://www.yiiframework.com/extensions/?category=8 - поищи там
для Юи 2 есть что-то такое: http://www.yiiframework.com/doc-2.0/guide-tutorial-console.html
стандартный логгер хорош тем что легко управляется из конфига, к нему могут быть какие-то плагины и тд. Ну и в логгер пишут сам Юи, это тоже может быть полезно.
Также можно сделать свой базовый класс для консольных команд и добавить в него метод для вывода текста через echo. Но тогда мы не можем видеть сообщения от Юи.
> parent::beforeAction($action, array $params);
Опечатка? что за array?
> И еще, что делать с фейкерскими имейлами?
генерировать шаблоном [email protected]
пароли прописать всем простые и одинаковые, чтобы ты мог под ними залогиниться
заодно нет риска отправить письмо реальным людям.
Обрати внимание, в faker это учтено и там есть свойство safeEmail: https://github.com/fzaninotto/Faker#fakerproviderinternet
Обрати внимание также, если бы ты использовал Доктрину, то у faker есть какая-то интеграция, чтобы автоматически находить нужные поля в моделях и заполнять: https://github.com/fzaninotto/Faker#populating-entities-using-an-orm-or-an-odm (хотя это наверно было бы не так быстро но зато кода меньше писать).
> логи
Есть такая штука: http://www.yiiframework.com/doc/guide/1.1/en/topics.logging
Надо посмотреть можно ли это перенаправить в консоль, желательно с метками времени, и стоит ли это использовать.
Вот еще список плагинов по теме: http://www.yiiframework.com/extensions/?category=8 - поищи там
для Юи 2 есть что-то такое: http://www.yiiframework.com/doc-2.0/guide-tutorial-console.html
стандартный логгер хорош тем что легко управляется из конфига, к нему могут быть какие-то плагины и тд. Ну и в логгер пишут сам Юи, это тоже может быть полезно.
Также можно сделать свой базовый класс для консольных команд и добавить в него метод для вывода текста через echo. Но тогда мы не можем видеть сообщения от Юи.
Вообще API у CConsoleCommand довольно беспорядочный. Хорошие дети, не проектируйте так:
http://www.yiiframework.com/doc/api/1.1/CConsoleCommand#copyFiles-detail
Непонятно каким вообще боком копирование файлов относится к консольным командам.
В этом плане конечно симфониевский Console Component намного образцовее спроектирован.
> но как быть с другими файлами, которые попадают в gitignore?
В гитигнор обычно добавляют всякие временные папки типа runtime и assets которые заполняются автоматически. Что в твоем гитигноре такого что тебе надо пересоздать руками?
>>557450
Покажи htaccess, где он лежит, конфиг Апача. Перезапускал Апач после правок конфига?
>>557529
Можно в переменную скопировать или как ты догадался, call_user_func использовать.
>>557561
Да
>>557574
Эмуляция для баз которые не умеют в плейсхолдеры. Mysql умеет, но включена или отключена эмуляция по умолчанию? Надо разбираться, держи:
http://php.net/manual/ru/pdo.prepared-statements.php
http://www.yiiframework.com/doc/api/1.1/CDbConnection#emulatePrepare-detail
http://php.net/manual/en/pdo.setattribute.php
http://stackoverflow.com/questions/10113562/pdo-mysql-use-pdoattr-emulate-prepares-or-not/10455228#10455228
https://bugs.php.net/search.php?cmd=display&search_for=ATTR_EMULATE_PREPARES+&x=0&y=0
Наконец у нас всегда есть вариант открыть исходники PHP и посмотреть код драйвера MySQL для PDO (Это Си, но он похож на PHP, только значков доллара перед переменными нет, перед переменными и функциями указывается тип, а точка значит примерно то же самое что и -> ):
https://github.com/php/php-src/blob/master/ext/pdo_mysql/mysql_driver.c#L616
Это mysql-специфичная функция инициализации нового объекта PDO, как я понимаю, и там сохраняется значение опции EMULATE_PREPARES (как я понимаю, она вызывается из конструктора).
https://github.com/php/php-src/blob/master/ext/pdo_mysql/mysql_driver.c#L390
Тут обрабатывается вызов setAttribute() и вроде тоже все сохраняется
https://github.com/php/php-src/blob/master/ext/pdo_mysql/mysql_driver.c#L175
Это вызывается при вызове prepare() или execute(), как я понял. Тут мы видим интересный алгоритм:
- если эмуляция включена, переходим в конец функции
- если версия MySQL меньше 4.01, то включаем эмуляцию
- иначе же ставим параметр указывающий на поддержку только плейсхолдеров-вопросиков:
stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
И вызываем функцию pdo_parse_params ( https://github.com/php/php-src/blob/master/ext/pdo/pdo_sql_parser.c#L441 ), код которой довольно сложный но интуиция мне подсказывает что функция заменяет в запросе те плейсхолдеры которые не поддерживаются базой (в данном случае именные плейсхолдеры с двоеточием).
Ну и наконец вызывается mysql_stmt_prepare, в описании которой ( https://dev.mysql.com/doc/refman/5.0/en/mysql-stmt-prepare.html ) мы видим что поддерживаются только плейсхолдеры-вопросы.
Таким образом мне кажется, запросы с порядковыми плейсхолдерами отправляются в MySQL, а вот запросы с именованными плейсхолдерами заменяются на стороне PDO.
> Пробовал наоборот включить ради теста
Попробуй указывать опцию в конструкторе PDO, некоторые опции нельзя менять после создания объекта.
> А теперь покажи мне то место в туториале, где говорится о методе для мультиинсерта
Ну это мне подсказал опыт, я думаю ты со временем тоже научишься чувствовать такие вещи. А туториал стоит прочесть в любом случае.
> или например про эмуляцию подготовленных выражений.
А это не особенность Юи, а PDO.
> но как быть с другими файлами, которые попадают в gitignore?
В гитигнор обычно добавляют всякие временные папки типа runtime и assets которые заполняются автоматически. Что в твоем гитигноре такого что тебе надо пересоздать руками?
>>557450
Покажи htaccess, где он лежит, конфиг Апача. Перезапускал Апач после правок конфига?
>>557529
Можно в переменную скопировать или как ты догадался, call_user_func использовать.
>>557561
Да
>>557574
Эмуляция для баз которые не умеют в плейсхолдеры. Mysql умеет, но включена или отключена эмуляция по умолчанию? Надо разбираться, держи:
http://php.net/manual/ru/pdo.prepared-statements.php
http://www.yiiframework.com/doc/api/1.1/CDbConnection#emulatePrepare-detail
http://php.net/manual/en/pdo.setattribute.php
http://stackoverflow.com/questions/10113562/pdo-mysql-use-pdoattr-emulate-prepares-or-not/10455228#10455228
https://bugs.php.net/search.php?cmd=display&search_for=ATTR_EMULATE_PREPARES+&x=0&y=0
Наконец у нас всегда есть вариант открыть исходники PHP и посмотреть код драйвера MySQL для PDO (Это Си, но он похож на PHP, только значков доллара перед переменными нет, перед переменными и функциями указывается тип, а точка значит примерно то же самое что и -> ):
https://github.com/php/php-src/blob/master/ext/pdo_mysql/mysql_driver.c#L616
Это mysql-специфичная функция инициализации нового объекта PDO, как я понимаю, и там сохраняется значение опции EMULATE_PREPARES (как я понимаю, она вызывается из конструктора).
https://github.com/php/php-src/blob/master/ext/pdo_mysql/mysql_driver.c#L390
Тут обрабатывается вызов setAttribute() и вроде тоже все сохраняется
https://github.com/php/php-src/blob/master/ext/pdo_mysql/mysql_driver.c#L175
Это вызывается при вызове prepare() или execute(), как я понял. Тут мы видим интересный алгоритм:
- если эмуляция включена, переходим в конец функции
- если версия MySQL меньше 4.01, то включаем эмуляцию
- иначе же ставим параметр указывающий на поддержку только плейсхолдеров-вопросиков:
stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
И вызываем функцию pdo_parse_params ( https://github.com/php/php-src/blob/master/ext/pdo/pdo_sql_parser.c#L441 ), код которой довольно сложный но интуиция мне подсказывает что функция заменяет в запросе те плейсхолдеры которые не поддерживаются базой (в данном случае именные плейсхолдеры с двоеточием).
Ну и наконец вызывается mysql_stmt_prepare, в описании которой ( https://dev.mysql.com/doc/refman/5.0/en/mysql-stmt-prepare.html ) мы видим что поддерживаются только плейсхолдеры-вопросы.
Таким образом мне кажется, запросы с порядковыми плейсхолдерами отправляются в MySQL, а вот запросы с именованными плейсхолдерами заменяются на стороне PDO.
> Пробовал наоборот включить ради теста
Попробуй указывать опцию в конструкторе PDO, некоторые опции нельзя менять после создания объекта.
> А теперь покажи мне то место в туториале, где говорится о методе для мультиинсерта
Ну это мне подсказал опыт, я думаю ты со временем тоже научишься чувствовать такие вещи. А туториал стоит прочесть в любом случае.
> или например про эмуляцию подготовленных выражений.
А это не особенность Юи, а PDO.
>>> А как посчитать это? Решиться ли это простым округлением?
>>Надо брать минимальное из расстояние по горизонтали и вертикали.
> А можно подсказку как это сделать?
В общем случае определение за сколько ходов животное дойдет из клетки A в B может быть сложным. Например если у нас животное ходит как шахматный конь, сложно понять сколько ему надо ходов.
Однако с кошкой все проще - она ходит в 8 сторон на $speed клеточек. Таким образом мы определяем расстояние между A и B по горизонтали, по вертикали, берем меньшее и делим на скорость кошки. Это дает нам, за сколько ходов кошка доберется от A до B.
Попробуй на бумаге или в редакторе нарисовать это.
> А как я буду оценивать вариант хода не имея возможности сравнивать его с другими ходами?
Оценочная функция дает оценку одному варианту хода (на клетку x, y) в баллах, она его ни с чем не сравнивает. Но я говорил о другом. Я говорил что поведение у разных животных разное, значит оценочные функции у них тоже разные и они должны быть в классе животного. А у тебя оценка делается в классе Animal, как будто все животные используют одну и ту же систему оценки хода и движутся по
одинаковой логике.
Но это не так. Например мышка должна избегать углов, а кошке их наличие без разницы.
Даже читать интересно, особенно после всех этих малопонятных статей про методы и все такое. Хоть начинает складываться картина того каким образом код пишется в реальных проектах, да и понятия, которые раньше тут встречал и не понимал раскрываются. Надеюсь, то, что я читаю там, применимо и к другим языкам, к тому же PHP. Понятное дело, что такие штуки как __proto__ там заменены на какие-то свои, но общая концепция ведь одинакова?
Применимо, но частично. Например прототипов в PHP нет, зато есть нормальные классы. Но конечно второй язык учить проще чем первый.
А насчет реальных проектов, если ты начинающий тебе наверно писать их рановато, но у нас есть задачка на студентов и на файлообменник, а также ты можешь сделать что-то на фреймворке (вот один анон пилит доску объявлений), вот тогда будет довольно приближено к реальным проектам.
А что касается _ _ proto _ _ , она там только для пояснения, в коде ее использовать нельзя (это не стандартное свойствот и работает не везде).
>между A и B по горизонтали, по вертикали
А как посчитать одновременно расстояние по вертикали и горизонтали?
>Но я говорил о другом. Я говорил что поведение у разных животных разное, значит оценочные функции у них тоже разные и они должны быть в классе животного. А у тебя оценка делается в классе Animal, как будто все животные используют одну и ту же систему оценки хода и движутся по
одинаковой логике.
Ну я переделал это теперь эти функции разделены и объявлены абстрактными чтобы переопределять поведение для каждого животного.
https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Mouse.php#L7
https://github.com/someApprentice/Cat-and-Mouse/blob/master/Classes/Mouse.php#L36
Ты все еще против циклов?
>>557829
>set это когда знаечние меняют извне. get возвращает значение. У нас никто символ для кошки менять не будет извне, мы прсто вызваем getSymbol а она вернет текущую иконку.
>>556079
>В твоем случае, что если кто-то унаследует класс и не переопределит поле symbol, что будет? Ведь в коде нет никаких намеков что его надо переопределить.
А как тогда намекнуть что поле symbol нужно переопределить? Добавить его в конструктор подойдет?
Доброе утро, ОП! Надо же, тред ещё не уплыл. Следуя твоим мудрым советам, всё ещё ковыряюсь в регэкспах
1) Задача про автоисправление ошибок. Перечитал урок, про \\b действительно там не увидел.
Сделал функцию, определяющую регистр (избавился от if) и сделал так, что не искажается регистр (меняю только 1 букву и всё).
Пробовал реализовать >Массив + цикл можно заменить на несколько вызовов функции
но обнаружил, что по сути написал тупую обёртку к той же preg_replace_callback - http://ideone.com/FQ0Jb1
ведь для каждого случая у меня разные 1) шаблон 2) коллбек, то есть мне их каждый раз отправлять, что тут можно повторно использовать не знаю. Единственное что можно было с текстом как с глобальной переменной работать, и тут вызов функции мог бы быть проще, но я так понял, глобальные переменные так лучше не использовать. В итоге вернулся к копипасту 3 раза как тут http://ideone.com/bD3KGf , всё равно красивее, чем мешанина из массивов.
В общем видимо тут уже ничего не улучшишь
2) Задача на автозамену http://ideone.com/c6pWA1
3) Задача на поиск e-mail http://ideone.com/RKsUDb тут я хотел сделать так, чтобы если в e-mail какой-то косяк, то он бы вообще не выводился, даже его часть, которая формально бы подходила, но столкнулся с разными вариантами, я там в тексте описал. Может быть, в этой задаче не надо так глубоко копать, но я пытаюсь поставить себя на твоё место и найти какие-нибудь скользкие варианты
Спасибо
Так с данными не работают в реальных проектах. Парси данные в соответствующие классы, где есть геттеры и сеттеры для всего, потом используй их. У тебя тут по меньшей мере 3 класса будет.
ОП, делаю задачку - числа с прописью. Вот сделал пока что только первую функцию где нужно сделать так, чтобы в зависимости от числа писало "рубля, рублей, рубль".
Чекни, пожалуйста, не сильно ли я наговнокодил?
Это копия, сохраненная 1 ноября 2015 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.