Вы видите копию треда, сохраненную 4 июня 2015 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Это тред для начинающих. Не написал за свою жизнь ни одной программы? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Netbeans PHP или PhpStorm (необязательно).
Предыдущий тред был тут: >>461342
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП отвечает даже на самые нубские вопросы. ОП заходит где-то раз в день, не жди его, решай задачки дальше.
У нас есть уроки по основам 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
>Дублируется. У тебя есть поле author в Article и есть поле name в Author.
Так оно же не в базе данных хранится, а формируется только при срабатывании этого специфического запроса. Ну и как сказать... это не 'поле author в Article' это совокупность полей из Article и Author и Subject.
>И почему в Article хранится только огрызок от автора, а не объект Author целиком?
Потому что мне для отображения статьи достаточно имени автора. Понадобится еще что-то - допишу в запрос. С другой стороны зачем брать Author-а целиком если нужно только имя?
>Ну и если у тебя есть класс Author то как получить Author из Article? Например как узнать дату регистрации автора статьи? Его id? Ссылку на его страницу?
Дописать нужные поля в запрос. Повторю картинку. Я тут не только имя автора взял, но и его биографию, также можно было взять айди и сформировать ссылку. То есть это я заморочился для наглядности с перечислением полей, можно просто SELECT * ... и туда попадет все на свете.
И да, как сделать, чтобы 6600 можно было выдать вообще понятия не имею. Дай подсказку, чтоле.
http://ideone.com/PvTHpo
Можно, но там есть дополнительное условие в задаче с заменой количества банкнот и добавлением новых. И при новых условиях выдать 6600 не получается, т.к. я не знаю как сделать проверку на сумму всех оставшихся в банкомате денег.
https://ideone.com/HTVZcS
Заранее спасибо, за конструктивные советы, ОП!
ок, я скажу твоей маме чтобы она разрешала тебе разговаривать с незнакомцами.
хотя ты уже заговорил.
Не ври, мой отец работает на мкс. Скоро я наконец-то впервые увижу его, расскажу ему о твоих выходках.
до этого питонил года 1,5 наверное, где-то с конца осени прошлого года взялся за пхп.
После django пришло стойкое осознания, как должена работать mvc.
Другой анон
Забежал на минутку. Я тут зависал зимой, решал задачки уже с базой данных студентов, но забросил. Сначала выучил django, а потом Java и Spring и занялся поиском работы. Работу я нашел на php, а значит я опять к вам. Сейчас делаю тестовое задания.
Зато ты не прочтешь устаревших вещей, это раз, во вторых, разбирайся, не все понимаешь с первого раза и бывает нужно несколько раз перечитать. Все, как и везде.
Вообще, в идеале учить все без привязки к конкретному языку, тогда и будет все годно.
>Чтобы делать эти задания, тебе надо установить Апач + PHP
Вы че бля, какой нахуй апач? Нгинкс онли
ну изучение 2-го ЯПа легче дается. Помимо того, что изучаешь какой-то конкретный ЯП, касаешься и других областей, так или иначе, общих для программирования в целом.
Возни с настройкой нгинкс + fpm много, выгоды для 1 пользователя нет. на практике часто нгинкс ставят не вместо а перед Апачом.
Это обучающий тред. Вместо того чтобы давать бессмысленные советы, надо давать объяснения и ссылки на статьи. Если ты больше 2 слов в стиле «только хардкор» сказать ничего не можешь то лучше промолчать.
>>468813
Не ругайся в нашем треде.
В юзерах: id, username;
В комментариях user_id, user_post;
То я обязан их связать жестко на уровне базы?
Я думаю, нужно связать. Это дает много преимуществ, главные из которых защита от вставки неправильных данных и понятность структуры базы. А какие преимущетва в том чтобы не делать связи?
А если я удалю из базы какого-то юзера? Удаляться и все комментарии которые связаны с его id? или mysql просто не даст мне это сделать? Пока все комментарии не будут удалены?
Я не знаком особо с го, просто читал статью на Хабре и знаю что его делают в гугле (конкурент для раста? почему они не хотят его развивать?) и что в нем нет: классов, ООП, исключений.
Движок использует Sqlite и по стилю написания кода напоминает велоcипед на PHP из дофреймворковой эпохи, когда каждый писал как хотел. Никаких MVC там нет, код живописно раскидан по разным папкам в виде функций.
Go довольно похож на PHP так что вы, аноны, тоже можете глянуть. Помните только что точка тут значит не склеивание строк, а обозначает либо неймспейс (то есть templates.Update() равносильно templates\Update() в PHP) либо обращение к полю (post.id значит $post->id в PHP).
Вот их набор плагинов к шаблонизатору, где мы видим неудобный способ работы со строками: https://github.com/kabukky/journey/blob/master/templates/handlebars.go
Магические числа:
https://github.com/kabukky/journey/blob/master/templates/handlebars.go#L259
> if values.CurrentTemplate == 1 { // post
> } else if values.CurrentTemplate == 0 { // index
> } else if values.CurrentTemplate == 3 { // author
Так как ООп нет, код раскидан по разным функциям в разных файлах без особой логики.
Так как нет исключений, код наполовину состоит из конструкций
if err != nil
return err
шаблонизатор — велосипедный handlebars c ужасным синтаксисом ( https://github.com/kabukky/promenade/blob/master/index.hbs ). Тот кто его делал никогда не видел нормальных шаблонизаторов (twig/jinja) и изобрел какой-то жуткий велосипед.
Работа с SQL напоминает PDO с примитивным data mapper: https://github.com/kabukky/journey/blob/master/database/retrieval.go
В общем, уровень довольно низкий, большое приложение на такой технологии не напишешь. Наша экосистема, PHP, на порядки лучше. Но кого заплюсуют в твиттере, го- или php-программиста, угадайте с одного раза?
Справедливости ради, го наверно не для этого делался, но все равно, это показательный пример, кругом полно неучей и черное в их глазах перепутано с белым, а лучшее с худшим. Почему-то простой сайт на php на главной hacker news не висит.
Я не знаком особо с го, просто читал статью на Хабре и знаю что его делают в гугле (конкурент для раста? почему они не хотят его развивать?) и что в нем нет: классов, ООП, исключений.
Движок использует Sqlite и по стилю написания кода напоминает велоcипед на PHP из дофреймворковой эпохи, когда каждый писал как хотел. Никаких MVC там нет, код живописно раскидан по разным папкам в виде функций.
Go довольно похож на PHP так что вы, аноны, тоже можете глянуть. Помните только что точка тут значит не склеивание строк, а обозначает либо неймспейс (то есть templates.Update() равносильно templates\Update() в PHP) либо обращение к полю (post.id значит $post->id в PHP).
Вот их набор плагинов к шаблонизатору, где мы видим неудобный способ работы со строками: https://github.com/kabukky/journey/blob/master/templates/handlebars.go
Магические числа:
https://github.com/kabukky/journey/blob/master/templates/handlebars.go#L259
> if values.CurrentTemplate == 1 { // post
> } else if values.CurrentTemplate == 0 { // index
> } else if values.CurrentTemplate == 3 { // author
Так как ООп нет, код раскидан по разным функциям в разных файлах без особой логики.
Так как нет исключений, код наполовину состоит из конструкций
if err != nil
return err
шаблонизатор — велосипедный handlebars c ужасным синтаксисом ( https://github.com/kabukky/promenade/blob/master/index.hbs ). Тот кто его делал никогда не видел нормальных шаблонизаторов (twig/jinja) и изобрел какой-то жуткий велосипед.
Работа с SQL напоминает PDO с примитивным data mapper: https://github.com/kabukky/journey/blob/master/database/retrieval.go
В общем, уровень довольно низкий, большое приложение на такой технологии не напишешь. Наша экосистема, PHP, на порядки лучше. Но кого заплюсуют в твиттере, го- или php-программиста, угадайте с одного раза?
Справедливости ради, го наверно не для этого делался, но все равно, это показательный пример, кругом полно неучей и черное в их глазах перепутано с белым, а лучшее с худшим. Почему-то простой сайт на php на главной hacker news не висит.
Ты определяешь это когда задаешь внешний ключ, советую прочесть эту статью: http://denis.in.ua/foreign-keys-in-mysql.htm
>>468873
$ man innodb
No manual entry for innodb
Уходи, в век интернета и HTMl ты вместо понятной полезной ссылки на русском даешь неработабщую команду. Интересно, почему посты с сажей почти всегда пишут такие как ты неучи? Там же написано «мудрец» перед галочкой, а не «неуч».
И конечно же главный признак неуча-велосипедиста — ангулар — там присуствует также. надо следовать за модой!
Нашел оправдание от авторов (или пиарщиков?) го про обработку ошибок: http://blog.golang.org/errors-are-values
Этот подход с написанием дополнительных функций is ugly as a fuck. Мы это проходили 30 лет назад, зачем к нему возвращаться?
Мир дали им ООП и исключения, нет хотим быть не такими как все.
Даже ноде.жс выглядит конфеткой по сравнению с этим говном.
Ты можешь скрыть реаизацию за счет метода. То есть сделать метод getAuthorName() у Article, а уже в нем писать либо
return $this->authroName;
либо
return $this->author->name;
И таким образом можно менять реализацию не трогая остальной код.
>>468399
Вот-вот.
>>468451
> И да, как сделать, чтобы 6600 можно было выдать
Это задача о ранце:
http://informatics.mccme.ru/mod/book/view.php?id=815
http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5
Там есть эффективный, но непростой алгоритм. Можно сделать не очень эффективный, но более простой алгоритм на основе рекурсии.
Рекурсия это когда функция вызывает сама себя. Функция может выглядеть примерно так:
перебратьКупюры(требуемаяСумма, набраннаяСумма, оставшиеся купюры) {
берем старшую из оставшихся купюр;
(меняем число этих купюр от максимального имеющегося до 0) {
если сумма набралась то ответ найден;
иначе вызываем функцию перебора купюр, передавая ей оставшиеся купюры (без старшей);
}
}
При каждом рекрсивном вызове передается набор купюр без самой старшей, так что за 5-6 вызовов мы дойдем до самой младшей. Этот код перебирает все возможные комбинации купюр.
Кстати тут можно использовать такую фичу php как генераторы: делаем чтобы функция перебора работала как генератор всех возможных комбинаций купюр и пишем:
foreach (generateCombinatons(..) as $combination) {
если комбинация купюр дает нужную сумму, выводим ее;
}
Ты можешь скрыть реаизацию за счет метода. То есть сделать метод getAuthorName() у Article, а уже в нем писать либо
return $this->authroName;
либо
return $this->author->name;
И таким образом можно менять реализацию не трогая остальной код.
>>468399
Вот-вот.
>>468451
> И да, как сделать, чтобы 6600 можно было выдать
Это задача о ранце:
http://informatics.mccme.ru/mod/book/view.php?id=815
http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5
Там есть эффективный, но непростой алгоритм. Можно сделать не очень эффективный, но более простой алгоритм на основе рекурсии.
Рекурсия это когда функция вызывает сама себя. Функция может выглядеть примерно так:
перебратьКупюры(требуемаяСумма, набраннаяСумма, оставшиеся купюры) {
берем старшую из оставшихся купюр;
(меняем число этих купюр от максимального имеющегося до 0) {
если сумма набралась то ответ найден;
иначе вызываем функцию перебора купюр, передавая ей оставшиеся купюры (без старшей);
}
}
При каждом рекрсивном вызове передается набор купюр без самой старшей, так что за 5-6 вызовов мы дойдем до самой младшей. Этот код перебирает все возможные комбинации купюр.
Кстати тут можно использовать такую фичу php как генераторы: делаем чтобы функция перебора работала как генератор всех возможных комбинаций купюр и пишем:
foreach (generateCombinatons(..) as $combination) {
если комбинация купюр дает нужную сумму, выводим ее;
}
>$numberOfBills = floor($totalAmount / $bill);
>\tif ($numberOfBills > $number) {
Это короче записать без ифа, через max/min
> $i++;
>\tif ($i == $billsCount && $totalAmount > 0) {
Эту проверку можно поставить после цикла и тогда переменная i не нужна
2 цикла для такой простой программы слишком много, к тому же ты их скопипастил а копипаста страшное зло. Убери второй цикл и копипасту. Можно например в массив запоминать число купюр.
> foreach ($textStrings as $string) {
>\t$stringLength = mb_strlen($string);
>\tif ($stringLength > $maxLength) {
С помощью функции array_map этот цикл можно заменить на одну строчку. Догадайся, как.
В остальном верно.
>>468578
там новые виды купюр добавлены
>>468607
> private $fieldSchema;
думаю нет никакого смысла хранить его как поле так как оно устаревает и пересоздается каждый ход. Думаю логичнее сделать его переменной.В полях лучше хранить более постоянные вещи.
> if(get_class($animal) === "Mouse") {
Это не очень ООП, так как добавление новых видов животных потребует правки кода тут. А надо чтобы их можно было добавлять без этого. Думаю логичнее у животного сделать метод возвращающий его обозначение.
> $chunkedField = array_chunk($this->fieldSchema, $this->fieldSize, true);
> $fieldSchemaLined = array_merge($fieldSchemaLined, $chunk);
Проще по моему сделать двухмерный массив вместо этих сложных преобразований которые тяжело читать и проверять.
Соответственно для координат лучше использовать привычные нам x и y ибо математика с позициями становится сложная, неудобная и легко запутаться.
Я вижу что у тебя эти сложные выисления засунуты везде и я не очень представляю как например такой код править. В уме представлять что надо прибавить или отнять, сложно.
А программы пишутся для людей и ты должен писать так, как будет понятнее человеку, а не компьютеру.
Например метод private function findVisibleField($sideSize) — чтобы его понять, надо сидеть и с листочком выписывать и проверять все эти формулы. Боюсь, это неприемлемо, я хочу чтобы там был наглядный и очевидный код. В случае с x/y будет примерно такое:
$leftBound = max(0, $x - $radius);
что в разы проще, по моему.
> abstract class Animal
> public function __construct($fieldSize)
Непонятно зачем животному передавать размер поля.
Думаю, лучше передавать животному ссылку на объект карты когда его добавляют на карту, а из этой карты оно может узнать ее размер.
Наличие собак лучше добавлять в стоимость расчета хода или даже в этап выбора возможных ходов. То есть кошка исключает те ходы, которые ведут к собаке, еще до расчета числа очков.
> class Game
> private $fieldSize;
> private $animals = [];
Эти поля дублируют поля в карте и думаю их лучше удалить. Дублирующиеся данные это всегда плохо, источник истины должен быть один.
> foreach ($textStrings as $string) {
>\t$stringLength = mb_strlen($string);
>\tif ($stringLength > $maxLength) {
С помощью функции array_map этот цикл можно заменить на одну строчку. Догадайся, как.
В остальном верно.
>>468578
там новые виды купюр добавлены
>>468607
> private $fieldSchema;
думаю нет никакого смысла хранить его как поле так как оно устаревает и пересоздается каждый ход. Думаю логичнее сделать его переменной.В полях лучше хранить более постоянные вещи.
> if(get_class($animal) === "Mouse") {
Это не очень ООП, так как добавление новых видов животных потребует правки кода тут. А надо чтобы их можно было добавлять без этого. Думаю логичнее у животного сделать метод возвращающий его обозначение.
> $chunkedField = array_chunk($this->fieldSchema, $this->fieldSize, true);
> $fieldSchemaLined = array_merge($fieldSchemaLined, $chunk);
Проще по моему сделать двухмерный массив вместо этих сложных преобразований которые тяжело читать и проверять.
Соответственно для координат лучше использовать привычные нам x и y ибо математика с позициями становится сложная, неудобная и легко запутаться.
Я вижу что у тебя эти сложные выисления засунуты везде и я не очень представляю как например такой код править. В уме представлять что надо прибавить или отнять, сложно.
А программы пишутся для людей и ты должен писать так, как будет понятнее человеку, а не компьютеру.
Например метод private function findVisibleField($sideSize) — чтобы его понять, надо сидеть и с листочком выписывать и проверять все эти формулы. Боюсь, это неприемлемо, я хочу чтобы там был наглядный и очевидный код. В случае с x/y будет примерно такое:
$leftBound = max(0, $x - $radius);
что в разы проще, по моему.
> abstract class Animal
> public function __construct($fieldSize)
Непонятно зачем животному передавать размер поля.
Думаю, лучше передавать животному ссылку на объект карты когда его добавляют на карту, а из этой карты оно может узнать ее размер.
Наличие собак лучше добавлять в стоимость расчета хода или даже в этап выбора возможных ходов. То есть кошка исключает те ходы, которые ведут к собаке, еще до расчета числа очков.
> class Game
> private $fieldSize;
> private $animals = [];
Эти поля дублируют поля в карте и думаю их лучше удалить. Дублирующиеся данные это всегда плохо, источник истины должен быть один.
Поставь var_dump($_POST); в скрипт и проверь.
>>468913
Хорошо, но хорошо бы еще показывать где пропущена запятая или пробел.
Также программа вставляет лишние запятые: https://ideone.com/Kb10ml
> \s(но|а)\s
Это не сработает если за словом идет запятая. Луче использовать \b
>>468917
> $patterns = array(
> $replacements = array(
Неудобно что буква и ее пара далеко друг от друга. Лучше сделать так
array(
'a' => 'a',
...
Заменять буквы можно и без регулярок, тут strtr хватит, она была в задаче про шифровку.
У тебя программа реагирует на все ангглийские буквы. Но надо искать только слова со смешанными алфавитами и в английских словах выделят русскую букву, а не как тут: https://ideone.com/awHmfP
Поставь var_dump($_POST); в скрипт и проверь.
>>468913
Хорошо, но хорошо бы еще показывать где пропущена запятая или пробел.
Также программа вставляет лишние запятые: https://ideone.com/Kb10ml
> \s(но|а)\s
Это не сработает если за словом идет запятая. Луче использовать \b
>>468917
> $patterns = array(
> $replacements = array(
Неудобно что буква и ее пара далеко друг от друга. Лучше сделать так
array(
'a' => 'a',
...
Заменять буквы можно и без регулярок, тут strtr хватит, она была в задаче про шифровку.
У тебя программа реагирует на все ангглийские буквы. Но надо искать только слова со смешанными алфавитами и в английских словах выделят русскую букву, а не как тут: https://ideone.com/awHmfP
надо было еще к this_is_my_variable вернуться для порядка.
Отработал в общем первую неделю. Меня посадил начальник пилить собственную небольшую хэлп-деск систему на кодигнайтере. Вроде бы успешно справляюсь, хоть и с помощью и подсказками, но начинаю понимать во всем этом. Пока что допиливаю админку. Сначала удивило то, что к любому методу, свойству или еще чему, можно обращаться через $this->user->add_user() например, хотя метод add_user() описан в моделе, а ты сидишь ковыряешь контроллеры. Как будто ты находишься в 1 глобальном объекте все время, и внутри него обращаешься к его же методам и свойствам. Везде такой подход используется в mvc - фреймворках?
Далее, ОП, я так и не смог разобраться с примером на написание микро шаблонизатора через анонимные функции и preg_replace_callback, но я обязательно вернусь к этому, не собираюсь бросать так сказать учебу, тем более что у меня работа пока всего по 4 часа в день.
Спасибо за ссылку >>468882 как раз возникли проблемы с базой. В общем там сделано так, что в базе просто лежат таблицы пользователей, сайтов, задач и событий. Они не связаны никак. И похоже что их уже нельзя связать. Ведь они заполнены. Либо придется вычищать все с базы, переделывать таблички, и после этого снова заполнять. Во всяком случае в phpmyadmin почему-то нет строчки со связями. Вот прикручиваю для примера скрин своей локальной поделки, и тут у меня все есть. Может быть конечно там другая версия phpmyadmin, но все же мне не ясно пока как это сделать.
Насколько я понял фреймворки это что-то типа шаблонов с готовыми решениями для создания однотипных сайтов? Долго ли их изучать и сложно ли дается это изучение? В чем между ними принципиальная разница?
digitalocean
Фреймворки это готовый код. Берешь и с их помощью пишешь в 2 строчки то, что в ручную заняло бы 20. Например вместо всякой еботни с базой, ты просто берешь и пишешь $this->db->update($table, $data);
И вот эта строчка за тебя установит соединение с базой, проверит валидность там, и все такое, потом внесет инфу туда и завершит соединение. Функция update - не входит в состав php, а является частью фреймворка. В общем это некая эволюция библиотек, когда разработчики писали свои функции, кидали их в отдельный файлик, который был этой самой библиотекой, а потом инклудили его в своих целях куда хотели.
Бутстрап это html - фреймворк, который упрощает работу с html, в нем сразу за тебя написаны все заготовки и вместо ебли с html\css, ты можешь просто прописать своей кнопке class="btn-danger" и у тебя будет готовая красная кнопка. Отлично взлетает вместо стандартного голого уродливого html.
>Насколько я понял фреймворки это что-то типа шаблонов с готовыми решениями для создания однотипных сайтов?
Это ты про CMS - Системы управления контентом. Вот они как раз позволяют быстро клепать однотипные сайты наверное даже без знания программирования. Ты просто ставишь эту CMS как движок, и используя её возможности делаешь свой бложик или галерею, ну и как-то так.
>Долго ли их изучать и сложно ли дается это изучение?
Смотря что ты уже знаешь, я вот год ковыряю кодинг и пока что совсем мало понимаю во фреймворках. Какая у тебя цель?
>В чем между ними принципиальная разница?
На этот вопрос я тебе ответить не могу. Думаю гугл поможет
Вот что говорят про твои вопросы в целом
https://toster.ru/q/15011
http://habrahabr.ru/post/50341/
Да я в курсе про CMS, уже имел дело с ними. Например натягивал шаблон на Opencart, вообще без знания php, чисто интуитивно догадывался что за что отвечает. Получилось даже, правда где-то по пути удалил что-то лишнее и в итоге модуль регистрации сломал.
Цель у меня на работу устроиться джуниором. Просто слышал, что мол особо глубоких знаний php не требуют, т.к. все равно все на фреймворках пишут. Уровень знаний слабенький, дошел до ООП и встал, чет тяжело дается. Вроде бы принцип понял, но чет какой-то огромный скачек между первым/вторым заданием и остальными про кошек мышек и превьюшку. Теряюсь сразу же, даже не знаю с чего начать.
У меня тоже были проблемы с этим. Долго рос и ковырялся неделями в одной задаче, больше конечно из-за лени или типа того.
Если хочешь можешь глянуть мой код по вектору. Он с косяками в плане архитектуры, но задачу решает на ура. Можешь попробовать от него оттолкнуться когда будешь сам решать
http://ideone.com/jKnvgR
Нет, я такой же как ты. На википедии все эти алгоритмы описаны по сути. Я так и не решил её хитрым алгоритмом, а решал просто жадным.
Выдаем купюры 5000 пока остаток больше 1000, далее начинаем выдавать 1000 пока остаток больше 1000, потом выдаем 100 пока больше 100 и далее выдаем мелоч. Вроде бы как-то так было.
Ай вонт ту райд ма майсикл!
ООП без математики.
Поскольку кусок кода маленький кину сюда
<div name="test">
<p>
Комментарий:<Br>
<textarea value="16" class="text" name="comment" cols="110" rows="4"></textarea>
</p>
<input style="margin-left: 50px; " type="checkbox" name="anonymous" >Анонимно
<button class="send">Отправить</button>
</div>
Как получить значение textarea после клика по .send?Если делаю по клику на саму textarea то через $(this).val() я его получаю, а как получить по клику на сенд чето не могу догнать. пробовал уже и prev(естественно, меняя порядок) и closest() и по селекторам. не могу :( помогите, пожалуйста
сделал так, но чую это костыльное решение:
$(".send").click(function(){
var text = $(this).parent().find(".text").val();
});
Лучше всего дать им id. Будет
<textarea id="comment" ...></textarea>
<button class="send" id="submit">Отправить</button>\t
\t<script>
\t\t$(document).ready(function() {
\t\t\t$("#submit").click(function(){
\t\t\t\talert($("#comment").val());
\t\t\t});
\t\t})
\t</script>
>>469426
>>469424
Вам всем надо идти изучать CSS селекторы и яваскрипт/DOM прежде чем изучать jQuery. Элемент формы во-первых легко ищется по имени, то есть [name=x], во-вторых по имени через DOM: form.elements.name
Форму легко найти через this.form в обработчике или через closest.
> $(this).parent().find(".text").val( );
Такое вообще писать неправильно. Стоит добавить один див например и это перестанет работать.
Да я уже понял. Вся суть в том, что картинки можно делать динамичными и они будут меняться в зависимости от каких-либо условий. Ту же капчу думаю не сложно сделать, просто на каждый параметр по массиву создаешь с вариантами и через array_rand выводишь случайное значение.
Ну не тока капчи, вотермарки можно ставить, можно обрезать картинки которые пользователь грузит на сервер, менять размер по всякому, можно написать какой-нибудь ебовый фильтр распознающий голых баб и автоматом стучащий в роскомнадзор и продать государству, применений миллион.
Круто, на самом деле. Просто я изначально начал смотреть видос на эту тему и там чувак ничего про это не рассказал, а просто какие-то кружочки рисовал и прочую фигню, вот я и подумал, мол нафиг этот недопейнт нужен.
Вообще, так и не нашел ни один нормальный курс видеоуроков. Везде какие-то косноязычные уебаны, которые про всякую хуйню рассказывают вместо полезной информации.
Не хочу за все говорить но на русском обычно курсы ведут чуваки типа Евгения Попова, более продвинутые ребята перекатываются в англоязычные комьюнити, поэтому так оно и есть.
Ну я английский вроде более-менее знаю, но изучать на нем языки программирования для меня слишком тяжко. Так что приходится довольствоваться тем, что есть. В любом случае, для меня главное суть понять и начать что-то делать, а в случае любых непоняток есть гугл.
уроки опа почитай. Я когда брался книжечки читать, не мог пробиться через предварительную воду в виде всяких настроек WAMP стеков и прочего описания как работает веб в первый день, а раз в первый день нихуя не сделано практического, то и интерес падал. Или ты не полный нуб? тогда php 5 в подлиннике читни.
> Сначала удивило то, что к любому методу, свойству или еще чему, можно обращаться через $this->user->add_user() например, хотя метод add_user() описан в моделе, а ты сидишь ковыряешь контроллеры
Странные вещи ты пишешь. $this->user->add_user() это вызов метода add_user() у объекта который хранится в $this->user, почему это контроллер? Контроллер это если бы ты писал $this->add_user(). А так в $this->user может быть любой другой объект (модель в данном случае). Ты точно уроки по ООП проходил?
Логичнее было бы спросить почему мы пишем $this->user-> ... а не $user = new UserModel( ); $user->...
Потому что в кодеигнитере загрузка и подключение разных компонентов сделаны именно таким образом, что ты пишешь например $this->load->model('x') и он загружает модель в $this->x. Тут можно задать еще один вопрос, а откуда загрузчик $this->load знает в какой контроллер надо добавить поле x? Ну это просто, он добавляет поле x не в контроллер, а в базровый класс CI_Controller, а все наши контроллеры от него унаследованы.
Так что посмотри выше, там где-то стоит $this->load
Кстати, так как основной класс фреймворка это CI_controller и он используется только для наследования и сам по себе не создается, то пока не создан контроллер, большинство методов фреймворка не работает, а get_instance() вернет null.
мануал: https://ellislab.com/codeigniter/user-guide/libraries/loader.html
https://ellislab.com/codeigniter/user-guide/general/controllers.html
Я советую тебе открыть исходники классов CI_Controller, Loader и посмотреть их самому. Кодеигнайтер небольшой по объему, простой и его легко изучать.
Вообще этогт класс Loader (изучи мануал и его код) это наследие времен PHP4 когда не было автозагрузчика и приходилось вот так вот вручную загружать нужные классы.
> Везде такой подход используется в mvc - фреймворках?
Везде по разному. Я думаю, этот вопрос пока что не имеет смысла так как ты не так понял код.
> И похоже что их уже нельзя связать. Ведь они заполнены
Неправда. Ты и SQL наверно не до конца изучил, если не знаешь что внешние ключи можно добавить на существующие таблицы командой ALTER TABLE ADD FOREIGN KEY: http://phpclub.ru/mysql/doc/alter-table.html
Проверить наличие ключей лучше не по отсутствию ссылки в phpmyadmin (это вообще ничего не говорит, кто его знает почему он ссылку не выводит) а сделав запрос SHOW CREATE TABLE x и посмотрев есть в описании таблицы ключи или нет (вообще. начинающим надо больше писать запросы руками и меньше пользоваться графическими инструментами; это инструменты для опытных программистов).
Тут правда есть подвох: добавить ключ получится только если все ссылки корректные, то есть все id ссылаются на существующие записи, а отсутствие ссылки обозначается как NULL (а не 0, -1 или как-то еще). Это можно проверить несколькиими запросами:
— проверить нет ли странных значение типа 0, -1
— проверить есть ли несуществующие id, это легко делается запросом с LEFT JOIN
— также, для создания внешнего ключа, типы колонок должны совпадать полностью. включая число в скобочках у INT(10)
Соответсвенно, надо проверить, нет ли расхождений, если есть то написать скрипт/SQL команду их исправления, и добавить ключи.
Я советую делать это не на продакшене, а сделать отдельную тестовую БД и на ней тренироваться. Как только у тебя получится провести миграцию из исходного дампа без единой ошибки, можно переходить на продакшен. Не забудь записать удачную последовательность SQL запросов в файл, чтобы не забыть.
Вообще, все изменения надо на тестовой базе сначала делать.
По мне так это делать минут 30, ну у тебя как у начинающего может несколько часов займет.
Ну и надо читать больше документации и смотреть код кодеигнитер. Без этого ты в нем разбираться не будешь.
> Сначала удивило то, что к любому методу, свойству или еще чему, можно обращаться через $this->user->add_user() например, хотя метод add_user() описан в моделе, а ты сидишь ковыряешь контроллеры
Странные вещи ты пишешь. $this->user->add_user() это вызов метода add_user() у объекта который хранится в $this->user, почему это контроллер? Контроллер это если бы ты писал $this->add_user(). А так в $this->user может быть любой другой объект (модель в данном случае). Ты точно уроки по ООП проходил?
Логичнее было бы спросить почему мы пишем $this->user-> ... а не $user = new UserModel( ); $user->...
Потому что в кодеигнитере загрузка и подключение разных компонентов сделаны именно таким образом, что ты пишешь например $this->load->model('x') и он загружает модель в $this->x. Тут можно задать еще один вопрос, а откуда загрузчик $this->load знает в какой контроллер надо добавить поле x? Ну это просто, он добавляет поле x не в контроллер, а в базровый класс CI_Controller, а все наши контроллеры от него унаследованы.
Так что посмотри выше, там где-то стоит $this->load
Кстати, так как основной класс фреймворка это CI_controller и он используется только для наследования и сам по себе не создается, то пока не создан контроллер, большинство методов фреймворка не работает, а get_instance() вернет null.
мануал: https://ellislab.com/codeigniter/user-guide/libraries/loader.html
https://ellislab.com/codeigniter/user-guide/general/controllers.html
Я советую тебе открыть исходники классов CI_Controller, Loader и посмотреть их самому. Кодеигнайтер небольшой по объему, простой и его легко изучать.
Вообще этогт класс Loader (изучи мануал и его код) это наследие времен PHP4 когда не было автозагрузчика и приходилось вот так вот вручную загружать нужные классы.
> Везде такой подход используется в mvc - фреймворках?
Везде по разному. Я думаю, этот вопрос пока что не имеет смысла так как ты не так понял код.
> И похоже что их уже нельзя связать. Ведь они заполнены
Неправда. Ты и SQL наверно не до конца изучил, если не знаешь что внешние ключи можно добавить на существующие таблицы командой ALTER TABLE ADD FOREIGN KEY: http://phpclub.ru/mysql/doc/alter-table.html
Проверить наличие ключей лучше не по отсутствию ссылки в phpmyadmin (это вообще ничего не говорит, кто его знает почему он ссылку не выводит) а сделав запрос SHOW CREATE TABLE x и посмотрев есть в описании таблицы ключи или нет (вообще. начинающим надо больше писать запросы руками и меньше пользоваться графическими инструментами; это инструменты для опытных программистов).
Тут правда есть подвох: добавить ключ получится только если все ссылки корректные, то есть все id ссылаются на существующие записи, а отсутствие ссылки обозначается как NULL (а не 0, -1 или как-то еще). Это можно проверить несколькиими запросами:
— проверить нет ли странных значение типа 0, -1
— проверить есть ли несуществующие id, это легко делается запросом с LEFT JOIN
— также, для создания внешнего ключа, типы колонок должны совпадать полностью. включая число в скобочках у INT(10)
Соответсвенно, надо проверить, нет ли расхождений, если есть то написать скрипт/SQL команду их исправления, и добавить ключи.
Я советую делать это не на продакшене, а сделать отдельную тестовую БД и на ней тренироваться. Как только у тебя получится провести миграцию из исходного дампа без единой ошибки, можно переходить на продакшен. Не забудь записать удачную последовательность SQL запросов в файл, чтобы не забыть.
Вообще, все изменения надо на тестовой базе сначала делать.
По мне так это делать минут 30, ну у тебя как у начинающего может несколько часов займет.
Ну и надо читать больше документации и смотреть код кодеигнитер. Без этого ты в нем разбираться не будешь.
Спасибо.
> PHP Notice: Undefined offset: 4 in /home/PSNgOx/prog.php on line 21
Ты берешь число от 0 до count(..) но в массиве нет элемента с номером count(..), так как если в массиве 4 элемента то они имеют индексы 0, 1, 2, 3, а индекса 4 нету.
>>469136
То же самое
>>469139
Фреймворк позволяет грамотному верстальщику не тратить время на верстку набора элементов (меню, кнопки), а взять и использовать готовые (которые предоставляет фреймворк).
Фреймворк не освобождает от необходимости знать HTML/CSS на хорошем уровне. Без этих знаний ты будешь либо делать некачсетвенную верстку либо тратить кучу времени на исправление простых ошибок, кастомизацию, на какие-то нестандартные вещи (которые требуются почти в каждом проекте).
Так что сначала убедись что у тебя есть знания этого самого CSS. Если ты в них не уверен, у нас есть задания (в первом посте) которые помогают выявить и исправить пробелы в знаниях.
>>469141
Внутри функции не видны наружные (глобальные) переменные, и наоборот снаружи не видны внутренние (локальные) переменные. Это сделано специально чтобы побудить тебя писать более качественный, а не запутанный, код.
Все переменные, которые нужны функции, ты должен явно передать как аргументы (через скобки). Возвращать значения из функции можно только через reutrn (на самом деле не только, есть еще ссылки, но в этой задаче и в других учебных задачах они не нужны).
>>469146
Как мы тебе ответим если мы не пользовались этим провайдером? Пока шишки не набьешь, не узнаешь. VPS за 60 или 120 долларов в год может оказаться ровно такого же качества.
Я из облачных провайдеров пользовался только селектелом, он качественный, но дороже и там за все надо платить.
>>469147
Нет. Это глупый и неправильный подход который запутаывает код. Данные надо передавать через аргументы.
>>469155
молодец, хорошо. А еще можно написать через функицю array_rand и будет гораздо короче, так как она сразу возвращает случайный индекс из массива.
>>469160
Первый пост треда с задачами. Ну и для тебя вот есть отдельные: https://github.com/codedokode/pasta/blob/master/interview-tasks.md
> PHP Notice: Undefined offset: 4 in /home/PSNgOx/prog.php on line 21
Ты берешь число от 0 до count(..) но в массиве нет элемента с номером count(..), так как если в массиве 4 элемента то они имеют индексы 0, 1, 2, 3, а индекса 4 нету.
>>469136
То же самое
>>469139
Фреймворк позволяет грамотному верстальщику не тратить время на верстку набора элементов (меню, кнопки), а взять и использовать готовые (которые предоставляет фреймворк).
Фреймворк не освобождает от необходимости знать HTML/CSS на хорошем уровне. Без этих знаний ты будешь либо делать некачсетвенную верстку либо тратить кучу времени на исправление простых ошибок, кастомизацию, на какие-то нестандартные вещи (которые требуются почти в каждом проекте).
Так что сначала убедись что у тебя есть знания этого самого CSS. Если ты в них не уверен, у нас есть задания (в первом посте) которые помогают выявить и исправить пробелы в знаниях.
>>469141
Внутри функции не видны наружные (глобальные) переменные, и наоборот снаружи не видны внутренние (локальные) переменные. Это сделано специально чтобы побудить тебя писать более качественный, а не запутанный, код.
Все переменные, которые нужны функции, ты должен явно передать как аргументы (через скобки). Возвращать значения из функции можно только через reutrn (на самом деле не только, есть еще ссылки, но в этой задаче и в других учебных задачах они не нужны).
>>469146
Как мы тебе ответим если мы не пользовались этим провайдером? Пока шишки не набьешь, не узнаешь. VPS за 60 или 120 долларов в год может оказаться ровно такого же качества.
Я из облачных провайдеров пользовался только селектелом, он качественный, но дороже и там за все надо платить.
>>469147
Нет. Это глупый и неправильный подход который запутаывает код. Данные надо передавать через аргументы.
>>469155
молодец, хорошо. А еще можно написать через функицю array_rand и будет гораздо короче, так как она сразу возвращает случайный индекс из массива.
>>469160
Первый пост треда с задачами. Ну и для тебя вот есть отдельные: https://github.com/codedokode/pasta/blob/master/interview-tasks.md
> особо глубоких знаний php не требуют, т.к. все равно все на фреймворках пишут
Ты не можешь пользоваться фреймворками без отличного знания PHP и тем более ООП. Это как пытаться решать уравнения с синусами не зная 4 арифметических действий. Фреймворки не для того чтобы не надо было учить php, а для того чтобы не писать код повторно. но на них часто сложнее писать чем без них.
> Вроде бы принцип понял, но чет какой-то огромный скачек между первым/вторым заданием и остальными про кошек мышек и превьюшку.
А ООО вектор делал? Пости код, пусть даже недоделанный, напиши на чем завис и попроси подсказку.
>>469216
Этот код надо писать целиком с нуля чтобы научиться. Если копирвоать куски, чуть видоизменяя, ничего не поймешь. То есть посмотреть конечно можно, но писать надо все равно самому, не копируя.
>>469222
Запости код, напиши на чем завис. Там алгоритм очень простой. Считаем сколько надо самых старших купюр, потом купюр поменьше и так до самых маленьких.
>>469225
Просто он видимо пропустил главы по PHP и из-за этого на реальных приложениях будет тупить и тратить время на простые проблемы.
>>469247
Для тебя (вообще, конечно я его всем даю) есть дополнительный пункт к этой задаче:
Пока ты решал задачу по выводу отчета о сотрудниках и департаментах, разразился мировой экономический кризис. Доходы компании начали снижаться, и совет директоров поставил перед руководством задачу принять меры. Менеджеры 3-го ранга, блестящие выпускники топовых экономических вузов столицы, быстро смогли разработать три альтернативных антикризисных решения:
1. Сократить в каждом департаменте 40% (округляя в большую сторону) инженеров, преимущественно самого низкого ранга. Если инженер является боссом, вместо него надо уволить другого инженера, не босса.
2. Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков, а количество выпиваемого им кофе с 50 до 75 литров. В тех департаментах, где руководитель не является аналитиком, заменить его на аналитика самого высшего ранга из этого департамента (а бывшего руководителя вернуть к обычной работе)
3. В каждом департаменте повысить 50% (округляя в большую сторону) менеджеров 1-го и 2-го ранга на один ранг с целью расширить их полномочия.
Совет директоров в затруднении: какой путь выбрать? Помоги им с этим, распечатав прогноз по потреблению и расходам (аналогичный тому что требуется в задаче) после принятия каждой из мер.
> особо глубоких знаний php не требуют, т.к. все равно все на фреймворках пишут
Ты не можешь пользоваться фреймворками без отличного знания PHP и тем более ООП. Это как пытаться решать уравнения с синусами не зная 4 арифметических действий. Фреймворки не для того чтобы не надо было учить php, а для того чтобы не писать код повторно. но на них часто сложнее писать чем без них.
> Вроде бы принцип понял, но чет какой-то огромный скачек между первым/вторым заданием и остальными про кошек мышек и превьюшку.
А ООО вектор делал? Пости код, пусть даже недоделанный, напиши на чем завис и попроси подсказку.
>>469216
Этот код надо писать целиком с нуля чтобы научиться. Если копирвоать куски, чуть видоизменяя, ничего не поймешь. То есть посмотреть конечно можно, но писать надо все равно самому, не копируя.
>>469222
Запости код, напиши на чем завис. Там алгоритм очень простой. Считаем сколько надо самых старших купюр, потом купюр поменьше и так до самых маленьких.
>>469225
Просто он видимо пропустил главы по PHP и из-за этого на реальных приложениях будет тупить и тратить время на простые проблемы.
>>469247
Для тебя (вообще, конечно я его всем даю) есть дополнительный пункт к этой задаче:
Пока ты решал задачу по выводу отчета о сотрудниках и департаментах, разразился мировой экономический кризис. Доходы компании начали снижаться, и совет директоров поставил перед руководством задачу принять меры. Менеджеры 3-го ранга, блестящие выпускники топовых экономических вузов столицы, быстро смогли разработать три альтернативных антикризисных решения:
1. Сократить в каждом департаменте 40% (округляя в большую сторону) инженеров, преимущественно самого низкого ранга. Если инженер является боссом, вместо него надо уволить другого инженера, не босса.
2. Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков, а количество выпиваемого им кофе с 50 до 75 литров. В тех департаментах, где руководитель не является аналитиком, заменить его на аналитика самого высшего ранга из этого департамента (а бывшего руководителя вернуть к обычной работе)
3. В каждом департаменте повысить 50% (округляя в большую сторону) менеджеров 1-го и 2-го ранга на один ранг с целью расширить их полномочия.
Совет директоров в затруднении: какой путь выбрать? Помоги им с этим, распечатав прогноз по потреблению и расходам (аналогичный тому что требуется в задаче) после принятия каждой из мер.
Неплохо, но вот пара улучшений:
— если хотя бы одна пара букв не совпала, из цикла можно выходить и дальше сравнивать незачем
— нужно подводить итог: палиндром слово или нет. Пока я итога не вижу.
> $length = mb_strlen($text1);
> $halfLength = floor($length / 2);
Это почему-то 2 раза скопировано.
> $text1
лучше оставить просто $text
Номера символов считаются странно:
> \t$f = mb_substr($text1, $i - 1, 1);
Тут получается -1, 0, 1, 2, ....
> $l = mb_substr($text1, -$i, 1);
Тут получается -0 = 0, -1, -2, -3 ...
Найти форму через сlosest и найти в ней элемент по имени [name=...]
>>469533
Графики, схемы. Иероглифы можно картинкой рисовать для тех у кого шрифт не установлен.
>>469744
Часто книги требуют каких-то предварительных знаний (это обычно написано в начале или предисловии). Например знания HTML/CSS и работы с сервером.
Также, по PHP много устаревших и плохих книг.
Я вообще-то про фрейморки на php речь вел. Bootstrap привел как пример того, с чем я имел дело. И насколько я помню там ничего сложного нет вообще и никаких суперзнаний не надо.
>>469776
Даже не начинал толком этот вектор делать, т.к. мне надоели задачки эти. Поначалу конечно забавно было, но они слишком уж абстрактные и я пойду лучше знания на практике применять, делая сайт. Если что непонятно будет и не смогу нагуглить, то спрошу в треде конечно.
Вот недомакет накидал дизайнер из меня хуевый, знаю. Все примитивно конечно, но для начала думаю сойдет. Постараюсь даже CMS запилить для предпросмотра вебмок, чтобы всякое говно не загружали.
http://ideone.com/LXofT6
>Странные вещи ты пишешь.
Да, так и знал что ересь какую-нибудь напишу. Конечно же у меня еще туговато с ООП, и да, я просто лишь хотел сказать, что пишем не так как на голом пхп с созданием объекта через
$object = new Class;
с дальнейшим обращением к свойствам и методам этого юзера таким образом:
$object->method(); $object->property;
А просто описывается новый класс, но объект не создается, и ко всему подряд можно обращаемся через $this->class->method();
$this->class->property;
>$this->load->model('x')
Так не пишем.
пишется в конфигах
$autoload['model'] = array('user', 'post', 'site');
например, и так подключаются все классы для модели.
В общем вот содержимое файла autoload.php из папки configs текущей версии игнайтера. http://ideone.com/c5nBKk Бтв вышла месяц назад 3.0
>Вообще этогт класс Loader (изучи мануал и его код) это наследие времен PHP4 когда не было автозагрузчика и приходилось вот так вот вручную загружать нужные классы.
А что есть автозагрузчик? Там есть в конфигах файл автолоад, и в него просто прописываешь классы новые. Наверное ты все же про старые версии фреймворка мне говоришь.
>Неправда. Ты и SQL наверно не до конца изучил, если не знаешь что внешние ключи можно добавить на существующие таблицы командой ALTER TABLE ADD FOREIGN KEY: http://phpclub.ru/mysql/doc/alter-table.html
В общем дело было в следующем, у нас таблицы были сделаны на Myisam, а он не поддерживает ключи: http://rtfm.co.ua/mysql-otlichiya-mezhdu-myisam-i-innodb/
В итоге после того как конвертировал все таблички в innoDB, стало возможно связать их.
>Я советую делать это не на продакшене, а сделать отдельную тестовую БД и на ней тренироваться.
И так ковыряюсь в специально скопированном сайте, а "продакшен" пока без изменений, как начальник посмотрит что я там написал наверное отправит на допил, потом же перенесем.
>Ну и надо читать больше документации и смотреть код кодеигнитер. Без этого ты в нем разбираться не будешь.
Спасибо, стараюсь, стал куда больше читать и гуглить в последнее время, что бы во всем разобраться с чем сейчас работаю.
>Странные вещи ты пишешь.
Да, так и знал что ересь какую-нибудь напишу. Конечно же у меня еще туговато с ООП, и да, я просто лишь хотел сказать, что пишем не так как на голом пхп с созданием объекта через
$object = new Class;
с дальнейшим обращением к свойствам и методам этого юзера таким образом:
$object->method(); $object->property;
А просто описывается новый класс, но объект не создается, и ко всему подряд можно обращаемся через $this->class->method();
$this->class->property;
>$this->load->model('x')
Так не пишем.
пишется в конфигах
$autoload['model'] = array('user', 'post', 'site');
например, и так подключаются все классы для модели.
В общем вот содержимое файла autoload.php из папки configs текущей версии игнайтера. http://ideone.com/c5nBKk Бтв вышла месяц назад 3.0
>Вообще этогт класс Loader (изучи мануал и его код) это наследие времен PHP4 когда не было автозагрузчика и приходилось вот так вот вручную загружать нужные классы.
А что есть автозагрузчик? Там есть в конфигах файл автолоад, и в него просто прописываешь классы новые. Наверное ты все же про старые версии фреймворка мне говоришь.
>Неправда. Ты и SQL наверно не до конца изучил, если не знаешь что внешние ключи можно добавить на существующие таблицы командой ALTER TABLE ADD FOREIGN KEY: http://phpclub.ru/mysql/doc/alter-table.html
В общем дело было в следующем, у нас таблицы были сделаны на Myisam, а он не поддерживает ключи: http://rtfm.co.ua/mysql-otlichiya-mezhdu-myisam-i-innodb/
В итоге после того как конвертировал все таблички в innoDB, стало возможно связать их.
>Я советую делать это не на продакшене, а сделать отдельную тестовую БД и на ней тренироваться.
И так ковыряюсь в специально скопированном сайте, а "продакшен" пока без изменений, как начальник посмотрит что я там написал наверное отправит на допил, потом же перенесем.
>Ну и надо читать больше документации и смотреть код кодеигнитер. Без этого ты в нем разбираться не будешь.
Спасибо, стараюсь, стал куда больше читать и гуглить в последнее время, что бы во всем разобраться с чем сейчас работаю.
Всё неправильно.
Должна быть таблица "пользователь", таблица "вопрос" и сводная таблица "вопрос-пользователь" где две колонки: "user_id" и "question_id".
Таблица:
user_info например
Поля:
alcohole_attitude:
financial_condition:
family_status:
Ну и делаешь там значения этих колонок со строгим выбром из: Женат / Замужем / Холост / Свободна / В поиске изи же?
Я тебе за 3 минуты накидал это с помощью https://slovari.yandex.ru/
И что мне это даст как потом сделать запрос чтобы оно все в одну таблицу попало с колонками типа
ID пользователя, вопрос о курении, вопрос об алкоголе, вопрос о семье...
1, курит, не пьет, замужем
ID пользователя, вопрос
1 курит
1 пьет
1 замужем
1 богат
2 не курит
2 не пьет
и так далее
>>469853
>469853\t
При чем тут вообще вопросы? Это ведь инфа которую юзеры заполняют о себе по сути, и больше нихуя. Можно хоть в таблице самого юзера хранить вместе с паролем, мылом и хэшем для запоминания. А можно и в отдельной, чисто вынести всю информационную поеботу в неё, начиная от возраста и заканчивая сколько раз в день дрочишь. хз надо смотреть как будет работать это дело по скорости, ибо у меня опыта нету.
Ну дык всю эту ебаторию не только хранить абы как надо, а еще доставать, выводить, сортировать по ней. Выходит что в таблице самого юзера надо хранить вот это
>>469858
alcohole_views_id - 1
smoking_views - 2
family_status - 0
и в отдельных таблицах типа alcohole_views две колонки - айди и само "отношение"
1 - выпиваю
2 - не пью
3 - по праздникам
Так их хотя бы можно согнать в одну таблицу через вложенный запрос.
Ох и выбрал же я себе задачу, сижу охуеваю с этих быдлопроблем.
и зачем тебе отдельные микро таблицы тогда? Не проще ли сделать просто массивы соответствий? Ты либо храни все сразу в одной табличке, в том числе и значения "выпиваю" "замужем"
Либо если тебе хочется хранить какие-то циферки, то сделай сорт оф языковой файл, где будут зашиты массивы вида:
$user_smoking[1 => 'курю'];
$user_smoking[2 => 'не курю'];
$user_smoking[3 => 'дрочу на элетронные сиги и сру всем этим в уши, смотрите же!'];ъ
И как по мне, так это выглядит проще и легче для работы, чем плодить кучу таблиц и связей в базе. Они просто будут лежать по сути мертвым грузом, и лишь жрать ресурсы сервера, в силу того что будут участвовать в запросах, ведь без них никак.
Потом даже можно в перспективе расширять сайт на второй язык, прикрутив например такой же файл, но с английским
$user_smoking[1 => 'курю'];
$user_smoking[2 => 'не курю'];
И подгружать его если пользователь выбрал языком интерфейса английский.
Ну я спрашивал за эту тему у авторитетов, мне дипломированные забугорные sql-синьоры сказали что за малейшее смешивание логики и данных сразу на парашу отправляют. Даже пол нельзя хранить в виде 1, 2, а в коде делать свич (секс) { '1': мужик, '2': баба} , все надо в отдельных таблицах.
ну это адепты учебников каких-нибудь. А теперь представь что вконтакте хранит всю инфу строго в базе, представь сколько там говна будет и какие длинные цепочки связей будут получаться. Инфа 100 для высоконагруженной хуйни используются подобные хитрые подходы.
и вообще у тебя не будет смешения логики и данных. У тебя будет логика так же ходить по приложению в том виде в котором она в базе лежит. То есть например выходит у тебя объект user
и у него поля
sex => 1
smoke => 3
suck_cock => 1
и где-то есть массив
$lang['sex'][0 => 'Пол', 1 => 'Мужской', 2=>'Женский', 3 => 'девочка внутри'];
А когда все приходит во вьюху или не знаю там, во фронтэнд, то эти значения подгружаются из массива соответствий
<p>Пол: <?= echo lang['sex'][user->sex] ?><p>
В общем как-то так. Хотя можно сделать что у тебя ни одной буквы русской там не будет:
<p><?= echo lang['sex'][0] ?>: <?= echo lang['sex'][user->sex] ?><p>
В общем я хуево объяснил, потому что я не могу подглядеть прямо сейчас в то, как там у меня на работке используется, но в принципе мне самому эта идея нравится. Имхо в базе надо хранить в текстовом виде только то, что не будет постоянно копипастой: всякие сообщения или емейлы там, а такое говно храни циферками в одной таблице и гоняй их куда хочешь, а как до вывода пользователю дело дошло, то подключай файл языка и выводи значения из него.
В этот раз старался поаккуратнее делать.
И пара вопросов:
1) Какой редактор кода для PHP имеет такую же хипстерскую подсветку синтаксиса, как и на Codeacademy? Или это можно накрутить в любом редакторе?
2) Где можно почитать об аккуратности оформления кода? Насколько для форматирования пригоден Phpformatter?
В сублиме техт и бракетс точка ио точно накатить няшную подсветку синтаксиса можно.
Привет, php. Вопрос следующего формата. Нужно написать на php одну фичу. я с php не работал, но придумал следующий дизайн: фронтенд рисую на штмл и js, а на php пишу только апи движок. Вопроса два: какие подводные камни у такого подхода и второй, что можно почитать/какой фрейморвк использовать для серверного php. Смотрю в сторону yii.
Да, данными обмениваться через json.
У меня в программе есть таблица с пользователями и таблица с предложениями пользователей, связаны, как один-ко-многим через user_id в таблице с предложениями. Вот, например, заходит на сайте человек через Стим, как определить через if, что зашёл? Потому что, то, что я видел, с логина Стима идёт редирект на запрашивающий разрешения на вход сайт с кучей информации в самой ссылке, что с ней делать? Еще, нужно ведь при такой конфигурации БД заносить строчку с пользователем Стима в таблицу пользователей, потому что к ней привязаны предложения из соответствующей таблицы?
Спасибо, а то запутался что-то, вчера столько времени потерял, чтобы разобраться, но толком ничего в голове не отложилось.
Скоро лето, и те кто ищут работу, могли бы сделать себе футболку с принтом в таком духе: "ищу работу php-программиста" Ну можно даже задизайнить на сером фоне пару тегов уровня <?php echo ""; ?>
Ну и летом гонять в ней по людным местам, вдруг взлетит.
Нет, просто шансы таким способом найти работу стремятся к нулю. Во-первых, работодателей с фирмами в IT-сфере не так уж и много, поэтому встретить такого просто гуляя на улице очень маловероятно. Шанс того, что он обратит внимание на надпись на твоей футболке еще меньше, ну а про шансы на то, что он вдруг решит подойти к тебе и предложить работу вообще можно не рассматривать.
Это ты на какие вакансии рассылаешь?
> пишем не так как на голом пхп с созданием объекта через
> $object = new Class;
Там то же самое, просто оно чуть менее явно написано. Вот смотри, ты сказал
> пишется в конфигах
> $autoload['model'] = array('user', 'post', 'site');
Давай посмотрим в коде, как это обрабатывается. Вот класс Loader (объект этот класса хранится в $this->load и отвечает за загрузку и создание разных объектов, вроде моделей, вью и тд):
https://github.com/bcit-ci/CodeIgniter/blob/master/system/core/Loader.php#L1248
Тут мы загружаем конфиг автозагрузки:
> if (file_exists(APPPATH.'config/autoload.php'))
>\t\t{
>\t\t\tinclude(APPPATH.'config/autoload.php');
>\t\t}
https://github.com/bcit-ci/CodeIgniter/blob/master/system/core/Loader.php#L1316
Тут мы подгружаем указанные в нем модели:
> if (isset($autoload['model']))
>\t\t{
>\t\t\t$this->model($autoload['model']);
>\t\t}
Давай еще посмотрим код метода model:
https://github.com/bcit-ci/CodeIgniter/blob/master/system/core/Loader.php#L234
Тут проверяется не передан ли нам массив, и если да, то мы вызваем метод model для каждого элемента:
> if (is_array($model))
>\t\t{
>\t\t\tforeach ($model as $key => $value)
Затем подключаются файлы модели и базового класса CI_Model, и наконец создается объект:
https://github.com/bcit-ci/CodeIgniter/blob/master/system/core/Loader.php#L305
> $CI->$name = new $model();
Вот видишь, хоть это и скрыто за функциями и конфигами, но в итоге все равно мы создаем объект через new. Ты можешь натыкать туда echo или var_dump и сам увидеть чему равны переменные и что куда передается.
И объекты тут точно такие же, с теми же полями и методами.
Мы их создавали явно, так как это проще, а изучать надо с простых примеров.
Ну и разумеется и конфиг autoload, и метод model описаны в документации по CI. Ну и даже без документации, взглянув в исходники, видно как это работает.
Ну и еще раз напомню, что этот подход с $this->load в современных фреймворках не используется.
> А просто описывается новый класс, но объект не создается, и
Объект создается, ты просто этого не нашел/не знал. Ну, теперь знаешь.
> пишем не так как на голом пхп с созданием объекта через
> $object = new Class;
Там то же самое, просто оно чуть менее явно написано. Вот смотри, ты сказал
> пишется в конфигах
> $autoload['model'] = array('user', 'post', 'site');
Давай посмотрим в коде, как это обрабатывается. Вот класс Loader (объект этот класса хранится в $this->load и отвечает за загрузку и создание разных объектов, вроде моделей, вью и тд):
https://github.com/bcit-ci/CodeIgniter/blob/master/system/core/Loader.php#L1248
Тут мы загружаем конфиг автозагрузки:
> if (file_exists(APPPATH.'config/autoload.php'))
>\t\t{
>\t\t\tinclude(APPPATH.'config/autoload.php');
>\t\t}
https://github.com/bcit-ci/CodeIgniter/blob/master/system/core/Loader.php#L1316
Тут мы подгружаем указанные в нем модели:
> if (isset($autoload['model']))
>\t\t{
>\t\t\t$this->model($autoload['model']);
>\t\t}
Давай еще посмотрим код метода model:
https://github.com/bcit-ci/CodeIgniter/blob/master/system/core/Loader.php#L234
Тут проверяется не передан ли нам массив, и если да, то мы вызваем метод model для каждого элемента:
> if (is_array($model))
>\t\t{
>\t\t\tforeach ($model as $key => $value)
Затем подключаются файлы модели и базового класса CI_Model, и наконец создается объект:
https://github.com/bcit-ci/CodeIgniter/blob/master/system/core/Loader.php#L305
> $CI->$name = new $model();
Вот видишь, хоть это и скрыто за функциями и конфигами, но в итоге все равно мы создаем объект через new. Ты можешь натыкать туда echo или var_dump и сам увидеть чему равны переменные и что куда передается.
И объекты тут точно такие же, с теми же полями и методами.
Мы их создавали явно, так как это проще, а изучать надо с простых примеров.
Ну и разумеется и конфиг autoload, и метод model описаны в документации по CI. Ну и даже без документации, взглянув в исходники, видно как это работает.
Ну и еще раз напомню, что этот подход с $this->load в современных фреймворках не используется.
> А просто описывается новый класс, но объект не создается, и
Объект создается, ты просто этого не нашел/не знал. Ну, теперь знаешь.
> А что есть автозагрузчик?
Автозагрузчик — это функция, которую вызывает PHP при обращении к несуществующему классу перед тем как выдать ошибку. Если функция найдет и подгрузит нужный класс, то код продолжит выполняться, если нет то будет ошибка.
Автозагрузчик избавляет от необходимости писать руками require/require_once/$this->load
Мануал
http://php.net/manual/ru/function.spl-autoload-register.php
http://habrahabr.ru/post/136761/
То что в CI это другое, это как раз загрузчик для тех версий PHP где автозагрузки еще не было. Я же написал что CI довольно древний.
> у нас таблицы были сделаны на Myisam,
> конвертировал все таблички в innoDB
Правильно сделал, MyISAM древний и примитивный движок и в 99% случаев проигрывает innoDB. Он еще и транзакции не поддерживает и плохо работает в многопоточной среде.
Ну и внешние ключи сэкономят тебе время на исправлении битых связей в базе, так что их стоит добавлять. И тот, кто будет после тебя изучать базу, сможет увидеть связи. Не забывай еще комментарии к колонкам и таблицам добавлять, если их смысл не очень очевиден. И не забудь что в MySQL много интересных типов данных вроде DATETIME, YEAR, ENUM и не надо все делать только INT и VARCHAR: http://phpclub.ru/mysql/doc/column-types.html
>>469836
Ты можешь открыть АПИ фейсбука или вконтакте и скопировать оттуда. Зачем изобретать велосипед?
> И сами колонки как можно назвать одним словом само 'отношение к бухлу'
drinking habits может?
>>469853
Не обязательно. Ты, как я понимаю. на EAV (Entity- Attribute-Value) намекаешь? Она используется когда полей очень много и неэффективно их в виде колонок делать. Но тут у нас полей не так много, они там почти все обязательные, так что проще их ENUM сделать, с ним работать проще.
>>469927
Если полей относительно немного, проще в таблице ENUM хранить, запросы короче и удобнее будут.
>>469940
> Не проще ли сделать просто массивы соответствий?
Ты неправильные вещи советуешь. Вот смотришь ты на дамп базы а там
1
2
3
2
1
И что это значит? В этом неудобно разбираться и ориентироваться. Для полей в которых несколько вариантов есть тип ENUM:
material_status ENUM ('rich', 'poor') NOT NULL
В коде разумеется мы, как добропорядочные программисты, используем для этих значений константы (и заодно получаем автодополнение от IDE):
$user->setMaterialStatus(User::MATERIAL_STATUS_RICH);
Не надо изобретать странные велосипеды, ухудшающие читаемость кода, когда все уже придумано до нас. Посмотри сам, насколько читабельно и понятно получается.
> чем плодить кучу таблиц и связей в базе.
Есть тип ENUM
Если данные представляют отдельную сущность, то ее выносят в отдельную таблицу. Ну например, города логично вынести в таблицу городов (так как там будет еще например привязана страна), а в таблице пользователей оставить только city_id. Заодно можно будет разрешить пользователям добавлять свои города.
> Они просто будут лежать по сути мертвым грузом, и лишь жрать ресурсы сервера,
С ресурсами как раз проблем нет, если есть желание, то можно просто закешировать в быстром кеше вроде APCu или Memcache эти массивы и не лазать каждый раз в базу. По сути будет тот же массив, только не описанный руками, а взятый из базы. Ну например если городов немного то можно их закешировать. Можно не кешировать, так как выборка их по id тоже очень быстро работает.
> у нас таблицы были сделаны на Myisam,
> конвертировал все таблички в innoDB
Правильно сделал, MyISAM древний и примитивный движок и в 99% случаев проигрывает innoDB. Он еще и транзакции не поддерживает и плохо работает в многопоточной среде.
Ну и внешние ключи сэкономят тебе время на исправлении битых связей в базе, так что их стоит добавлять. И тот, кто будет после тебя изучать базу, сможет увидеть связи. Не забывай еще комментарии к колонкам и таблицам добавлять, если их смысл не очень очевиден. И не забудь что в MySQL много интересных типов данных вроде DATETIME, YEAR, ENUM и не надо все делать только INT и VARCHAR: http://phpclub.ru/mysql/doc/column-types.html
>>469836
Ты можешь открыть АПИ фейсбука или вконтакте и скопировать оттуда. Зачем изобретать велосипед?
> И сами колонки как можно назвать одним словом само 'отношение к бухлу'
drinking habits может?
>>469853
Не обязательно. Ты, как я понимаю. на EAV (Entity- Attribute-Value) намекаешь? Она используется когда полей очень много и неэффективно их в виде колонок делать. Но тут у нас полей не так много, они там почти все обязательные, так что проще их ENUM сделать, с ним работать проще.
>>469927
Если полей относительно немного, проще в таблице ENUM хранить, запросы короче и удобнее будут.
>>469940
> Не проще ли сделать просто массивы соответствий?
Ты неправильные вещи советуешь. Вот смотришь ты на дамп базы а там
1
2
3
2
1
И что это значит? В этом неудобно разбираться и ориентироваться. Для полей в которых несколько вариантов есть тип ENUM:
material_status ENUM ('rich', 'poor') NOT NULL
В коде разумеется мы, как добропорядочные программисты, используем для этих значений константы (и заодно получаем автодополнение от IDE):
$user->setMaterialStatus(User::MATERIAL_STATUS_RICH);
Не надо изобретать странные велосипеды, ухудшающие читаемость кода, когда все уже придумано до нас. Посмотри сам, насколько читабельно и понятно получается.
> чем плодить кучу таблиц и связей в базе.
Есть тип ENUM
Если данные представляют отдельную сущность, то ее выносят в отдельную таблицу. Ну например, города логично вынести в таблицу городов (так как там будет еще например привязана страна), а в таблице пользователей оставить только city_id. Заодно можно будет разрешить пользователям добавлять свои города.
> Они просто будут лежать по сути мертвым грузом, и лишь жрать ресурсы сервера,
С ресурсами как раз проблем нет, если есть желание, то можно просто закешировать в быстром кеше вроде APCu или Memcache эти массивы и не лазать каждый раз в базу. По сути будет тот же массив, только не описанный руками, а взятый из базы. Ну например если городов немного то можно их закешировать. Можно не кешировать, так как выборка их по id тоже очень быстро работает.
В MySQL есть ENUM для этого
>>469951
Если ты не будешь следовать «учебникам» то получишь кашу, в которой никто не разберется потом. Я видел творения людей, которые не читали учебники и тоже наверно считали что это все ерунда и они лучше знают как делать. У них там такая каша, в которой после них никто не может разобраться. Человек блуждает в потемках, но при этом наверно себя считает непризнанным гением.
Насчет вконтакте — ты прав, там не используются стандартные подходы, потому что у них большой объем данных, и огромная нагрузка и один сервер с базой данных не выдержит ее (но на раннем этапе, первая версия использовала обычный MySQL без наворотов). Данные надо разносить на тысячи серверов (шардинг), и это очень сложная штука. Плюс, некоторые данные хранятся не в базе, а в написанных ими на Си NoSQL хранилищах (код есть на гитхабе). Но это исключительные случаи, в 99% случаев подходы «из учебников» работают отлично. Ну и разумеется я уверен что разработчики вконтакте эти стандартные подходы прекрасно знают.
А то ,что ты пытаешься сказать, что «нормализация или внешние ключи это пустая трата времени и придумана дураками, не надо читать учебники, а надо изобретать свой велосипед», вот это неправильно. Всех велосипедистов надо гнать вон из профессии, от них один вред. Они набыдлокодят, а потом другим людям разбираться.
> и какие длинные цепочки связей будут получаться.
На практике они не такие и длинные и базы данных с ними прекрасно справляются.
>>469963
> например выходит у тебя объект user
> и у него поля
> и где-то есть массив
Это пример того как делать не надо. Есть ENUM и константы. Вот удовольствие потом искать и догадываться где какой массив чему соовтетствует.
> Пол: <?= echo lang['sex'][user->sex] ?>
А надо так: <?= $user->getSexAsString() ?> или так: <?= $userHelper->printSexAsString($user) ?>
Перевод тоже делается не так. То, что ты придумал, это опять же велосипед на массивах. Для переводов есть open source расширение gettext (которое используется не только в PHP, но и например почти во всех linux утилитах). И с ним в качестве ключа используется английская версия строки:
<p><?= gettext('Sex', $domain) ?></p>
Где $domain это что-то вроде файла, откуда брать перевод. разумеется, напрямую использовать gettext не обязательно и можно написать свою обертку поверх него. А можно пойти дальше и встроить поддержку переводов в шаблонизатор (я не видел ни одного такого, но когда-то пытался навелосипедить что-то вроде этого. Сейчас я бы не стал делать велосипед, а расширил какой-нибудь твиг и отправил авторам пулл реквест):
<p>[[Sex:]]</p>
чтобы короче писать и чтобы шаблонизатор сам генерировал нужный код.
Сами переводы хранятся в файлах формата .mo для которых есть open source редакторы. Разумеется, текстовые строки не надо вписывать туда руками, я писал скрипт который обходил исходный код и собирал строки в mo файл на английском, после чего с него делался русский перевод.
Потом там еще нужны функции для подстановки в строку значений и склонения чисел. Ну например чтобы сделать переводимую фразу «5 человек написали 3 комментария» придется чуть исхитриться, так как слова склоняются в зависимости от чисел (а глаголы в зависимости от пола). gettext это не поддерживает, но у нас есть расширение intl, пришедшее из явы, где такая возможность есть, там свой синтаксис и эта фраза пишется примерно так:
{peopleCount, plural, offset:1
=0 {никто не оставил}
=1 {{peopleCount} человек оставил}
=2 {{peopleCount} человека оставили}
=3 {{peopleCount} человек оставили}
}
{commentCount, plural, offset:1
=0 {ни одного комментария}
=1 {{commentCount} комментарий}
=2 {{commentCount} комментария}
=3 {{commentCount} комментариев}
}
Да, это довольно сложный синтаксис (но зато он мощный и дает возможность, как ты видишь, правильно склонять слова), и я когда-то писал альтернативный велосипед, но сегодня я пожалуй взял бы Intl (а там еще есть много других полезных для локализации функций).
Мораль: если бы вместо изобретения велосипедов ты погуглил и почитал про то, что есть, то получил бы более удобную систему с использованием распространненных форматов файлов, распространенных библиотек, с инструментами и инфраструктурой для работы с ней. То, что есть по уровню качества и удобства гораздо выше самописного велосипеда.
Кстати, если тебе интересны какие-то вопросы по локализации, я могу подсказать. Это все же учебный тред и наша цель обучать людей, а не просто говоирть что они поступают неправильно.
В MySQL есть ENUM для этого
>>469951
Если ты не будешь следовать «учебникам» то получишь кашу, в которой никто не разберется потом. Я видел творения людей, которые не читали учебники и тоже наверно считали что это все ерунда и они лучше знают как делать. У них там такая каша, в которой после них никто не может разобраться. Человек блуждает в потемках, но при этом наверно себя считает непризнанным гением.
Насчет вконтакте — ты прав, там не используются стандартные подходы, потому что у них большой объем данных, и огромная нагрузка и один сервер с базой данных не выдержит ее (но на раннем этапе, первая версия использовала обычный MySQL без наворотов). Данные надо разносить на тысячи серверов (шардинг), и это очень сложная штука. Плюс, некоторые данные хранятся не в базе, а в написанных ими на Си NoSQL хранилищах (код есть на гитхабе). Но это исключительные случаи, в 99% случаев подходы «из учебников» работают отлично. Ну и разумеется я уверен что разработчики вконтакте эти стандартные подходы прекрасно знают.
А то ,что ты пытаешься сказать, что «нормализация или внешние ключи это пустая трата времени и придумана дураками, не надо читать учебники, а надо изобретать свой велосипед», вот это неправильно. Всех велосипедистов надо гнать вон из профессии, от них один вред. Они набыдлокодят, а потом другим людям разбираться.
> и какие длинные цепочки связей будут получаться.
На практике они не такие и длинные и базы данных с ними прекрасно справляются.
>>469963
> например выходит у тебя объект user
> и у него поля
> и где-то есть массив
Это пример того как делать не надо. Есть ENUM и константы. Вот удовольствие потом искать и догадываться где какой массив чему соовтетствует.
> Пол: <?= echo lang['sex'][user->sex] ?>
А надо так: <?= $user->getSexAsString() ?> или так: <?= $userHelper->printSexAsString($user) ?>
Перевод тоже делается не так. То, что ты придумал, это опять же велосипед на массивах. Для переводов есть open source расширение gettext (которое используется не только в PHP, но и например почти во всех linux утилитах). И с ним в качестве ключа используется английская версия строки:
<p><?= gettext('Sex', $domain) ?></p>
Где $domain это что-то вроде файла, откуда брать перевод. разумеется, напрямую использовать gettext не обязательно и можно написать свою обертку поверх него. А можно пойти дальше и встроить поддержку переводов в шаблонизатор (я не видел ни одного такого, но когда-то пытался навелосипедить что-то вроде этого. Сейчас я бы не стал делать велосипед, а расширил какой-нибудь твиг и отправил авторам пулл реквест):
<p>[[Sex:]]</p>
чтобы короче писать и чтобы шаблонизатор сам генерировал нужный код.
Сами переводы хранятся в файлах формата .mo для которых есть open source редакторы. Разумеется, текстовые строки не надо вписывать туда руками, я писал скрипт который обходил исходный код и собирал строки в mo файл на английском, после чего с него делался русский перевод.
Потом там еще нужны функции для подстановки в строку значений и склонения чисел. Ну например чтобы сделать переводимую фразу «5 человек написали 3 комментария» придется чуть исхитриться, так как слова склоняются в зависимости от чисел (а глаголы в зависимости от пола). gettext это не поддерживает, но у нас есть расширение intl, пришедшее из явы, где такая возможность есть, там свой синтаксис и эта фраза пишется примерно так:
{peopleCount, plural, offset:1
=0 {никто не оставил}
=1 {{peopleCount} человек оставил}
=2 {{peopleCount} человека оставили}
=3 {{peopleCount} человек оставили}
}
{commentCount, plural, offset:1
=0 {ни одного комментария}
=1 {{commentCount} комментарий}
=2 {{commentCount} комментария}
=3 {{commentCount} комментариев}
}
Да, это довольно сложный синтаксис (но зато он мощный и дает возможность, как ты видишь, правильно склонять слова), и я когда-то писал альтернативный велосипед, но сегодня я пожалуй взял бы Intl (а там еще есть много других полезных для локализации функций).
Мораль: если бы вместо изобретения велосипедов ты погуглил и почитал про то, что есть, то получил бы более удобную систему с использованием распространненных форматов файлов, распространенных библиотек, с инструментами и инфраструктурой для работы с ней. То, что есть по уровню качества и удобства гораздо выше самописного велосипеда.
Кстати, если тебе интересны какие-то вопросы по локализации, я могу подсказать. Это все же учебный тред и наша цель обучать людей, а не просто говоирть что они поступают неправильно.
Мне тоже такой подход с массивом в конфиге казался логичным. А вот нихрена, читнул еще с десяток источников, буквально все настаивают что все данные надо хранить в бд. Нагруженность и прочее это все с их слов решается кешированием, хотя я толком еще не знаю как оно работает.
>>470043
Читал про enum тоже, тут пишут что это отстой http://komlenic.com/244/8-reasons-why-mysqls-enum-data-type-is-evil/
>EAV (Entity- Attribute-Value)
Вот это мне даже больше понравилось то о чем >>469853 писал. И мне даже подсказали как одним красивым запросом потом получить что-то типа 'мужчина, пьет, не курит, разведен' т. е. строка из всех значений вот этих опций с указанием сепаратора, например запятой. Но дальше с ней делать нечего, порядок опций произвольный может прийти 'мужчина, курит, пьет', а может 'пьет, мужчина, курит', короче распарсить это нереально.
Короче буду делать отдельные таблицы да и все.
Статья показывает только отрицательные стороны и довольно однобока.
Плюсы ENUM: запросы получаются проще и нагляднее, не нужны джойны.
> 1. Data isn't being treated like data.
не согласен, ENUM можно считать отдельным типом c ограниченным наборром значений. Как он там внутри хранится, не принципиально, так как мы этого не видим.
> 2. Changing the member list of ENUM columns is very expensive.
Если только добавлять новые значения то не дорого. Если менять то да, но точно так же дорого менять любую другую колонку, не только ENUM. Да и вряд ли у тебя будут миллиарды записей, а если у тебя их миллион ты просто ночью на минуту поставишь заглушку на сайт (или может быть на минуту запретишь обновлять профили) и сделаешь миграцию.
> 3. It's impossible to add additional attributes or related info
если тебе надо добавлять дополнительные аттрибуты (или значений много), не используй ENUM. Ну например для городов конечно надо делать отдельную таблицу.
> 4. Getting a list of distinct ENUM members is a pain.
Это да, обычно этот список просто прописывается в коде. Ну например пол или материальное положение не меняется каждый день и их можно прописать константами.
> 5. ENUM columns may only offer limited or negligible effects on optimization
А никто и не говорил что он для оптимизации, он для упрощения запросов. Зачем создавать лишнюю таблицу если можно не создавать?
> You can't reuse the member-list of an ENUM column in other tables.
да, но это как правило никогда и не требуется
> ENUM columns have noteable gotchas.
> Suppose you have ENUM('blue', 'black', 'red') and you attempt to insert 'purple': MySQL actually truncates the illegal value to ' ' (an empty string)
надо использовать строгий режим в MySQL (почему вы его не используете?) Тогда вставка неправильного значения приведет к ошибке.
> пример с числами
Согласен, подвох, но я и не предлагаю использовать числовые ключи
> 8. ENUM has limited portability to other DBMS
Перенос на другую ДБ это такая боль, что ENUM тут особой роли не играет. Ну и если ты используешь константы в коде, то тебе надо будет поменять только значения в них. Если ты изолируешь код работы с БД в одном слое классов, то опять же это будет проще.
Ну и автор не написал про недостатки подхода с отдельной таблицей. Допустим, у нас есть «семейное положение», реализованное отдельной таблицей. Как в коде поставить статус «женат»? Откуда мы узнаем id этого статуса?
В случае с ENUM все просто: у нас есть константа
const STATUS_MARRIED = 'married';
В случае с таблицей мы как-то должны руками приписать id, что гораздо непонятнее.
Ну в общем, смотри сам, но как по мне, так автор однобоко освещает тему, напирая на недостатки, часть из которых на практике не проявляется.
> И мне даже подсказали как одним красивым запросом потом получить что-то типа 'мужчина, пьет, не курит, разведен' т. е. строка из всех значений вот этих опций с указанием сепаратора
А тебе рассказали что в MySQL ограничение на длину строки GROUP_CONCAT, по моему по умолчанию в 1024 байта если не меньше? Будь осторожен, если ты например используешь его для получения списка id (например id всех друзей пользователя) то лишние id просто молча обрежутся.
> короче распарсить это нереально.
И не надо. Это изначально неправильно, надо выбирать их как отдельные поля.
>>EAV (Entity- Attribute-Value)
Это обычно используется когда очень много атрибутов и не все обязательные. Ну например свойства товаров: у монитора есть размер диагонали, а у чайника нет, зато есть объем и мощность.
Впрочем можно и к анкете пользователя применить, особенно если там очень много пунктов.
Выбирать атрибуты из EAV проще всего отдельным запросом, вроде SELECT ... FROM user_attributes WHERE user_id = :user_id и потом на стороне PHP разгребать полученный массив. Ну и при таком подходе потом кеш будет проще добавить.
Статья показывает только отрицательные стороны и довольно однобока.
Плюсы ENUM: запросы получаются проще и нагляднее, не нужны джойны.
> 1. Data isn't being treated like data.
не согласен, ENUM можно считать отдельным типом c ограниченным наборром значений. Как он там внутри хранится, не принципиально, так как мы этого не видим.
> 2. Changing the member list of ENUM columns is very expensive.
Если только добавлять новые значения то не дорого. Если менять то да, но точно так же дорого менять любую другую колонку, не только ENUM. Да и вряд ли у тебя будут миллиарды записей, а если у тебя их миллион ты просто ночью на минуту поставишь заглушку на сайт (или может быть на минуту запретишь обновлять профили) и сделаешь миграцию.
> 3. It's impossible to add additional attributes or related info
если тебе надо добавлять дополнительные аттрибуты (или значений много), не используй ENUM. Ну например для городов конечно надо делать отдельную таблицу.
> 4. Getting a list of distinct ENUM members is a pain.
Это да, обычно этот список просто прописывается в коде. Ну например пол или материальное положение не меняется каждый день и их можно прописать константами.
> 5. ENUM columns may only offer limited or negligible effects on optimization
А никто и не говорил что он для оптимизации, он для упрощения запросов. Зачем создавать лишнюю таблицу если можно не создавать?
> You can't reuse the member-list of an ENUM column in other tables.
да, но это как правило никогда и не требуется
> ENUM columns have noteable gotchas.
> Suppose you have ENUM('blue', 'black', 'red') and you attempt to insert 'purple': MySQL actually truncates the illegal value to ' ' (an empty string)
надо использовать строгий режим в MySQL (почему вы его не используете?) Тогда вставка неправильного значения приведет к ошибке.
> пример с числами
Согласен, подвох, но я и не предлагаю использовать числовые ключи
> 8. ENUM has limited portability to other DBMS
Перенос на другую ДБ это такая боль, что ENUM тут особой роли не играет. Ну и если ты используешь константы в коде, то тебе надо будет поменять только значения в них. Если ты изолируешь код работы с БД в одном слое классов, то опять же это будет проще.
Ну и автор не написал про недостатки подхода с отдельной таблицей. Допустим, у нас есть «семейное положение», реализованное отдельной таблицей. Как в коде поставить статус «женат»? Откуда мы узнаем id этого статуса?
В случае с ENUM все просто: у нас есть константа
const STATUS_MARRIED = 'married';
В случае с таблицей мы как-то должны руками приписать id, что гораздо непонятнее.
Ну в общем, смотри сам, но как по мне, так автор однобоко освещает тему, напирая на недостатки, часть из которых на практике не проявляется.
> И мне даже подсказали как одним красивым запросом потом получить что-то типа 'мужчина, пьет, не курит, разведен' т. е. строка из всех значений вот этих опций с указанием сепаратора
А тебе рассказали что в MySQL ограничение на длину строки GROUP_CONCAT, по моему по умолчанию в 1024 байта если не меньше? Будь осторожен, если ты например используешь его для получения списка id (например id всех друзей пользователя) то лишние id просто молча обрежутся.
> короче распарсить это нереально.
И не надо. Это изначально неправильно, надо выбирать их как отдельные поля.
>>EAV (Entity- Attribute-Value)
Это обычно используется когда очень много атрибутов и не все обязательные. Ну например свойства товаров: у монитора есть размер диагонали, а у чайника нет, зато есть объем и мощность.
Впрочем можно и к анкете пользователя применить, особенно если там очень много пунктов.
Выбирать атрибуты из EAV проще всего отдельным запросом, вроде SELECT ... FROM user_attributes WHERE user_id = :user_id и потом на стороне PHP разгребать полученный массив. Ну и при таком подходе потом кеш будет проще добавить.
Почему такое :
$tags = $this->get('request')->query->get('tags') ;
// echo '<pre>';
$factQB
->leftJoin('f.tags', 't')
->add('where', $factQB->expr()->in('t.id', array(':tags')))
->setParameter( 'tags', $tags);
выдаёт такое :
[2015-04-28 11:50:27] request.CRITICAL: Uncaught PHP Exception Doctrine\ORM\Query\QueryException: "Invalid parameter number: number of bound variables does not match number of tokens" at /var/www/factcloud/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php line 100 {"exception":"[object] (Doctrine\\ORM\\Query\\QueryException: Invalid parameter number: number of bound variables does not match number of tokens at /var/www/factcloud/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php:100)"} []
???HALP????
Почитать немного можно тут
http://help.yandex.ru/openid/index.xml
http://openidexplained.com/
http://stackoverflow.com/a/353892
http://media.techtarget.com/tss/static/articles/content/OpenID/clientdiagram1.gif
Вот спецификация, много букв: https://openid.net/specs/openid-authentication-2_0.html
Суть OpenID в том что вместо регистрации на сайте ты вписываешь свой логин на каком-то openid провайдере (сервисе), например:
cat.livejournal.com
openid.yandex.ru/vasya (не уверен что верно написал)
Сайт перенаправляет тебя к провайдеру, где ты подтверждаешь передачу данных, и после этого перенаправляет назад где ты без регистрации оказываешься залогиненным под своим openid логином.
Преимущество openid в том что он очень открытый и децентрализованный: тебе не надо договариваться с провайдерами или получать у каждого ключи, чтобы залогинивать их пользователей. Если ты хочешь стать провайдером, тебе не надо нигде получать разрешение. Ты можешь сам стать провайдером и логиниться везде через свой собственный сайт, храня все данные у себя. У Oauth которым пользуются соцсети, такой открытости и близко нет.
Логин тут имеет форму URL и для некоторых пользователей это сложно, помнить его. Потому для них придумали дополнительную опцию, когда пользователь жмет «зайти через яндекс», его отправляют на яндекс, там он логинится и дает разрешение, и его перенаправляет назад на сайт залогиненным. Разница в том, что не надо помнить и вводить на сайте свой openId, он тут спрятан от пользователя. Забыл, как это называется.
Есть у OpenId и недостатки.
Теперь подробнее.
Логин, как видишь, имеет форму URL и на этот URL можно отправлять специальные запросы. Если провайдер действительно поддерживает OpenID то в странице полученной с этого URL будут специальные заголовки или метатеги с нужной информацией (например с URL на который надо слать запросы или перенаправлять пользователя). Этот процесс проверки называется «discovery».
К примеру если открыть исходный код cat.livejournal.com то увидим:
<link rel="openid2.provider" href="http://www.livejournal.com/openid/server.bml" />
<link rel="openid.server" href="http://www.livejournal.com/openid/server.bml" />
<meta http-equiv="X-XRDS-Location" content="http://cat.livejournal.com/data/yadis" />
Это говорит о том что LJ поддерживает openId и указанный URL cat.livejournal.com является OpenID идентификатором пользователя на этом сервисе. В метатегах прописаны точки API для взаимодействия, то есть куда слать запросы.
Вот исходный код с яндекса, openid.yandex.ru/vasya/
<link rel="openid2.provider" href="http://openid.yandex.ru/server/" />
<link rel="openid2.local_id" href="http://openid.yandex.ru/vasya/" />
<link rel="openid.server" href="http://openid.yandex.ru/server/" />
<meta http-equiv="X-XRDS-Location" content="http://openid.yandex.ru/vasya/yadis/" />
Ок, мы получили адреса API (этот процесс назывался discovery). Теперь через API мы получаем адрес страницы, куда надо редиректить пользователя чтобы он дал разрешение передать данные. Это называется Authentication request. Получив его, мы редиректим пользователя. Если все ок. то сайт отредиректит пользователя назад к нам, добавив в URL одноразовую метку (если все плохо то он отредиректит пользователя к нам передав код ошибки). Имея метку, мы напрямую отправляем запрос и по этой метке получаем данные пользователя, и можем создать для него аккаунт или залогинить.
Если пользователь уже давал согласие то после редиректа к провайдеру тот сразу же редиректит пользователя назад к нам с меткой.
Если что-то непонятно, учточняй.
> Еще, нужно ведь при такой конфигурации БД заносить строчку с пользователем Стима в таблицу пользователей, потому что к ней привязаны предложения из соответствующей таблицы?
Внешние логины делают через отдельную таблицу со связью много-к-одному к таблице пользователей.
Почитать немного можно тут
http://help.yandex.ru/openid/index.xml
http://openidexplained.com/
http://stackoverflow.com/a/353892
http://media.techtarget.com/tss/static/articles/content/OpenID/clientdiagram1.gif
Вот спецификация, много букв: https://openid.net/specs/openid-authentication-2_0.html
Суть OpenID в том что вместо регистрации на сайте ты вписываешь свой логин на каком-то openid провайдере (сервисе), например:
cat.livejournal.com
openid.yandex.ru/vasya (не уверен что верно написал)
Сайт перенаправляет тебя к провайдеру, где ты подтверждаешь передачу данных, и после этого перенаправляет назад где ты без регистрации оказываешься залогиненным под своим openid логином.
Преимущество openid в том что он очень открытый и децентрализованный: тебе не надо договариваться с провайдерами или получать у каждого ключи, чтобы залогинивать их пользователей. Если ты хочешь стать провайдером, тебе не надо нигде получать разрешение. Ты можешь сам стать провайдером и логиниться везде через свой собственный сайт, храня все данные у себя. У Oauth которым пользуются соцсети, такой открытости и близко нет.
Логин тут имеет форму URL и для некоторых пользователей это сложно, помнить его. Потому для них придумали дополнительную опцию, когда пользователь жмет «зайти через яндекс», его отправляют на яндекс, там он логинится и дает разрешение, и его перенаправляет назад на сайт залогиненным. Разница в том, что не надо помнить и вводить на сайте свой openId, он тут спрятан от пользователя. Забыл, как это называется.
Есть у OpenId и недостатки.
Теперь подробнее.
Логин, как видишь, имеет форму URL и на этот URL можно отправлять специальные запросы. Если провайдер действительно поддерживает OpenID то в странице полученной с этого URL будут специальные заголовки или метатеги с нужной информацией (например с URL на который надо слать запросы или перенаправлять пользователя). Этот процесс проверки называется «discovery».
К примеру если открыть исходный код cat.livejournal.com то увидим:
<link rel="openid2.provider" href="http://www.livejournal.com/openid/server.bml" />
<link rel="openid.server" href="http://www.livejournal.com/openid/server.bml" />
<meta http-equiv="X-XRDS-Location" content="http://cat.livejournal.com/data/yadis" />
Это говорит о том что LJ поддерживает openId и указанный URL cat.livejournal.com является OpenID идентификатором пользователя на этом сервисе. В метатегах прописаны точки API для взаимодействия, то есть куда слать запросы.
Вот исходный код с яндекса, openid.yandex.ru/vasya/
<link rel="openid2.provider" href="http://openid.yandex.ru/server/" />
<link rel="openid2.local_id" href="http://openid.yandex.ru/vasya/" />
<link rel="openid.server" href="http://openid.yandex.ru/server/" />
<meta http-equiv="X-XRDS-Location" content="http://openid.yandex.ru/vasya/yadis/" />
Ок, мы получили адреса API (этот процесс назывался discovery). Теперь через API мы получаем адрес страницы, куда надо редиректить пользователя чтобы он дал разрешение передать данные. Это называется Authentication request. Получив его, мы редиректим пользователя. Если все ок. то сайт отредиректит пользователя назад к нам, добавив в URL одноразовую метку (если все плохо то он отредиректит пользователя к нам передав код ошибки). Имея метку, мы напрямую отправляем запрос и по этой метке получаем данные пользователя, и можем создать для него аккаунт или залогинить.
Если пользователь уже давал согласие то после редиректа к провайдеру тот сразу же редиректит пользователя назад к нам с меткой.
Если что-то непонятно, учточняй.
> Еще, нужно ведь при такой конфигурации БД заносить строчку с пользователем Стима в таблицу пользователей, потому что к ней привязаны предложения из соответствующей таблицы?
Внешние логины делают через отдельную таблицу со связью много-к-одному к таблице пользователей.
— Не индексируется поисковиками
— при ошибке JS пользователь ничего не увидит и сайт просто не будет реагировать не на что, неприятно. По моему интуитивному ощущениею 99% разработчиков не умеют и не мониторят клиентские ошибки.
— если не реализуешь работу с URL то не будет работать история, переходы вперед/назад, обновление страницы, поделиться ссылочкой, закладки
— получаешь кучу сложностей вместо того чтобы сделать просто и логично. Ты можешь с сервера отдавать страницы сразу, а зачем-то вместо этого городишь какие-то приложения, JSON, клиентский рендеринг и прочие сложности. Зачем? чтобы в твоем коде труднее было разбираться и чтобы его дольше было писать? По такой логике, давай еще пару сервисов добавим и несколько API, чтобы сервер тоже не брал данные напрямую из базы, а обращался к третьему API какому-нибудь.
API обычно используют для взаимодействия между отдельными сервисами.
> . я с php не работал,
ну так изучи его сначала. Я как чувствовал, что ты всю эту сложную схему городишь просто от незнания или нежелания что-то изучать.
> какой фрейморвк использовать для серверного php
Yii 2 вполне подойдет.
В сообщении написано же
> number of bound variables does not match number of tokens
Число плейсхолдеров не соответствует числу заданных через SetParameter параметров.
Я советую тебе сдампить SQL который он сгенерировал и посмотреть как выглядит запрос:
$sql = $factQB->getSql( );
var_dump($sql);
Посчитать число плейсхолдеров.
Потом сдампить $tags и посмотреть что в нем.
Ну и дальше если непонятно, пиши в тред с подробностями, подумаем еще.
https://ideone.com/3MTsk9
Готовься, анон!
Прикольно.
>Это ты на какие вакансии рассылаешь?
php - разработчик, лол.
>Нет, просто шансы таким способом найти работу стремятся к нулю.
Ну я понимаю блин, но вдруг прокатит.
Ну так сразу php-разработчиком не берут, т.к. опыта и знаний нет, то может стоит для начала джуном пойти работать, чтобы этот самый опыт и знания получить?
Тогда замучу пол и семейные отношения на enum по идее это вообще никогда трогать не надо будет, а всякие привычки на отдельных таблицах заодно посмотрим с чем будет больше гемора.
Новосиб, я тут уже кулстори пилил два раза, вот взяли как раз, нделю уже работаю.
Блять, хуево. Я как раз собираюсь летом в Новосиб перебираться. Неужели так все печально? Вроде объявления смотрел, требования везде довольно божеские.
Не, все норм. Просто я реально начинающий был. Вот тут мониторь например. Можешь заранее написать большинству этих контор и попросить тестовые задания как будешь собираться переезжать. http://rabota.ngs.ru/vacancy?q=php
Алсо для человека который решил все задачи из треда или посмотрел курсы специалиста полностью не будет сложным за недельку справиться с любым из них. Одним например нужно сделать блог с маломальским дизайном, другим админку. В общем если ты уже что-то умеешь то найдешь себе работу. Еще есть конторы в Академе, и там кадровый голод постоянный, пиши им. Можешь даже просто открыть 2_гис, найти там "Технопарк" И звонить во все конторы которые в нем и в окрестных домах базируются и предлагать себя, лол.
А, ну тогда норм. Переезжать где-то в июле наверное буду, так что еще пару месяцев есть на то, чтобы выучить все что нужно для трудоустройства.
https://github.com/tokotun/uppy
>>466105
>По поводу класса File: мне кажется, не надо было объединять классы, а надо было работу с медиаданными поместить в класс MeiaInfo.
>Ведь в ООП объект это данные + методы для работы с ними и медиаданные тянут на отдельный объект.
>Но если у тебя есть причины оставить это в классе File, можно оставить.
-----Мне не понравилось, что классу file приходится вызывать посторониие функции, для обработки данных в своём поле.
>>466108
>Вот тут довольно хитро вычисляется путь. Ты берешь не максимального ребенка комментария, а максимального потомка:
> https://github.com/tokotun/uppy/blob/master/uppy/app/CommentsMapper.php#L62
>Было бы наверно гораздо проще, если бы было поле parent_id и мы могли взять максимального из детей с указанным parent_id, и увеличить последнюю цифру в нем.
Теперь сделал чуть проще. И берутся только ребёнки. И увеличивается последняя цифра в пути.Подойдёт?
>В PDO есть функция lastInsertId http://php.net/manual/ru/pdo.lastinsertid.php
Я так понял эта функция берёт id последней вставленной строки. То есть сначала вставляем информацию о файле в БД, и только потом сохраняем файл на сервер.
С тем ID, под которым была сохранена информация о файле в БД.
>Ты не учел что картинки бывают в разных форматах.
https://github.com/tokotun/uppy/blob/master/uppy/app/Uploader.php#L62
Это сейчас все форматы так перечислять? Вижу что код будет повторятся, но лучше придумать не могу.
Очевидный Специалист с Борисовым же(вроде 2014 года - без вырезок и норм).1 и 2 уровень - мастхев, в 3-ем говорится об ооп и так по мелочи. Но в ООП я бы вьезжал по другому руководству. Есть еще за 2010 - там совсем все разжевано и много говорится о подводных камнях, про приведение типов тоже много, как помню. Все, что непонятно можно прочесть в Котерове. На перых парах должно хватить.
Я могу сказать, что если я знаю, как работает технология, то схватываю информацию по ней на лету.
Так вот, собственно, не мог бы ты объяснить мне в какую сторону копать по каким ключам гуглить информацию по роутингу, правам доступа и подобным вещам для лёгкого движка под свои нужды. Желательно был бы материал, в котором описывается поэтапно написание своего.
Если у тебя есть такой материал, то я буду очень благодарен. Можно и на английском, его я знаю на довольно неплохим уровне.
И ещё, я много читал информации по ООП. Вроде, всё логично и очевидно, но если открою чужой код, то очень теряюсь. в частности, я не обрёл дзен, и не очень понимаю, зачем это всё.
да
> никаких суперзнаний не надо.
ну так да, знаний CSS достаточно, суперзнаний и не надо
> Поначалу конечно забавно было, но они слишком уж абстрактные и я пойду лучше знания на практике применять, делая сайт
Задачи там для того, чтобы научить писать ООП код, правильно делать классы, методы и прочее. Все современные фреймворки на ООП, очевидно что надо его знать чтобы ими полноценно пользоваться.
Без хорошего знания PHP/ООП ничего не выйдет. Ты можешь сам попробовать это сделать и проверить что получится.
Вообще, если тебе больше нравятся какие-то свои идеи реализовать, а не наши задачи решать, это тоже неплохо, я даже могу код посмотреть и подсказать что не так. В принципе не так важно каким путем ты идешь к цели, а важен результат.
>>469812
как это читать? выглядит как бессвязный бред если честно, надписи транслитом, знаки доллара везде и какая-то неуклюжая пародия на нормальный ООП. На PHP получается гораздо аккуратнее и понятнее, а хаскелль лучше для вычисления Фибоначчи оставить :)
В программе что-то у тебя мало выходит, там во втором банке около 61270.
> $bank[2];
Вот это плохая идея, так писать, так как ничего не понятно, что значит «2»? Надо либо использовать имена для ключей вроде $bank['percent'] либо, что лучше, вообще не использовать массив, а передавать обычными переменными.
> $balance = $balance + $percent
Проценты надо прибавлять по другому, надо прибавлять не 0.04 рубля (или гривны), а 0.04 от суммы долга.
> Какой редактор кода для PHP имеет такую же хипстерскую подсветку
По моему во всех редакторах подсветка задается темой, и в интернете можно найти любые темы, и светлые на темном, и темные на светлом.
> Где можно почитать об аккуратности оформления кода?
В стандартах PSR которые приняли разработчики основных фреймворков. В принципе там даже текст читать не обязательно, на примерах кода все видно:
http://www.php-fig.org/psr/psr-1/ru/
http://www.php-fig.org/psr/psr-2/ru/
(все переведено на русский)
> Насколько для форматирования пригоден Phpformatter?
Вроде как пригоден, также если у тебя IDE то там скорее всего есть горячая клавиша для форматирования, а в настройках можно выбрать стиль. Это удобнее. Вот горячие клавиши: https://gist.github.com/codedokode/8759492
И еще, лучше когда функция ничего не выводит сама, а возвращает результат числом, а тот кто ее вызвал. его выводит. ЧТобы вернуть несколько значений, можно использовать массив.
>>470022
Ну не знаю, не знаю, не уверен что это хорошая идея. Ведь допустим сантехники не пишут о том что они сантехники.
>>470130
Причина вот этом месте:
> [,!\.\?][^\s]
Тут регулярка при совпадении съедает запятую и идущую за ней букву. Я думаю, лучше поменять подход на такой. Ищем
(любое число пробелов)(знак)(любое число пробелов)
и заменяем это на
(знак)(1 пробел)
Ну и preg-split тут наверно не очень подходит, заменять лучше через preg_replace. Чтобы не потерять знак, используй скобки и подстановки вроде $1 в выражении для замены.
А тебя до выполнения тестового задания/вопросов отсеивают или после? Если после то значит знаний маловато и надо их улучшать.
>>470247
> for ($i = 0; $mistake1Counter!=$i; $i++){
Тут ошибка. preg_match_all кладет результат в массив массивов (2-мерный массив), а ты пишешь:
$mistake1Counter!=$i
$mistake1Counter это массив, а $i число, как можно их сравнивать? что ты ждешь от этого? Массив конечно всегда будет не равен числу.
Ты можешь увидеть что в $mistake1Counter с помощью команды
var_dump($mistake1Counter );
Там, как ты видишь массив массивов. Причем preg_match_all может расположить элементы в нем 2 разными способами, они описаны в мануале: http://php.net/manual/ru/function.preg-match-all.php
Если ты не знаешь, как вывести все элементы из него, то тебе надо вспомнить foreach и может быть перечитать урок про массивы.
Ну и вместо того чтобы писать 4 одинаковых куска кода, надо использовать массив и цикл, разумеется (или функции, если ты их уже прошел). Копипаста недопустима.
Есличто-то еще непонятно, задавай вопросы
> Мне не понравилось, что классу file приходится вызывать посторониие функции, для обработки данных в своём поле.
Это ты правильно подумал, потому что хорошей практикой сичтается делать поля закрытыми так, чтобы никакой другой класс не мог в них залезть. Но я предлагал немного другое. Я предлагал вынести в отдельный класс не только методы работы с медиаданными но и сами медиаданные тоже.
Таким образом это бы выглядело примерно так:
$file->mediaData->getBitrate( );
> Теперь сделал чуть проще. И берутся только ребёнки.
ну пусть будет так. Только функцию strToArr я бы переименовал более понятно например pathToString или pathToArray
> То есть сначала вставляем информацию о файле в БД, и только потом сохраняем файл на сервер.
Разумеется, ты не можешь получить id пока не вставишь запись. Зато мы избегаем гонки при многопоточной работе (а веб приложения многопоточные). Да и как иначе? SELECT MAX/SELECT COUNT не работает так как в промежутке между вызовом SELECT и INSERT в базе может появиться сколько угодно новых записей.
Альтернативный способ это генерировать id самому, для уникальности используя например id машины, id процесса, текущее время + увеличивающийся счетчик (например хранящийся в APC кеше). Но по моему это сложнее.
>Это сейчас все форматы так перечислять?
Там всего 3 формата, JPG, GIF, PNG, это немного.
> imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
Это копипастить не надо.
> imagejpeg($thumb, "{$this->uploadPath}thumbs/$filename");
превьюшки не обязатеьно в том же формате сохранять.
Ну и даже если надо, я бы сделал 2 отдельных функции, загрузить картинку в указанном формате и сохранить в указанном формате, а не писал бы все сплошной стеной кода. Мне кажется с разбиением на функции будет аккуратнее.
> if ($mime == 'image/jpeg'){
> if ($mime == 'image/gif'){
> if ($mime == 'image/png'){
Это уместнее писать через if/elseif чтобы была видна связь между блоками. И что если ни один вариант не выполнится?
> Мне не понравилось, что классу file приходится вызывать посторониие функции, для обработки данных в своём поле.
Это ты правильно подумал, потому что хорошей практикой сичтается делать поля закрытыми так, чтобы никакой другой класс не мог в них залезть. Но я предлагал немного другое. Я предлагал вынести в отдельный класс не только методы работы с медиаданными но и сами медиаданные тоже.
Таким образом это бы выглядело примерно так:
$file->mediaData->getBitrate( );
> Теперь сделал чуть проще. И берутся только ребёнки.
ну пусть будет так. Только функцию strToArr я бы переименовал более понятно например pathToString или pathToArray
> То есть сначала вставляем информацию о файле в БД, и только потом сохраняем файл на сервер.
Разумеется, ты не можешь получить id пока не вставишь запись. Зато мы избегаем гонки при многопоточной работе (а веб приложения многопоточные). Да и как иначе? SELECT MAX/SELECT COUNT не работает так как в промежутке между вызовом SELECT и INSERT в базе может появиться сколько угодно новых записей.
Альтернативный способ это генерировать id самому, для уникальности используя например id машины, id процесса, текущее время + увеличивающийся счетчик (например хранящийся в APC кеше). Но по моему это сложнее.
>Это сейчас все форматы так перечислять?
Там всего 3 формата, JPG, GIF, PNG, это немного.
> imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
Это копипастить не надо.
> imagejpeg($thumb, "{$this->uploadPath}thumbs/$filename");
превьюшки не обязатеьно в том же формате сохранять.
Ну и даже если надо, я бы сделал 2 отдельных функции, загрузить картинку в указанном формате и сохранить в указанном формате, а не писал бы все сплошной стеной кода. Мне кажется с разбиением на функции будет аккуратнее.
> if ($mime == 'image/jpeg'){
> if ($mime == 'image/gif'){
> if ($mime == 'image/png'){
Это уместнее писать через if/elseif чтобы была видна связь между блоками. И что если ни один вариант не выполнится?
> информацию по роутингу
Slim, Slilex, Symfony Routing. Сейчас в каждом фреймворке свой роутер + можно поискать отдельную библиоетку на сайте phptrends.
> правам доступа
ACL, Symfony aCL, опять же на phptrends поискать
> Желательно был бы материал, в котором описывается поэтапно написание своего.
Гугли по фразам «writing router in php» и аналогично на русском, может где то есть туториал, но я не уверен.
> Вроде, всё логично и очевидно, но если открою чужой код, то очень теряюсь
Может ты берешься за сложное? Открываешь код на фреймворке не изучив документацию на сам фреймворк?
> Догадываюсь что на них навешиваются события, по клику отправляется ajax-запрос на сервер, если прошло нормально то во фронте просто меняется на другое состояние("уже читаю")
да, это все яваскрипт
> Чтобы не потерять знак, используй скобки и подстановки вроде $1 в выражении для замены.
Я не знал что нужно разбивать всю регулярку по скобкам, и чтобы не потерять часть ставить кроме $1, еще и $2.
https://ideone.com/HURfqM
Ты так описал проблему, что я еле понял. И при этом не приложил пример кода.
> обработчики не срабатывают, если установлены на элементы подгруженные через аякс.
Если они установлены то они сработают. В твоем случае ты их наверно просто не установил.
> Но работают, если функции тоже подгрузить через аякс.
???
> Как быть?
После вставки элементов в страницу поставить на них нужные обработчики. Или, что умнее, использовать делегирование, по моему через метод .on() который ставит обработчик на родительский элемент, и ловит всплывающие события.
Вот прочти этот раздел для начала, так как я вижу что ты события знаешь очень плохо и тебе надо начать с теории: https://learn.javascript.ru/events-and-interfaces
Когда прочтешь, то скорее всего сам догадаешься что делать. Если нет, пиши.
программа работает, это хорошо, но я думаю, надо ее чуть упростить еще.
> не знал что нужно разбивать всю регулярку по скобкам
Ну зато теперь знаешь, это тебе наверняка еще пригодится, так как позволяет делать сложные замены в тексте одной командой.
Я вижу, ты сообразительный. Попробуй подумать, как совместить 2 этих действия в одно:
> Исправляем ошибку вида "слово, " и " ,слово"
> Исправляем ошибку вида "слово,слово"
Я думаю, что исправить расстановку пробелов вокруг запятой и других знаков можно одним регулярным выражением.
> if ($key > 0) {
> $result = $result . ". " . $sentence;
В таких случаях проще части предлоежений складывать в масиив, а после цикла объединить его в строку через implode.
Кстати, обнаружил интересную фишку гитхаба, там можно смотреть изменения за период времени. Может тебе (и другим анонам) пригодится. Жмем зеленую маленькую кнопку слева вверху и вводим нужные выражения:
https://github.com/tokotun/uppy/compare/master@%7B2015-04-20%7D...master
Это показывает например что ты сделал за последнюю неделю.
Мануал: https://help.github.com/articles/comparing-commits-across-time/
Разумеется то же самое можно сделать без гитхаба, в гите в командной строке: git diff 6c6c6d780 master
Вот еще по мелочам:
> uppy/container/×.×
Точка в линуксе не имеет особого значения и можно писать просто × (или нельзя? скрытые файлы начинающиеся с точки не попадут в список? я не помню). А твое выражение как раз соответствует не всем файлам, а только если в имени есть точка. Файл Makefile например ему не соответствует
Потом еще гляну.
Уже кучу туторов перерыл и вот такое мне кидает
2015/04/29 15:57:24 [error] 3127#0: *7128 FastCGI sent in stderr: "PHP message: PHP Fatal error: Class 'GearmanClient' not found in /var/www/factcloud/vendor/mmoreram/gearman-bundle/Mmoreram/GearmanBundle/Service/GearmanClient.php on line 232" while reading response header from upstream, client: 127.0.0.1, server: factcloud.localhost, request: "POST /app_dev.php/registration HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "factcloud.localhost", referrer: "http://factcloud.localhost/app_dev.php/registration"
я функцию завел, которая добавляла работников каких и сколько ей прикажешь. Внутри был цикл, да. Выполнялся столько раз, сколько работников требовалось.
Подглядывай если совсем туго будет, ну и естественно можно не только таким способом делать
http://ideone.com/IsAdmu
>>470750
Среднее по департаменту же, считаешь сначала сколько всего во всей конторе, там как раз строчкой выше надо посчитать, и делишь на количество департаментов. Изи.
Ну я так и думал, что через цикл. Просто думал, мб другие методы есть. И дык проблема то там в том, что в результате ОП-а получается не общая сумма деленная на количество департаментов, а совсем другое число.
Напиши как ты устанавливал Gearman? Просто строчку в composer.json добавил?
также, дай ссылку на документацию геармана, на инструкцию по установке.
Я в сети вижу упоминание какого-то pecl extension, ты его не устанавливал?
>>470750
> Среднее на департамент или сотрудника?
число бумаг или кофе или потраченных денег — среднее среди 4 департаментов.
> то ответы нифига не сходятся с результатом.
если ты про результат который в учебнике приведен то там цифры могут быть взяты с потолка
>>470768
ОП программу не запускал и числа выдумал
Подскажи, как складывать результаты работы функции. Т.е. записываю я
$employees = Employee::createEmployee(9, 'manager', 1, 'purchases');
$employees = Employee::createEmployee(3, 'manager', 2, 'purchases');
Но оно не складывается в один массив, а вторая строка перезаписывает первую.
Ну и покажи как ты сделал, интересно же
Зависит от функционала, а вообще, думаю, что можно, но не только тему нужно будет нятануть. Я вообще только что плагин закончил писать, как первое задания, для вордпресса.
Вордпресс не подходит для этого.
> Надо писать собственные плагины для этого?
да, весь функционал доски объявлений придется реализовать плагином.
>>470860
Сессия хранится на сервере (по умолчанию это файл в временной папке) так что нет. Но я не понимаю, почему надо использовать сессию. ты можешь просто класть в куки id и подпись к этому id на основе хеша, и злоумышленник ее не подделает.
>>470797
$array[] = $element чтобы добавить элемент к конец массива
Что за плагин написал? Тоже хочу написать плагин, может кто заданием поделится по плагинам для вордпресс?
function out() {
$posts = $wpdb->get_results("SELECT ID, post_title FROM $wpdb->posts WHERE post_status = 'publish'
AND post_type='post' ORDER BY comment_count DESC LIMIT 0,4");
return $posts;
}
add_action('the_content', 'out');
Выбивает ошибку, говорит что это не объект, точнее выводит вот это:
Call to a member function get_results() on a non-object
Если выводить просто без хука то выводит нормально, тут конечно всё очевидно но я не могу въехать только начал изучать вордпресс, может кто подсказать?
Хуй знает, анон. Я знаю, как прокрастинация может жизнь попортить, сам постоянно от нее страдаю. Но в последнее время довольно легко заставить себя учить, т.к. меня уже тошнит от моего мухосранска и от рашки в целом. И если начинаю лениться, то просто смотрю в окно и это чувство отвращения дает мне импульс для того, чтобы заняться делом.
С одной стороны, все твои замечания понятны, с другой, я даже не знаю как сформулировать вопрос, поэтому решил написать как есть.
Вот смотри:
Прошлый раз ты предлагал избавиться от массива $map и сделать универсальный алгоритм для выбора ходов »467892 »468288
Допустим мы избавились от массива $map, заменив его на SplObjectStorage со всеми Животными, и оставим параметры ширины и высоты.
Допустим мы хотим сделать ход каким-то Животным. Для этого мы сначала получаем новый SplObjectStorage с Животными, которые попадают в обзор.
И тут у меня происходит ступор
»467892
>— оцениваем каждый ход в баллах
Как оценить каждый ход? Ведь всяких различных вариаций разных ходов очень-очень много! Может быть по 3-4 Кошки (если ход делается Мышкой), может начаться Котовый Апокалипсис, в котором в 90% клеток будут Кошки, могут появиться сверх быстрые и зрячие мышки, которые видят всю карту и могут за один ход переместиться в любое место на карте... И как из этого всего узнать какой ход наиболее выгодный? Как рассчитать наибольшее расстояние от всех кошек сразу?
Такое же дерьмо. Даже нечего добавить - мотивируют только негативные ощущения.
>>470795
>
>Напиши как ты устанавливал Gearman? Просто строчку в composer.json добавил?
>также, дай ссылку на документацию геармана, на инструкцию по установке.
>Я в сети вижу упоминание какого-то pecl extension, ты его не устанавливал?
Спасибо, уже разобрался. Мой судо, какой то не судо и не хочет некоторые штуки0 делать
Привет анон. Тут есть чуваки использующие Zend framework 2? Мне нужно понять зачем нужны и стоит ли использовать hydrators. Судя по всему гидратор используется чтобы в контроллере изящно закачать данные из формы в объект одним методом, вместо построенного сохранения. Однако для этого приходится реализовывать этот интерфейс в моделях с методами гидрации и закачки и в итоге ты нихуя не выигрываешь да еще и грузишь лишним модель - MVC way модель объекта не должно волновать как с ней будут взаимодействовать. Сущность ради сущности? Ооп ради ооп?
Или я упускаю что-то? Поясни плз бро нахуя это нужно в Zend framework 2?
> Для этого мы сначала получаем новый SplObjectStorage с Животными, которые попадают в обзор.
простого массива хватит наверно, но можно и SplObjectSt.
> Как оценить каждый ход? Ведь всяких различных вариаций разных ходов очень-очень много!
У мышки выбор из 5 вариантов:
— остаться на месте
— влево
— вверх
— вправо
— вниз
У кошки не более 9 вариантов хода. Это немного.
Это конечно при условии что мы считаем на 1 ход вперед, а больше и не надо. Если надо считать на несколько ходов вперед, то да, число вариантов становится огромным. В таких случаях надо исключать совсем невыгодные ветки из дерева вариантов, чтобы уменьшить выбор.
> И как из этого всего узнать какой ход наиболее выгодный?
Оценит каждый ход в баллах и сделать выбор.
Ну вот пример, допустим мы мышка, у нас есть 5 вариантов хода, мы можем оценить выгоду хода на клеточку X так:
— берем расстояние от X до ближайшей кошки. За каждую клеточку добавляем 100 баллов. то есть если например от X до кошки 4 клетки то получается 400 баллов. Если кошек вообще нет то добавляем 500 баллов.
— берем число кошек видимое из X и за каждую вычитаем 3 балла, но не более 60 в сумме.
— берем число возможных ходов из клетки X (то есть свободных клеточек) и за каждую прибавляем 10 баллов. Например из угловой клетки всего 2 выхода, а из центральной 4 выхода.
Таким образом если от X до ближайшей кошки 4 клетки, из нее видно 3 кошки и есть 3 выхода, то получается 400 - 3 + 30 = 427 баллов.
Аналогично мы считаем баллы для других клеточек и решаем на какую сходить.
> Ведь всяких различных вариаций разных ходов очень-очень много!
Не более 9 вариантов при нынешних правилах.
> Может быть по 3-4 Кошки
ну и пусть
> может начаться Котовый Апокалипсис, в котором в 90% клеток будут Кошки,
И что? Алгоритм не справится? Я думаю, справится.
> Как рассчитать наибольшее расстояние от всех кошек сразу?
Найти ближайшую. Нас интересует расстояние до ближайшей кошки, а не до самой дальней.
> Для этого мы сначала получаем новый SplObjectStorage с Животными, которые попадают в обзор.
простого массива хватит наверно, но можно и SplObjectSt.
> Как оценить каждый ход? Ведь всяких различных вариаций разных ходов очень-очень много!
У мышки выбор из 5 вариантов:
— остаться на месте
— влево
— вверх
— вправо
— вниз
У кошки не более 9 вариантов хода. Это немного.
Это конечно при условии что мы считаем на 1 ход вперед, а больше и не надо. Если надо считать на несколько ходов вперед, то да, число вариантов становится огромным. В таких случаях надо исключать совсем невыгодные ветки из дерева вариантов, чтобы уменьшить выбор.
> И как из этого всего узнать какой ход наиболее выгодный?
Оценит каждый ход в баллах и сделать выбор.
Ну вот пример, допустим мы мышка, у нас есть 5 вариантов хода, мы можем оценить выгоду хода на клеточку X так:
— берем расстояние от X до ближайшей кошки. За каждую клеточку добавляем 100 баллов. то есть если например от X до кошки 4 клетки то получается 400 баллов. Если кошек вообще нет то добавляем 500 баллов.
— берем число кошек видимое из X и за каждую вычитаем 3 балла, но не более 60 в сумме.
— берем число возможных ходов из клетки X (то есть свободных клеточек) и за каждую прибавляем 10 баллов. Например из угловой клетки всего 2 выхода, а из центральной 4 выхода.
Таким образом если от X до ближайшей кошки 4 клетки, из нее видно 3 кошки и есть 3 выхода, то получается 400 - 3 + 30 = 427 баллов.
Аналогично мы считаем баллы для других клеточек и решаем на какую сходить.
> Ведь всяких различных вариаций разных ходов очень-очень много!
Не более 9 вариантов при нынешних правилах.
> Может быть по 3-4 Кошки
ну и пусть
> может начаться Котовый Апокалипсис, в котором в 90% клеток будут Кошки,
И что? Алгоритм не справится? Я думаю, справится.
> Как рассчитать наибольшее расстояние от всех кошек сразу?
Найти ближайшую. Нас интересует расстояние до ближайшей кошки, а не до самой дальней.
Доки читал?
http://zf2.com.ua/doc/65
Написано, что это просто интерфейс для загрузки данных из модели или в модель в виде массива. Использовать можно как угодно, в том числе и для работы с формами. Если не надо то ты его реализовывать и использовать не обязан.
если ты хочешь отфильтровать посты то лучше сделать так: https://codex.wordpress.org/Plugin_API/Filter_Reference/posts_where
film (id, name)
category(id, name)
film_category(id_film, id_category)
фильмы, категории и их связь, задача: выбрать фильмы состоящие в выбранных категориях (допустим id этих категорий 1 и 2) я делаю так:
SELECT FROM film WHERE (SELECT COUNT() FROM film_category WHERE film_category.id_film = film.id AND film_category.id_category IN (1,2)) = 2
есть ли способ проще?
Спасибо за ссылку, я просто хочу разобраться как работать с базой данных в вордпресс. Например как вывести что то из базы данных в the_content
сформулируй конкретную задачу, в зависимости от неё не всегда нужны чистые sql запросы, иногда можно воспользоваться средствами wp (и лучше потому что чем меньше своего суешь тем легче потом разбираться в этом другим и тем это безопаснее, я так считаю)
Мне сейчас самое элементарное, допустим есть база данных с некоторыми значениями, как вывести эти данные в поле the_content()
1. Дополнительный класс, какой-нибудь UserCustom, только с id и name. Не нравится он мне тем что этот класс по смыслу дублирует обычный User. Да и странно что маппер будет возвращать два разных объекта, то есть одинаковых по смыслу но с разными именами. И не универсально; для другой страницы может еще город понадобиться, не напасешься таких классов. Короче, это ерунда, я уже понял.
2. Дополнительный метод у маппера, а не класс. Но возвращаем все равно User. Просто вместо монструозного
getUser() {
sql = 'SELECT * ... FROM users LEFT JOIN cities ... LEFT JOIN ... LEFT JOIN ... ';
делаем getCustomUser(array $fields) {
$fields = implode($fields, ', ');
sql = "SELECT $fields FROM users WHERE id = ?";
+ универсально, для разных страниц можно выбирать свои наборы полей
- несколько запутано
3. Тоже самое, только под каждый набор полей отдельный осмысленный метод. Например
getUserAsFriend() {
sql = "SELECT $id, $name ..."; //для отображения в списке друзей
getUserAsMember() {
sql = "SELECT $id, $name, $phone ... //для отображения в списке членов группы
getUserAsCandidate() {
sql = "SELECT $id, $name, $city, $family_status... //для отображения в списке поиска для знакомств, допустим
- несколько однотипных методов, хотя по идее слишком много и не надо
+ все кристально понятно и симпатично
Однако объединяет их один жирный минус про который ты как-то говорил. Методы класса должны возвращать предсказуемый, актуальный результат, нельзя чтобы user->getAge() возвращал пустой массив из-за того что возраст не присвоили. Я все методы переделал под магический __get(), но все равно странно что в одной вьюхе у User-a можно обращаться к чему угодно, в другой к имени и городу, а в третьей еще к чему-то. Хотя вроде как в этом и была вся задумка.
1. Дополнительный класс, какой-нибудь UserCustom, только с id и name. Не нравится он мне тем что этот класс по смыслу дублирует обычный User. Да и странно что маппер будет возвращать два разных объекта, то есть одинаковых по смыслу но с разными именами. И не универсально; для другой страницы может еще город понадобиться, не напасешься таких классов. Короче, это ерунда, я уже понял.
2. Дополнительный метод у маппера, а не класс. Но возвращаем все равно User. Просто вместо монструозного
getUser() {
sql = 'SELECT * ... FROM users LEFT JOIN cities ... LEFT JOIN ... LEFT JOIN ... ';
делаем getCustomUser(array $fields) {
$fields = implode($fields, ', ');
sql = "SELECT $fields FROM users WHERE id = ?";
+ универсально, для разных страниц можно выбирать свои наборы полей
- несколько запутано
3. Тоже самое, только под каждый набор полей отдельный осмысленный метод. Например
getUserAsFriend() {
sql = "SELECT $id, $name ..."; //для отображения в списке друзей
getUserAsMember() {
sql = "SELECT $id, $name, $phone ... //для отображения в списке членов группы
getUserAsCandidate() {
sql = "SELECT $id, $name, $city, $family_status... //для отображения в списке поиска для знакомств, допустим
- несколько однотипных методов, хотя по идее слишком много и не надо
+ все кристально понятно и симпатично
Однако объединяет их один жирный минус про который ты как-то говорил. Методы класса должны возвращать предсказуемый, актуальный результат, нельзя чтобы user->getAge() возвращал пустой массив из-за того что возраст не присвоили. Я все методы переделал под магический __get(), но все равно странно что в одной вьюхе у User-a можно обращаться к чему угодно, в другой к имени и городу, а в третьей еще к чему-то. Хотя вроде как в этом и была вся задумка.
>изобретатель ORM
Грузи непосредственные данные сущности каждый раз. Связные сущности грузи лениво. Сделай интерфейс для возможности жадной загрузки связей.
мимо
Нуу, сейчас у меня затык не в этом, загрузка связанных сущностей кое-как реализована. Вопрос вот в чем, если очень кратко: есть большой, сложный (относительно) объект, в зависимости от ситуации он может быть нужен как целиком так и 1-2 его свойства. Вот если нужны 1-2 свойства, как лучше поступить - специальный метод, в кач-ве параметров принимающий требуемые поля или несколько методов с заранее в них определенными наборами свойств. В любом их этих вариантов тот большой сложный объект возвращается как бы полупустым, что мне не нравится, с другой стороны в этом и есть смысл. Я уверен что в ООП эта задача часто встает, но чет не могу нагуглить. А может не надо мудрить а всякий раз возвращать все подряд свойства. Ну кроме связанных если они не нужны.
function out() {
global $wpdb;
$table_item = $wpdb->prefix.worm_item;
$posts = $wpdb->get_results("SELECT*FROM $table_item");
foreach ($posts as $post) {
echo $post->name;
echo "<br>";
}
}
add_filter('the_content', 'out');
Я понимаю что это кажется немного не безопасный метод, правильнее использовать ООП и наверное дата мапер (его можно использовать в вордпресс?). Но вопрос не в этом, вопрос в том как сделать каждую запись из таблицы отдельным постом? У меня они высвечиваются все в каждой записи а мне надо что бы каждая строка была отдельной записью? Как это можно сделать без изменений в теме? Я так понимаю что то надо менять в этой части?
add_filter('the_content', 'out');
Что то надо ставить вместо the_content ?
Совет не очень, но все-таки.
Не нужно мудрить. Для тех, кто используют орм, производительность dal стоит не на первом месте, иначе бы они денормализовали бд и запускали спшки через минимально возможную прослойку кода.
А на счет вытаскивания пары свойств — it depends. Если эта пара свойств дергается примерно так же часто, как и полноценный тяжелый объект, и просадка производительности критична, то имеет смысл это место оптимизировать. Если это делается через орм, то дать возможность выполнения кастомного сиквел кода или запуска спшки с мапингом на соответствующую сущность (типа UserInfo, а не User).
А вообще, то, что ты пишешь
>getUserAsCandidate(), getUserAsMember() etc
это не орм, а обычный интерфейс dal, где методы пишутся под конкретные use cases.
>специальный метод, в кач-ве параметров принимающий требуемые поля
Тут тоже интересный момент, зависящий от интерфейса твоего орм. Если ты планируешь нечто подобное:
[code]
context.users.where("$name = 'vasya'").join(context.userGroups, "$1.id = $2.userId").select("...")
[/code]
то все очевидно довольно-таки.
тред не читал, пхп забыл >>471448 - кун
Я бы с радостью попробовал совсем за недорого, то есть вообще бесплатно, но у меня наверное не очень хороший уровень, впринципе знаю основы ООП, могу написать корявый простой интернет-магазин, блог, доску объявлений впринципе тоже могу. Если не жалко потратить немного времени пиши на goran3919@gmail.com
Неужели если кто-то проработает 2 года он в категорию мидлов сразу перейдет? Фиг там.
/
@return \Vendor\Namespace\Something
/
public function getInstanceOfSomething()
{
return new Somethng();
}
Нужен ли тут слэш перед Vendor?
И еще.
/
@return this
/
public function getThis()
{
return $this;
}
Допустимо ли писать this? Или необходимо указывать неймспэйс\имя возвращаемого класса?
Да, перейдет. За 2 года можно и сеньором стать, если ты активный социоблядок.
Джун не от того джун, что йоба фреймворк не знает, а от того, что не знаком с процессами разработки по. От чего косячит и требует надзора.
Разница в уровне тех. скилла между джуном и интермедиатом — +- пара подводных камней.
Сажа прикреилась, разметка потерялась. Но суть вопроса ясна, думаю.
Что-то я тебе не верю, ибо хуёвые сказки ты травишь. Это, как тимлиды в 19 и сеньеры в 23, это, как ноют про то, что трейни предлагают сразу 700 баксов и социальный пакет. Все сказки, которые к реальности отношения не имеют. Думаешь, что джун сможет принимать архитектурные решения? Думаешь, что джун будет знать nosql хранилища и т.п.? Не будет такого. Конечно может, если речь о веб-студиях, ты прав, но в серьезных проектах это не так.
Я могу дать задачу на вордпресс, именно на плагины, если ты так хочешь. Задача из 2 частей, попроще и посложнее.
1) сделай плагин который позволяет задать «черный список» слов и выражений, которые считаются спамерскими. Комментарии с такими словами не должны публиковаться и сразу удаляться. При этом желательно не выводить сообщение об ошибке, а делать вид как будто все ок.
В админке должна быть отдельная страница, со списком слов и выражений, просто текстареа, каждое выражение с новой строки, и галочка или кнопка для включения/отключения плагина (можно не делать если это делается стандартными средствами вордпресса).
Выражения при сравнении игнорируют регистр букв, всякие небуквенные символы вроде запятых и html (а еще желательно чтобы нельзя было обойти список заменой русских букв на похожие латинские). То есть выражение «казино» соответствуют комментариям «заходите в КаЗиНо», «заходите в к.<b>а</b>.з.и.н.о.».
В выражениях можно использовать символ «звездочка» который значит «любые буквы». Ну например, выражение «лекарств×» соответствует словам «лекарство», «лекартства», «лекарственный». Можно найти готовую функцию или библиотеку, которая позволяет работать с такими выражениями, не обязательно писать свой алгоритм. Также, это можно делать в последнюю очередь, пока сделай без звездочек.
Также на странице в админке должно выводится число заблокированных комментариев и должно быть поле для проверки, куда можно вставить текст и проверить проходит он фильтр или нет.
Замечание: надо всего лишь найти хук, отвечающий за публикацию комментария и проверять этот комментарий.
2) усложненная версия. Сделай кроме «черного» списка еще возможность высчитывать рейтинг доверия к комментаторам.
Принцип такой: у каждого пользователя есть рейтинг доверия, по умолчанию он равен 0. Рейтинг доверия показывает, сколько комментариев можно опубликовать без модерации. Каждый одобренный комментарий увеличивает рейтинг на k, k задается в админке и может быть дробным, по умолчанию k = 1. То есть:
— пользователя оставляет первый комментарий, он идет на модерацию
— после одобрения пользователь получает рейтинг k = 1 и оставляет второй комментарий без модерации
— после этого третий комментарий снова идет на модерацию
— после одобрения пользователь получает рейтинг = 2 и может оставить 2 комментария без премодерации
— после этого комментарий снова подвергается модерации
— после этого пользователь может следующие 3 комментария без модерации
При этом есть определенный предел, он тоже задается в админке (maxTrustRating) выше которого k не увеличивается.
То есть пользователь постепенно накапливает что-то вроде рейтинга доверия. Если комментарий помечен модератором как спам (в том числе уже ранее одобренный) или был распознан как спам системой из первого пункта, рейтинг сбрасывается в ноль. Если число спам-комментариев больше определенного предела (maxSpamComments), пользователь отключается от этой системы (считается что он навсегда потерял доверие), рейтинг сбрасывается в ноль и все его дальнейшие комментарии подлежат модерации.
В админке должно быть:
— страница с настройкой параметров и выводом статистики: число автоматически одобренных комментариев, число пользователей с рейтингов больше нуля.
— страница для просмотра пользователей, выводит пользователей по убыванию рейтинга, для каждого пользователя пишет число оставленных/опубликованных комменатриев, рейтинг доверия, число спам комментариев, потерял ли он доверие или нет. На странице есть фильтр, позволяющий показывать только доверенных/потерявших доверие пользователей. Есть кнопки для принудительного лишения доверия или сброса репутации в начальное состоняие.
Если этот функционал можно сделать не отдельной страницей, а интегрировать в существующую админку пользователей, было бы здорово.
Пользователь идентифицируется по логину, если он зарегистрирован, если нет то по IP, то есть для анонимных пользователей рейтинг считается для Ip адреса.
замечания: тут скорее всего надо будет делать дополнительные таблицы в базе или добавлять колонки к существующим.
Реализовать это надо как плагин к вордпрессу, соотвествующий всем стандартным требованиям к плагинам и устоявшимся принципам их разработки. Устанавливаться и удаляться плагин должен стандартными средствами вордпресс, причем если установить и удалить плагин несколько раз подряд, ничего ломаться не должно. Изменять код самого вордпресса или сторонних плагинов, разумеется недопустимо, так как для проверки я захочу поставить плагин на чистую инсталляцию вордпресса.
Что скажешь? Ту работы недели на 2-4 в неспешном темпе.
Код (только самого плагина) хорошо бы постить на гитхаб.
Я могу дать задачу на вордпресс, именно на плагины, если ты так хочешь. Задача из 2 частей, попроще и посложнее.
1) сделай плагин который позволяет задать «черный список» слов и выражений, которые считаются спамерскими. Комментарии с такими словами не должны публиковаться и сразу удаляться. При этом желательно не выводить сообщение об ошибке, а делать вид как будто все ок.
В админке должна быть отдельная страница, со списком слов и выражений, просто текстареа, каждое выражение с новой строки, и галочка или кнопка для включения/отключения плагина (можно не делать если это делается стандартными средствами вордпресса).
Выражения при сравнении игнорируют регистр букв, всякие небуквенные символы вроде запятых и html (а еще желательно чтобы нельзя было обойти список заменой русских букв на похожие латинские). То есть выражение «казино» соответствуют комментариям «заходите в КаЗиНо», «заходите в к.<b>а</b>.з.и.н.о.».
В выражениях можно использовать символ «звездочка» который значит «любые буквы». Ну например, выражение «лекарств×» соответствует словам «лекарство», «лекартства», «лекарственный». Можно найти готовую функцию или библиотеку, которая позволяет работать с такими выражениями, не обязательно писать свой алгоритм. Также, это можно делать в последнюю очередь, пока сделай без звездочек.
Также на странице в админке должно выводится число заблокированных комментариев и должно быть поле для проверки, куда можно вставить текст и проверить проходит он фильтр или нет.
Замечание: надо всего лишь найти хук, отвечающий за публикацию комментария и проверять этот комментарий.
2) усложненная версия. Сделай кроме «черного» списка еще возможность высчитывать рейтинг доверия к комментаторам.
Принцип такой: у каждого пользователя есть рейтинг доверия, по умолчанию он равен 0. Рейтинг доверия показывает, сколько комментариев можно опубликовать без модерации. Каждый одобренный комментарий увеличивает рейтинг на k, k задается в админке и может быть дробным, по умолчанию k = 1. То есть:
— пользователя оставляет первый комментарий, он идет на модерацию
— после одобрения пользователь получает рейтинг k = 1 и оставляет второй комментарий без модерации
— после этого третий комментарий снова идет на модерацию
— после одобрения пользователь получает рейтинг = 2 и может оставить 2 комментария без премодерации
— после этого комментарий снова подвергается модерации
— после этого пользователь может следующие 3 комментария без модерации
При этом есть определенный предел, он тоже задается в админке (maxTrustRating) выше которого k не увеличивается.
То есть пользователь постепенно накапливает что-то вроде рейтинга доверия. Если комментарий помечен модератором как спам (в том числе уже ранее одобренный) или был распознан как спам системой из первого пункта, рейтинг сбрасывается в ноль. Если число спам-комментариев больше определенного предела (maxSpamComments), пользователь отключается от этой системы (считается что он навсегда потерял доверие), рейтинг сбрасывается в ноль и все его дальнейшие комментарии подлежат модерации.
В админке должно быть:
— страница с настройкой параметров и выводом статистики: число автоматически одобренных комментариев, число пользователей с рейтингов больше нуля.
— страница для просмотра пользователей, выводит пользователей по убыванию рейтинга, для каждого пользователя пишет число оставленных/опубликованных комменатриев, рейтинг доверия, число спам комментариев, потерял ли он доверие или нет. На странице есть фильтр, позволяющий показывать только доверенных/потерявших доверие пользователей. Есть кнопки для принудительного лишения доверия или сброса репутации в начальное состоняие.
Если этот функционал можно сделать не отдельной страницей, а интегрировать в существующую админку пользователей, было бы здорово.
Пользователь идентифицируется по логину, если он зарегистрирован, если нет то по IP, то есть для анонимных пользователей рейтинг считается для Ip адреса.
замечания: тут скорее всего надо будет делать дополнительные таблицы в базе или добавлять колонки к существующим.
Реализовать это надо как плагин к вордпрессу, соотвествующий всем стандартным требованиям к плагинам и устоявшимся принципам их разработки. Устанавливаться и удаляться плагин должен стандартными средствами вордпресс, причем если установить и удалить плагин несколько раз подряд, ничего ломаться не должно. Изменять код самого вордпресса или сторонних плагинов, разумеется недопустимо, так как для проверки я захочу поставить плагин на чистую инсталляцию вордпресса.
Что скажешь? Ту работы недели на 2-4 в неспешном темпе.
Код (только самого плагина) хорошо бы постить на гитхаб.
>>470923
> class Department
>\tpublic function createEmployee($amount, $profession, $rank, $chief = 0)
Это лучше вынести из департамента. каждый класс должен заниматься своим делом (принцип single responsibility: http://getinstance.info/articles/php/the-single-responsibility-principle/ )
Департамент не создает работников. Он принимает их на работу.
Более того, смотри, твой департамент не дает возможности просто добавить существующего работника в него:
$department = new Department('Название');
$employee = new Employee('manager', 3, 0);
// упс ... а как принять этого работника на работу в департамент?
Традиционно, когда один класс содержит объекты другого, у него делают методы вроде addEmployee($e) и removeEmployee($e) чтобы их можно было добавлять и удалять.
> $this->name = $name;
> $this->employees[]
Я не вижу где определено это поле. Конечно php автоматически создает поле при записи в него, но ты не должен так делать так как страдает читабельности и понятность код: я не могу увидеть список всех полей в классе не прочитав код целиком.
> public function DepartmentEmployeersNumber
В PHP принято писать функции с маленькой первой буквы и начинать с глагола в стиле сделайЧтоТо: getEmployeeCount
> if ($chief == 1) {
Для обознаечния понятий «да»/«нет» лучше использовать булевые(логические) значения true/false http://php.net/manual/ru/language.types.boolean.php
> return round ($this->DepartmentPayment() / $this->DepartmentPages(), 1);
Округлением должен заниматься тот кто выводит, а департамент должен возвращать точные данные. Ты же не знаешь какая точность нужна вызывающему эту функцию.
> if ($profession == 'manager') {
>\t\t\t$this->payment = 500;
>\t\t\t$this->coffee = 20;
>\t\t\t$this->pages = 200;
Боюсь, что это плохая идея. Во-первых, чтобы добавить новую профессию, нам надо править код функции и она будет разрастаться. Во-вторых при таком подходе мы не можем для одной профессии поменять формулу расчета зарплаты на другую.
Гораздо удобнее сделать не один класс Сотрудник, а 4 класса: Инженер, Менеджер, и т.д. Тогда мы можем легко менять например правила расчет зарплаты или кофе для каждой профессии. Естественно, копипастить одинаковый код в 4 класса не надо — создай базовый абстрактный класс Сотрудник и унаследуй от него 4 класса-профессии. Также в этом случае мы можем добавить новую профессию, добавив новый класс и не меняя существующий код.
Наследование позволяет создавать класс не с нуля. а расширяя существующий класс: http://php.net/manual/ru/language.oop5.inheritance.php
«Абстрактный» — это класс, объект которого нельзя создать. Он предназначен для наследования от него других классов: http://php.net/manual/ru/language.oop5.abstract.php
Также, тебе для лучшего понимания ООП (вообще, я всем ее даю) я хочу дать вторую часть этой задачи:
----
Пока ты решал задачу по выводу отчета о сотрудниках и департаментах, разразился мировой экономический кризис. Доходы компании начали снижаться, и совет директоров поставил перед руководством задачу принять меры. Менеджеры 3-го ранга, блестящие выпускники топовых экономических вузов столицы, быстро смогли разработать три альтернативных антикризисных решения:
1. Сократить в каждом департаменте 40% (округляя в большую сторону) инженеров, преимущественно самого низкого ранга, которые не являются боссами.
2. Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков, а количество выпиваемого им кофе с 50 до 75 литров. В тех департаментах, где руководитель не является аналитиком, заменить его на аналитика самого высшего ранга из этого департамента (а бывшего руководителя вернуть к обычной работе)
3. В каждом департаменте повысить 50% (округляя в большую сторону) менеджеров 1-го и 2-го ранга на один ранг с целью расширить их полномочия.
Совет директоров в затруднении: какой путь выбрать? Помоги им с этим, распечатав прогноз по потреблению и расходам (аналогичный тому что требуется в задаче) после принятия каждой из мер.
----
Если что-то непонятно, справшивай.
>>470923
> class Department
>\tpublic function createEmployee($amount, $profession, $rank, $chief = 0)
Это лучше вынести из департамента. каждый класс должен заниматься своим делом (принцип single responsibility: http://getinstance.info/articles/php/the-single-responsibility-principle/ )
Департамент не создает работников. Он принимает их на работу.
Более того, смотри, твой департамент не дает возможности просто добавить существующего работника в него:
$department = new Department('Название');
$employee = new Employee('manager', 3, 0);
// упс ... а как принять этого работника на работу в департамент?
Традиционно, когда один класс содержит объекты другого, у него делают методы вроде addEmployee($e) и removeEmployee($e) чтобы их можно было добавлять и удалять.
> $this->name = $name;
> $this->employees[]
Я не вижу где определено это поле. Конечно php автоматически создает поле при записи в него, но ты не должен так делать так как страдает читабельности и понятность код: я не могу увидеть список всех полей в классе не прочитав код целиком.
> public function DepartmentEmployeersNumber
В PHP принято писать функции с маленькой первой буквы и начинать с глагола в стиле сделайЧтоТо: getEmployeeCount
> if ($chief == 1) {
Для обознаечния понятий «да»/«нет» лучше использовать булевые(логические) значения true/false http://php.net/manual/ru/language.types.boolean.php
> return round ($this->DepartmentPayment() / $this->DepartmentPages(), 1);
Округлением должен заниматься тот кто выводит, а департамент должен возвращать точные данные. Ты же не знаешь какая точность нужна вызывающему эту функцию.
> if ($profession == 'manager') {
>\t\t\t$this->payment = 500;
>\t\t\t$this->coffee = 20;
>\t\t\t$this->pages = 200;
Боюсь, что это плохая идея. Во-первых, чтобы добавить новую профессию, нам надо править код функции и она будет разрастаться. Во-вторых при таком подходе мы не можем для одной профессии поменять формулу расчета зарплаты на другую.
Гораздо удобнее сделать не один класс Сотрудник, а 4 класса: Инженер, Менеджер, и т.д. Тогда мы можем легко менять например правила расчет зарплаты или кофе для каждой профессии. Естественно, копипастить одинаковый код в 4 класса не надо — создай базовый абстрактный класс Сотрудник и унаследуй от него 4 класса-профессии. Также в этом случае мы можем добавить новую профессию, добавив новый класс и не меняя существующий код.
Наследование позволяет создавать класс не с нуля. а расширяя существующий класс: http://php.net/manual/ru/language.oop5.inheritance.php
«Абстрактный» — это класс, объект которого нельзя создать. Он предназначен для наследования от него других классов: http://php.net/manual/ru/language.oop5.abstract.php
Также, тебе для лучшего понимания ООП (вообще, я всем ее даю) я хочу дать вторую часть этой задачи:
----
Пока ты решал задачу по выводу отчета о сотрудниках и департаментах, разразился мировой экономический кризис. Доходы компании начали снижаться, и совет директоров поставил перед руководством задачу принять меры. Менеджеры 3-го ранга, блестящие выпускники топовых экономических вузов столицы, быстро смогли разработать три альтернативных антикризисных решения:
1. Сократить в каждом департаменте 40% (округляя в большую сторону) инженеров, преимущественно самого низкого ранга, которые не являются боссами.
2. Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков, а количество выпиваемого им кофе с 50 до 75 литров. В тех департаментах, где руководитель не является аналитиком, заменить его на аналитика самого высшего ранга из этого департамента (а бывшего руководителя вернуть к обычной работе)
3. В каждом департаменте повысить 50% (округляя в большую сторону) менеджеров 1-го и 2-го ранга на один ранг с целью расширить их полномочия.
Совет директоров в затруднении: какой путь выбрать? Помоги им с этим, распечатав прогноз по потреблению и расходам (аналогичный тому что требуется в задаче) после принятия каждой из мер.
----
Если что-то непонятно, справшивай.
> Call to a member function get_results() on a non-object
Это ошибка которая значит что $wpdb не является оьъектом и потому ты не можешь вызывать у нее методы. С помощью var_dump($wpdb); определи что там хранится.
Алсо, это случайно не внутри функции происходит? Ты знаешь что внутри функции по умолчанию глобальные переменные недоступны? Если нет то тебе сначала надо повторить как работают области видимости переменных в php и перечитать этот раздел мануала: http://php.net/manual/ru/language.variables.scope.php
>>471311
Это надо делать через джойны, подзапросы довольно неэффективны и плохо читаются.
http://www.anton-pribora.ru/articles/mysql/mysql-join
http://rche.ru/1009_razbiraemsya-s-sql-join-vizualnoe-predstavlenie.html
соответственно джойнишь фильмы на таблицу связи и пишешь SELECT film.id FROM ... JOIN ... WHERE fc.id_category IN (1,2);
Обязательно изучи джойны, это основы SQL и надо знать. Также, у нас в треде в первом посте есть хорошее, годное задание на SQL, которое позволит тебе понять джойны лучше.
Я мимо проходил, но вот тут пишут что это можно сделать как-то через add_filter: http://premium.wpmudev.org/forums/topic/how-do-i-add-data-into-the_content
Я также замечу что это очень неочевидная вещь, потому советую оставить в твоем плагине подробный и легко заметный комментарий про то как он модифицирует данные.
Ну и также подумай, а надо ли тебе менять то что вернет get_the_content? Может для твоей задачи можно сделать отдельную функцию и добавить в шаблон?
>>471426
> Несколько напрягает то что для этих целей создаются полноценные экземпляры класса. Куда логичнее было бы получать только то что нужно.
Это тоже одна из проблем ORM, частичная выборка. Помни, что функции всегда должны возвращать актуальный и точный результат. Если ты создаешь объект у которого заполнена часть полей, и этот объект неотличим от заполненного, то будут проблемы.
В доктрине есть специальное слово PARTIAL для выборки частично заполненных объектов: http://odiszapc.ru/doctrine/dql-doctrine-query-language/#13241_partial
Это однозначно неправильный путь. Я сталкивался с очень труднообнаружимыми багами при использовании этой опции, и я вообще не понимаю зачем она там дана.
Потому в той же доктрине, если тебе нужны не все поля, то лучше выбирать результаты например в виде массива вместо объекта (тут минус в том что мы возвращаемся к массиво-ориентированному программированию).
Также, сама концепция ORM предполагает что ты выбираешь из базы объекты (целиком), а не отдельные поля.
Соответственно для выборки части полей придется делать один из 2 вариантов:
— хранить в объекте список заполненных полей и при обращении к незаполненным выкидывать исключение (минус: по мере роста программы в ней начнут вылетать эти исключения, и каждый раз тебе надо будет искать откуда пришел объект, искать SQL запрос и добавлять вручную поля)
— сделать ленивую загрузку некоторых полей из базы при первом обращении к ним. То есть мы выбираем объект с основными полями, а при обращении к ленивым идут дополнительные запросы. Для этого разумеется код должен это поддерживать, например использовать геттеры-сеттеры вместо публичных полей. Ленивую загрузку можно реализовать так: ленивые поля хранятся завернутыми в прокси-объект, который знает, загружено поле или нет и если нет, то у него есть ссылка на маппер для загрузки поля:
// создаем объект в маппере
$user = new User( );
$user->setName($name); // не ленивое поле
$proxy = FieldProxy::createLazy($this, 'community', $user); // объект оборачивающий ленивое поле который полезет в базу при попытке чтения
$user->setCommunityProxy($proxy);
// прокси с уже проставленным значением которое не полезет в базу
$firendsProxy = FieldProxy::createWithValue($friends);
$user->setFriendsproxy($friendsProxy);
Как видишь при такой схемы мы можем загружать поля сразу или отложенно, прозрачно для работающего с объектом кода.
Соответственно в классе $user мы при попытке прочитать ленивое поле обращаемся к прокси объекту:
public function getCommunity()
{
return $this->community->getValue( ); // вызывает SQL запрос через маппер если поле еще не загружено
}
public function setCommunity($community)
{
$this->community->setValue($communtiy);
}
Как видишь, это требует некоторого усложнения кода. Также при ленивой загрузке мы можем получить проблему N + 1 запроса (если мы в цикле проходимся по массиву пользователей и обращаемся к их ленивым полям).
Но вернемся назад: а так ли нам нужны ленивые поля? намного ли медленнее выбирать все поля? У нас, в IT мире не принято в таких случаях верить словам или ощущениям, а принято делать тесты. Диванные оптимизаторы тут не нужны. Раз уж ты заговорил про оптимизацию, то подкрепи свои слова таблицами и графиками.
Сделай таблицу, например с 100 000 записей со случайными значениями (хинт: вставлять по 1000 записей одной транзакцией быстрее чем 1000 отдельных). Затем напиши 2 варианта кода, которые в цикле например 1000 раз выбирают случайную запись. Первый вариант кода выбирает все поля, второй только часть. замерь время выполнения обоих. Если хочешь, можно еще выполнять этот код для разного числа итераций (от 1 до 100000) и по ним построить красивый график к екселе/опеноффисе.
Заметь что генерация случайных чисел в больших количествах требует времени. Чтобы это не влияло на тест, лучше сгенерировать массив случайных id для выборки до теста.
Заметь что есть разница в способе хранения больших полей типа TEXT. Потому если ты не загружаешь поля типа TEXT (особенно если в них много данных), разница может быть больше, чем если не загружать поля типа INT или VARCHAR.
Тесты займут у тебя часа 2-3. Я советую их сделать, это даст тебе полезные навыки, чуть-чуть поднимет твой уровень и заодно ты узнаешь большую ли выгоду мы получаем не выбирая часть полей, и стоит ли с этим заморачиваться.
Я мимо проходил, но вот тут пишут что это можно сделать как-то через add_filter: http://premium.wpmudev.org/forums/topic/how-do-i-add-data-into-the_content
Я также замечу что это очень неочевидная вещь, потому советую оставить в твоем плагине подробный и легко заметный комментарий про то как он модифицирует данные.
Ну и также подумай, а надо ли тебе менять то что вернет get_the_content? Может для твоей задачи можно сделать отдельную функцию и добавить в шаблон?
>>471426
> Несколько напрягает то что для этих целей создаются полноценные экземпляры класса. Куда логичнее было бы получать только то что нужно.
Это тоже одна из проблем ORM, частичная выборка. Помни, что функции всегда должны возвращать актуальный и точный результат. Если ты создаешь объект у которого заполнена часть полей, и этот объект неотличим от заполненного, то будут проблемы.
В доктрине есть специальное слово PARTIAL для выборки частично заполненных объектов: http://odiszapc.ru/doctrine/dql-doctrine-query-language/#13241_partial
Это однозначно неправильный путь. Я сталкивался с очень труднообнаружимыми багами при использовании этой опции, и я вообще не понимаю зачем она там дана.
Потому в той же доктрине, если тебе нужны не все поля, то лучше выбирать результаты например в виде массива вместо объекта (тут минус в том что мы возвращаемся к массиво-ориентированному программированию).
Также, сама концепция ORM предполагает что ты выбираешь из базы объекты (целиком), а не отдельные поля.
Соответственно для выборки части полей придется делать один из 2 вариантов:
— хранить в объекте список заполненных полей и при обращении к незаполненным выкидывать исключение (минус: по мере роста программы в ней начнут вылетать эти исключения, и каждый раз тебе надо будет искать откуда пришел объект, искать SQL запрос и добавлять вручную поля)
— сделать ленивую загрузку некоторых полей из базы при первом обращении к ним. То есть мы выбираем объект с основными полями, а при обращении к ленивым идут дополнительные запросы. Для этого разумеется код должен это поддерживать, например использовать геттеры-сеттеры вместо публичных полей. Ленивую загрузку можно реализовать так: ленивые поля хранятся завернутыми в прокси-объект, который знает, загружено поле или нет и если нет, то у него есть ссылка на маппер для загрузки поля:
// создаем объект в маппере
$user = new User( );
$user->setName($name); // не ленивое поле
$proxy = FieldProxy::createLazy($this, 'community', $user); // объект оборачивающий ленивое поле который полезет в базу при попытке чтения
$user->setCommunityProxy($proxy);
// прокси с уже проставленным значением которое не полезет в базу
$firendsProxy = FieldProxy::createWithValue($friends);
$user->setFriendsproxy($friendsProxy);
Как видишь при такой схемы мы можем загружать поля сразу или отложенно, прозрачно для работающего с объектом кода.
Соответственно в классе $user мы при попытке прочитать ленивое поле обращаемся к прокси объекту:
public function getCommunity()
{
return $this->community->getValue( ); // вызывает SQL запрос через маппер если поле еще не загружено
}
public function setCommunity($community)
{
$this->community->setValue($communtiy);
}
Как видишь, это требует некоторого усложнения кода. Также при ленивой загрузке мы можем получить проблему N + 1 запроса (если мы в цикле проходимся по массиву пользователей и обращаемся к их ленивым полям).
Но вернемся назад: а так ли нам нужны ленивые поля? намного ли медленнее выбирать все поля? У нас, в IT мире не принято в таких случаях верить словам или ощущениям, а принято делать тесты. Диванные оптимизаторы тут не нужны. Раз уж ты заговорил про оптимизацию, то подкрепи свои слова таблицами и графиками.
Сделай таблицу, например с 100 000 записей со случайными значениями (хинт: вставлять по 1000 записей одной транзакцией быстрее чем 1000 отдельных). Затем напиши 2 варианта кода, которые в цикле например 1000 раз выбирают случайную запись. Первый вариант кода выбирает все поля, второй только часть. замерь время выполнения обоих. Если хочешь, можно еще выполнять этот код для разного числа итераций (от 1 до 100000) и по ним построить красивый график к екселе/опеноффисе.
Заметь что генерация случайных чисел в больших количествах требует времени. Чтобы это не влияло на тест, лучше сгенерировать массив случайных id для выборки до теста.
Заметь что есть разница в способе хранения больших полей типа TEXT. Потому если ты не загружаешь поля типа TEXT (особенно если в них много данных), разница может быть больше, чем если не загружать поля типа INT или VARCHAR.
Тесты займут у тебя часа 2-3. Я советую их сделать, это даст тебе полезные навыки, чуть-чуть поднимет твой уровень и заодно ты узнаешь большую ли выгоду мы получаем не выбирая часть полей, и стоит ли с этим заморачиваться.
> Дополнительный класс, какой-нибудь UserCustom,
Это создает путаницу так как пользователь может быть представлен в 2 видах. плучается будет часть функций которым нужен USer, а часть которым UserCustom? даже если унаследовать User от UserCustom, будет не очень удобно.
И как ты будешь делать связи? Например у комментария есть метод getUser, ты сделаешь второй метод getUserCustom? И что ты будешь делать если полный User не загружен, а мы вызваем getUser?
Вообще, в доктрине есть «легкие» прокси-объекты, которые хранят только id, они называются Reference. Доктрина генерирует эти классы как наследников классов которые они представляют. То есть Reference для User это класс-наследник User (и потмоу это нормально работает с тайп-хинтами и instanceof и ты можешь передать User Reference везде где нужен User). И как только ты пытаешься вызвать любой метод кроме getId(), происходит обращение к базе, и объект заполняется данными.
Вот мануал: http://doctrine-orm.readthedocs.org/en/latest/reference/advanced-configuration.html#reference-proxies
// создаем reference без данных из базы, только в id
$item = $em->getReference('MyProject\Model\Item', $itemId);
// добавляем к другому объекту
$cart->addItem($item);
// а вот тут произойдет запрос к базе и заполенние полей
echo $item->getName( );
> Дополнительный метод у маппера, а не класс. Но возвращаем все равно User.
Не забывай что методы User должны возвращать актуальную и точную информацию независимо от того как он создан. Недопустимо чтобы например $user->getFriends не возвращал друзей если они есть.
Если ты хочешь не загружать часть данных. то тебе нужно абстрагировать их отсутствие, спрятав их в прокси-объекты.
>все кристально понятно и симпатично
Верный путь к бардаку и багам.
> Однако объединяет их один жирный минус про который ты как-то говорил. Методы класса должны возвращать предсказуемый, актуальный результат,
Потому для ленивой загрузки надо использовать прокси-объекты которые загружают данные при первом обращении к ним прозрачно для вызывающего кода.
>>471429
Да
> Дополнительный класс, какой-нибудь UserCustom,
Это создает путаницу так как пользователь может быть представлен в 2 видах. плучается будет часть функций которым нужен USer, а часть которым UserCustom? даже если унаследовать User от UserCustom, будет не очень удобно.
И как ты будешь делать связи? Например у комментария есть метод getUser, ты сделаешь второй метод getUserCustom? И что ты будешь делать если полный User не загружен, а мы вызваем getUser?
Вообще, в доктрине есть «легкие» прокси-объекты, которые хранят только id, они называются Reference. Доктрина генерирует эти классы как наследников классов которые они представляют. То есть Reference для User это класс-наследник User (и потмоу это нормально работает с тайп-хинтами и instanceof и ты можешь передать User Reference везде где нужен User). И как только ты пытаешься вызвать любой метод кроме getId(), происходит обращение к базе, и объект заполняется данными.
Вот мануал: http://doctrine-orm.readthedocs.org/en/latest/reference/advanced-configuration.html#reference-proxies
// создаем reference без данных из базы, только в id
$item = $em->getReference('MyProject\Model\Item', $itemId);
// добавляем к другому объекту
$cart->addItem($item);
// а вот тут произойдет запрос к базе и заполенние полей
echo $item->getName( );
> Дополнительный метод у маппера, а не класс. Но возвращаем все равно User.
Не забывай что методы User должны возвращать актуальную и точную информацию независимо от того как он создан. Недопустимо чтобы например $user->getFriends не возвращал друзей если они есть.
Если ты хочешь не загружать часть данных. то тебе нужно абстрагировать их отсутствие, спрятав их в прокси-объекты.
>все кристально понятно и симпатично
Верный путь к бардаку и багам.
> Однако объединяет их один жирный минус про который ты как-то говорил. Методы класса должны возвращать предсказуемый, актуальный результат,
Потому для ленивой загрузки надо использовать прокси-объекты которые загружают данные при первом обращении к ним прозрачно для вызывающего кода.
>>471429
Да
> Вот если нужны 1-2 свойства,
Может просто загрузить их в виде массива? Или же пиши прокси-объект, мне кажется у него код будет не сложный, там всего несколько публичных методов: получить знаечние, задать значение. И если значения нет то он обращается к мапперу и запрашивает его из базы.
С помощью прокси можно сделать ленивым как отдельное поле, так и набор полей или сущность.
> А может не надо мудрить а всякий раз возвращать все подряд свойства
Сделай сначала тесты и посмотрим какая разница в скорости.
Ты делаешь неправильно. Фильтр не должен ничего выводить на страницу, он должен возвращать результат через reutrn
Более того фильтр предназначен только для одной цели: модификации текста какой-то части поста. Ни для чего другого он не подходит. Ты уверен что тебе нужен фильтр?
Вывести что угодно можно через шаблон (они вроде называются темы). Может лучше сделать свою тему например которая выводит то, что тебе надо.
Смотри примеры кода в документации: https://codex.wordpress.org/Function_Reference/add_filter (читал ли ты ее? надо читать)
>правильнее использовать ООП и наверное дата мапер (его можно использовать в вордпресс?)
Теоретически можно, но тебе придется его написать самому. Лучше использовать стандартные функции вордпресса для работы с базой, выборки постов и т.д. Маппер можно использовать для своих таблиц и объектов, но не для постов.
> вопрос в том как сделать каждую запись из таблицы отдельным постом?
Должна быть стандартная функция выборки постов из базы, нагугли и используй ее. Не изобретай велосипеды.
> У меня они высвечиваются все в каждой записи а мне надо что бы каждая строка была отдельной записью?
Фильтр не для этого. Фильтр для изменения текста каждой выводимой записи. Например добавить информацию об авторе в конец поста или заменить коды в тексте на картинки.
Алсо, сформулируй что тебе надо сделать понятно, потому что я пока ничего не понял.
Я читал мельком, довольно бесполезная на мой взгляд книга. Там просто примеры кода, если ты прочтешь мануал/изучишь нужные функции ты сам такое напишешь без книги.
>>471448
> иначе бы они денормализовали бд
Ты не видел БД автора вопроса а уже предлагаешь ее денормализовать.
> и запускали спшки через минимально возможную прослойку кода.
Ну вот только не надо этого, ок? твои «спшки» не интегрируются нормально в код, не масштабируются, не отлаживаются и т.д. и никто кроме стариков их не использует в наши дни.
Технология для ленивой загрузки объектов/полей реализуется через прокси-объекты. В Доктрине например есть прокси-объект Entity reference.
>>471457
В лесах Африки.
>>471466
Если у тебя есть лишнее время лучше тратить его на изучение и выполнение каких-то учебных задач, а не на выполнение примитивной работы. Это примерно то же самое что если ты хочешь стать инженером и идешь на стройку разнорабочим набраться опыта. У нас в первом посте есть задания, в том числе например на Yii,если ты его уже знаешь ,могу скинуть ссылку на туториал по Симфони 2 ( http://symfony.in.ua/symfony2-jobeet-tutorial.html нужно знание ООП/MVC ).
Опыт и уровень самостоятельности.
>>471582
Я много раз видел, пишут @return self, так что думаю допустимо. Также (это мое личное мнение) @return self лучше не писать, так как любая нормальная IDE должна это уметь распознавать сама.
self упомянут в документации: http://phpdoc.org/docs/latest/references/phpdoc/types.html
Неймспейс писать в доках не надо, там действуют такие же правила как для обычного кода. то ест если у тебя вверху написано namespacу или use то они же и в доках работают.
Вот это упомянуто: http://phpdoc.org/docs/latest/references/phpdoc/types.html#valid-class-name
> Нужен ли тут слэш перед Vendor?
Зависит от того какие у тебя namespace/use стоят. Правила такие же как в обычном коде.
> За 2 года можно и сеньором стать,
Если ты живешь в стране 23 летних сеньоров?
>>471591
джун не будет знать, но справедливости ради, если у человека голова хорошо работает, он читает всякие статьи, документацию, работает в хорошей компании то за 2 года можно многое узнать. Ну и если речь про аутсорс, то за миддлов/сеньоров можно больше денег взять, потому там взаимный интерес у работодателя и работника.
>Ленивую загрузку можно реализовать так: ленивые поля хранятся завернутыми в прокси-объект, который знает, загружено поле или нет и если нет, то у него есть ссылка на маппер для загрузки поля
Так-так, щас, я уже почти все понял. Но как этот прокси-объект поймет загружено поле или нет? isset($this->{$field}) всегда вернет true потому что поле объявляется одновременно с экземпляром
class User {
private $id,$name,$phone...
}
empty() и ==NULL тоже не подходит, поля могут быть просто не заполнены если юзер что-то не указал
Получается что все равно надо хранить какой-то список filledFields.
> isset($this->{$field}) всегда вернет true
А зачем тебе isset? Ты должен обращаться к прокси-полям особо, не как к обычным. То есть нужна поддержка со стороны класса User, он должен знать что такие-то поля реализованы через прокси. Можно просто их список сделать.
> тоже не подходит, поля могут быть просто не заполнены если юзер что-то не указал
Перечитай мой пост, с прокси полями ты должен работать примерно так:
public function getSomething()
{
return $this->somethingProxy->getvalue( );
}
И соответственно проверить это поле можно так:
$value = $user->getSomething( );
if (empty($value)) [
...
}
> Получается что все равно надо хранить какой-то список filledFields.
Информация о том загружено ли значение хранится внутри прокси. ПРимерно так:
public function getValue()
{
if (!$this->isDataLoaded) {
$this->loadValueFromDb( );
}
return $this->value;
}
Ну и разумеется, при наличии прокси ты можешь сделать опию в маппере, говоряющую какие поля и связи загружать сразу, а какие лениво.
И еще раз напомню про тесты: сделай тесты. Я не увреен что от реализации ленивых полей есть выигрыш. От ленивых связей наверно он есть, а вот насчет полей не уверен.
> isset($this->{$field}) всегда вернет true
А зачем тебе isset? Ты должен обращаться к прокси-полям особо, не как к обычным. То есть нужна поддержка со стороны класса User, он должен знать что такие-то поля реализованы через прокси. Можно просто их список сделать.
> тоже не подходит, поля могут быть просто не заполнены если юзер что-то не указал
Перечитай мой пост, с прокси полями ты должен работать примерно так:
public function getSomething()
{
return $this->somethingProxy->getvalue( );
}
И соответственно проверить это поле можно так:
$value = $user->getSomething( );
if (empty($value)) [
...
}
> Получается что все равно надо хранить какой-то список filledFields.
Информация о том загружено ли значение хранится внутри прокси. ПРимерно так:
public function getValue()
{
if (!$this->isDataLoaded) {
$this->loadValueFromDb( );
}
return $this->value;
}
Ну и разумеется, при наличии прокси ты можешь сделать опию в маппере, говоряющую какие поля и связи загружать сразу, а какие лениво.
И еще раз напомню про тесты: сделай тесты. Я не увреен что от реализации ленивых полей есть выигрыш. От ленивых связей наверно он есть, а вот насчет полей не уверен.
Кстати, с прокси придется еще сделать чтобы работал такой сценарий:
$user = $mapper->find(1); // загружаем пользователя с минимумом полей
$user->setSomething('aaa'); // обновляем прокси-поле, обращения к БД не должно быть так как мы записываем новое знаечние
$mapper->save($user); // маппер должен увидеть прокси-поля в объекте, и он должен сохранить в базу только измененные, то есть только поле someting, а другие не трогать. Если он попытается получить значения не загруженных полей, пойдут лишние SELECT запросы.
>То есть нужна поддержка со стороны класса User, он должен знать что такие-то поля реализованы через прокси. Можно просто их список сделать.
Тогда, пожалуй, это не то что мне хотелось. Точнее под мой хитрый план надо делать все поля с возможностью ленивой загрузки. Ну потому что, в том примере где мне нужно только имя и айди все остальные поля выходит что должны быть ленивыми. А они задуманы, судя по всему, для полей которые достаются из других таблиц, в этом случае можно выгадать какую-то оптимизацию. А, вспомнил, ты в прошлом треде про что-то такое писал и тот чел что первый мне ответил тоже самое имел ввиду. Нее, это не так круто как хотелось бы.
Тогда облегченный объект. minifiedUser, userPreview, userAvatar (в значении 'представлениеПользователя', а не 'картинкаПользователя')
>плучается будет часть функций которым нужен USer, а часть которым UserCustom?
Нннет. Единственное предназначение этого мини-юзера быть переданным во вью и показать там аватарку + имя. Больше он нигде не будет использоваться, вызываться, передаваться.
Ну или массив.
То есть тесты ты делать не хочешь и исходишь из своих фантазий что 2 поля выбрать намного быстрее чем 32? Это плохой подход. Ты оптимизируешь то, что может быть оптимизаций не требует.
> Единственное предназначение этого мини-юзера быть переданным во вью и показать там аватарку + имя. Больше он нигде не будет использоваться, вызываться, передаваться.
ты можешь попробовать, но мне кажется это может привести к сложностям и такой подход будет вызывать проблемы по мере роста приложения.
Не забудь сделать чтобы User наследовался от MiniUser — тогда его можно будет передавать в работающие с ним функции.
> Ну или массив.
Бардак будет
Я не настолько крут чтобы так сразу представить как сделать тесты. Зароюсь еще в них и тебя лишний раз задолбаю, а тут и так все туговато идет. Главное чтоб хуже не было, зато у меня все по фен-шую теперь: весь профайл - О Тоторо, превьюшки - тиби тоторо.
Спасибо, анончик, заработало. У меня есть ещё вопрос, если позволите. Пикрелейтед-код шлёт в COM порт значение ползунка с html от 0 - 255, я не уверен в том, что переменная будет считываться функцией exec вообще. Данные-то отправляются, я вижу это по мигающиму светодиоду на переходнике usb-uart, но вот считывается ли переменная или отправляется как просто набор символов я не знаю.
да. У него уязвимость позволяющая выполнить любу. команду, только под виндой не rm rf а что-то другое надо писать, например format d:
>>471770
Тебе лучше сначала PHP и HTML изучить хотя бы на базовом уровне. Иначе ты так будешь на каждой мелкой проблеме тупить и ошибок наделаешь. А со знаниями PHP все быстро и правильно сделаешь.
> но вот считывается ли переменная или отправляется как просто набор символов я не знаю.
перенпараввь вывод в файл вместо порта и посмотри
Так тест несложно сделать:
$start = microtime(true);
for ($i = от 1 до N) {
делаем действие, например выбор случайной записи;
}
$end = microtime(true);
$timePassed = $end - $start;
echo $timePassed;
Почему элемент массива не преобразуется в строку?
http://ideone.com/Fhk7ki
в implode нужно передавать массив. А у тебя передается значение в $matches2[0] == string(13) "трaляля"
Покажи мне хоть одно коммерчески успешное приложение на D.
Насчет MiniUser: тут есть загвоздка в том, что когда ты захочешь загрузить полную информацию о пользователе, у тебя будет 2 объекта относящихся к одному пользователю: MiniUser и User и дублирование это конечно не очень хорошо.
Я подумал немного и мне тут пришла еще одна идея в голову с ленивой загрузкой. Допустим у нас есть задача иногда загружать только основные поля в пользователе, а иногда полную информацию. И при этом разумеется следовать всем хорошим практикам разработки ПО.
Мы можем разбить класс Пользователь на 2 части: основная информация о пользователе (User) + все остальное (Profile). Так как эти части связаны, то логично Profile поместить внутрь юзера примерно так:
echo $user->getName( );
echo $user->getProfile( )->getSomething( );
Ну и особенность тут в том, что объект $profile мы можем грузить лениво. То есть мы можем засунуть его в прокси,и это позволяет нам либо грузить только маленький набор полей (только User), либо полный (User + Profile). Прокси скрывает от нас все это и загружает Profile лениво при первом обращении к нему.
Код выглядит примерно так:
public function getProfile()
{
return $this->profileProxy->getValue( );
}
Конечно еще есть вариант сделать без прокси и выбрасывать исключение при попытке вызвать getProfile если Pofile не загружен:
public function getProfile()
{
if (!$this->profile) {
throw new \Exception("You have not load profile for user id = $this->id");
}
return $this->profile;
}
Но по моему вариант с прокси и ленивой загрузкой профиля гораздо удобнее и спасает нас от отладки причин этих исключений и позволяет при желании прозрачно подгрузить недостающие данные.
Соответственно так же, через прокси можно загружать связи пользователя с другими объектами. Прокси дает тебе выбор, грузить данные жадно, сразу или лениво, по мере надобности, прозрачно для работающего с объектом кода.
Насчет MiniUser: тут есть загвоздка в том, что когда ты захочешь загрузить полную информацию о пользователе, у тебя будет 2 объекта относящихся к одному пользователю: MiniUser и User и дублирование это конечно не очень хорошо.
Я подумал немного и мне тут пришла еще одна идея в голову с ленивой загрузкой. Допустим у нас есть задача иногда загружать только основные поля в пользователе, а иногда полную информацию. И при этом разумеется следовать всем хорошим практикам разработки ПО.
Мы можем разбить класс Пользователь на 2 части: основная информация о пользователе (User) + все остальное (Profile). Так как эти части связаны, то логично Profile поместить внутрь юзера примерно так:
echo $user->getName( );
echo $user->getProfile( )->getSomething( );
Ну и особенность тут в том, что объект $profile мы можем грузить лениво. То есть мы можем засунуть его в прокси,и это позволяет нам либо грузить только маленький набор полей (только User), либо полный (User + Profile). Прокси скрывает от нас все это и загружает Profile лениво при первом обращении к нему.
Код выглядит примерно так:
public function getProfile()
{
return $this->profileProxy->getValue( );
}
Конечно еще есть вариант сделать без прокси и выбрасывать исключение при попытке вызвать getProfile если Pofile не загружен:
public function getProfile()
{
if (!$this->profile) {
throw new \Exception("You have not load profile for user id = $this->id");
}
return $this->profile;
}
Но по моему вариант с прокси и ленивой загрузкой профиля гораздо удобнее и спасает нас от отладки причин этих исключений и позволяет при желании прозрачно подгрузить недостающие данные.
Соответственно так же, через прокси можно загружать связи пользователя с другими объектами. Прокси дает тебе выбор, грузить данные жадно, сразу или лениво, по мере надобности, прозрачно для работающего с объектом кода.
> $dataC = parent::__construct($data);
Конструктор по задумке ничего не возвращает. Соответственно если ты нарушаешь это разумное правило то приготовься страдать.
Собственно дальше этой ошибки я код смотреть не стал, мне не очень интересно разбираться в хаках и странных особенностях PHP, это надо перечитывать мануал.
Алсо, там еще одна логическая ошибка:
> ($dataA = false)
> $dataA .= "A";
ты определись, что тут у тебя, строка или логические значения
Я канеш хуй знает, т.к. новичок еще, но как насчет того, чтобы передавать функции какое-то значение и в зависимости от этого значения выводить только нужную инфу? Т.е. пишем что-нибудь типа this->getProfile('profile') и он выводит то, что нужно. Если другая инфа нужна, то пишем this->getProfile('full_info').
Это каким образом? Разве сервер позволяет редактировать код? Насколько я знаю он даже php файл не отображает.
но вообще, кто к чему склоняется? Что насчет Laravel? Про Symphony 2 думать не хочу, он слишком монстр, как по мне(хотя
если есть здравые аргументыв его пользу, то можно переубедить). Да, кстати, что еще подскажите по-поводу ORM? Есть ли у
php какие-то годные? Сейчас я занимаюсь тем, что работаю на удаленке допиливая всякую хрень, вроде плагинов для wordpress-а
и думаю поискать работу в офисе, а для неё лучше бы всего какой-нибудь фреймворк знать.
Ты принцип инкапсуляци этим соблюдать не будешь. Почитай об ООП, для каждого поля класса нужен отедльный геттер и сэттер. За то, что ты предложил нужно руки отрывать, потому, что поддерживать такой код сложно
> да. У него уязвимость позволяющая выполнить любу. команду, только под виндой не rm rf а что-то другое надо писать, например format d:
Кажется я понял, значение переменной задаётся через командную строку, а потом функцией exec выполняется системой. Да, это пиздец уязвимость. Я вчера читал про методы post и get, один из шлёт значение через строку браузера, а второй обращается напрямую к пхп, поможет ли второй метод закрыть уязвимость?
Что сложного то? Подписал в каммент какие значения можно писать и все. И не нужно на каждый пук по методу писать. Я понял принцип этого метода инкапсуляции, но я не понимаю, почему ему нужно беспрекословно следовать не задумываясь о том, целесообразно ли это в данном случае.
Поясняю: 1) если сломаешь один геттер, сломаешь весь скрипт, 2) выйдет явно большой кусок кода - с ним неудобно работать, 3) если ты добавишь новое поле, тебе придеться редактировать один геттер, а не просто создать новый, не трогая старые. Вот как-то так. Дядьки не зря придумали, что бы ты придерживался его, когда используешь парадигму ООП и все уже набили шишек до тебя.
>>472033
Инкапсуляция ни при чем равно как и способ получения значения поля. Это и так реализовано, на каждое поле есть свой метод-геттер, если нужно имя я пишу getName(), если нужен телефон - getPhone() и так далее. Задача и проблема в том чтобы иметь способ в зависимости от потребностей получать только те данные которые нужны, не просто выводить, а вообще как бы загружать в объекты которые потом показываются на странице. Вот смотрите на пикчу >>471719
Там один и тот же объект - User встречается 4 раза. Первый - это стенка на которой выводятся вообще все данные пользователя, включая любимые песни и пляски. Остальные три - это вот те мелкие аватарки и имена в разделе friends. Да, они выводятся просто - getName() и все, но при этом они ПРОДОЛЖАЮТ СОДЕРЖАТЬ внутри себя песни и пляски, просто к ним не обращаются. Перфекционисту внутри меня от этого печет - зачем тащить из базы все подряд если оно точно не будет использоваться.
Вот мы и думаем что б тут намутить. Я придумал новый класс minifiedUser у которого есть только getID() и getName(). Это не круто тем что работая с таблицей users мы в разных ситуациях получаем объекты разных классов пусть и похожих по смыслу. ОП придумал все свойства кроме name вынести в специальный хитрый объект profile внутри объекта user. Если его не трогаешь, то внутри ничего и нет, а если запросишь песни и пляски он по-тихому достанет их из базы и выдаст как обычно. Мне это почти нравится, но перфекционизм не сдается - выходит что сущность-User обучается не своему делу - лазить в базу, пусть и через этот прокси-объект, а кроме того залезание в базу будет происходить там где происходит обращение, то есть в html-шаблоне, где согласно mvc-парадигме должны быть только картиночки с котятами.
Не вздумайте страдать такой херней, пацаны.
Ну такой минимализм как здесь, я научился ещё делать когда впервые попробовал ХТМЛ. А как делать тогда хипстоту которая на моих пикчах?
>>471917
Потомок может вызывать методы родителя и обращаться к его полям, если они помечены как protected или public (не private).
class B extends A {
public function doDomething()
{
// взываем методы родителя
$this->callSomething1( );
$a = $this->callSomething2(1, 2,3);
$this->someField = 1;
// вызываем одноименный метод родителя
$x = parent::doSomething( );
}
}
Если ты опишешь что именно тебе нужно сделать, я дам более конкретный совет. Я вижу по тому твоему коду что ты не очень понимаешь как это работает.
>>471991
Спасибо, но это не совсем то.
Анон справшивал про другое, он интересуется как загружать данные из базы соблюдая при этом хорошие практики. Вывести информацию он и так может.
> пишем что-нибудь типа this->getProfile('profile') и он выводит то,
Если функция что-то выводит, то она должна называться не get а print Profile иначе будет путаница. название функции должно отражать что она делает.
Также, вместо строк вроде 'profile' надо использовать константы, например PRINT_PROFILE_SMALL, PRINT_PROFILE_FULL. Ты получишь при этом понятность кода, защиту от опечаток, автодополнение в IDE.
>>472029
Функция exec выполняет переданную ей команду точно так же как если бы ты набрал ее в командной строке (что такое командная строка: https://gist.github.com/codedokode/10539568 ). Команда в твоем случае формируется с использованием полученных от пользователя данных. Следовательно, хитро подставив туда нужные данные мы можем выполнить ту команду которая нам нужна. «Команда» в данном случае значит любая программа, в том числе мы можем запустить программу скачивающую троян и запускающую его.
Ну например, у тебя команда формируется так:
echo $text > COM9
Где $text приходит от пользователя. Что будет если мы передадим в качестве $text
hello | notepad.exe (попробуй вписать это в форму и отправить ее)
Получится команда
echo hello | notepad.exe > COM9
Эта команда запустит (попробует запустить) блокнот. Разумеется мы можем запустить не только блокнот, а например скрипт на VB или скачать любой exe файл командой ftp и запустить его. То есть злоумышленник может выполнить любую команду на твоем компьютере и сделать с ним что угодно.
Я проверил у себя — блокнот запускается.
exec() очень опасная штука. Ты должен 10 раз подумать прежде чем ее использовать. Использовать exec с данными от пользователя — это то же самое что пустить злоумышленника к своему компьютеру и разрешить нажимать любые кнопки.
Ну и в твоем случае: ты должен изучить PHP прежде чем его использовать. Когда люди, не умеющие хорошо программировать берутся за такие вещи, получаются ошибки и уязвимости.
Например китайцы которые делают роутеры, нанимают неквалифицированных программистов для разработки ПО. В итоге мы получаем уязвимости в этих роутерах которыми с радостью пользуются хакеры и американские спецслужбы. Уязвимость в роутере очевидно позволяет перехватывать весь твой трафик со всем твоими паролями и сообщениями которые ты пишешь, а также взламывать банки с твоего IP адреса. Не знаю, приятно тебе сравнение с китайскими программистами но ты делаешь то же самое. Если ты бы завтра начал продавать свой светильник, мы бы получили черный вход на компьютерах всех пользователей кто бы его купил.
А, что с этим делать? Сделай тщательную проверку переменной $text чтобы передать можно было только определенные значения, например 0 или 1, а все остальные значения бы отвергались.
>>471917
Потомок может вызывать методы родителя и обращаться к его полям, если они помечены как protected или public (не private).
class B extends A {
public function doDomething()
{
// взываем методы родителя
$this->callSomething1( );
$a = $this->callSomething2(1, 2,3);
$this->someField = 1;
// вызываем одноименный метод родителя
$x = parent::doSomething( );
}
}
Если ты опишешь что именно тебе нужно сделать, я дам более конкретный совет. Я вижу по тому твоему коду что ты не очень понимаешь как это работает.
>>471991
Спасибо, но это не совсем то.
Анон справшивал про другое, он интересуется как загружать данные из базы соблюдая при этом хорошие практики. Вывести информацию он и так может.
> пишем что-нибудь типа this->getProfile('profile') и он выводит то,
Если функция что-то выводит, то она должна называться не get а print Profile иначе будет путаница. название функции должно отражать что она делает.
Также, вместо строк вроде 'profile' надо использовать константы, например PRINT_PROFILE_SMALL, PRINT_PROFILE_FULL. Ты получишь при этом понятность кода, защиту от опечаток, автодополнение в IDE.
>>472029
Функция exec выполняет переданную ей команду точно так же как если бы ты набрал ее в командной строке (что такое командная строка: https://gist.github.com/codedokode/10539568 ). Команда в твоем случае формируется с использованием полученных от пользователя данных. Следовательно, хитро подставив туда нужные данные мы можем выполнить ту команду которая нам нужна. «Команда» в данном случае значит любая программа, в том числе мы можем запустить программу скачивающую троян и запускающую его.
Ну например, у тебя команда формируется так:
echo $text > COM9
Где $text приходит от пользователя. Что будет если мы передадим в качестве $text
hello | notepad.exe (попробуй вписать это в форму и отправить ее)
Получится команда
echo hello | notepad.exe > COM9
Эта команда запустит (попробует запустить) блокнот. Разумеется мы можем запустить не только блокнот, а например скрипт на VB или скачать любой exe файл командой ftp и запустить его. То есть злоумышленник может выполнить любую команду на твоем компьютере и сделать с ним что угодно.
Я проверил у себя — блокнот запускается.
exec() очень опасная штука. Ты должен 10 раз подумать прежде чем ее использовать. Использовать exec с данными от пользователя — это то же самое что пустить злоумышленника к своему компьютеру и разрешить нажимать любые кнопки.
Ну и в твоем случае: ты должен изучить PHP прежде чем его использовать. Когда люди, не умеющие хорошо программировать берутся за такие вещи, получаются ошибки и уязвимости.
Например китайцы которые делают роутеры, нанимают неквалифицированных программистов для разработки ПО. В итоге мы получаем уязвимости в этих роутерах которыми с радостью пользуются хакеры и американские спецслужбы. Уязвимость в роутере очевидно позволяет перехватывать весь твой трафик со всем твоими паролями и сообщениями которые ты пишешь, а также взламывать банки с твоего IP адреса. Не знаю, приятно тебе сравнение с китайскими программистами но ты делаешь то же самое. Если ты бы завтра начал продавать свой светильник, мы бы получили черный вход на компьютерах всех пользователей кто бы его купил.
А, что с этим делать? Сделай тщательную проверку переменной $text чтобы передать можно было только определенные значения, например 0 или 1, а все остальные значения бы отвергались.
В Симфони 2 много полезного, шаблонизатор Twig, хороший роутер, Doctrine 2. Но начинать лучше с фреймворка попроще вроде Yii2. Если хочешь у нас есть огромное задание (в первом посте) на этот фреймворк, которое ты можешь начать делать, а я буду проверять код и давать советы.
> Да, кстати, что еще подскажите по-поводу ORM? Есть ли у
php какие-то годные?
Doctrine 2 которая может использоваться отдельно, а также входит в Симфони 2. В Yii2 тоже есть средство для загрузки данных из БД под названием ActiveRecord.
>>472033
У анона речь о другом по моему.
>>472034
Данные как в GET так и в POST приходят от пользователя и он может передать что угодно. То что они не видны в адресной строке ничего не значит так как хакер будет пользоваться не браузером, а программой которая позволяет передать что угодно. Из твоего сообщения я вижу что ты не понимаешь протокол HTTP и как работает браузер.
Браузер просто соединяется с сервером (в твоем случае Апач) и послыает ему запрос по протоколу HTTP (протокол это что-то вроде языка для общения программ между собой, там есть свои команды). Разумеется хакер может соединиться с сервером и без браузера, напрямую, и послать запрос любого вида с любыми данными. Он может положить в POST и GET (а также в COOKIE, SERVER и другие глобальные переменные) любые данные.
>>472038
По моему ты не понял о чем речь. То, что ты предлагаешь не лучшая идея, так как тут есть 2 варианта:
— мы не проверяем при обращении загружали ли мы это поле из базы или нет — 100% баг
— мы проверяем и выбрасываем исключение при обращении к незагруженному полю — по мере развития программы эти исключения будут вылетать постоянно, так как у нас в программе получаются «полноценные» и «неполноценные» объекты, почти неотличимые друг от друга.
А мы хотим чтобы все было единообразно. Если функция printUserNameAndAge принимает на вход объект User и допустим пишет его имя и возраст — мы хотим чтобы она работала с абсолютно любым объектом класса User независимо от того какие поля мы в него загрузили. Это реализуется через прокси-объекты с ленивой загрузкой как вариант.
> Я понял принцип этого метода инкапсуляции, но я не понимаю, почему ему нужно беспрекословно следовать
Чтобы код был надежным, не пропускал ошибки не замеченными, чтобы объекты не позволяли себя неправильно использовать. В конечном итоге все это вместе экономит время на понимание кода новичком и на отладку (меньше времени на поиск ошибок, меньше времени на исправление).
В Симфони 2 много полезного, шаблонизатор Twig, хороший роутер, Doctrine 2. Но начинать лучше с фреймворка попроще вроде Yii2. Если хочешь у нас есть огромное задание (в первом посте) на этот фреймворк, которое ты можешь начать делать, а я буду проверять код и давать советы.
> Да, кстати, что еще подскажите по-поводу ORM? Есть ли у
php какие-то годные?
Doctrine 2 которая может использоваться отдельно, а также входит в Симфони 2. В Yii2 тоже есть средство для загрузки данных из БД под названием ActiveRecord.
>>472033
У анона речь о другом по моему.
>>472034
Данные как в GET так и в POST приходят от пользователя и он может передать что угодно. То что они не видны в адресной строке ничего не значит так как хакер будет пользоваться не браузером, а программой которая позволяет передать что угодно. Из твоего сообщения я вижу что ты не понимаешь протокол HTTP и как работает браузер.
Браузер просто соединяется с сервером (в твоем случае Апач) и послыает ему запрос по протоколу HTTP (протокол это что-то вроде языка для общения программ между собой, там есть свои команды). Разумеется хакер может соединиться с сервером и без браузера, напрямую, и послать запрос любого вида с любыми данными. Он может положить в POST и GET (а также в COOKIE, SERVER и другие глобальные переменные) любые данные.
>>472038
По моему ты не понял о чем речь. То, что ты предлагаешь не лучшая идея, так как тут есть 2 варианта:
— мы не проверяем при обращении загружали ли мы это поле из базы или нет — 100% баг
— мы проверяем и выбрасываем исключение при обращении к незагруженному полю — по мере развития программы эти исключения будут вылетать постоянно, так как у нас в программе получаются «полноценные» и «неполноценные» объекты, почти неотличимые друг от друга.
А мы хотим чтобы все было единообразно. Если функция printUserNameAndAge принимает на вход объект User и допустим пишет его имя и возраст — мы хотим чтобы она работала с абсолютно любым объектом класса User независимо от того какие поля мы в него загрузили. Это реализуется через прокси-объекты с ленивой загрузкой как вариант.
> Я понял принцип этого метода инкапсуляции, но я не понимаю, почему ему нужно беспрекословно следовать
Чтобы код был надежным, не пропускал ошибки не замеченными, чтобы объекты не позволяли себя неправильно использовать. В конечном итоге все это вместе экономит время на понимание кода новичком и на отладку (меньше времени на поиск ошибок, меньше времени на исправление).
Я наверное с Yii2 и начну, спасибо. Вообще - да, наверное возьмусь за него.
Мне ActiveRecord очень не нравки, вполне можно к Yii2 прикрутить Doctrine 2, значит все окей.
>>472038
Аноны, эти дискуссии лучше подкреплять кусочками кода иначе окажется что вы каждый разные вещи себе представляете. Напишите сначала кусок кода на несколько строк, а потом аргументируйте, а не рассуждайте абстрактно.
Ну и инкапсуляция нужна конечно. Чем больше приложение тем больше она там нужна.
>>472075
Можно я еще попродвигаю свою идею?
> Вот смотрите на пикчу
Забыл пикчу
> Там один и тот же объект - User встречается 4 раза.
Что насчет идеи про разделение пользователя на основные данные и дополнительные в объекте Profile? Мне кажется тебе как раз бы подошло, тебе бы почти везде нужен был бы только сам User без Profile.
> Да, они выводятся просто - getName() и все, но при этом они ПРОДОЛЖАЮТ СОДЕРЖАТЬ внутри себя песни и пляски, просто к ним не обращаются. Перфекционисту внутри меня от этого печет - зачем тащить из базы все подряд если оно точно не будет использоваться.
Я и предлагаю вынести все лишние поля в Profile спрятанный за прокси, и также вынести другие связанные сущности из других таблиц (например посты на стенке тоже спрятать за прокси и по умолчанию не грузить).
То есть мы разбиваем данные на отдельные объекты и при вызове маппера определяем какие сущности грузятся сразу жадно, какие лениво. Прокси позволяет делать это прозрачно для кода.
Ну например мы грузим User сразу, а Profile, PostsCollection, FriendsCollection, SongsCollection лениво.
И мне кажется разделение надо делать именно на уровне объектов, а не полей. Во-первых, полей много, десятки-сотни и мы можем получить десятки-сотни запросов на подзагрузку. Во-вторых, ну что это за объект в котором половины полей нет? Это ведь надо где-то хранить информацию, есть они или нет, и проверять, или прятать каждое поле за прокси.
Ну и если надо то Profile можно еще разбить, хотя я совневаюсь что есть смысл.
Кстати, было бы круто если в доктрине появилась возможность так разбивать объекты. Там лениво можно подгружать только сущности из других таблиц и нельзя (вроде бы нельзя, не уверен, надо проверить, там что-то похожее было) одну таблицу замапить на несколько классов.
> Мне это почти нравится, но перфекционизм не сдается - выходит что сущность-User обучается не своему делу - лазить в базу, пусть и через этот прокси-объект,
Тут нет такой проблемы так как User не знает откуда прокси берет данные. Например мы можем заложить в прокси ссылку на маппер, а можем сразу заложить данные при жадной загрузке. А может он их как-нибудь из кеша грузит. Прокси создается и внедряется в юзера внутри маппера и только маппер знает что в нем содержится и источник данных. User этого не знает, единственное что он знает что надо вызвать getValue или setValue для доступа к этим данным.
То есть прокси это что-то вроде коробки которую надо открыть чтобы полуичть данные.
Прокси приудмал не я, они как раз в доктрине и других ORM используются.
> а кроме того залезание в базу будет происходить там где происходит обращение, то есть в html-шаблоне,
Это верно, потому мы чуть выгибаем парадигму MVC и говорим что шаблон не должен явно лезть в базу, он должен выводить те данные что ему передали. А если при открытии волшебного ящика он достает значения из базы то это ок.
То есть в шаблоне
$mapper->getUser это не ок
$user->getName это ок
Если не подогнуть парадигму, то мы не можем использовать ленивую загрузку и должны все прописывать явно + делать исключения если данные не загружены и как следствие по мере развития проекта исправлять эти исключения и прописывать загрузку дополнительных полей. То есть становится неудобно.
При этом идея MVC в разделении (вообще, многие проблемы в программировании решаются принципом разделяй и властвуй) кода который работает с базой и который выводит данные. Разделение есть? Есть, маппер работает с базой, шаблон выводит данные.
Ну и если у тебя есть идеи лучше, давай, напиши. Тогда можно сравнить плюсы и минусы. Мне главным преимуществом видится это:
— у нас нет дублирования нескольких объектов для 1 сущности
— допустим вчера функции были нужны поля A, B, C из класса User, а сегодня D, E, F. Функция будет продолжать работать как ни в чем не бывало, разве что может быть вызывая дополнительные запросы в базу. Но это лучше чем вернуть неправильные данные или упасть с исключением что поля не загружены, верно?
— можно поменять ленивую загрузку на жадную, не трогая ни одной строчки кода, кроме вызова маппера. Ни одной строчки! Мы можем играть с оптимизациями как хотим, не меняя код. Если какой-то подход не позволяет легко играть с оптимизацией то скорее всего мы и не будем этим заниматься, так как некогда.
— теоретически мы бы могли даже сделать автоматическое определение что надо загружать лениво,а что жадно. Запускаем программу 1000 раз (при этом записываем число и типы запросов) и смотрим к каким скрытым за прокси сущностям идет обращение, те которые нужны более чем в 80% случаев (или те которые генерируют больше N запросов в базу) грузим в следующий раз жадно. Вот мы и получили автоматический оптимизатор кода. Разве это не круто? Кстати, я про такие пока не слышал.
Ну и по сути это развитие твоей идеи про miniUser. В ней мне не понравилось то, что мы можем получить 2 объекта (User и MiniUser) ссылающихся на 1 пользователя и я просто исправил этот недостаток.
>>472038
Аноны, эти дискуссии лучше подкреплять кусочками кода иначе окажется что вы каждый разные вещи себе представляете. Напишите сначала кусок кода на несколько строк, а потом аргументируйте, а не рассуждайте абстрактно.
Ну и инкапсуляция нужна конечно. Чем больше приложение тем больше она там нужна.
>>472075
Можно я еще попродвигаю свою идею?
> Вот смотрите на пикчу
Забыл пикчу
> Там один и тот же объект - User встречается 4 раза.
Что насчет идеи про разделение пользователя на основные данные и дополнительные в объекте Profile? Мне кажется тебе как раз бы подошло, тебе бы почти везде нужен был бы только сам User без Profile.
> Да, они выводятся просто - getName() и все, но при этом они ПРОДОЛЖАЮТ СОДЕРЖАТЬ внутри себя песни и пляски, просто к ним не обращаются. Перфекционисту внутри меня от этого печет - зачем тащить из базы все подряд если оно точно не будет использоваться.
Я и предлагаю вынести все лишние поля в Profile спрятанный за прокси, и также вынести другие связанные сущности из других таблиц (например посты на стенке тоже спрятать за прокси и по умолчанию не грузить).
То есть мы разбиваем данные на отдельные объекты и при вызове маппера определяем какие сущности грузятся сразу жадно, какие лениво. Прокси позволяет делать это прозрачно для кода.
Ну например мы грузим User сразу, а Profile, PostsCollection, FriendsCollection, SongsCollection лениво.
И мне кажется разделение надо делать именно на уровне объектов, а не полей. Во-первых, полей много, десятки-сотни и мы можем получить десятки-сотни запросов на подзагрузку. Во-вторых, ну что это за объект в котором половины полей нет? Это ведь надо где-то хранить информацию, есть они или нет, и проверять, или прятать каждое поле за прокси.
Ну и если надо то Profile можно еще разбить, хотя я совневаюсь что есть смысл.
Кстати, было бы круто если в доктрине появилась возможность так разбивать объекты. Там лениво можно подгружать только сущности из других таблиц и нельзя (вроде бы нельзя, не уверен, надо проверить, там что-то похожее было) одну таблицу замапить на несколько классов.
> Мне это почти нравится, но перфекционизм не сдается - выходит что сущность-User обучается не своему делу - лазить в базу, пусть и через этот прокси-объект,
Тут нет такой проблемы так как User не знает откуда прокси берет данные. Например мы можем заложить в прокси ссылку на маппер, а можем сразу заложить данные при жадной загрузке. А может он их как-нибудь из кеша грузит. Прокси создается и внедряется в юзера внутри маппера и только маппер знает что в нем содержится и источник данных. User этого не знает, единственное что он знает что надо вызвать getValue или setValue для доступа к этим данным.
То есть прокси это что-то вроде коробки которую надо открыть чтобы полуичть данные.
Прокси приудмал не я, они как раз в доктрине и других ORM используются.
> а кроме того залезание в базу будет происходить там где происходит обращение, то есть в html-шаблоне,
Это верно, потому мы чуть выгибаем парадигму MVC и говорим что шаблон не должен явно лезть в базу, он должен выводить те данные что ему передали. А если при открытии волшебного ящика он достает значения из базы то это ок.
То есть в шаблоне
$mapper->getUser это не ок
$user->getName это ок
Если не подогнуть парадигму, то мы не можем использовать ленивую загрузку и должны все прописывать явно + делать исключения если данные не загружены и как следствие по мере развития проекта исправлять эти исключения и прописывать загрузку дополнительных полей. То есть становится неудобно.
При этом идея MVC в разделении (вообще, многие проблемы в программировании решаются принципом разделяй и властвуй) кода который работает с базой и который выводит данные. Разделение есть? Есть, маппер работает с базой, шаблон выводит данные.
Ну и если у тебя есть идеи лучше, давай, напиши. Тогда можно сравнить плюсы и минусы. Мне главным преимуществом видится это:
— у нас нет дублирования нескольких объектов для 1 сущности
— допустим вчера функции были нужны поля A, B, C из класса User, а сегодня D, E, F. Функция будет продолжать работать как ни в чем не бывало, разве что может быть вызывая дополнительные запросы в базу. Но это лучше чем вернуть неправильные данные или упасть с исключением что поля не загружены, верно?
— можно поменять ленивую загрузку на жадную, не трогая ни одной строчки кода, кроме вызова маппера. Ни одной строчки! Мы можем играть с оптимизациями как хотим, не меняя код. Если какой-то подход не позволяет легко играть с оптимизацией то скорее всего мы и не будем этим заниматься, так как некогда.
— теоретически мы бы могли даже сделать автоматическое определение что надо загружать лениво,а что жадно. Запускаем программу 1000 раз (при этом записываем число и типы запросов) и смотрим к каким скрытым за прокси сущностям идет обращение, те которые нужны более чем в 80% случаев (или те которые генерируют больше N запросов в базу) грузим в следующий раз жадно. Вот мы и получили автоматический оптимизатор кода. Разве это не круто? Кстати, я про такие пока не слышал.
Ну и по сути это развитие твоей идеи про miniUser. В ней мне не понравилось то, что мы можем получить 2 объекта (User и MiniUser) ссылающихся на 1 пользователя и я просто исправил этот недостаток.
Ну и смотри анон, перфекционизм до добра не доводит (хотя начинающему он полезен). Как бы мы тут не загубили твой проект.
>>472120
Ищи туториалы по рисованию иллюстраций (это вроде называется иллюстрации, а не дизайн) например по таким словам:
minimalist illustration
how to draw minimalist illustration
>>472182
> вполне можно к Yii2 прикрутить Doctrine 2
Это не лучшая идея так как он там не родной, разные полезные плагины рассчитаны именно на AR. То есть ты либо останешься без плагинов и расширений, либо тебе придется написать слой абстракции, маскирующий твои модели под AR.
C User и MiniUser еще такая проблема: что должны возвращать методы? Допустим у нас есть метод получить автора поста:
$pos->getAuthor()
Что он вернет? User? MiniUser? нам надо делать 2 метода:
$post->getAuthorMini()
$post->getAuthorFull()
И так для каждой связи, каждого метода который возвращает пользователя. Либо же нам надо сделать конвертор который превращает один объект в другой. То есть если ты перфекционист то должен интуитивно понимать что 2 объекта для одной сущности это плохо. Такие вещи должны делаться через оборачивание одного объекта в другой (User содержит внутри MiniUser или наоборот), а не через дублирование данных.
Но сейчас я подумал, что это скорее всего делается не из благих побуждений, а чтобы тесты вроде speedtest показывали более хороший результат. Там ведь скачивается небольшой объем данных, и буст можно рассчитать таким образом, чтобы тест укладывался в него по времени. Вдобавок speedtest сохраняет результаты пользователей и позволяет просматривать среднюю скорость по провайдеру. Провайдеры с бустом очевидно будут выше в этом списке и получат больше клиентов.
А еще я подумал, если есть какой-то способ определять сервера speedtest то можно до них выделить более широкий канал. Или как-то обманывать этот тест скармливая ему данные из локального источника на большой скорости. У меня ощущение что мой провайдер что-то такое делает, потому что там почти везде (кроме далеких городов с большим пингом вроде Токио или СФ) выдается цифра под 100 мбит.
Такие дела.
Ну я то думал, это только ради пользователей, а не тестов делается!
Справедливости ради, эта скорость выше определенного предела все равно ни на что не влияет, так как сайт не может отдавать контент быстрее определенного предела, к тому же самая популярная соцсеть в России у нас подсоединена напрямую без ограничений по скорости.
> самая популярная соцсеть в России у нас подсоединена напрямую без ограничений по скорости
шта?
Крупный провайдер может напрямую подключиться к вконтакте, соответственно трафик там по моему бесплатный и он может его предоставлять без ограничений скорости:
https://vk.com/help.php?page=peering
http://vk.com/dataix_peering
Аналогично у яндекса что-то было.
Ну а гугл и ютуб вообще свои сервера кеширующие ставит к провайдеру. У меня иногда некоторые видео качаются с сервера из локальной сети.
Ебать ты мамкин конспиролог
Ебать, тред ведет ещё оп-долбоеб с синдромом вахтера? Сажи на хуй, сажи блядь.
>Сделай тщательную проверку переменной $text чтобы передать можно было только определенные значения, например 0 или 1, а все остальные значения бы отвергались.
Так норм будет?
$table_item = $wpdb->prefix.bulletin;
$posts = $wpdb->get_results("SELECT*FROM $table_item");
foreach ($posts as $post) {
echo $post->title;
echo $post->description;
....
}
как мне сделать ссылку $post->title; на станицу с единичным постом, the_permalink() не помогает, стандартным образом в таком случае
<a href="single.php?id=<?= $post->id;?>"> echo $post->title;</a>
Тоже не получается, что делать? Я так понял стандартный цикл вордпресс работает только с таблицей постс?
>Забыл пикчу
Не забыл, я там прост номер поста дал в котором она была. Вот посвежей.
В общем, мыслей всяких много, пишу код как стахановец, поэтому вкратце:
1. вариант с мини-юзерами сделал, все работает, подводных камней все еще не вижу.
2. вариант с Profile и ленивой загрузкой... сложновато, но я не сдаюсь. Кое-что написалось, а запара вот где. Пойдем от обратного. Мы где-то во вью впервые вызвали $user->profile->getCity(). Город у нас изначально не загружен. Объект Profile смотрит во внутреннюю переменную $isDataLoaded (по умолчанию false) понимает что он пустой и запускает внутренний метод loadDataFromDb() который призван достать вообще все данные(не только город) и полностью заполнить профиль, после чего поменять $isDataLoaded на true для будущих обращений и вернуть наконец город. А вот внутри этого метода, добывающего данные, начинается какое-то хождение по кругу, ведь методы маппера возвращают экземпляры User-а, а нам нужно что-то типа ассоциативного массива который можно раскидать по полям Профайла. То есть нам там класс DB больше помог бы, а не маппер. Либо мапперу надо добавить метод getDataForProfile(), но это ведь уже не маппер получится
3. Есть еще другие идеи, но хочу добить твою.
is_numeric это недостаточно хорошо. is_numeric пропустит странные штуки вроде 2.3e-04 и может быть что-то еще.
Сделай нормальную проверку, например регулярным выражением что это именно число от 0 до 255 (или до 999 если это проще).
Тут лучше быть параноиком.
Хотя, нет. Я перечитал твой код и тут опять дыра. Код опять неправильный из-за неправильного использования &
Сейчас ты проверяешь непонятно что, я думаю что если в $va1 передать 0, а в остальных переменных команду то она пройдет.
Я же писал выше что ты сначала должен иучить PHP:
is_numeric ($va1 & $va2)
сначала преобразует значения из va1/2 к числу и сделает над ними побитовое И. Скорее всего получится число 0, которое вполне устроит функцию is_numeric и таким образом мы можем передать что угодно.
В любом случае операция & вернет число и это значит что любые значения va1-3 пройдут проверку (ты ведь не тестировал свой код и не пробовал подставить туда что-то левое, верно?)
Такие опасные функции как exec должны использовать люди, понимающие, что они делают, а не копипастящие куски кода из инернета. Безопасный код так не пишется.
Также, если ты выдал заголовок Location то браузер не отобразит текст ошибки.
Попробуй изучить какие есть функции для получения ссылки Не на текущий, а на произвольный пост. То есть тебе нужна функция вида
get_something_link($post)
в которую явно передается пост. Функция the_permalink, как я понимаю, завязана на глобальные переменные которые выставляются стандартными функциями перебора постов и тебе она не подойдет.
также ты можешь открыть ее код (рекомендуется) и посмотреть откуда она берет ссылку. Может быть там и найдешь ответ. Может быть нет.
>>472353
> Мы где-то во вью впервые вызвали $user->profile->getCity()
Должно быть $user->getProfile()->get...
> А вот внутри этого метода, добывающего данные, начинается какое-то хождение по кругу, ведь методы маппера возвращают экземпляры User-а, а нам нужно что-то типа ассоциативного массива который можно раскидать по полям Профайла.
Это ты неправильно сделал маппер. Тебе надо сделать отдельные методы для получения отдельных компонент, по типу:
$this->profile = $mapper->loadProfileForUser(...);
$this->city = $mapper->loadCityForSomething(...);
ну и лучше это сделать не явно вписывая код загрузки и заполнения в User, а используя прокси, которые создаются в маппере. Маппер лучше знает какие данные есть, а какие надо загрузить потом. А у тебя User с базой работает, а это не соответствует идеям data mapper и код делает запутаннее с большей вероятностью сломаться.
>ну и лучше это сделать не явно вписывая код загрузки и заполнения в User, а используя прокси, которые создаются в маппере.
Так он не в User вписан, а в Profile, я думал Profile это и есть прокси. Ну вот что-то такое
//Создание в маппере
$user = new User;
$user ->setName('Alex'); //обычное поле
$profile = new Profile;
$user ->setProfile($profile); //пустой профайл
//применение во view
<span>Phone: <?=$user->getProfile()->phone; ?></span>
<span>Skype: <?=$user->getProfile()->skype; ?></span>
(магический гет чтоб не писать множество getSomething)
Прокси это отдельный объект который может либо содержать внутри модель, либо содержать ссылку на маппер и загружать модель при первом обращении.
Прокси создается и вставляется в User в маппере. Он нужен чтобы модель (User) не думала откуда брать данные. Соответственно код в User будет выглядеть так:
publuc finction getProfile()
{
return $this->profileProxy->getValue( );
}
Его лучше сделать отдельным классом чтобы в модель не засовывать логику ленивой загрузки (которой там не место). Более того, моделей много и тебе придется копипастить эту логику, что плохо.
Вообще ты можешь вместо getProfile() как метода писать
$user->profile->skype
Если твой магический _ _ get умеет сам находить и вызывать геттер.
И еще смотри какие у тебя недостатки:
— в модель вписана логика ленивой загрузки, которой там не место. Модель должна знать, что данные загружаются лениво и что любое поле может отсутствовать и это значит что тебе придется любой доступ к полю заворачивать в if ($dataIsLoaded) либо надеяться на магический геттер.
— магический геттер никогда не вызвыает методы вроде getSomething, значит мы не можем перехватить обращение к полю и добавить туда какие-то проверки или еще что-то. Тем более что ты создаешь публичные свойства. Это впрочем можно потом исправить.
— ты создаешь маппер внутри модели что вообще неправильно. Где этот маппер возьмет ссылку на соединение с БД например? И сколько у тебя в итоге мапперов будет в программе? Это верный путь к бардаку. Почитай-ка урок (он не очень простой, но суть в том, что ты не должен создавать маппер внутри модели, а должен использовать ранее созданный экземпляр) https://gist.github.com/codedokode/e1d31a31b37d5f635057
Ну и вспомни аналогию с краном и контейнером. Маппер создает объекты модели, а не наоборот.
Соответственно реализация с прокси делается примерно так:
— либо (что проще) маппер при создании User засовывает в него прокси для Profile, но сам Profile пока не создает. Так как маппер создает прокси, то он может передать ему ссылку на себя и нужные для ленивой загрузки параметры
Этот вариант требует кооперации со стороны User: модель должна знать что профиль хранится внутри прокси и должна уметь вызывать у него getValue()
— либо (что сложнее) прокси может быть наследником Profile (ProfileProxy), перехватывать обращение к любому методу или полю и перед вызовом метода у Profile загружать данные из БД. Создается такой прокси тоже в маппере. Преимущество в том, что от самой модели Profile не требуется поддержки и ей не надо знать откуда берутся данные (в предыдущем способе модель содержит прокси внутри и знает о его существовании, тут нет). Минус в том что это очень сложно, так как надо генерировать прокси-классы наследники для каждой модели. Доктрина так делает, но это сложно в реализации.
И еще смотри какие у тебя недостатки:
— в модель вписана логика ленивой загрузки, которой там не место. Модель должна знать, что данные загружаются лениво и что любое поле может отсутствовать и это значит что тебе придется любой доступ к полю заворачивать в if ($dataIsLoaded) либо надеяться на магический геттер.
— магический геттер никогда не вызвыает методы вроде getSomething, значит мы не можем перехватить обращение к полю и добавить туда какие-то проверки или еще что-то. Тем более что ты создаешь публичные свойства. Это впрочем можно потом исправить.
— ты создаешь маппер внутри модели что вообще неправильно. Где этот маппер возьмет ссылку на соединение с БД например? И сколько у тебя в итоге мапперов будет в программе? Это верный путь к бардаку. Почитай-ка урок (он не очень простой, но суть в том, что ты не должен создавать маппер внутри модели, а должен использовать ранее созданный экземпляр) https://gist.github.com/codedokode/e1d31a31b37d5f635057
Ну и вспомни аналогию с краном и контейнером. Маппер создает объекты модели, а не наоборот.
Соответственно реализация с прокси делается примерно так:
— либо (что проще) маппер при создании User засовывает в него прокси для Profile, но сам Profile пока не создает. Так как маппер создает прокси, то он может передать ему ссылку на себя и нужные для ленивой загрузки параметры
Этот вариант требует кооперации со стороны User: модель должна знать что профиль хранится внутри прокси и должна уметь вызывать у него getValue()
— либо (что сложнее) прокси может быть наследником Profile (ProfileProxy), перехватывать обращение к любому методу или полю и перед вызовом метода у Profile загружать данные из БД. Создается такой прокси тоже в маппере. Преимущество в том, что от самой модели Profile не требуется поддержки и ей не надо знать откуда берутся данные (в предыдущем способе модель содержит прокси внутри и знает о его существовании, тут нет). Минус в том что это очень сложно, так как надо генерировать прокси-классы наследники для каждой модели. Доктрина так делает, но это сложно в реализации.
И опять же, метод loadDataFromDB должен быть в маппере, а ты переносишь работу с базой в модель. В модели может быть разве что что-нибудь для установки свойств из массива.
И еще там, где ты проставляешь свойства из массива, надо делать проверку что такое свойство на самом деле есть иначе ошибки в именах полей или их количестве будут молча игнорироваться, что плохо.
Может лучше сделать только часть задания, например возможность создавать и проходить тесты без авторизации и регистрации? Так да, оно большое.
Может, но думаю, что все-таки начну с сайта с файликами потому, что хочу уметь с ними работать, к тому же, туда есть пара идей в будущеем, как улучшить
>>472474
>>472478
Анон, кажется все получилось.
1. Загрузили жадно: профиль заполнен, маппер не передается, isDataLoaded = правда.
2. Загрузили лениво: профиль пуст, но зато есть маппер, isDataLoaded = неправда.
3. Запросили телефон: профиль заполнен, isDataLoaded = правда, обращайся дальше к чему хочешь. маппер, наверное, и удалить можно.
Крутой челендж для меня.
ГОСПОДИ ИИСУСЕ Я КОМАНДУЮ ПАРАДОМ
Это хорошо, что все работает, но у тебя есть один недочет (имхо) в реализации. Недочет из-за того что ты смешал код ленивой загрузки и код модели в одном классе.
Допустим мы сделаем в классе Profile (который у тебя называется Proxy) метод hasSkype:
public function hasSkype()
{
return !empty($this->skype);
}
При такой реализации обращение к этому методу изнутри не вызовет загрузки данных и выдаст неверный результат:
var_dump($user->profile->hasSkype( )); // упс
В твоей реализации единственный способ избежать бага это дописать явно вызов кода ленивой загрузки:
public function hasSkype()
{
$this->loadDataIfNotLoaded( );
return !empty($this->skype);
}
И такой код придется писать в начале каждого метода. Потому что в твеой реализации модель должна помнить про то что поля могут остутсововать и делать проверку. Это неудобно и засоряет код.
Доктрина решает эту проблему тем, что она генерирует для каждого класса обертку с такими вызовами. То есть для твоего Profile она бы сгенеировала такой класс:
class ProfileProxy extends Profile
{
private $isDataLoaded = false;
private function loadDataIfNotLoaded() { .... }
// переопределяем метод чтобы перехватить обращение к нему
public function hasSkype()
{
$this->loadDataIfNotLoaded( );
return parent::hasSkype( ); // теперь спокойно вызываем исходный метод
}
}
Этот подход хорош тем что твоему классу не надо знать ничего про ленивую загузку: прокси берет это на себя при первом обращении к любому методу класса. Но он сложен в реализации, так как надо уметь генерировать код (есть готовые библиотеки для этого, которые позволяют такие образом создать прокси-класс для любого класса).
Потому я тебе предлагаю другой подход, разделить отдельно класс Profile (в котором хранятся данные) и класс Proxy (который загружает из базы объект Profile при первом обращении).
Прокси создается в маппере и есть 2 варианта создания: с загруженными данными либо без данных, но со ссылкой на маппер для последующей ленивой загузки.
Иначе тебе придется копипастить код ленивой загрузки в каждую модель. Ну например завтра ты захочешь добавить к пользователю лениво загружаемую FriendsList и тебе придется туда копипастить код ленивой загрузки.
Ну и я чувствую что мы такими темпами через месяц сделаем свой клон доктрины.
Это хорошо, что все работает, но у тебя есть один недочет (имхо) в реализации. Недочет из-за того что ты смешал код ленивой загрузки и код модели в одном классе.
Допустим мы сделаем в классе Profile (который у тебя называется Proxy) метод hasSkype:
public function hasSkype()
{
return !empty($this->skype);
}
При такой реализации обращение к этому методу изнутри не вызовет загрузки данных и выдаст неверный результат:
var_dump($user->profile->hasSkype( )); // упс
В твоей реализации единственный способ избежать бага это дописать явно вызов кода ленивой загрузки:
public function hasSkype()
{
$this->loadDataIfNotLoaded( );
return !empty($this->skype);
}
И такой код придется писать в начале каждого метода. Потому что в твеой реализации модель должна помнить про то что поля могут остутсововать и делать проверку. Это неудобно и засоряет код.
Доктрина решает эту проблему тем, что она генерирует для каждого класса обертку с такими вызовами. То есть для твоего Profile она бы сгенеировала такой класс:
class ProfileProxy extends Profile
{
private $isDataLoaded = false;
private function loadDataIfNotLoaded() { .... }
// переопределяем метод чтобы перехватить обращение к нему
public function hasSkype()
{
$this->loadDataIfNotLoaded( );
return parent::hasSkype( ); // теперь спокойно вызываем исходный метод
}
}
Этот подход хорош тем что твоему классу не надо знать ничего про ленивую загузку: прокси берет это на себя при первом обращении к любому методу класса. Но он сложен в реализации, так как надо уметь генерировать код (есть готовые библиотеки для этого, которые позволяют такие образом создать прокси-класс для любого класса).
Потому я тебе предлагаю другой подход, разделить отдельно класс Profile (в котором хранятся данные) и класс Proxy (который загружает из базы объект Profile при первом обращении).
Прокси создается в маппере и есть 2 варианта создания: с загруженными данными либо без данных, но со ссылкой на маппер для последующей ленивой загузки.
Иначе тебе придется копипастить код ленивой загрузки в каждую модель. Ну например завтра ты захочешь добавить к пользователю лениво загружаемую FriendsList и тебе придется туда копипастить код ленивой загрузки.
Ну и я чувствую что мы такими темпами через месяц сделаем свой клон доктрины.
задания в первом посте смотрел, неинтересно? сделать файлообменник или сайт для проведения тестов? Это правда учебные задания.
Ну и если тебе надо еще, у меня есть готовые идеи (подробного описания идей нет):
— BookHub, сервис для желающих обмениваться книгами
— TweetAlarm, одностраничная напоминалка которая в указанный день напомнит через твиттер что ты должен сделать
— music tv — выбираешь интересующий тебя жанр или канал или исполнителя, и она играет музыку (видео с ютуба или другого источника). Система обучается и подстраивается под твои вкусы, если ты например пропускаешь треки или наоборот плюсуешь. Можно создавать и публиковать плейлисты с минимальными затратами усилий, например «сохрани все что я сегодня слушал и не пропускал».
— Редактор для совместного написания кода (wtf?)
— living pictures — делает из фото анимацию, например добавляя какой-нибудь эффект поверх или что-нибудь еще. Ты скажешь что это никому не нужно а я скажу что инстаграм продался за огромные деньги, а ведь в нем нет анимации, а у нас будет.
— игра в ассоциации (не помню что это значит)
— torrent monitor — добавляешь сериал, который тебя интересует, выбираешь качество/тип/релизера и он автоматически отслеживает появление новых серий и выводит на одной страничке
— heapp — сервис, позволяющий создать свою «свалку» заметок, файлов, ссылок (клон evernote?)
— Сервис где можно писать разные мелодии, выкладывать их, смешивать, форкать и переделывать (то есть что-то вроде гитхаба для музыки)
Блядь у меня бомбит. Пишел на первую свою работу, ща думаю охуенно покодю, применю знания, наберусь нового, короче заебца.
И вот выясняется, главный кодер в конторе - хейтер ооп, функциональщик. Ооп ненужно, то ненужно, Се не нужно. Нужно создать таблицу? Нормализация не нужна, ибо "абстрактная хуйня избыточная, в нашем конкретном случае конкретно для этой таблицы достаточно 2й формы или даже 1". И такое повсюду. Бляд у меня люто бомбит от этого а спорить не могу, ибо нуб и мнение свое не смогу перед авторитетом отстоять.
Задвигал мне этот чувак, что это слишком "жирно" - позволять себе кодить идеально или даже оченьорошо
Мне валить срочно или такое норм?
Иди в аутсорсинг каким-нибудь джавистом. Там всё строится на распиле денег на разработку за счёт шизофреников, упрямо дрочащих на инструменты и методологии, позволяющие решать задачу в 1000 раз дольше, чем можно было.
Но как же душа? Чувство прекрасного? Моральное удовлетворение в конце концов?
Неприятно от опознавания того факта что проделана работа и проделана некачественно и можно было бы лучше. Разве не так?
Ну насколько я понимаю, если мелкие сайтики клепать в промышленных массштабах, то ООП тебя только тормозить будет и по сути нахуй не нужно. Понятное дело, что если проект крупный и будет поддерживаться множеством людей, то без ООП никуда.
Люди разные бывают и запросы у клиентов тоже разные. Есть те, которые по сто лет вылизывают код, а есть те, которым важнее КПД и они могут сделать работающий ( пусть и не идеально) сайт в разы быстрее. А если особой разницы нет, то многие клиенты попросту не будут ждать, пока первая категория там свой код вылижет.
Если ты все-таки к первой категории принадлежишь, то можешь поискать другую контору, где будут работать люди с похожими взглядами, т.к. очевидно, что текущий главный кодер явно не из них.
Психически здоровый человек не может любить программирование, увлекаться им, получать какое-то там эстетическое удовольствие от кода. Сами эти словосочетания некорректны, "не тайпчекаются". В программировании нет самоценности, предназначение кода лишь в том, чтобы решать какую-то внешнюю задачу, достигать какие-то внешние цели. Это чисто-инфраструктурная деятельность и рационально-мыслящий человек будет искать любые пути, чтобы минимизировать её необходимость для достижения своих целей - будь то домашняя автоматизация или работа ради денег.
Ну насчет ООП не нужен, это как минимум неправильно. ООП не вчера появился, он стал модным в конце 80-х и большинство проектов с тех пор пишется с его использованием (в мире PHP раньше действительно с ООП было плохо, но если брать новые проекты то они тоже все на ООП). ООП позволяет сделать код более надежным и понятным, разбивая его на изолированные классы.
Другой вопрос, что человек привык писать так, как он пишет, и ты вряд ли его переубедишь. Ну и спорить вообще довольно непродуктивно, так как тебя нанимали писать код а не устраивать холивары.
> это слишком "жирно" - позволять себе кодить идеально или даже оченьорошо
Мне например чтобы использовать ООП (или нормализацию) не надо прикладывать какие-то дополнительные усилия, это как-то само собой получается, и соответственно отказом от ООП я не сэкономлю время, а скорее даже потрачу больше на выяснение сколько полей вот в этом массиве получаемом из той функции.
ООП это часть языка и отказываться от него это то же самое что например отказываться от циклов или функций — а давайте все писать сплошной стеной кода, чтобы не тратить времени? Это называется write only код: код который потом никто не сможет прочесть.
Такой подход может сработать если вы делаете простые однотипные сайтики, но мое мнение:
1) времени отказом от ООП не сэкономить (при условии что разработчики его понимают и правильно используют, а не городят сложные абстракции)
2) поддерживать плохо написанный код тяжелее. за ваш стиль кодирования придется расплачиваться потраченным временем тому, кто потом будет поддерживать и править сайт.
Аналогично отказ от создания нормальной схемы БД не сэкономит особо времени, а усложнит поддержку и доработку.
Вы еще фреймворки и гит небось не используете?
Ну и для тебя лично это значит что твой уровень знаний и навыков не вырастет. Если ты потом придешь в другую компанию, где например используют ООП/тестирование/CI и другие хорошие практики, и напишешь на собеседовании им код на функциях и массивах, это вряд ли произведет хорошее впечатление.
Смотри сам, если у тебя нет альтернативы получше то может быть придется потерпеть. Но долго задерживаться я не советую. Это явно место не для тебя, а для тех кто не хочет развиваться. выгоднее работать там, где ты можешь научиться и поднять свой уровень.
> хейтер ооп, функциональщик.
Функциональщик это хаскеллист. А это процедурщик.
>>472772
И давай я угадаю, с явой ты знаком понаслышке, больших проектов не делал ни на ней ни на других языках, пишешь 20-строчные скриптики в одно лицо на каком-нибудь Питоне и думаешь что весь остальной софт не сложнее?
> позволяющие решать задачу в 1000 раз дольше, чем можно было.
Бизнес вряд ли захотел бы платить в 1000 раз больше если можно заплатить в 1000 раз меньше. Может быть ты придешь и сделаешь ту же работу например в сто раз быстрее чем куча бездельников с архитекторами, коде интегрейшен, системами контроля версий, объектно-реляционными мапперами, многослойной архитектурой и прочим ненужным мусором который придумывают неучи-теоретики вроде Фаулера?
Ну насчет ООП не нужен, это как минимум неправильно. ООП не вчера появился, он стал модным в конце 80-х и большинство проектов с тех пор пишется с его использованием (в мире PHP раньше действительно с ООП было плохо, но если брать новые проекты то они тоже все на ООП). ООП позволяет сделать код более надежным и понятным, разбивая его на изолированные классы.
Другой вопрос, что человек привык писать так, как он пишет, и ты вряд ли его переубедишь. Ну и спорить вообще довольно непродуктивно, так как тебя нанимали писать код а не устраивать холивары.
> это слишком "жирно" - позволять себе кодить идеально или даже оченьорошо
Мне например чтобы использовать ООП (или нормализацию) не надо прикладывать какие-то дополнительные усилия, это как-то само собой получается, и соответственно отказом от ООП я не сэкономлю время, а скорее даже потрачу больше на выяснение сколько полей вот в этом массиве получаемом из той функции.
ООП это часть языка и отказываться от него это то же самое что например отказываться от циклов или функций — а давайте все писать сплошной стеной кода, чтобы не тратить времени? Это называется write only код: код который потом никто не сможет прочесть.
Такой подход может сработать если вы делаете простые однотипные сайтики, но мое мнение:
1) времени отказом от ООП не сэкономить (при условии что разработчики его понимают и правильно используют, а не городят сложные абстракции)
2) поддерживать плохо написанный код тяжелее. за ваш стиль кодирования придется расплачиваться потраченным временем тому, кто потом будет поддерживать и править сайт.
Аналогично отказ от создания нормальной схемы БД не сэкономит особо времени, а усложнит поддержку и доработку.
Вы еще фреймворки и гит небось не используете?
Ну и для тебя лично это значит что твой уровень знаний и навыков не вырастет. Если ты потом придешь в другую компанию, где например используют ООП/тестирование/CI и другие хорошие практики, и напишешь на собеседовании им код на функциях и массивах, это вряд ли произведет хорошее впечатление.
Смотри сам, если у тебя нет альтернативы получше то может быть придется потерпеть. Но долго задерживаться я не советую. Это явно место не для тебя, а для тех кто не хочет развиваться. выгоднее работать там, где ты можешь научиться и поднять свой уровень.
> хейтер ооп, функциональщик.
Функциональщик это хаскеллист. А это процедурщик.
>>472772
И давай я угадаю, с явой ты знаком понаслышке, больших проектов не делал ни на ней ни на других языках, пишешь 20-строчные скриптики в одно лицо на каком-нибудь Питоне и думаешь что весь остальной софт не сложнее?
> позволяющие решать задачу в 1000 раз дольше, чем можно было.
Бизнес вряд ли захотел бы платить в 1000 раз больше если можно заплатить в 1000 раз меньше. Может быть ты придешь и сделаешь ту же работу например в сто раз быстрее чем куча бездельников с архитекторами, коде интегрейшен, системами контроля версий, объектно-реляционными мапперами, многослойной архитектурой и прочим ненужным мусором который придумывают неучи-теоретики вроде Фаулера?
> Бизнес вряд ли захотел бы платить в 1000 раз больше если можно заплатить в 1000 раз меньше
Ты, я смотрю, дохуя в бизнесе понимаешь.
>Потому я тебе предлагаю другой подход, разделить отдельно класс Profile (в котором хранятся данные) и класс Proxy (который загружает из базы объект Profile при первом обращении).
А у меня все так и есть. Может по вар_дампу не видно. У юзера в поле $profile хранится экземпляр Proxy, который при обращении возвращает экземпляр Profile, как-то так. И у маппера есть флажок. Вот. Еще не вычитывал, может где-то что получше можно сделать. Но, пожалуй, это уже бета версия, а то так до ассемблера можно дойти.
И кстати, ты говорил про перехват обращений к свойствам - это внутри __get() легко можно сделать, правда довольно криво:
__get($name) {
$method_name='get'.ucfirst($name);
if (method_exists($this, $method_name)) {
return $this->{$method_name}();
} else {
return $this->{$name};
}
Ну или явно перечислить
if ($name=='skype') {
return $this->getSkype();
} else {
return $this->{$name};
}
Правда если перехватывать несколько, то нужен будет свич, но то уже такое.
От некачественной работы может быть выигрывает бизнес (он выигрывает не от низкого качества кода; он выигрывает от найма более дешевых специалистов, пишуших этот код; сам по себе некачественный код не дает никакой выгоды), но лично ты проигрываешь.
Ты бы мог работать в профессиональном коллективе, узнавать что-то новое, повышать свой уровень (и увеличивать выбор потенциальных мест работы что обычно соответствует росту зарплаты), иметь мотивацию приходить на работу, а вместо этого сидишь и занимаешься тупой не развивающей работой, как развозчик корзинок в супермаркете.
Потому лично для тебя, не для бизнеса, выгоднее стараться делать как можно лучше.
>>472775
Ты неправильно понимаешь. Заявления о тормозящем ООП надо сопровождать тестами. Также помни что маленькие сайтики обычно посещает 100 человек в день и тормозить там особо нечему.
Я работал над хайлоад сайтом и код там использовал MVC/ООП (без фанатизма и абстракций).
>>472778
Бизнесу выгодно побыстрее, программисту выгодно развивать свой уровень и перейти на более сложную и интересную работу а не клепать всю жизнь примитивные сайты и выслушивать на утренней планерке от менеджера на сколько мы отстаем от графика и на сколько часов нам сегодня придется задежаться. Ты ведь не работал в компаниях которые делают простые сайты на потоке (я тоже не работал, но читал отзывы)? Там обычно низкие зарплаты (ибо с высокой ты не сможешь делать сайт за 3000 рублей, ну сам подумай если час работы стотысячного программиста стоит 100 000 / 168 = 600 рублей, это без налогов и соцвыплат, а ведь для сайта нужна еще верстка, настройка и тд), штрафы во все поля, менеджеры и корпоративные ценности, большая текучка и люди долго не задерживаются.
> в бизнесе понимаешь.
Те кто создают программные продукты и зарабатывают на них, я думаю, понимают. Майкрософт, Эппл, Гугл, Яндекс и остальные. А ты пытаешься создать впечатление что они ничего не понимают, а ты один умный. Если ты такой умный то почему не создаешь более успешный бизнес который будет делать те же продукты с в 1000 раз меньшими затратами на абстрактных монадах высшего порядка и не выбьешь этих старых неудачников с рынка?
>>472782
Обоснуй
>>472792
Мне кажется лучше сделать немного по-другому. Вынести из прокси создание Profile и простановку свойств в маппер, который этим должен заниматься. То есть:
public function getProfile()
{
if (!$this->isDataLoaded) {
$this->profile = $this->profileMapper->getProfileById($this->profileId);
}
...
А то ты размазываешь логику маппинга данных из БД и создаение объекта Profile между маппером и прокси. Мне кажется прокси должен быть очень тонким и всю работу по загрузке объектов оставить в маппере.
Ну и если сделать метод в маппере то мы можем грузить Profile даже без юзера, не знаю, нужно ли это, но все же.
Также, не очень понятно зачем там метод setProfile который принимает массив если он может принимать готовый объект Profile (а создает этот объект пусть маппер).
Также не очень понятно почему ты передаешь id в getProfile. Мне кажется логичнее вставлять id на этапе создания прокси, так как он неизменен.
Также я бы сделал у прокси 2 статических конструктора, а обычный сделал приватным. Конструкторы такие:
— создать прокси имея заполненный объект
— создать прокси без объекта, имея его id и ссылку на маппер
Таким образом ты не можешь создать прокси не передав ему все нужные параметры и тебе не надо ломать голову что ты должен передать, а что не обязательно.
В маппере, мне кажется вместо $method (который должен принимать константы, а не строки) удобнее сделать массив $greedyLoad который говорит какие сущности надо подгрузить жадно. У тебя ведь в будущем кроме Profile будут еще другие дополнительные сущности связанные с пользоватем, наверно (например, посты, список друзей и тд).
> И кстати, ты говорил про перехват обращений к свойствам - это внутри _ _ get() легко можно сделать, правда довольно криво:
Это вполне нормальный способ, фреймворки так делают. ucfirst можно не писать так как функции регистронезависимы.
> Ну или явно перечислить
Лучше наверно не перечислять, там будет при 30 полях 30 вариантов.
> в бизнесе понимаешь.
Те кто создают программные продукты и зарабатывают на них, я думаю, понимают. Майкрософт, Эппл, Гугл, Яндекс и остальные. А ты пытаешься создать впечатление что они ничего не понимают, а ты один умный. Если ты такой умный то почему не создаешь более успешный бизнес который будет делать те же продукты с в 1000 раз меньшими затратами на абстрактных монадах высшего порядка и не выбьешь этих старых неудачников с рынка?
>>472782
Обоснуй
>>472792
Мне кажется лучше сделать немного по-другому. Вынести из прокси создание Profile и простановку свойств в маппер, который этим должен заниматься. То есть:
public function getProfile()
{
if (!$this->isDataLoaded) {
$this->profile = $this->profileMapper->getProfileById($this->profileId);
}
...
А то ты размазываешь логику маппинга данных из БД и создаение объекта Profile между маппером и прокси. Мне кажется прокси должен быть очень тонким и всю работу по загрузке объектов оставить в маппере.
Ну и если сделать метод в маппере то мы можем грузить Profile даже без юзера, не знаю, нужно ли это, но все же.
Также, не очень понятно зачем там метод setProfile который принимает массив если он может принимать готовый объект Profile (а создает этот объект пусть маппер).
Также не очень понятно почему ты передаешь id в getProfile. Мне кажется логичнее вставлять id на этапе создания прокси, так как он неизменен.
Также я бы сделал у прокси 2 статических конструктора, а обычный сделал приватным. Конструкторы такие:
— создать прокси имея заполненный объект
— создать прокси без объекта, имея его id и ссылку на маппер
Таким образом ты не можешь создать прокси не передав ему все нужные параметры и тебе не надо ломать голову что ты должен передать, а что не обязательно.
В маппере, мне кажется вместо $method (который должен принимать константы, а не строки) удобнее сделать массив $greedyLoad который говорит какие сущности надо подгрузить жадно. У тебя ведь в будущем кроме Profile будут еще другие дополнительные сущности связанные с пользоватем, наверно (например, посты, список друзей и тд).
> И кстати, ты говорил про перехват обращений к свойствам - это внутри _ _ get() легко можно сделать, правда довольно криво:
Это вполне нормальный способ, фреймворки так делают. ucfirst можно не писать так как функции регистронезависимы.
> Ну или явно перечислить
Лучше наверно не перечислять, там будет при 30 полях 30 вариантов.
Задания смотрел сделал всё кроме сайта для проведения тестов. Спасибо за идеи, попробую реализовать, хорошо что времени у меня впринципе много, понимаю почти в каждой из них надо уметь неплохо парсить, кстати, какой метод для парсинга посоветуешь? Что лучше использовать?
>ООП тебя только тормозить будет.
Имелось ввиду, что функциональщиой на мелких проектах лепить быстрее, чем ООП.
>
>Попробуй изучить какие есть функции для получения ссылки Не на текущий, а на произвольный пост. То есть тебе нужна функция вида
>
>get_something_link($post)
У меня ещё проблема, я не знаю как передать ID на страницу single.php или ID в вордпресс передаётся это автоматически? И на странице single.php достаточно просто написать функцию получения этого самого ИД?
Парсить (скрейпить)? По моему это почти нигде не нужно, разве что в torrent monitor если у торрент трекера нет API, либо в heapp, остальные приложения либо используют API либо не используют внешние сервисы.
Заниматься скрейпингом довольно тупиковый путь так как он постоянно будет ломаться и требовать подстройки.
> какой метод для парсинга посоветуешь
DOM + XPAth или библиотека для поиска по CSS селекторам вроде phpQuery. какие еще могут быть варианты?
>>472806
А, понял. Мне кажется, это акутально только для программистов плохо знакомых с ООП, так как если ты с ним хорошо знаком то все как-то само собой пишется (а если плохо знаком то быстро научишься). Ну и если ты используешь фреймворк то ООП знать надо в любом случае, а без фрйемворка как раз писать будет дольше и неудобнее.
Там должна быть функция для получения id текущего поста.
Набираем в гугле
wordpress api get post id
И в первой же ссылке видим ответ.
Почему это может на работать? Путь указан верно 100%. На сервер приходит "GET /assets/libraries/node_modules/angular/angular.min.js".
Потому что путь резолвится относительно URL самой страницы.
Страница http://example.com/lalala -> получается http://example.com/assets/libraries..
Страница http://example.com/lalala/1/2/3 -> получается http://example.com/lalala/1/2/assets/libraries..
.. значит подняться на 1 уровень выше.
http://htmlbook.ru/samhtml/ssylki/absolyutnye-i-otnositelnye-ssylki
Чтобы поставить любой символ, кроме цифр, который может встречаться сколько угодно раз, а может и не встречаться, пишу так:
/[^0-9]?{1,}/
Это правильно, или знак вопроса надо ставить после {}?
Квантификаторы (звездочка, плюс, вопрос, фигурные скобки) нельзя ставить друг за друг за другом, они должны стоять сразу после символа или скобок к которым относятся.
В твоем случае надо писать
([^0-9]?){1,}
Но писать так не надо, а надо чуть подумать.
> может встречаться сколько угодно раз, а может и не встречаться,
Для этого есть символ звездочка, он значит от 0 до бесконечности повторений.
Алсо мануал http://php.net/manual/ru/regexp.reference.repetition.php
Ну вот так я выполнил задание: http://ideone.com/wbprnC
Сильно криво?
По заданию надо чтоб этот номер можно было вводить хоть как, главное чтобы он совпадал.
Для начала неплохо, но регулярку надо бы сделать покороче и попроще. Алсо, надо бы проверить на большом числе номеров.
Задачу про номера телефонов надо проверить на большом числе телефонов, чтобы убедиться что твой код правильный. Но руками подставлять номера — долго и скучно. Пусть работает робот, а не человек!
Для этого давай добавим в программу тесты, чтобы сразу было видно, верно все работает или нет. Сделай 2 списка номеров (правильные и нет), добавь их в программу и напиши цикл, который их по очереди прогоняет через регулярку и проверяет что они определяются как надо (если нет — надо вывести какой именно номер не распознается правильно).
Вот список номеров:
Правильные: array('84951234567', '+74951234567', '8-495-1-234-567', ' 8 (8122) 56-56-56', '8-911-1234567', '8 (911) 12 345 67', '8-911 12 345 67', '8 (911) - 123 - 45 - 67', '+ 7 999 123 4567', '8 ( 999 ) 1234567', '8 999 123 4567');
Неправильные: array('02', '84951234567 позвать люсю', '849512345', '849512345678',
'8 (409) 123-123-123', '7900123467', '5005005001', '8888-8888-88',
'84951a234567', '8495123456a',
'+1 234 5678901', // неверный код страны
'+8 234 5678901', // либо 8 либо +7
'7 234 5678901' // нет +
);
Алсо по архитектуре и проектированию бд что почитать, желательно кратко, но насыщенно? С sql знаком
>Вынести из прокси создание Profile и простановку свойств в маппер, который этим должен заниматься.
Переделал
>Мне кажется логичнее вставлять id на этапе создания прокси
Переделал
>Также я бы сделал у прокси 2 статических конструктора
Сделал, вроде все так.
>обычный сделал приватным.
Да он теперь вроде и не нужен
>который должен принимать константы, а не строки
А в чем профит констант? И где их надо объявлять?
>В маппере, мне кажется вместо $method (который должен принимать константы, а не строки) удобнее сделать массив $greedyLoad который говорит какие сущности надо подгрузить жадно. У тебя ведь в будущем кроме Profile будут еще другие дополнительные сущности связанные с пользоватем, наверно (например, посты, список друзей и тд).
Ну а это самое интересное. Вот сижу, думаю. С профайлом вопросов нет, даже если город берется из другой таблицы это ну очень близкое свойство для юзера, как запись в паспорте. А вот список друзей или записи на стенке... Вот смотрю я на сущность User и вижу что она какая-то сильно умная и жирная получается. Вот не помню, у Зандстры кажется где-то читал что первый звоночек о том что пора пилить новый класс - если текущий сильно много знает и умеет. Кроме того, чем толще User тем толще UserMapper, и чтоб уметь лениво грузить друзей, посты и прочее ему придется обучаться лазить едва ли не во все таблицы. И уже сейчас он ворочает юзерами, профайлами, проксями, а потом будет постами, френдлистами еще чем-то. Кроме того, у меня та модель что между контроллером и маппером (кажется ее доменной называют?) она наоборот ничего не делает, а просто возвращает то что ей вернул маппер. Может таки разумнее разнести по разным объектам и в контроллере получать как
$model = new UserHandler; //доменная(?) модель
$data['user'] = $model ->getUser(); //внутри просто вызов юзермаппера
$data['friends'] = $model ->getFriendsList(); //вызов маппера по таблице с отношениями юзеров
$data['wall'] = $model ->getWall(); //вызов маппера по постам
С другой стороны, круто иметь возможность написать $user->getFriendList().
Словом, в очень больших раздумьях я.
>Вынести из прокси создание Profile и простановку свойств в маппер, который этим должен заниматься.
Переделал
>Мне кажется логичнее вставлять id на этапе создания прокси
Переделал
>Также я бы сделал у прокси 2 статических конструктора
Сделал, вроде все так.
>обычный сделал приватным.
Да он теперь вроде и не нужен
>который должен принимать константы, а не строки
А в чем профит констант? И где их надо объявлять?
>В маппере, мне кажется вместо $method (который должен принимать константы, а не строки) удобнее сделать массив $greedyLoad который говорит какие сущности надо подгрузить жадно. У тебя ведь в будущем кроме Profile будут еще другие дополнительные сущности связанные с пользоватем, наверно (например, посты, список друзей и тд).
Ну а это самое интересное. Вот сижу, думаю. С профайлом вопросов нет, даже если город берется из другой таблицы это ну очень близкое свойство для юзера, как запись в паспорте. А вот список друзей или записи на стенке... Вот смотрю я на сущность User и вижу что она какая-то сильно умная и жирная получается. Вот не помню, у Зандстры кажется где-то читал что первый звоночек о том что пора пилить новый класс - если текущий сильно много знает и умеет. Кроме того, чем толще User тем толще UserMapper, и чтоб уметь лениво грузить друзей, посты и прочее ему придется обучаться лазить едва ли не во все таблицы. И уже сейчас он ворочает юзерами, профайлами, проксями, а потом будет постами, френдлистами еще чем-то. Кроме того, у меня та модель что между контроллером и маппером (кажется ее доменной называют?) она наоборот ничего не делает, а просто возвращает то что ей вернул маппер. Может таки разумнее разнести по разным объектам и в контроллере получать как
$model = new UserHandler; //доменная(?) модель
$data['user'] = $model ->getUser(); //внутри просто вызов юзермаппера
$data['friends'] = $model ->getFriendsList(); //вызов маппера по таблице с отношениями юзеров
$data['wall'] = $model ->getWall(); //вызов маппера по постам
С другой стороны, круто иметь возможность написать $user->getFriendList().
Словом, в очень больших раздумьях я.
>Есть ли в php области применения, где обходяться без фреймворков и клепания по-шаблонах?
Нет. Серьёзно.
>>Вынести из прокси создание Profile и простановку свойств в маппер, который этим должен заниматься.
> Переделал
Не до конца. Ты простановку свойств из массива почему-то делаешь в прокси. Это во-первых неправильно так как ты не можешь таким образом получить снаружи доступ к приватным свойствам, во-вторых неединообразно. И непонятно почему прокси этим должен заниматься если создание объектов и заполнение свойств задача маппера.
Ну и ты опять гоняешь массивы туда-сюда хотя мог бы передавать объект.
Логично получать объект Profile из маппера:
if (!$this->dataIsLoaded) {
$this->profile = $this->mapper->loadProfile($this->id);
}
> А в чем профит констант?
Профит в том что код понятный и надежный (например не получится сделать опечатку). Со стороками ровно наоборот:
— непонятно что значит строка, к чему относится и какие еще есть варианты строк
— если ты опечатаешься то это не будет обнаружено
— нет автодополнения в IDE
> И где их надо объявлять?
Там, к чему они относятся. Константа в данном случае управляет работой маппера и должна быть объявлена в нем:
Mapper::LOAD_GREEDY
Смотри, насколько нагляднее: видно к чему относится константа, за что отвечает,а заглянув в класс можно увидеть какие еще есть варианты выбора. И как бонус работает автодополнение в IDE.
В моем учебнике эта тема не освещается, но ты должен бы был ее изучить когда изучал ООП. А то ты пишешь ORM а как использовать константы не знаешь.
Константы это конечно не самое идеальное решение, более идеальное это enum (специальный тип данных, который содержит в себе все возможные значения), но в PHP пока их не завезли. В других языках (например C#: https://msdn.microsoft.com/ru-ru/library/sbbt4032.aspx ) это делается так:
— объявляем enum с вариантами:
enum LoadType {
GREEDY = 1,
LAZY = 2
};
— указываем в функции что она принимает только значения из этого энума:
User getUserById(int id, LoadType method) {
...
Теперь во-первых любому очевидно какие значения можно передать в method, а если кто-то ошибется то компилятор выдаст ошибку.
Это конечно лучше чем в PHP, где нет проверки на то что ты передал допустимую константу.
> С профайлом вопросов нет, даже если город берется из другой таблицы это ну очень близкое свойство для юзера, как запись в паспорте
Город лучше сделать отдельной моделью, так как потом ты захочешь по городу определять страну например и тд. Ну и вообще отдельная сущность — отдельный класс.
> Кроме того, чем толще User тем толще UserMapper, и чтоб уметь лениво грузить друзей, посты и прочее ему придется обучаться лазить едва ли не во все таблицы.
Лучше для этого сделать еще мапперы, маппер для городов, для друзей и тд, для каждой отдельной таблицы свой. А не сваливать работу со всеми таблицами в один.
> Кроме того, у меня та модель что между контроллером и маппером (кажется ее доменной называют?) она наоборот ничего не делает, а просто возвращает то что ей вернул маппер.
Она делает важную вещь, она хранит в себе информацию о пользователе и позволяет ее передавать. И она по мере роста проекта будет много где использоваться, любая функция которой нужна информаия о юзере будет ее принимать на вход. Это чуть ли не основа твоего приложения.
И как без нее? К массивам возвращаться? Это верный путь в ад. Тебе в любом случае нужно что-то чтобы хранить данные пользователя.
> а потом будет постами, френдлистами еще чем-то.
Нужны отдельные объекты КоллекцияПостов (содержит Посты) и СписокДрузей (содержит Пользователей), а юзер будет содержать их в себе, спрятанные за прокси. Каждая за своим прокси конечно.
> Может таки разумнее разнести по разным объектам и в контроллере получать как
> $model = new UserHandler; //доменная(?) модель
А что значит UserHandler? Слово Handler ничего не значит, получается это то же самое что User? Я не очень понимаю. В твоем коде по моему тогда проще напрямую обращаться к мапперам:
$user = $userMapper->getUserById(..);
$firends = $firendsMapper->getFriendsForUserId(..);
То есть я не понимаю зачем этот «UserHandler» вообще нужен.
Ты можешь так сделать, но тогда у тебя будет больше ручной возни. Например ты должен вручную явно выбирать сущности, которые нужны и передавать их как отдельные переменные (по мере роста объема проекта и числа моделей этого рутинного кода будет все больше), а так достаточно одного объекта User из которого остальные можно получить через связи.
Код конечно сложнее, но я же предупреждал что писать свой ORM это боль и унижение.
>>Вынести из прокси создание Profile и простановку свойств в маппер, который этим должен заниматься.
> Переделал
Не до конца. Ты простановку свойств из массива почему-то делаешь в прокси. Это во-первых неправильно так как ты не можешь таким образом получить снаружи доступ к приватным свойствам, во-вторых неединообразно. И непонятно почему прокси этим должен заниматься если создание объектов и заполнение свойств задача маппера.
Ну и ты опять гоняешь массивы туда-сюда хотя мог бы передавать объект.
Логично получать объект Profile из маппера:
if (!$this->dataIsLoaded) {
$this->profile = $this->mapper->loadProfile($this->id);
}
> А в чем профит констант?
Профит в том что код понятный и надежный (например не получится сделать опечатку). Со стороками ровно наоборот:
— непонятно что значит строка, к чему относится и какие еще есть варианты строк
— если ты опечатаешься то это не будет обнаружено
— нет автодополнения в IDE
> И где их надо объявлять?
Там, к чему они относятся. Константа в данном случае управляет работой маппера и должна быть объявлена в нем:
Mapper::LOAD_GREEDY
Смотри, насколько нагляднее: видно к чему относится константа, за что отвечает,а заглянув в класс можно увидеть какие еще есть варианты выбора. И как бонус работает автодополнение в IDE.
В моем учебнике эта тема не освещается, но ты должен бы был ее изучить когда изучал ООП. А то ты пишешь ORM а как использовать константы не знаешь.
Константы это конечно не самое идеальное решение, более идеальное это enum (специальный тип данных, который содержит в себе все возможные значения), но в PHP пока их не завезли. В других языках (например C#: https://msdn.microsoft.com/ru-ru/library/sbbt4032.aspx ) это делается так:
— объявляем enum с вариантами:
enum LoadType {
GREEDY = 1,
LAZY = 2
};
— указываем в функции что она принимает только значения из этого энума:
User getUserById(int id, LoadType method) {
...
Теперь во-первых любому очевидно какие значения можно передать в method, а если кто-то ошибется то компилятор выдаст ошибку.
Это конечно лучше чем в PHP, где нет проверки на то что ты передал допустимую константу.
> С профайлом вопросов нет, даже если город берется из другой таблицы это ну очень близкое свойство для юзера, как запись в паспорте
Город лучше сделать отдельной моделью, так как потом ты захочешь по городу определять страну например и тд. Ну и вообще отдельная сущность — отдельный класс.
> Кроме того, чем толще User тем толще UserMapper, и чтоб уметь лениво грузить друзей, посты и прочее ему придется обучаться лазить едва ли не во все таблицы.
Лучше для этого сделать еще мапперы, маппер для городов, для друзей и тд, для каждой отдельной таблицы свой. А не сваливать работу со всеми таблицами в один.
> Кроме того, у меня та модель что между контроллером и маппером (кажется ее доменной называют?) она наоборот ничего не делает, а просто возвращает то что ей вернул маппер.
Она делает важную вещь, она хранит в себе информацию о пользователе и позволяет ее передавать. И она по мере роста проекта будет много где использоваться, любая функция которой нужна информаия о юзере будет ее принимать на вход. Это чуть ли не основа твоего приложения.
И как без нее? К массивам возвращаться? Это верный путь в ад. Тебе в любом случае нужно что-то чтобы хранить данные пользователя.
> а потом будет постами, френдлистами еще чем-то.
Нужны отдельные объекты КоллекцияПостов (содержит Посты) и СписокДрузей (содержит Пользователей), а юзер будет содержать их в себе, спрятанные за прокси. Каждая за своим прокси конечно.
> Может таки разумнее разнести по разным объектам и в контроллере получать как
> $model = new UserHandler; //доменная(?) модель
А что значит UserHandler? Слово Handler ничего не значит, получается это то же самое что User? Я не очень понимаю. В твоем коде по моему тогда проще напрямую обращаться к мапперам:
$user = $userMapper->getUserById(..);
$firends = $firendsMapper->getFriendsForUserId(..);
То есть я не понимаю зачем этот «UserHandler» вообще нужен.
Ты можешь так сделать, но тогда у тебя будет больше ручной возни. Например ты должен вручную явно выбирать сущности, которые нужны и передавать их как отдельные переменные (по мере роста объема проекта и числа моделей этого рутинного кода будет все больше), а так достаточно одного объекта User из которого остальные можно получить через связи.
Код конечно сложнее, но я же предупреждал что писать свой ORM это боль и унижение.
> Есть ли в php области применения, где обходяться без фреймворков и клепания по-шаблонах?
Как без фреймворков? С нуля писать? А в чем выгода делать дольше то, что можно сделать быстро?
> Это довольно уныло его использывать.
ну цель фреймворка дать тебе возможность быстро сделать сайт, а не строить красивые абстрактные конструкции.
Что тебе было бы интересно делать? Напиши, а я скажу какие языки/платформы для этого используются.
> Алсо по архитектуре и проектированию бд что почитать, желательно кратко, но насыщенно
Не знаю, у нас только материалы начального уровня есть, про внешние ключи, отношения и т.д. Ну и задачки есть на SQL относительно простые, в первом посте треда.
Если ты знаешь про нормализацию, отношения между таблицами, внешние ключи, этого тебе хватит для большинства случаев.
a - количество юзеров из одной локации
b - колчество юзеров из другой локации
c - количество юзеров из третьей локации
n - количество юзеров, которое может обработать сервер.
n всегда меньше a + b + c
надо найти количество конфигураций, в которых сервер может обработать максимальное количество юзеров, т.е. в которых выполняется a + b + c = n
например, a = 1, b = 1, c = 1, n = 2
a b c
1 1 0 = 2
1 0 1 = 2
0 1 1 = 2
ответ: 3 варианта
второй пример: a = 2, b = 2, c = 2, n = 2
a b c
1 1 0 = 2
1 0 1 = 2
0 1 1 = 2
2 0 0 = 2
0 2 0 = 2
0 0 2 = 2
Ответ: 6 вариантов
Понятно, что нужно делать итерации, но я никак не могу вспомнить как решается в математике подобная задача и стакнулся.
a - количество юзеров из одной локации
b - колчество юзеров из другой локации
c - количество юзеров из третьей локации
n - количество юзеров, которое может обработать сервер.
n всегда меньше a + b + c
надо найти количество конфигураций, в которых сервер может обработать максимальное количество юзеров, т.е. в которых выполняется a + b + c = n
например, a = 1, b = 1, c = 1, n = 2
a b c
1 1 0 = 2
1 0 1 = 2
0 1 1 = 2
ответ: 3 варианта
второй пример: a = 2, b = 2, c = 2, n = 2
a b c
1 1 0 = 2
1 0 1 = 2
0 1 1 = 2
2 0 0 = 2
0 2 0 = 2
0 0 2 = 2
Ответ: 6 вариантов
Понятно, что нужно делать итерации, но я никак не могу вспомнить как решается в математике подобная задача и стакнулся.
>>Как без фреймворков? С нуля писать? А в чем выгода делать дольше то, что можно сделать быстро?
Нет, это я прекрасно понимаю. Просто спрашиваю может есть вещи, где фреймворки не подойдут, допустим из-за избыточности?
>>Что тебе было бы интересно делать? Напиши, а я скажу какие языки/платформы для этого используются.
Не знаю, хех. Ну как ты сказал выше строить красивые абстрактные конструкции
>>Если ты знаешь про нормализацию, отношения между таблицами, внешние ключи, этого тебе хватит для большинства случаев.
Ну ладно. Сам может что интересное найду
в гугл ходил, как-то не удалоось
>Лучше для этого сделать еще мапперы, маппер для городов, для друзей и тд, для каждой отдельной таблицы свой. А не сваливать работу со всеми таблицами в один
Они-то у меня уже заготовлены, только если я связанный объект-профайл получаю лефт джоинами по разным таблицам внутри getUserById() маппера UserMapper то получается что и друзей со стенками надо там же и также создавать, а иначе как? Мапперы внутри маппера? Не круто и не решает проблемы раздувания getUserById(). Потому идея вынести их на уровень выше - в скучающую пустую доменную модель мне показалась симпатичной.
>А что значит UserHandler?
Ну я смотрел у других такие модели называются точно также как контроллеры и соответственно как разделы в урл сайта. То есть 'Users'. Мне %сущностьОбработчик% более говорящим показалось.
>Она делает важную вещь, она хранит в себе информацию о пользователе и позволяет ее передавать.
Ну сейчас сам User c этим вполне справляется.
Вот же дурачек. CSS детекть с мобыли ли седить и по какой-нибудь отдельной особенности выделяй нужны элементы.
Посоветуйте что можно новичку почитать о безопасности вебприложений? Вот что б прям с нуля и было объяснено почему тот код уязвим и нужно писать так.
я имею в виду перенос строки(чето тупонул). Ну а вообще и абзацы тоже. В общем как сделать, чтобы текст, который написал пользователь выводился так же, как он его ввел?
На CI полно говносайтов и говноработы, впринципе, изучить его труда не составит, но не привязывайся только к нему. Есть Laravel и более кошерные вещи, с которыми работать гораздо приятнее.
Так вот, Users:findOne($username) ищет в id, как устроить поиск по полю username?
А если открыть исходный код (Ctrl + U) то абзацев нет или там они есть? Может и не исчезают?
И второй вопрос, HTML знаешь? Про тег для абзацев или разрыва строки слыщал?
Я дурак. Уже увидел
> изучить его труда не составит
Мне он собственно потому и понравился что сразу все понятно сходу. Сейчас еще 3 версия вышла, я вот и размышляю, может и взлетит.
Итак, мне надо вывести в индексном шаблоне данные из таблицы которую я создал сам. Это у меня получается, дальше мне нужно открыть эту единичную запись в отдельном шаблоне, задача стандартная. Как мне это сделать? Куда должна вести ссылка с записи в индексном шаблоне и как она должна выглядеть? Как передать ID записи через эту ссылку? Есть ли пример подобного где-нибудь? Я не использую цикл вордпресс поэтому пермалинк и подобное не подходит.
ссылка должна выглядеть так
имя контроллера/имя метода(в твоем случае отображающего одну запись)/параметр(в твоем случае айди)
То есть контроллер/single/12
Или вместо single надо написать какой-то метод вордпрес а не название шаблона.
да, метод который есть у этого контроллера и который отвечает за показ одной записи. по идее он там уже должен быть написан
Он записан если делать через стандартный цикл вордпресс, там есть удобная функция the_permalink() но тут всё по другому, так как работаю с созданной таблицей в базе данных а не с уже существующей.
Хм, извиняюсь за глупый вопрос, но как сделать проверку двух массивов за один цикл? И еще почему-то пишу в цикле:
preg_match($regexp, $correctNumbers), а оно не компилится. Что не так?
>получается что и друзей со стенками надо там же и также создавать, а иначе как? Мапперы внутри маппера? Не круто и не решает проблемы раздувания getUserById(). Потому идея вынести их на уровень выше - в скучающую пустую доменную модель мне показалась симпатичной.
Так, я это как следует обдумал, кое-что не так страшно. Добычу связанных сущностей можно выносить в getFriendList(), getWallPosts(), getPlaylist() также как тут мы вынесли getProfile(), внутри getUserById() останется только сборка их воедино в зависимости от того жадно или лениво нужно получать. Однако дилемма 'мапперы внутри маппера' vs 'лефт джоины' внутри этих getFriendList() остается.
>Она делает важную вещь, она хранит в себе информацию о пользователе и позволяет ее передавать.
>Ну сейчас сам User c этим вполне справляется.
Ааа, я все перепутал. Доменная модель это User и есть, а этот мой не сильно нужный "UserHandler" - это то что называют service http://martinfowler.com/eaaCatalog/serviceLayer.html Вот же стыд. Меня на каком-то позорном ресурсе ввели в заблуждение. nevermind this.
Поясните, пожалуйста. Всегда было интересно. В PHP запрос идет от пользователя к серверу, а есть ли обратная реализация? Как сделать к примеру, чат, который не будет атаковать сервер запросами, а наоборот, чтобы запросы шли с сервера. Вот появилось новое сообщение на сервере, он хуяк прислал его получателю, а не чтобы получатель каждое н-ное количество секунд запрашивал есть ли новое сообщение?(это как пример)
$app->config(array('templates.path' => './templates/'));
$app->get('/', function () use ($app){
$app->render('index.html');
});
Почему-то страничка не рендерится, а показывается "как есть" с тегами, и т. д. Почему?(
>— BookHub, сервис для желающих обмениваться книгами
Тут надо делать аккаунт для каждого? Или достаточно просто дать возможность выкладывать книги для обмена и оставлять свои данный (телефон, емейл и т.д.)?
Анон, жив ли ты еще? Я тут подумал, что твою задачу можно (наверно) решить без вызова exec вообще и без риска запустить троян. Тебе надо записать строку в COM-порт (или его аналог). В Windows (и Линукс) ком-порт доступен в виде псевдо-файла, который можно открыть и писать в него стандартными файловыми функциями, в нашем случае file_put_contents (или если ты любитель старой школы то fopen/fwrite/fclose).
Проверить успешность записи можно по результату который вернет эта функция.
Вопрос только как указать имя файла? Во времена DOS порт был доступен как псевдофайл с именем COM× и вполне возможно что сработает что-то вроде
COM9 или \COM9
Может быть, это не сработает (так как мы испльзуем пространтсво имен Windows) и выдаст ошибку что файла нет (которую твой нынешний скрипт все равно не отобразит на экране и ты о ней не узнаешь).
Тогда можно попробовать исплоьзовать специальные длинные пути: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#win32_device_namespaces
Путь имеет вид \\.\COM9 но из-за экранирование бекслешей надо писать "\\\\.\\COM9" (больше бекслешей для бога бекслешей!)
В linux запись в ком-порт ведется через файл устройства где-то в /dev, может быть /dev/ttyS1 или как-то аналогично, это надо гуглить.
Ну и PHP конечно изучать надо в любом случае, чтобы не останавливаться на каждой проблеме.
В общем вот быдлокласс, которым я присобачиваю каптчу Яндекса как на двачике: http://pastebin.com/SPe1esQM
Дальше я получаю каптчу следующим образом:
$captcha = new yandexCaptcha($key);
$captcha_url = $captcha->get()['url'];
$captcha_code = $captcha->get()['code'];
Проблема в том, что когда я полчаю captcha_code, то, судя по всему, функция начинается с начала, опять обращается к яндексу и я получаю значение уже с другого XML, код не совпадает с URL и каптча не работает.
Что интересно, если обращатся по процедурному, типа
$captcha_url = yandexCaptcha::get()['url'];
$captcha_code = yandexCaptcha::get()['code'];
То все работает верно, а если в ООП виде, то нет.
Где я туплю?
Кстати. Если ты установишь расширение XDebug то var_dump будет выводить список полей вертикально и с форматированием, а не в строчку. А сообщения об ошибках будут более подробными.
>>473079
Это называется размещение, когда у нас есть N переменных которые могут принимать M значений.
Алогритм простой:
— сгенерировать все возможные размещения a и b (значение c получается как n - a - b)
— отобрать из них те которые соответствуют условию задачи
— посчитать сколько их
Как генерировать все возможные размещения? Ну в твоем случае хватит цикла:
перебрать a от 0 до максимума
для каждого a переьрать все значения b от 0 до максимума
для каждой пары a, b вычислить c и добавить в список
В более сложных случаях когда число переменных заранее неизвестно, нужны более сложные алгоритмы:
https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%89%D0%B5%D0%BD%D0%B8%D0%B5
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%9A%D0%BE%D0%BC%D0%B1%D0%B8%D0%BD%D0%B0%D1%82%D0%BE%D1%80%D0%B8%D0%BA%D0%B0/%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%89%D0%B5%D0%BD%D0%B8%D1%8F
http://study-and-dev.com/blog/sda_theory_combinatoric/
http://cppalgo.blogspot.ru/2012/03/blog-post_20.html
http://e-maxx.ru/algo/generating_combinations
http://algolist.manual.ru/maths/combinat/
Ну и может их не обязательно генерировать, может тут можно формулами из комбинаторики посчитать.
Кстати. Если ты установишь расширение XDebug то var_dump будет выводить список полей вертикально и с форматированием, а не в строчку. А сообщения об ошибках будут более подробными.
>>473079
Это называется размещение, когда у нас есть N переменных которые могут принимать M значений.
Алогритм простой:
— сгенерировать все возможные размещения a и b (значение c получается как n - a - b)
— отобрать из них те которые соответствуют условию задачи
— посчитать сколько их
Как генерировать все возможные размещения? Ну в твоем случае хватит цикла:
перебрать a от 0 до максимума
для каждого a переьрать все значения b от 0 до максимума
для каждой пары a, b вычислить c и добавить в список
В более сложных случаях когда число переменных заранее неизвестно, нужны более сложные алгоритмы:
https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%89%D0%B5%D0%BD%D0%B8%D0%B5
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%9A%D0%BE%D0%BC%D0%B1%D0%B8%D0%BD%D0%B0%D1%82%D0%BE%D1%80%D0%B8%D0%BA%D0%B0/%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%89%D0%B5%D0%BD%D0%B8%D1%8F
http://study-and-dev.com/blog/sda_theory_combinatoric/
http://cppalgo.blogspot.ru/2012/03/blog-post_20.html
http://e-maxx.ru/algo/generating_combinations
http://algolist.manual.ru/maths/combinat/
Ну и может их не обязательно генерировать, может тут можно формулами из комбинаторики посчитать.
Вот математический расчет. Обозначим число конфигураций K переменных дающих в сумме N как confiCount(K, N)
В твоей задача надо найти configCount(3, N)
Попробуем определить чему равно configCount. Во-первых, для 1 переменной число конфигураций равно 1, так как есть единственное значение которое в сумме дает N:
configCount(1, N) = 1
При большем числе переменных мы можем вывести рекуррентное соотношение:
configCount(K, N) = ∑(i = от 0 до N)(configCount(K - 1, N - i))
То есть число конфигураций для 3 переменных получается суммированием числа конфигураций из 2 переменных для всех возможных значений первой.
(из этих 2 формул хаскеллист бы написал решение задачи в 2 строки, но мы не хаскеллисты и продолжим размышять).
Теперь если раскрыть эти формулы для 3 переменных, то получим:
configCount(3, N) = ∑(i = 0 .. N)(configCount(2, N - i)) = ∑(i = 0 .. N)(∑(j = 0 .. N - i)(configCount(1, N - i - j))) = ∑(i = 0 .. N)(∑(j = 0 .. N - i)(N - i - j))
Я не знаю, как посчитать эту сумму, так что если тут есть анон разбирающийся в математике, хорошо бы было услышать его мнение.
Я даже поставлю вопрос к математикам более широко: если есть выражение
∑(i = 0 .. N)f(i)
Можно ли как-то его преобразовать, чтобы избавиться от суммы? Явно напоминает интеграл.
>>473101
> строить красивые абстрактные конструкции
Изучай языки со всякими альтернативными парадигмами, например Хаскелл и его экосистема, Эрланг. Там этого навалом.
> Просто спрашиваю может есть вещи, где фреймворки не подойдут, допустим из-за избыточности?
Фреймворки ориентированы на создание сайтов, так что если ты делаешь не сайт то они тебе не подойдут.
Яваскриптом. Алсо не понимаю, зачем менять ссылки на странице.
>>473111
> Они-то у меня уже заготовлены, только если я связанный объект-профайл получаю лефт джоинами по разным таблицам внутри getUserById() маппера UserMapper то получается что и друзей со стенками надо там же и также создавать, а иначе как?
Да. Но при этом если ты хочешь загрузить друзей отдельно без джойнов, то логично это делать в отдельном маппере друзей. Ну и при этом постарайся избежать дублирования кода.
> Потому идея вынести их на уровень выше - в скучающую пустую доменную модель мне показалась симпатичной.
Это плохая идея так как ты размазываешь загрузку сущностей из БД по нескольким классам. Если метод getUserByID становится толстым то разбей его на несколько более маленьких.
> Ну я смотрел у других такие модели называются точно также как контроллеры и соответственно как разделы в урл сайта. Т
контроллеры соответствуют разделам и страницам, а модели таблицам в БД (точнее сущностям из предметной области). Их число и название не всегда совпадает.
>>473124
У меня есть 2 урока по XSS/CSRF:
https://github.com/codedokode/pasta/blob/master/security/xss.md
https://github.com/codedokode/pasta/blob/master/security/xsrf.md
По SQL инъекциям можно нагуглить статьи:
Вики: https://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_SQL-%D0%BA%D0%BE%D0%B4%D0%B0
Школофорум: http://forum.antichat.ru/thread43966.html
+ статьи с хабра
http://habrahabr.ru/post/148701/
По остальным уязвимостям, не знаю, что посоветовать, книги вряд ли полезны так как устаревают, особенно если они переведенные.
Яваскриптом. Алсо не понимаю, зачем менять ссылки на странице.
>>473111
> Они-то у меня уже заготовлены, только если я связанный объект-профайл получаю лефт джоинами по разным таблицам внутри getUserById() маппера UserMapper то получается что и друзей со стенками надо там же и также создавать, а иначе как?
Да. Но при этом если ты хочешь загрузить друзей отдельно без джойнов, то логично это делать в отдельном маппере друзей. Ну и при этом постарайся избежать дублирования кода.
> Потому идея вынести их на уровень выше - в скучающую пустую доменную модель мне показалась симпатичной.
Это плохая идея так как ты размазываешь загрузку сущностей из БД по нескольким классам. Если метод getUserByID становится толстым то разбей его на несколько более маленьких.
> Ну я смотрел у других такие модели называются точно также как контроллеры и соответственно как разделы в урл сайта. Т
контроллеры соответствуют разделам и страницам, а модели таблицам в БД (точнее сущностям из предметной области). Их число и название не всегда совпадает.
>>473124
У меня есть 2 урока по XSS/CSRF:
https://github.com/codedokode/pasta/blob/master/security/xss.md
https://github.com/codedokode/pasta/blob/master/security/xsrf.md
По SQL инъекциям можно нагуглить статьи:
Вики: https://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_SQL-%D0%BA%D0%BE%D0%B4%D0%B0
Школофорум: http://forum.antichat.ru/thread43966.html
+ статьи с хабра
http://habrahabr.ru/post/148701/
По остальным уязвимостям, не знаю, что посоветовать, книги вряд ли полезны так как устаревают, особенно если они переведенные.
Фреймворк из эпохи PHP4 c кучей рудиментов, не поддерживает композер по моему, фирма которая его делала его забросила. Если ты его запустишь по современным PHP то он сыплет предупреждениями, код убогий (на мой взгляд) по нынешним меркам.
>>473177
> но вставлять их не хотелося.
Если ты изучал HML то должен знать что любое число пробелов и переводов строк воспринимается как один пробел. Соответственно ты должен либо с помощью CSS и свойства white-space ( http://htmlbook.ru/css/white-space ) это менять, либо вставлять br
> решил с помощью функции nl2br.
ну так она и вставляет br
>>473183
> может многим надоел своими вопросами
Это тред специально для начинающих и вопросов от них. Задавай сколько хочешь вопросов.
> Это у меня получается, дальше мне нужно открыть эту единичную запись в отдельном шаблоне, задача стандартная. Как мне это сделать?
В архитектуре вордпресса все сущности (которые можно запостить отдельным постом и открыть на отдельной странице) — это разновидности постов. Медиафайлы, пункты меню, статические страницы — это тоже виды постов. Я советую как-то разобраться и изучить, например изучить схему БД вопрдпресса, какие там есть таблицы, почитать какие есть функции API и тд.
Ты можешь создать свои сущности через custom post types. При этом можно наверно заджойнить свою таблицу на таблицу постов либо хранить данные в дополнительных колонках (если можно заджойнить отдельную таблицу то это более красивое и надежное решение).
Соответственно если бы ты изучил эту архитектуру и делал бы в соотвествии с ней то не пришлось бы страдать и мучаться. А если ты делаешь поперек, по своему то ты должен все реализовать сам, начиная от того как получить контроль при вызове определенного URL, как выбрать данные, как вывести.
Чтение:
https://codex.wordpress.org/Post_Types
http://truemisha.ru/blog/wordpress/post-types.html
http://rightblog.ru/1967
http://habrahabr.ru/post/97247/
http://habrahabr.ru/sandbox/39487/
http://wpcafe.org/tutorials/rukovodstvo-po-kastomnyim-tipam-zapisey-wordpress/
(некоторые статьи плохие потому в первую очередт читай мануал)
Советую тебе рассмотреть вариант переделать твой тип записей на custom post type. Это сбережет силы и время.
Ну и помни что вордпресс это блоговый движок и возможности его расширения ограничены. Это не фреймворк.
Фреймворк из эпохи PHP4 c кучей рудиментов, не поддерживает композер по моему, фирма которая его делала его забросила. Если ты его запустишь по современным PHP то он сыплет предупреждениями, код убогий (на мой взгляд) по нынешним меркам.
>>473177
> но вставлять их не хотелося.
Если ты изучал HML то должен знать что любое число пробелов и переводов строк воспринимается как один пробел. Соответственно ты должен либо с помощью CSS и свойства white-space ( http://htmlbook.ru/css/white-space ) это менять, либо вставлять br
> решил с помощью функции nl2br.
ну так она и вставляет br
>>473183
> может многим надоел своими вопросами
Это тред специально для начинающих и вопросов от них. Задавай сколько хочешь вопросов.
> Это у меня получается, дальше мне нужно открыть эту единичную запись в отдельном шаблоне, задача стандартная. Как мне это сделать?
В архитектуре вордпресса все сущности (которые можно запостить отдельным постом и открыть на отдельной странице) — это разновидности постов. Медиафайлы, пункты меню, статические страницы — это тоже виды постов. Я советую как-то разобраться и изучить, например изучить схему БД вопрдпресса, какие там есть таблицы, почитать какие есть функции API и тд.
Ты можешь создать свои сущности через custom post types. При этом можно наверно заджойнить свою таблицу на таблицу постов либо хранить данные в дополнительных колонках (если можно заджойнить отдельную таблицу то это более красивое и надежное решение).
Соответственно если бы ты изучил эту архитектуру и делал бы в соотвествии с ней то не пришлось бы страдать и мучаться. А если ты делаешь поперек, по своему то ты должен все реализовать сам, начиная от того как получить контроль при вызове определенного URL, как выбрать данные, как вывести.
Чтение:
https://codex.wordpress.org/Post_Types
http://truemisha.ru/blog/wordpress/post-types.html
http://rightblog.ru/1967
http://habrahabr.ru/post/97247/
http://habrahabr.ru/sandbox/39487/
http://wpcafe.org/tutorials/rukovodstvo-po-kastomnyim-tipam-zapisey-wordpress/
(некоторые статьи плохие потому в первую очередт читай мануал)
Советую тебе рассмотреть вариант переделать твой тип записей на custom post type. Это сбережет силы и время.
Ну и помни что вордпресс это блоговый движок и возможности его расширения ограничены. Это не фреймворк.
> Я не использую цикл вордпресс поэтому пермалинк и подобное не подходит.
Почему? Чем они не подходят? «я не осилил документацию» уважительной причиной не считается, скажу сразу.
Имхо тут нужны custom post types.
Если нет то тебе нужен свой обработчик, чтобы при обращении по определнному URL вызвыался твой код. Для этого есть такие вещи:
— WP Rewrite: https://codex.wordpress.org/Class_Reference/WP_Rewrite - наверно можно туда добавить свой обработчик как-то чтоюы например при обращении по адресу /something/NNN вызывался скрипт your_plugin.php?id=NNN
— хук: http://stackoverflow.com/a/16545672 — имхо это костыль
В вордпресс есть контроллеры? Если так то анону может подойти этот вариант.
>>473215
Я не понял что значит «проверка 2 массивов»? Что тебе надо проверить?
Если тебе надо обойти все элементы 2 массивов то надо либо 2 цикла либо объединить массивы в один.
> И еще почему-то пишу в цикле:
preg_match($regexp, $correctNumbers), а оно не компилится. Что не так?
регулярка неправильная может? Покажи код
>>473232
> дилемма 'мапперы внутри маппера' vs 'лефт джоины'
По моему проблемы нет. Либо ты выбираешь огромный массив данных джойном и внутренними функциями в маппере UserMapper создаешь из них Profile,FriendsList либо если это делается отдельным запросом то это делается в другом маппере.
>>473266
Разницу по моему нетрудно запомнить. Моделей может быть много, например загрузил 10 юзеров и имеешь 10 объектов. А сервис почти всегда в одном экземпляре (разве что при тестировании ты захочешь каждый раз для чистоты эксперимента создавать новый).
Сервис обычно это более высокоуровневый класс, который выше слоя мапперов и может их вызывать. Ну например сервис UserService может содержать метод регистрации который:
— принимает на вход модель User (ее должен создать и заполнить тот кто вызвал метод, например контроллер)
— маппером сохраняет ее в БД
— отправляет письмо с помощью сервиса уведомлений
— увеличивает показатели стастичтики вызывая соотв. сервис
— начисляет пользователю бонусы за регистрацию
То есть сервисы обычно делают более сложные операции (бизнес-логику), а маппер это просто «глупый» низкоуровневый класс для работы с таблицей в БД.
Получается такая слоеная архитектура, где вышележащий слой использует классы из нижележащего. Опять же это разделение делается ради того чтобы в большом приложении не запутаться в коде.
В вордпресс есть контроллеры? Если так то анону может подойти этот вариант.
>>473215
Я не понял что значит «проверка 2 массивов»? Что тебе надо проверить?
Если тебе надо обойти все элементы 2 массивов то надо либо 2 цикла либо объединить массивы в один.
> И еще почему-то пишу в цикле:
preg_match($regexp, $correctNumbers), а оно не компилится. Что не так?
регулярка неправильная может? Покажи код
>>473232
> дилемма 'мапперы внутри маппера' vs 'лефт джоины'
По моему проблемы нет. Либо ты выбираешь огромный массив данных джойном и внутренними функциями в маппере UserMapper создаешь из них Profile,FriendsList либо если это делается отдельным запросом то это делается в другом маппере.
>>473266
Разницу по моему нетрудно запомнить. Моделей может быть много, например загрузил 10 юзеров и имеешь 10 объектов. А сервис почти всегда в одном экземпляре (разве что при тестировании ты захочешь каждый раз для чистоты эксперимента создавать новый).
Сервис обычно это более высокоуровневый класс, который выше слоя мапперов и может их вызывать. Ну например сервис UserService может содержать метод регистрации который:
— принимает на вход модель User (ее должен создать и заполнить тот кто вызвал метод, например контроллер)
— маппером сохраняет ее в БД
— отправляет письмо с помощью сервиса уведомлений
— увеличивает показатели стастичтики вызывая соотв. сервис
— начисляет пользователю бонусы за регистрацию
То есть сервисы обычно делают более сложные операции (бизнес-логику), а маппер это просто «глупый» низкоуровневый класс для работы с таблицей в БД.
Получается такая слоеная архитектура, где вышележащий слой использует классы из нижележащего. Опять же это разделение делается ради того чтобы в большом приложении не запутаться в коде.
Да, называется вебсокет —браузер устанавливает соединение с сервером и тот может слать в него данные. Обычно вебсокт-сервер пишут на всяких Node.jS, но сейчас и в PHP есть библиотека Ractchet для этого.
До вебсокетов это называлось COMET (оно использовало аякс, ифреймы, флеш и другие технологии), но сейчас COMET нужен только разве что для старых браузеров. Есть библиотеки которые умеют выбирать и использовать нужную технлогию.
Стандартная модель работы через Апач (или php-fpm) тут не подходит, мы должны в нашем сервере сами открыть порт, сами принимать соединения и т.д. Тут не так давно заходил какой-то анон, (который у нас учился раньше), и он как раз на этом Ratchet сделал чат.
>>473378
Посмотри инспектором Ctrl + shift + I на вкладке Network заголовки. в частности Content-Type
Ну и нужно больше подробностей
>>473436
Аккаунт не самоцель, цель сделать процесс обмена (и использования сайта) наиболее удобным. Я думаю, что тут аккаунт нужен, так как процесс использования выглядит так. Представим, что ты пользователь у которого есть несколько не нужных ему книг и котоый хочет их обменять на что-нибудь что он еще не читал:
— пользователь регистиуется максимально быстро и безболезненно (+ вход через соцсети без лишних вопросов)
— пользователь максимально просто, удобно, без заполнения огромных форм пишет какие книгу у него есть
— может быть выбирает какие темы ему интересны, может нет. лучше не заставлять это делать
— может искать того у кого есть нужная книга и делать предложение обменяться
— сайт также ведет статистику по пользователям, можно делать отзывы, то есть ты можешь зайти на страницу пользователя и сделать какие-то выводы о нем (например: «обещал встретиться и не приехал. Не советую с ним связываться»)
Выкладывать в общий доступ контактные данные плохая идея, это оттолкнет людей. Думаю, что желающие должны сначала найти друг друга на сайте, может быть обменяться сообщениями (может быть нет), а потом делиться контактами.
Ты не должен спешить писать код. Начни с проектирования со стороны пользователя. Продумай несколько сценариев:
— ты хочешь почитать что-нибудь в определенном жанре, но не знаешь что именно
— у тебя есть книга которая тебе больше не нужна и ты хочешь ее обменять
— у тебя есть книга и ты готов отдать ее бесплатно
— ты пока не хочешь регистрироваться, а хочешь посмотреть что это за сайт, можно ли ему доверять и т.д.
Имея сценарии, подумай как наиболее быстро и удобно для тебя как пользователя было бы решить свою задачу.
Тут также можно сделать рекомендательный сервис, например если тебе нравятся несколько книг которые выложил человек то скорее всего и другие его книги понравятся тоже. Рекомендательный сервис может быть доступен и без регистрации, просто люди заходят и смотрят что другие люди читают. Более того ,страницу на этом сервисе можно использовать как myanimelist, чтобы расскзать другим какие книги ты прочел.
После этого у тебя уже должно быть представление, какие разделы и страницы есть на сайте, как они связаны.
То есть не ставь код на первое место, начни с проектирования взаимодействия и пользовательских сценариев.
Да, называется вебсокет —браузер устанавливает соединение с сервером и тот может слать в него данные. Обычно вебсокт-сервер пишут на всяких Node.jS, но сейчас и в PHP есть библиотека Ractchet для этого.
До вебсокетов это называлось COMET (оно использовало аякс, ифреймы, флеш и другие технологии), но сейчас COMET нужен только разве что для старых браузеров. Есть библиотеки которые умеют выбирать и использовать нужную технлогию.
Стандартная модель работы через Апач (или php-fpm) тут не подходит, мы должны в нашем сервере сами открыть порт, сами принимать соединения и т.д. Тут не так давно заходил какой-то анон, (который у нас учился раньше), и он как раз на этом Ratchet сделал чат.
>>473378
Посмотри инспектором Ctrl + shift + I на вкладке Network заголовки. в частности Content-Type
Ну и нужно больше подробностей
>>473436
Аккаунт не самоцель, цель сделать процесс обмена (и использования сайта) наиболее удобным. Я думаю, что тут аккаунт нужен, так как процесс использования выглядит так. Представим, что ты пользователь у которого есть несколько не нужных ему книг и котоый хочет их обменять на что-нибудь что он еще не читал:
— пользователь регистиуется максимально быстро и безболезненно (+ вход через соцсети без лишних вопросов)
— пользователь максимально просто, удобно, без заполнения огромных форм пишет какие книгу у него есть
— может быть выбирает какие темы ему интересны, может нет. лучше не заставлять это делать
— может искать того у кого есть нужная книга и делать предложение обменяться
— сайт также ведет статистику по пользователям, можно делать отзывы, то есть ты можешь зайти на страницу пользователя и сделать какие-то выводы о нем (например: «обещал встретиться и не приехал. Не советую с ним связываться»)
Выкладывать в общий доступ контактные данные плохая идея, это оттолкнет людей. Думаю, что желающие должны сначала найти друг друга на сайте, может быть обменяться сообщениями (может быть нет), а потом делиться контактами.
Ты не должен спешить писать код. Начни с проектирования со стороны пользователя. Продумай несколько сценариев:
— ты хочешь почитать что-нибудь в определенном жанре, но не знаешь что именно
— у тебя есть книга которая тебе больше не нужна и ты хочешь ее обменять
— у тебя есть книга и ты готов отдать ее бесплатно
— ты пока не хочешь регистрироваться, а хочешь посмотреть что это за сайт, можно ли ему доверять и т.д.
Имея сценарии, подумай как наиболее быстро и удобно для тебя как пользователя было бы решить свою задачу.
Тут также можно сделать рекомендательный сервис, например если тебе нравятся несколько книг которые выложил человек то скорее всего и другие его книги понравятся тоже. Рекомендательный сервис может быть доступен и без регистрации, просто люди заходят и смотрят что другие люди читают. Более того ,страницу на этом сервисе можно использовать как myanimelist, чтобы расскзать другим какие книги ты прочел.
После этого у тебя уже должно быть представление, какие разделы и страницы есть на сайте, как они связаны.
То есть не ставь код на первое место, начни с проектирования взаимодействия и пользовательских сценариев.
Remote Address:127.0.0.1:8085
Request URL:http://localhost:8085/File_sharing/
Request Method:GET
Status Code:200 OK
Connection:Keep-Alive
Content-Length:916
Content-Type:text/templates
Date:Mon, 04 May 2015 19:51:58 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.4.7 (Ubuntu)
А зачем ты делаешь 2 вызова get() и 2 запроса к яндексу? Это же неэффективно как минимум. Сетевые взаимодействия не бесплатны и требуют времени.
Мне лень смотреть, посмотри документацию, если несколько раз делать запрос для получения капчи, он вернет одну и ту же капчу или он каждый раз генерирует другую качу с другим кодом. Может ты генеируешь 2 капчи, URL беешь от первой а код от второй.
> Что интересно, если обращатся по процедурному, типа
> То все работает верно, а если в ООП виде, то нет.
Чушь. Причина не в этом, а в чем-то другом.
Также вот еще косяки:
— имя класса с большой буквы
— работа с сетью сделана плохо, ни проверок на ощибки ни таймаутов не выставлено. ты думаешь интернет запросы в 100% случаев выполняются успешно? АПи яндекса тоже иногда выдают ошибки или не отвечают и надо это обрабатывать.
— XML в ответе тоже может быть невалидным из-за ошибки на стороне яндекса
— при подстановке данных в URL надо их кодировать процентным кодированием в urlencode иначе пользователь может видоизменить URL зароса и может быть обойти проверку
> Content-Type:text/templates
Это. Тип говорит браузеру о том какого типа ответ и как его надо отобразить: как текст, HTML, картинку, аудиозапись и тд. Для HTML там должно быть text/html; charset=utf-8
Найди где выставляется тип и убери это. Ты какой-ниьудь плагин хитрый к слиму не подключил? настройки PHP не менял?
https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_MIME-%D1%82%D0%B8%D0%BF%D0%BE%D0%B2
Хотел тебе дать ссылку про этот заголовок и вообще про заголовки в HTTP, но не смог нагуглить ничего хорошего.
$app->contentType('text/html') помог.
Можно это как-то вначале писать, чтобы в каждом роутере не переписывать?
Я удалил .htaccess, который composer стянул. Пока не удалял, вылезала 500 ошибка.
Этот тип должен стоять по умолчанию. Ты какие-то плагины к слиму подключал? Сборку используешь левую или с официального сайта? В php.ini ничего не менял?
Проверить конфигурацию php можно так. Поставь в index.php код
phpinfo( );
die( );
в начало и открой страницу. Это выведет настройки PHP, там 2 колонки, значение по умолчанию и текущее. Там найди mimetype ( http://php.net/manual/ru/ini.core.php#ini.default-mimetype ), и посмотри какие значения стоят.
> А зачем ты делаешь 2 вызова get() и 2 запроса к яндексу?
Так я один запрос хочу, а не два. Я не понимаю почему два раза, если я хочу вторым запросом get() всего навсего забрать из массива то, что в него уже попало. Видимо я просто не понимаю чего-то и хочу помощи.
> Мне лень смотреть, посмотри документацию, если несколько раз делать запрос для получения капчи, он вернет одну и ту же капчу или он каждый раз генерирует другую качу с другим кодом. Может ты генеируешь 2 капчи, URL беешь от первой а код от второй.
Вот именно, в этом и проблема. Я не хочу генерировать еще раз, зачем оно второй раз обращается к яндексу?
> Чушь. Причина не в этом, а в чем-то другом.
В том то и дело, что в процедурном виде все работает так, как я задумывал.
> — имя класса с большой буквы
> — работа с сетью сделана плохо, ни проверок на ощибки ни таймаутов не выставлено. ты думаешь интернет запросы в 100% случаев выполняются успешно? АПи яндекса тоже иногда выдают ошибки или не отвечают и надо это обрабатывать.
> — XML в ответе тоже может быть невалидным из-за ошибки на стороне яндекса
Это все пока пофигу.
> — при подстановке данных в URL надо их кодировать процентным кодированием в urlencode иначе пользователь может видоизменить URL зароса и может быть обойти проверку
Хрен там, не даст движок ничего запостить пока не получит от Яндекса <ok />
Создал проект в phpstorm как Composer'ский, указал слим (slim/slim). Как проект создался, подключил еще bootstrap.
> Я не понимаю почему два раза, если я хочу вторым запросом get() всего навсего забрать из массива то, что в него уже попало. Видимо я просто не понимаю чего-то и хочу помощи.
когда ты пишешь
$object->doSomething()
(обрати внимание на скобки) то это вызов метода. Вызов метода значит что PHP заходит в него и выполняет написанный внутри него код (и шлет запрос на сервер яндекса, и разбирает его и т д). Поставь в начале метода
echo "Send request...\n";
и посмотри сколько раз выведется эта надпись.
Если что, «метод» это функция у объекта.
Если ты 2 раза пишешь $captcha->get() то код в методе выполняется тоже 2 раза и отправляется 2 запроса. Обязательно поставь туда echo и посчитай сам.
> Я не хочу генерировать еще раз, зачем оно второй раз обращается к яндексу?
Потому что ты 2 раза вызываешь метод и таким образом 2 раза просишь PHP выполнить код, написанный в нем.
PHP просто выполняет команды которые ты написал последовательно, сверху вниз. Он не думает о том, что например у нас уже есть массив и мы могли бы не обращаться к серверу. Если ты говоришь ему выполнить метод 2 раза то он его и выполнит 2 раза.
> В том то и дело, что в процедурном виде все работает так, как я задумывал.
Поставь туда echo перед отправкой запроса и посмотри сколько раз выведется текст.
> Хрен там, не даст движок ничего запостить пока не получит от Яндекса
Это все равно уязвимость. Пользователь может дописать к запросу произвольное число отдельных параметров. Чтобы понять чем это опасно надо изучить всю докуменатацию по АПИ. Ну условно говоря может там есть параметр типа test=1 который выдает ok независимо от результат или параметр типа deleteMyAccount=1 который вообще удаляет аккаунт.
Так что ты зря споришь. Надо делать нормально, чтобы сразу было видно что тут все безопасно а не так что «может тут и есть уязвимость»
> Я не понимаю почему два раза, если я хочу вторым запросом get() всего навсего забрать из массива то, что в него уже попало. Видимо я просто не понимаю чего-то и хочу помощи.
когда ты пишешь
$object->doSomething()
(обрати внимание на скобки) то это вызов метода. Вызов метода значит что PHP заходит в него и выполняет написанный внутри него код (и шлет запрос на сервер яндекса, и разбирает его и т д). Поставь в начале метода
echo "Send request...\n";
и посмотри сколько раз выведется эта надпись.
Если что, «метод» это функция у объекта.
Если ты 2 раза пишешь $captcha->get() то код в методе выполняется тоже 2 раза и отправляется 2 запроса. Обязательно поставь туда echo и посчитай сам.
> Я не хочу генерировать еще раз, зачем оно второй раз обращается к яндексу?
Потому что ты 2 раза вызываешь метод и таким образом 2 раза просишь PHP выполнить код, написанный в нем.
PHP просто выполняет команды которые ты написал последовательно, сверху вниз. Он не думает о том, что например у нас уже есть массив и мы могли бы не обращаться к серверу. Если ты говоришь ему выполнить метод 2 раза то он его и выполнит 2 раза.
> В том то и дело, что в процедурном виде все работает так, как я задумывал.
Поставь туда echo перед отправкой запроса и посмотри сколько раз выведется текст.
> Хрен там, не даст движок ничего запостить пока не получит от Яндекса
Это все равно уязвимость. Пользователь может дописать к запросу произвольное число отдельных параметров. Чтобы понять чем это опасно надо изучить всю докуменатацию по АПИ. Ну условно говоря может там есть параметр типа test=1 который выдает ok независимо от результат или параметр типа deleteMyAccount=1 который вообще удаляет аккаунт.
Так что ты зря споришь. Надо делать нормально, чтобы сразу было видно что тут все безопасно а не так что «может тут и есть уязвимость»
Та верю я тебе, вижу что два раза вызывается, просто я не понимаю почему. Я вообще не программист и в этом месте догнать не могу.
> Пользователь может дописать к запросу произвольное число отдельных параметров.
Там примитивное АРІ, максимум это он может еще и отправить свое сообщение в Яндекс на спам-проверку. И вообще, как я и говорил, это все пока пофигу. Это полуработующий пример, какая разница что куда можно подставить на локалхосте и какие имена классов, лол.
Потому что ты написал 2 раза команду «вызвать метод который отправляет запрос к яндексу». ты написал команду 2 раза, она 2 раза выполнилась и 2 раза отправила запрос.
> Я вообще не программист
Тогда передай эту задачу программисту
>>473615
Я не заходил на сайт, но просто возпроизвети видеофайл можно тегом video из HTML 5. более сложные вещи делаются на явакрипте (раньше на флеше делали, но он умер).
> Потому что ты написал 2 раза команду «вызвать метод который отправляет запрос к яндексу». ты написал команду 2 раза, она 2 раза выполнилась и 2 раза отправила запрос.
Что ты из пустого в порожнее переливаешь? Знаю я что сделал, не знаю как правильно сделать. Подскажи лучше.
> Тогда передай эту задачу программисту
Как я понял это тред для новичков. Чего ты из себя илитку корчишь?
Если ты не программист и тебе главное решить задачу, и у тебя есть код который работал (процедурный), то зачем что-то исправлять? Просто бери код который работал и используй и забудь про ООП. Зачем время тратить на это?
Если ты хочешь научиться делать правильно или разобраться как это работает, то начинай изучение PHP с самого начала, с переменных, циклов, функций, и тд., а не берись сразу за сложные вещи. Тут есть учебник в первом посте.
>Я не заходил на сайт, но просто возпроизвети видеофайл можно тегом video из HTML 5. более сложные вещи делаются на явакрипте (раньше на флеше делали, но он умер).
Суть в том что при нажатии на кнопку плей, начинает воспроизводится другое видео. Я это только что попытался сделать на ПХП, скачал плейер, поместил в массив адреса нужных мне видео, через rand начал выбирать из массива случайные видео и подключать их в плейер но сделать это у меня получилось только при помощи кнопки формы, то есть под видео есть кнопка формы нажимаешь на ней и видео меняется, но мне надо что бы оно менялось при нажатии на плей.
Это надо с яваскриптом заморачиваться. PHP работает на севрере до отображения страницы, а яваскрипт в браузере уже во время этого.
Я яваскрипт вообще не знаю, есть код самого плейера, но я мало что могу в нём понять, я так понимаю надо что то сделать что бы при определённом событии таком как нажатие на плей менялось видео, но как детектировать где там плей в этом коде.
Я играюсь и пишу сорт оф гостевуху, просто так, от скуки. Вот написал все процедурно, стало скучно, решил переписать классами и наткнулся на проблему. Я, как сисадмин по профессии, не воспринимаю линейные книги, только научным методом. Впрочем, так и скажи, что сам не знаешь, чего демагогию разводить на пару постов.
Твоим методом ты ничему не научишься. Но если ты предпочитаешь его, то попробуй в программу наставить echo. С их помощью ты увидишь что выполняется в каком порядке и может быть сможешь найти причину. Также ты можешь использовать команду var_dump чтобы увидеть что хранится в переменной.
>>473653
Мне что-то лень разбираться в чужом коде. Попробуй сам разобрать, например отладчиком в браузере (Ctrl + Shift + i) посмотри что там происходит на сайте. Вот статья про отладчик: http://habrahabr.ru/post/143767/
> Твоим методом ты ничему не научишься
А вот и не правда. Додумался перенести часть, отвечающую за скачивание XML-ки с Яндекса в __construct, теперь все четко работает.
Все, нашел. Эта ошибка возникает, когда в apache2.conf в теге Directory написано AllowOverride All.
Как тогда делать, чтобы адрес был такой project/name, а не project/index.php/name?
>По моему проблемы нет. Либо ты выбираешь огромный массив данных джойном и внутренними функциями в маппере UserMapper создаешь из них Profile,FriendsList либо если это делается отдельным запросом то это делается в другом маппере.
С первым вариантом все понятно. А вот второй... (пикча) Перечитал немало дискуссий на тему DAO inside another DAO, вощемта, все сходятся во мнении что это зло. Как по-другому вызвать другой маппер вне текущего я хоть убей не вижу.
>Разницу по моему нетрудно запомнить.
Да говорю ж где-то подцепил заразу о том что user это типа entity model, а userhandler/userservice - domain model. А может не про php шла речь. Больше не перепутаю
Кстати, угадай с чем я столкнулся в getFriendList? Проблема n+1. Тут перфекционизм оказался очень кстати я прям сразу увидел что foreach(100 раз) { 'SELECT * ' } это вообще не круто и даже сам ее решил через промежуточный запрос, а потом SELECT ... WHERE ... IN (). А потом уже вспомнил что ты про нее предупреждал.
>Как по-другому вызвать другой маппер вне текущего я хоть убей не вижу.
ААА, нет, стоп-стоп-стоп, придумал!
вместо
$playlist = $this->getPlaylist($id);
$proxy_playlist = ProxyPlaylist::createGreed($playlist);
будет
$playlist= new Playlist;
$proxy_playlist = ProxyPlaylist::createGreed(new MusicMapper, $playlist);
А внутри прокси уже делай что хочешь!
id записей никогда не обновляют так как это требует обновлять все ссылки, чревато багами и т д. Если тебе нужны номера без пропусков, сделай отдельную колонку.
В твоем случае вообще можно генерировать номера на стороне PHP наверно, а не хранить их в базе.
>>473756
> Как по-другому вызвать другой маппер вне текущего я хоть убей не вижу
Нужен вышестоящий объект хранящий в себе все мапперы (или создающий их по мере необходимости).
В доктрине для этого есть метод у EntityManager: $em->getRepository('Friends')
Соответственно если у тебя есть какой-то высокоуровневый объект хранящий все мапперы то можно обращаться к нему и получать маппер из него. Или что лучше, можно сделать в этом объекте метод для создания прокси с указанным маппером. Примерно так:
$proxy = $manager->createProxyFor('Friends', $id)
В доктрине по моему этот метод наызвается createReference.
С его помощью мы получаем прокси для любого маппера и любого id.
> Да говорю ж где-то подцепил заразу о том что user это типа entity model, а userhandler/userservice - domain model
entity это сущность, например Пользователь, Пост и тд.
domain model — модель предметной области — http://en.wikipedia.org/wiki/Domain_model — так часто называют все сущности и связи между ними вместе.
Тут действительно есть путаница что иногда моделью называют 1 класс, а иногда все вместе.
> и даже сам ее решил через промежуточный запрос
да, при жадной выборке надо выбирать связанные сущности через WHERE IN.
Кстати, я думаю что проблему N + 1 можно обнаруживать автоматически. Можно в режиме отладки считать число ленивых загрузок сущностей и при превышении порога выдавать предупреждение на экран или в логи, заодно сообщая каких именно сущностей и сколько было загружено.
Ну и как я писал выше, можно вообще сделать умную систему которая анализируя статистику о ленивых загузках определяет какие сущности надо загружать жадно, а какие лениво так, чтобы минимизировать число запросов или еще какие-то критерии.
id записей никогда не обновляют так как это требует обновлять все ссылки, чревато багами и т д. Если тебе нужны номера без пропусков, сделай отдельную колонку.
В твоем случае вообще можно генерировать номера на стороне PHP наверно, а не хранить их в базе.
>>473756
> Как по-другому вызвать другой маппер вне текущего я хоть убей не вижу
Нужен вышестоящий объект хранящий в себе все мапперы (или создающий их по мере необходимости).
В доктрине для этого есть метод у EntityManager: $em->getRepository('Friends')
Соответственно если у тебя есть какой-то высокоуровневый объект хранящий все мапперы то можно обращаться к нему и получать маппер из него. Или что лучше, можно сделать в этом объекте метод для создания прокси с указанным маппером. Примерно так:
$proxy = $manager->createProxyFor('Friends', $id)
В доктрине по моему этот метод наызвается createReference.
С его помощью мы получаем прокси для любого маппера и любого id.
> Да говорю ж где-то подцепил заразу о том что user это типа entity model, а userhandler/userservice - domain model
entity это сущность, например Пользователь, Пост и тд.
domain model — модель предметной области — http://en.wikipedia.org/wiki/Domain_model — так часто называют все сущности и связи между ними вместе.
Тут действительно есть путаница что иногда моделью называют 1 класс, а иногда все вместе.
> и даже сам ее решил через промежуточный запрос
да, при жадной выборке надо выбирать связанные сущности через WHERE IN.
Кстати, я думаю что проблему N + 1 можно обнаруживать автоматически. Можно в режиме отладки считать число ленивых загрузок сущностей и при превышении порога выдавать предупреждение на экран или в логи, заодно сообщая каких именно сущностей и сколько было загружено.
Ну и как я писал выше, можно вообще сделать умную систему которая анализируя статистику о ленивых загузках определяет какие сущности надо загружать жадно, а какие лениво так, чтобы минимизировать число запросов или еще какие-то критерии.
ого, круто. я ещё вчера Autobahn|JS нагуглил,
это то же самое, что и Ractchet, только на js, или нет? Возможно, Ractchet это серверное решение, а Autobahn|JS клиентское?
но для вебсокета нужно поднять отдельный сервер, или как? На обычном хостинге это возможно?
> Эта ошибка возникает, когда в apache2.conf в теге Directory написано AllowOverride All.
Это не причина ошибки. Ты просто отключил чтение файла htaccess (в котором настоящая ошибка). Причина ошибки в логах, посмотри их.
> Как тогда делать, чтобы адрес был такой project/name, а не project/index.php/name?
Исправить эту ошибку, так как она явно связана с ошибкой в htaccess файле. А htaccess файл нужен чтобы работали красивые URL, он перенаправляет все обращения к сайту на index.php
По умолчанию в базе даты хранятся в виде yyyy-mm-dd, а на сайте должны выводиться в привычном виде dd.mm.yyyy.
Сейчас у меня две функции под это дело написаны, которые гоняют дату туда сюда из одного формата в другой, можно как-нибудь избавиться от этого костыля?
Так же интересует вопрос, как сделать так, что бы таймстамп ставился в столбец created при создании записи, а при изменении timestamp ставился в столбец edited
> но для вебсокета нужно поднять отдельный сервер, или как?
Твое PHP приложение и есть сервер. Оно само открывает порт, принимает запросы и передает данные. Ты должен его прописать в автозагрузку (в линуксе это bash-скрипт в папке /etc/init.d, а в системах с новомодным systemd это конфиг-файл в какой-то папке) на сервере, причем запускать надо не напрямую, а из-под программы супервизора вроде monit, supervisord которая будет вести логи, перезапускать при падениях и тд.
Для этого нужны права администратора на севрере. Если у тебя шаред хостинг то тебе либо надо будет вручную запускать это приложение либо вообще это запрещено.
> это то же самое, что и Ractchet, только на js, или нет?
Нет, по моему это реализация системы событий поверх вебсокетов. ну то есть то что тебе пригодится.
на сайте же написано:
> provides an open-source implementation of The Web Application Messaging Protocol (WAMP) .
WAMP это протокол работающий поверх вебсокет-соединения. Без него ты сам должен придумывать форматы пакетов, сам их создавать, парсить и тд. Ведь вебсокет это просто канал по которому можно пересылать строки текста.
в Ratchet есть модуль для поддержки WAMP на сервере.
> Сейчас у меня две функции под это дело написаны, которые гоняют дату туда сюда из одного формата в другой, можно как-нибудь избавиться от этого костыля?
При загрузке из Бд надо делать преобразование дат. Не знаю что ты за функцию написал, но из MySQL формата преобразуется так:
$timestamp = strtotime($mysqlDate);
$datetime = new DateTime($mysqlDate)
А обратно так:
$mysqlDate = date('Y-m-d H:i:s', $timestamp);
$mysqlDate = $datetime->format(...);
> как сделать так, что бы таймстамп ставился в столбец created при создании записи, а при изменении timestamp ставился в столбец edited
http://stackoverflow.com/questions/4897002/mysql-current-timestamp-on-create-and-on-update
https://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html
http://baron.su/content/mysql-i-avtomaticheskoe-obnovlenie-taymstampov
> Вместе с тем, мне больше по душе другой вариант, реализация через триггеры
Вот это не делай
так же как и любую другу программу. из командной строки ssh. Хостинг может запрещать долгоживующие приложения, имей в виду.
вот тут можно почитать по основы командной строки: https://gist.github.com/codedokode/10539568
Нужен ssh доступ разумеется.
По умолчанию при завершении ssh сессии прогаммы запущенные из нее завешаются, обойти это можно команжой nohup
>то можно обращаться к нему и получать маппер из него. Или что лучше, можно сделать в этом объекте метод для создания прокси с указанным маппером
Все, кристально ясно.
А вот нужно ли вообще делать опцию жадно/лениво для загрузки юзеров через getFriendList, getCommunityMembers и т.д.? Не могу так сходу представить зачем много юзеров может целиком понадобиться. Код бы упростился.
Спасибо за пояснения. У меня теперь следующий вопрос: Если я хочу хранить все даты в базе в едином формате, то мне придется хранить их в формате TIMESTAMP? Я хотел бы хранить их в DATETIME просто. Но не получилось создать столбец
с типом DATETIME и атрибутом on update current timestamp.
Видимо этот атрибут можно делать только с типом timestamp? Вообще прочитал про типы http://www.mysql.ru/docs/man/DATETIME.html
http://www.spravkaweb.ru/mysql/sql/vartype
и у меня собственно возникло ощущение, что хоть TIMESTAMP и выглядит в базе точно так же как DATETIME
но
>TIMESTAMP \tДата и время в формате timestamp. Однако при получении значения поля оно отображается не в формате timestamp, а в виде ГГГГММДДЧЧММСС, что сильно умаляет преимущества его использования в PHP
и по факту я буду иметь столбцы с разными форматами, и мне придется писать дополнительные php функции для приведения дат к одинаковому виду.
>Не знаю что ты за функцию написал, но из MySQL формата преобразуется так...
Вот мои функции конвертирования дат:
http://ideone.com/y1OyPi
"SELECT DISTINCT orders.id, orders.user_id,orders.area,orders.address, orders.user_name, orders.email, orders.phone, orders.address,DATE_FORMAT(orders.time_order, '%d:%m:%Y %H:%i') FROM orders, rule_group, rul_areas WHERE rule_group.id_units='7' AND rule_group.id_group=orders.us_g AND rul_areas.id_agency='3' AND rul_areas.id_area=orders.area OR orders.user_id='0' ORDER BY orders.id DESC LIMIT 0, 50"
Тут суть в том, что менеджерам в зависимости от их отдела и региона показываются заказы(допустим одному менеджеру показывает одну группу пользователей, а другому другую). Но у пользователей не зарегистрированных на сайте нет группы и их не отображает в запросе, поэтому пришлось дописать "OR orders.user_id='0'"(гостям присвается айди равный нулю).
Так вот, когда в конце появляется OR orders.user_id='0' запрос начинает работать так медленно, что браузер не всегда дожидается его выполнения.
Это странно, вроде всё просто, даже некуда оптимизировать.
Повторюсь: дело не в основном запросе, он работает мгновенно, долго выполняется только когда в конце я прописываю OR.(не ругайте, запрос не я писал)
нету, абу трет все html треды, не хочет конкуренции
То есть ты хочешь юзеров в этом случае только лениво грузить? Ну можешь не делать, если думаешь что это не понадобится. Но лучше бы конечно подумать как сделать эту опцию без усложнени кода.
Как я понимаю, загрузка FriendsList идет в 2 (или 3) этапа:
1) грузим список друзей + мин. инф. о юзере одним джойном (или 2 запросами, первым получаем id, вторым самих пользователей)
2) если надо, грузим что-то еще для этих пользователей жадно отдельными запросами.
Второй этап по моему можно как-то сделать за счет кода в UserMapper, разве нет? То есть сделать в юзер-маппере функцию вроде preloadUserData(User[] $users, array $greedyLoad) и вызывать ее. ну и все функции которые выбирают пользователей, могли бы ее использовать.
Ну это так, просто рассуждения, если не надо то можешь это не делать.
> Но не получилось создать столбец
с типом DATETIME и атрибутом on update current timestamp.
Почитай разниц между этими тиами. DATETIME это абстрактное время без указания часового пояса. TIMESTAMP это настоящее время с часовым поясом, которое однозначно определяет точку во времени.
> Дата и время в формате timestamp. Однако при получении значения поля оно отображается не в формате timestamp, а в виде ГГГГММДДЧЧММСС, что сильно умаляет преимущества его использования в PHP
По моему это давно неактуально. Ты можешь проверить на практике, сделав запрос. Ну и если что то такой формат тоже можно распарсить.
> и мне придется писать дополнительные php функции для приведения дат к одинаковому виду.
Это часть задачи преобразователя данных между БД и PHP. Если ты захочешь true/false хранить то их тоже придется в 0/1 преобразовывать например.
> Вот мои функции конвертирования дат:
Они время не сохраняют, я бы сразу время приписал.
О, блин, всякий раз когда я думаю что уже достиг дна боли мне стучатся снизу. Что если потребуется передавать параметры для выборок самих связанных сущностей? Например, если мне нужны не все друзья, а первые 5, или песни определенного жанра? Раздувать параметры исходного метода?
getUserById($id, $friend_limit, $favorite_music_genre)
Да нет, нереально такой балаган потом разгрести. Да это вообще нереально сделать.
> Но у пользователей не зарегистрированных на сайте нет группы и их не отображает в запросе, поэтому пришлось дописать "OR orders.user_id='0'"(гостям присвается айди равный нулю).
Неправильно, надо писать NULL так как если ты пишешь 0 то ты не можешь проставить внешние ключи (а ты их не проставил, зря, почитай на досуге статью http://denis.in.ua/foreign-keys-in-mysql.htm )
Также у тебя не стоят скобки и я не понимаю к чему именно относится OR. Если ты смешиваешь AND/OR надо ставить скобки чтобы не было путаницы. Я наизусть их приоритеты не помню.
> Так вот, когда в конце появляется OR orders.user_id='0' запрос начинает работать так медленно,
> Это странно, вроде всё просто, даже некуда оптимизировать.
Если все так просто почему он долго работает? Если бы все было просто то умение оптимизировать было бы не нужно — все бы само работало.
Все зависит от версии MySQL (старые не умеют мерджить индексы при OR) и от наличия индексов в табице. Оптимизация сложная тема и требует практики. Вот тебе список статей для изучения; прочти их все после чего попробуй сделать EXPLAIN и выяснить из него причину тормозов.
Ты можешь запостить результат EXPLAIN, я тоже гляну и скажу на что еще можно обратить внимание. Также напиши какие индексы есть (увидеть можно сделав SHOW CREATE TABLE x) и какие из них по твоему mysql могла бы использовать тут.
Ссылки вот в этом уроке: https://gist.github.com/codedokode/10539213#%D0%98%D0%BD%D0%B4%D0%B5%D0%BA%D1%81%D1%8B
Скопирую их сюда
http://ruhighload.com/post/%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0+%D1%81+%D0%B8%D0%BD%D0%B4%D0%B5%D0%BA%D1%81%D0%B0%D0%BC%D0%B8+%D0%B2+MySQL
http://www.mysql.ru/docs/man/MySQL_indexes.html
http://habrahabr.ru/post/211022/
Современные весии MySQL умеют объединять индексы при OR (разумеется для этого нужные индексы должны существовать). Это описано тут
https://dev.mysql.com/doc/refman/5.0/en/index-merge-optimization.html (англ)
Если у тебя есть индекс по x и по y то запрос WHERE x = 1 OR y =2 сможет использовать оба (до 5-й версии этот запрос не мог использовать индексы вообще). Но у тебя там в запросе еще ORDER так что все чуть сложнее чем в моем примере.
Но прочти сначала статьи по ссылкам.
>>474028
> Например, если мне нужны не все друзья, а первые 5, или песни определенного жанра? Раздувать параметры исходного метода?
Лучше несколько методов. А они могут вызывать универсальный внутренний метод выборки.
В унивесальный метод можно передавать либо массив условий либо сделать класс Criteria задающий параметры выбора и сортировки.
В доктрине (я же советовал тебе ее изучить) вообще есть минимум 3 варианта (перечислены тут http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#querying ). Для простых случаев там есть такой метод:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-simple-conditions
> $tenUsers = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0);
Аргументы функции find такие: http://www.doctrine-project.org/api/orm/2.2/class-Doctrine.ORM.EntityRepository.html#_find
Также условия можно указать объектом класса Criteria:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-criteria
Для сложных случаев ты пишешь запрос на языке DQL напоминающем SQL:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-dql
Да, они сделали свой язык запросов, свой парсер этого языка, дерево узлов AST и еще кучу всего. Я же предупреждал?
Тебе впрочем наверно можно обойтись без своего языка.
Современные весии MySQL умеют объединять индексы при OR (разумеется для этого нужные индексы должны существовать). Это описано тут
https://dev.mysql.com/doc/refman/5.0/en/index-merge-optimization.html (англ)
Если у тебя есть индекс по x и по y то запрос WHERE x = 1 OR y =2 сможет использовать оба (до 5-й версии этот запрос не мог использовать индексы вообще). Но у тебя там в запросе еще ORDER так что все чуть сложнее чем в моем примере.
Но прочти сначала статьи по ссылкам.
>>474028
> Например, если мне нужны не все друзья, а первые 5, или песни определенного жанра? Раздувать параметры исходного метода?
Лучше несколько методов. А они могут вызывать универсальный внутренний метод выборки.
В унивесальный метод можно передавать либо массив условий либо сделать класс Criteria задающий параметры выбора и сортировки.
В доктрине (я же советовал тебе ее изучить) вообще есть минимум 3 варианта (перечислены тут http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#querying ). Для простых случаев там есть такой метод:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-simple-conditions
> $tenUsers = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0);
Аргументы функции find такие: http://www.doctrine-project.org/api/orm/2.2/class-Doctrine.ORM.EntityRepository.html#_find
Также условия можно указать объектом класса Criteria:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-criteria
Для сложных случаев ты пишешь запрос на языке DQL напоминающем SQL:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-dql
Да, они сделали свой язык запросов, свой парсер этого языка, дерево узлов AST и еще кучу всего. Я же предупреждал?
Тебе впрочем наверно можно обойтись без своего языка.
$rus=array('А','Б','В','Г','Д','Е','Ё','Ж','З','И','Й','К','Л','М','Н','О','П','Р','С','Т','У','Ф','Х','Ц','Ч','Ш','Щ','Ъ','Ы','Ь','Э','Ю','Я','а','б','в','г','д','е','ё','ж','з','и','й','к','л','м','н','о','п','р','с','т','у','ф','х','ц','ч','ш','щ','ъ','ы','ь','э','ю','я',' ');
$lat=array('a','b','v','g','d','e','e','gh','z','i','y','k','l','m','n','o','p','r','s','t','u','f','h','c','ch','sh','sch','y','y','y','e','yu','ya','a','b','v','g','d','e','e','gh','z','i','y','k','l','m','n','o','p','r','s','t','u','f','h','c','ch','sh','sch','y','y','y','e','yu','ya','-');
$upload->name = str_replace($rus, $lat, $upload->name);
\t
То есть не меняет кирилицу на латиницу, в то время как в процедурном стиле спокойно меняет.
я имею ввиду, чтобы было
vendor
----css
----templates
----classes
----....
----autoload.php
----index.php
Вендор это папка композера куда он скачивает и кладет библиотеки. Там не должно быть ни строчки твоего кода и ты в ней не должен ничего трогать.
Это просто ужас. Все на что меня хватит это
getUserById($id, $method = LOAD_LAZY, array $conditions)
Пример $conditions: ['friendlist' => ' LIMIT 10', 'playlist' => ' AND genre = jazz' ]
там свичи, дальше рассовывание по конструкторам менеджера проксей, передача в мапперы и конкат в конец запроса
>Лучше несколько методов. А они могут вызывать универсальный внутренний метод выборки.
Имеешь ввиду что-то типа
UserMapper
{
getUserById ($id) {... return getUser(['user_id'],[$id])}
getUserByEmail ($email) {... return getUser(['email'],[$email])}
getUserByEmailAndPassword ($email, $password) {... return getUser(['email','password'],[$email,$password])}
getUser(array $fields, array $values, array $conditions) {тут вся логика выборки}
}
?
Вот есть у меня например таблица юзеров
user_id
user_name
Далее таблица задач
task_id
task_title
task_body
user_id //кто создал задачу, связано с user_id в юзерах
Далее допустим я хочу сделать 3 таблицу с комментариями
comment_id
comment_body
task_id //кто над ней работает и оставляет комментарии, связано с task_id
user_id // и теперь вот тут ВОПРОС я хочу связать это поле с user_id в юзерах. Можно ли так делать? У меня получится замкнутый круг из связей. Или это сразу фейл в проектировке базы?
Ты прав, я что-то затупил, это ведь у comment будет как бы 2 родительские, а сам он может быть удален.
Я ставлю там галочки on update restrict
Так что теперь невозможно почти ничего удалять в базе, только если прям подчистить все комментарии.
Ну было бы странно вместе с коментом удалять автора или запись где он сделан. restrict в 95% правильный выбор.
извиняюсь, случайно склеил индексы
Это должны быть разные функции. Ну подумай головой:
найти юзера по id и по условию
Если у тебя есть id зачем тебе условие? Это должны быть 2 отдельные функции, вроде find в доктрине (ищет 1 запись по id), findBy (ищет много по условию) и findOneBy (ищет 1 по условию). Ну и дополнительные функции если нужны какие-то специфичные выборки. Все уже придумано до нас.
> getUserByEmai
Доктрина реализует метод findBySomething для любого поля за счет магического метода call, но тебе думаю проще использовать массив чтобы искать по email.
>>474321
Не можно а нужно прописать связи с помощью внешних ключей: http://denis.in.ua/foreign-keys-in-mysql.htm
Праивльная мысль.
>>474356
Там условие имеет другой смысл: надо ли удалять комментарии при удалении их автора. Или же надо запрещать такое? Или же менять user-id на NULL у комментария?
>>474416
Ты прочел статью про типы выборки и про индексы? Прочти тут внимательно http://www.mysql.ru/docs/man/EXPLAIN.html
MySQL не обладает какой-то особой магией. Если в запросе несколько таблиц, она должна выбрать все подходящие записи из первой, присоединить к каждой записи из второй и так далее. Без индексов это дает огромное число записей и как следствие медленный запрос. Индекс если ты прочел статью, позволяет делать 2 вещи:
— быстро отобрать небольшую часть записей по условию, например WHERE x = 1
— отобрать записи в отсортированном порядке например в случае ORDER BY x LIMIT 10 индекс по x значительно ускорит запрос.
Если ты это не понял то тебе надо остановиться, вернуться к статьям, поэкспериментировать на простых запросах с одной таблицей с большим числом записей, порисовать индексы, пока не начнешь понимать эти вещи на интуитивном уровне.
Теперь разберем твой запрос.
У тебя в первой строчке стоит type = ALL. Это самое плохое (если только у тебя в таблице не 10 строк), это полный обход всей таблицы, самый медленный способ выборки.
В колонке rows видно что читается 7000 строк (что кстати немного и такое число за долю секунды обходится если памяти достаточно).
Почему так? давай посмотрим правее:
possible_keys — какие индексы теоретически можно было бы использовать
key — какой был использован (никакой не был, потому и тип ALL)
Если посмотреть на запрос и посмотреть какое у нас стоит условие выборки из orders:
SELECT FROM orders ... WHERE (.... условия к другим таблицам ... ) OR order.user_id = 0
Условие OR тут не дает применить индекс так как за счет OR в выборку попадают не только записи где user_id = 0, а и с любым другим значением.
По той же причине нельзя начать с таблицы rule_group и выбрать из нее записи с id_units = 7 так как OR нейтрализует это условие делая его необязательным.
Соответственно мы имеем полный перебор таблицы, к каждой строчке присоединяется аж по 40 строк из второй и к каждой этой строке присоединяется по 200 строк из третьей. Тип index который там использован это второй после all по медленности. Это уже скорее всего из-за отстутсвия нужных индексов.
Тебе надо попробовать как-то переписать запрос. Так, чтобы можно было отобрать небольшую часть записей из одной таблицы и присоединять к ним другие индексом. Может тут стоит вместо user_id = 0 испольщовать LEFT JOIN вместо INNER JOIN который ты используешь.
Если ты плохо понял о чем я пишу, то вернись к индексам и изучи их пока не начнешь понимать. Я могу подсказать что-то по простым примерам, например с одной таблицей.
Это должны быть разные функции. Ну подумай головой:
найти юзера по id и по условию
Если у тебя есть id зачем тебе условие? Это должны быть 2 отдельные функции, вроде find в доктрине (ищет 1 запись по id), findBy (ищет много по условию) и findOneBy (ищет 1 по условию). Ну и дополнительные функции если нужны какие-то специфичные выборки. Все уже придумано до нас.
> getUserByEmai
Доктрина реализует метод findBySomething для любого поля за счет магического метода call, но тебе думаю проще использовать массив чтобы искать по email.
>>474321
Не можно а нужно прописать связи с помощью внешних ключей: http://denis.in.ua/foreign-keys-in-mysql.htm
Праивльная мысль.
>>474356
Там условие имеет другой смысл: надо ли удалять комментарии при удалении их автора. Или же надо запрещать такое? Или же менять user-id на NULL у комментария?
>>474416
Ты прочел статью про типы выборки и про индексы? Прочти тут внимательно http://www.mysql.ru/docs/man/EXPLAIN.html
MySQL не обладает какой-то особой магией. Если в запросе несколько таблиц, она должна выбрать все подходящие записи из первой, присоединить к каждой записи из второй и так далее. Без индексов это дает огромное число записей и как следствие медленный запрос. Индекс если ты прочел статью, позволяет делать 2 вещи:
— быстро отобрать небольшую часть записей по условию, например WHERE x = 1
— отобрать записи в отсортированном порядке например в случае ORDER BY x LIMIT 10 индекс по x значительно ускорит запрос.
Если ты это не понял то тебе надо остановиться, вернуться к статьям, поэкспериментировать на простых запросах с одной таблицей с большим числом записей, порисовать индексы, пока не начнешь понимать эти вещи на интуитивном уровне.
Теперь разберем твой запрос.
У тебя в первой строчке стоит type = ALL. Это самое плохое (если только у тебя в таблице не 10 строк), это полный обход всей таблицы, самый медленный способ выборки.
В колонке rows видно что читается 7000 строк (что кстати немного и такое число за долю секунды обходится если памяти достаточно).
Почему так? давай посмотрим правее:
possible_keys — какие индексы теоретически можно было бы использовать
key — какой был использован (никакой не был, потому и тип ALL)
Если посмотреть на запрос и посмотреть какое у нас стоит условие выборки из orders:
SELECT FROM orders ... WHERE (.... условия к другим таблицам ... ) OR order.user_id = 0
Условие OR тут не дает применить индекс так как за счет OR в выборку попадают не только записи где user_id = 0, а и с любым другим значением.
По той же причине нельзя начать с таблицы rule_group и выбрать из нее записи с id_units = 7 так как OR нейтрализует это условие делая его необязательным.
Соответственно мы имеем полный перебор таблицы, к каждой строчке присоединяется аж по 40 строк из второй и к каждой этой строке присоединяется по 200 строк из третьей. Тип index который там использован это второй после all по медленности. Это уже скорее всего из-за отстутсвия нужных индексов.
Тебе надо попробовать как-то переписать запрос. Так, чтобы можно было отобрать небольшую часть записей из одной таблицы и присоединять к ним другие индексом. Может тут стоит вместо user_id = 0 испольщовать LEFT JOIN вместо INNER JOIN который ты используешь.
Если ты плохо понял о чем я пишу, то вернись к индексам и изучи их пока не начнешь понимать. Я могу подсказать что-то по простым примерам, например с одной таблицей.
Молодец, все верно почитал.
>>474111
str_replace работает одинаково везде. Сделай короткий пример кода где он не работает и выложи на ideone, мы глянем. Также ты можешь глянуть в логи php (по моему они в папке Апача по умолчанию) и поискать там причину ошибки. У тебя наверно вывод ошибок на экран отключен и ты их не видишь.
Вот у этого >>474241 анона все работает.
>>474267
Твой код должен быть снаружи vendor. Это позволяет легко увидеть где чей код.
>>474283
preg_match находит только первое совпадение с шаблоном. То что он вернул это массив с кусками найденного текста.
Чтобы найти все совпадения нужен preg_match_all он есть в мануале и в уроке где-то описан.
>>474301
Сложно сказать в абстрактном случае. Покажи заголовок и название функции.
>>474309
Диванные оптимизаторы пожаловали в тред?
Молодец, все верно почитал.
>>474111
str_replace работает одинаково везде. Сделай короткий пример кода где он не работает и выложи на ideone, мы глянем. Также ты можешь глянуть в логи php (по моему они в папке Апача по умолчанию) и поискать там причину ошибки. У тебя наверно вывод ошибок на экран отключен и ты их не видишь.
Вот у этого >>474241 анона все работает.
>>474267
Твой код должен быть снаружи vendor. Это позволяет легко увидеть где чей код.
>>474283
preg_match находит только первое совпадение с шаблоном. То что он вернул это массив с кусками найденного текста.
Чтобы найти все совпадения нужен preg_match_all он есть в мануале и в уроке где-то описан.
>>474301
Сложно сказать в абстрактном случае. Покажи заголовок и название функции.
>>474309
Диванные оптимизаторы пожаловали в тред?
>Чтобы найти все совпадения нужен preg_match_all он есть в мануале
Очень ленюсь читать что-то кроме урока (а там до задания точно не было про all), надо побороть это в себе.
Все правильно теперь?
http://ideone.com/kff7y8
А там, про preg_match_all сразу после задания написано, извиняюсь.
blog.com/single.php?id=12
Мне надо подключить к этому посту возможность комментирования, но после нажатия кнопки (submit) в форме, id из ссылку убирается а следовательно и убирается сам пост, остаётся только пустой шаблон. Как избежать этого?
В дополнении скажу что попробовал решить эту проблему тем способом, что вынес добавку комментария на другую страницу, на которую помимо всех нужных данных передаю в скрытой форме id записи, если всё успешно добавляется то делаю обратно перенаправленнее на страницу вывода поста и передаю через ссылку переменную в этим самым id который сохранил в скрытой форме. Это кажется очень костыльно.
Огромный тебе респект за твои задачи и помощь. Ты очень хороший человек.
И респект все тем кто помогал и помогает новичкам в этом треде.
Люблю вас
О, это очень неплохо, какие тестовые задания были при приёме на работу, какими скилами обладал когда устраивался?
http://ideone.com/cayqOl
>PHP Notice: Undefined variable: testNumder in /home/E5g0Ke/prog.php on line 20
>PHP Notice: Undefined variable: testNumder in /home/E5g0Ke/prog.php on line 21
Хочешь расскажу? у меня тестовым заданием было написать хуйню, которая считает среднее арифметическое всех элементов массива. Я подумал что это задание с подвохом, вспомнил посты ОП-а, когда в каждом таком случае он на мои несколько строк в коде придлагал готовую php-функцию
и написал
$a = array_sum($arr) / count($arr);
После чего меня попросили написать через цикл, лол.
3 недели уже работаю.
>>474623
Новосиб, ну учебник прорешал почти до кошек-мышек. С MySQL ковырялся, создавал гостевуху например. Далее по мелочи html и css, сейчас бутстрап немного знаю уже. Ну что еще могу сказать, до этого брался за несколько тестовых заданий на джуна в крутые конторы, но еще до знания ООП и mvc, писал кучу лапши и дропал от несовершенства и неуверенности в себе. Это кстати уже второй мой успех в устройстве на работу, первый раз взяли вообще нихуя не спросив по сути, а что спросили - то я ответил что не могу. Так что советую тебе делать больше попыток. Отсылать резюме и прочее говно. На выходных набыдлокодил вот http://serdce-kamnya.comeze.com раковую поделку для ХС-треда (если не знаком с игрой и мемсами, то покажется еще большей хуйней чем есть)
скрипт показывать пока что стыдно, внутри пиздец полный и мрак. Пока что ссессиями даже толком не разобрался и там дублируются куски кода через if,ы
Сижу смотрю курсы специалиста с 0, что бы заполнить пробелы, и делаю это прямо сейчас, если бы мог вернуть время назад, то сразу бы их начал смотреть не ебя вола. ОП очень классный парень и многим помог, но его учебник не сделает из вас специалистов, нужно дохуя еще чего читать. Ну или паразитировать на опытных людях (на том же ОП-е треда например) Как-то так. Вот кстати картинка хорошая, себе схоронил и с вами делюсь.
А ну и да, я не >>474579 этот парень.
class A {function callClassName() { return ???; }}
class B extends A {}
$b=new B;
echo $b->callClassName();
Что надо написать вместо ??? чтоб написало В?
При __CLASS__ пишет А
При get_class($this) пишет ничего и Warning: First parameter must either be an object or the name of an existing class in
Хотя var_dump(get_class($this)) пишет В
Везде по 5 звезд ради упрощения. Чем больше скилл, тем больше шанс на победу. Скидывай сезон и будет лучше винрейт в следующем.
Ну блин, ты точно собрался быть веб разработчиком, если не можешь гугл открыть?
https://www.google.ru/?gfe_rd=cr&ei=GE3DU837IpWEYMrlgbAL&gws_rd=ssl#newwindow=1&q=%D1%81%D0%BF%D0%B5%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D1%81%D1%82+php+2013+%D1%82%D0%BE%D1%80%D1%80%D0%B5%D0%BD%D1%82
Класс для работы с БД один имхо, а в нем два метода соответственно 1 для скачивания, второй для аплоада. Сам пока не делала, но имхо так бы сделал.
>>474645
http://ideone.com/1NBibJ
Хз в чем у тебя проблема, может ошибка в написании просто была?
Идею поня, интересно, сейчас добиваю файлообменник, вроде бы немного понимаю как можно реализовать что ты говоришь, попробую что то написать.
Предложили сделать 3 тестовых задания. Задания на алгоритмы. Я их сделаю(кстати, одно из них на поиск минимального пути в графе, так что делайте задачку про станции метро).
Но возникла проблема. Требуют, что бы код считывал с консоли значения чисел? Я нашел мануал, но есть ли с годными пояснениями для нубов? http://docstore.mik.ua/orelly/webprog/pcook/ch20_04.htm
Надо добавлять в форму на самой страниц скрытое поле с id
> если всё успешно добавляется то делаю обратно перенаправленнее на страницу вывода поста
Вроде нормально. После успешной обработки пост запроса надо в любом случае делать редирект.
Вот мой урок про работу с формами: https://github.com/codedokode/pasta/blob/master/forms.md может там есть что полезное
>>474569
хтмлакадеми вроде неплохой уровень, кодеакадеми вроде примитивный. А ты не хочешь прорешать наши задания из первого поста треда? Там в конце верстка сайта из макета и у тебя будет неплохой базовый навык верстки в том числе все эти флоаты, инлайн блоки и т д .
Верстку лучше бы знать, ты же не будешь звать верстальика переставить кнопку.
>>474579
Молодец! Пусть твой пост посрамит скептиков которые думают что у нас тут все несерьезно.
Только не забывай что от джуниора ждут ежедневного впитывания новых знаний и развивайся дальше. Почивать на лаврах пока рано.
Опечатка в переменной, смотри сообщения внизу.
>>474622
> После чего меня попросили написать через цикл
Они наверно не знали про array_sum и сами решали циклом :) Кстати прочти тогда и про функции array_column, array_map и array_count_values — они иногда тоже позволяют заменить цикл. Ну если ты решал задачу про Маяковского то наверно вспомнишь как max + array_map позволяют найти максимальную длину строки в массиве строк без циклов.
>>474673
Почитай сначала урок про работу с Бд. Я советую тебе как и другим сделать тут data mapper: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
И внимательно перечитай комментарии к задаче про студентов (ты ее пропустил? зря, зря), там много универсальных советов: https://github.com/codedokode/pasta/blob/master/student-list.md
>>474707
> требуют, что бы код считывал с консоли значения чисел?
Ты умеешь работать с файлами? В линукс (и в PHP как следствие) потоки ввода с клавитатуры и вывода на экран очень похожи на файлы и с ними можно работать теми же функциями: fwrite, fread, fgets, file_get_contents (почитай мануал по этим и по соседним с ними функциями, лучше весь раздел про работу с файлами)
Но есть одна разница, что файл ты обычно должен открыть (или передать его имя чтобы функция открыла его за тебя), после открытия ты получаешь дескриптор файла который передаешь в функции работы с файлом.
Если ты этого не знаешь опять же полистай этот раздел: http://php.net/manual/ru/book.filesystem.php
Для потока ввода с консоли (то есть с клавиатуры) открывать ничего не надо. Он уже открыт и его дескриптор хранится в константе STDIN: http://php.net/manual/ru/features.commandline.io-streams.php
Заметь что он доступен только при запуске из консоли (что логично).
На тот случай если он не открыт, ты можешь просто передавать специальный путь php://stdin вместо имени файла. Это описано тут:
http://php.net/manual/ru/wrappers.php.php
Это может показаться сложным для начинающего потому можешь задавать вопросы (именно по вводу-выводу).
Вот еще тебе гайд по командной строке: https://gist.github.com/codedokode/10539568
Ну и для тестирования тебе наверно надоест руками вводить исходные данные. Консоль позволяет автоматизировать это и перенаправить данные из файла на стандартный вход программы (то есть сделать вид что их ввел пользователь). Это делается так:
php program.php < file.txt
Описание перенаправления потоколв ввода вывода:
https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BD%D0%B0%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2%D0%B2%D0%BE%D0%B4%D0%B0-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4%D0%B0
Windows: http://geektimes.ru/post/60604/
http://cmcmsu.no-ip.info/1course/dos.io.redirect.htm
http://www.windowsfaq.ru/content/view/260/57/
linux: http://rus-linux.net/book1.php?name=book1/gl-05/gl_05_05.html
http://younglinux.info/book/export/html/214
http://xgu.ru/wiki/%D0%A1%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D1%8B%D0%B5_%D0%BF%D0%BE%D1%82%D0%BE%D0%BA%D0%B8_%D0%B2%D0%B2%D0%BE%D0%B4%D0%B0/%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4%D0%B0
ОБрати внимание что потоков вывода два: стандартный вывод stdout и поток ошибок stderr. Это сделано чтобы ты мог сохранять результаты работы программы в один файл, а ошибки в другой или на консоль. Хорошая программа пишет ошиьки именно в поток ошибок (echo пишет в стандартный вывод и не подходит, нужен fwrite(STDERR, ...))
Эти знания тебе скорее всего еще пригодятся так что почитай.
Опечатка в переменной, смотри сообщения внизу.
>>474622
> После чего меня попросили написать через цикл
Они наверно не знали про array_sum и сами решали циклом :) Кстати прочти тогда и про функции array_column, array_map и array_count_values — они иногда тоже позволяют заменить цикл. Ну если ты решал задачу про Маяковского то наверно вспомнишь как max + array_map позволяют найти максимальную длину строки в массиве строк без циклов.
>>474673
Почитай сначала урок про работу с Бд. Я советую тебе как и другим сделать тут data mapper: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
И внимательно перечитай комментарии к задаче про студентов (ты ее пропустил? зря, зря), там много универсальных советов: https://github.com/codedokode/pasta/blob/master/student-list.md
>>474707
> требуют, что бы код считывал с консоли значения чисел?
Ты умеешь работать с файлами? В линукс (и в PHP как следствие) потоки ввода с клавитатуры и вывода на экран очень похожи на файлы и с ними можно работать теми же функциями: fwrite, fread, fgets, file_get_contents (почитай мануал по этим и по соседним с ними функциями, лучше весь раздел про работу с файлами)
Но есть одна разница, что файл ты обычно должен открыть (или передать его имя чтобы функция открыла его за тебя), после открытия ты получаешь дескриптор файла который передаешь в функции работы с файлом.
Если ты этого не знаешь опять же полистай этот раздел: http://php.net/manual/ru/book.filesystem.php
Для потока ввода с консоли (то есть с клавиатуры) открывать ничего не надо. Он уже открыт и его дескриптор хранится в константе STDIN: http://php.net/manual/ru/features.commandline.io-streams.php
Заметь что он доступен только при запуске из консоли (что логично).
На тот случай если он не открыт, ты можешь просто передавать специальный путь php://stdin вместо имени файла. Это описано тут:
http://php.net/manual/ru/wrappers.php.php
Это может показаться сложным для начинающего потому можешь задавать вопросы (именно по вводу-выводу).
Вот еще тебе гайд по командной строке: https://gist.github.com/codedokode/10539568
Ну и для тестирования тебе наверно надоест руками вводить исходные данные. Консоль позволяет автоматизировать это и перенаправить данные из файла на стандартный вход программы (то есть сделать вид что их ввел пользователь). Это делается так:
php program.php < file.txt
Описание перенаправления потоколв ввода вывода:
https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BD%D0%B0%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2%D0%B2%D0%BE%D0%B4%D0%B0-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4%D0%B0
Windows: http://geektimes.ru/post/60604/
http://cmcmsu.no-ip.info/1course/dos.io.redirect.htm
http://www.windowsfaq.ru/content/view/260/57/
linux: http://rus-linux.net/book1.php?name=book1/gl-05/gl_05_05.html
http://younglinux.info/book/export/html/214
http://xgu.ru/wiki/%D0%A1%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D1%8B%D0%B5_%D0%BF%D0%BE%D1%82%D0%BE%D0%BA%D0%B8_%D0%B2%D0%B2%D0%BE%D0%B4%D0%B0/%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4%D0%B0
ОБрати внимание что потоков вывода два: стандартный вывод stdout и поток ошибок stderr. Это сделано чтобы ты мог сохранять результаты работы программы в один файл, а ошибки в другой или на консоль. Хорошая программа пишет ошиьки именно в поток ошибок (echo пишет в стандартный вывод и не подходит, нужен fwrite(STDERR, ...))
Эти знания тебе скорее всего еще пригодятся так что почитай.
мануал древний. fopen в принципе необязателен так как поток ввода уже открыт и его дескриптор хранится в STDIN.
Также там не описана удобная функция fgets читающая одну строку из потока.
Ребята короче такая тема, надо создавать временный архив с набором файлов и в дальнейшем скачивать его с последующим удалением.
Генерацию архива реализую средствами пхп ZipArchive.
Но при $zip->addFile('C://[имя файла]); создается пустой архив, но с объемом не равным 0. Как пофиксить?
ЗЫ: $zip->addFile('[имя файла в корневой папке сайта]'); все ок
>>474722
Был на уровне задачи про файлообменик плюс я дропнул вышку кодера, начал разбираться с Yii и решил указать его на HH, мне сами позвали с вакансии где симфони и куда я вообще не подходил по моему мнению.
На собеседование были базовые вопросы и задачкa на взаимодействие с БД(как обработать постоянно добавляюшиеся строки ), немного JS. Все сделал.
Сказал что симфони не знаю, с йии мало знаком, но в итоге мне перезвонили.
Возможно это потому что я просил меньше денег чем было в этой вакансии на ХХ.
Но я в любом случае рад что попал не в битрикс контору, а вотвполне интересное место
И еще: https://ideone.com/PSC6VW - в этой задаче не сделал исправление, слишком много надо дописывать регулярок (для каждой буквы свою же надо будет писать, да?).
Из всей тему про регулярки не понял только, как выделить все слово, в котором нашли нужную букву/часть. Как у тебя в примере выполнения задания про опечатки, ОП.
>немного JS
О, спасибо за эту инфу, всё-таки на ПХП тоже надо яваскрипт, надо хоть немного подучить.
>на взаимодействие с БД(как обработать постоянно добавляюшиеся строки )
Вот тут немного не понял, в смысле обработать постоянно добавляющиеся строки?
Скачиваешь:
https://getcomposer.org/download/
Устанавливаешь нужные вещи
https://packagist.or>>474727
>>474727
>>474726
Спасибо
Статья на хабре: http://habrahabr.ru/post/145946/
Официальный подробный но англоязычный мануал: https://getcomposer.org/
Гайд по командой строке (советую прочесть): https://gist.github.com/codedokode/10539568
Установить можно инсталлятором который все сделает сам или (как я делаю) скачать с сайта composer.phar и запускать указывая путь к нему:
php d:/temp/composer.phar install
phar, если ты не знаком, это такой формат архива который содержит внутри php файлы и который php умеет распаковывать и запускать. Фактически это zip архив с файлами. Инстаяллятор устанавливает тебе этот phar и настраивает систему чтобы он запускался одной командой composer (он делает это добавляя файл composer.bat). Я не люблю инсталляторы и запускаю все вручную.
Композер это средство для установки сторонних библиотек. Идея в том что ты просто пишешь в файлике какие библиотеки каких версий тебе нужны, и запускаешь композер, а он их скачивает, находит зависимости, настраивает автозагрузку, в общем все делает за тебя. Когда ты захочешь их обновить, хватит еще одной команды.
> сперва надо с помощью cd перейти в корень проекта и там уже запускать команду на скачивание?
да
> composer.json руками создавать?
быстрее наверно руками, но там есть команда require https://getcomposer.org/doc/03-cli.md#require которая добавляет имя библиотеки и версию в файл, то есть ты можешь использовать ее:
php composer.phar require vendor/package:2.×
Но по моему руками написать файл будет быстрее.
Названия пакетов и номера версий можно посмотреть тут: https://packagist.org/ — это официальный репозиторий где хранится полный список. Набери например slim и увидишь подходящие пакеты. Согласись удобнее чем руками искать, скачивать архивы, распаковывать и тд.
Название пакета обычно состоит из 2 частей вроде slim/slim. Первое это ник автора на гитхабе или другом сервисе, второе это название проекта.
Когда ставишь версию, лучше не привязываться к однйо версии, а писать например
2.× — это значит любая версия начинающаяся с 2. Это позволит обновить библиотеку когда выйдет новая версия.
Когда привыкнешь пользоваться композером, тебе что-то руками скачивать расхочется, композер гораздо удобнее.
Статья на хабре: http://habrahabr.ru/post/145946/
Официальный подробный но англоязычный мануал: https://getcomposer.org/
Гайд по командой строке (советую прочесть): https://gist.github.com/codedokode/10539568
Установить можно инсталлятором который все сделает сам или (как я делаю) скачать с сайта composer.phar и запускать указывая путь к нему:
php d:/temp/composer.phar install
phar, если ты не знаком, это такой формат архива который содержит внутри php файлы и который php умеет распаковывать и запускать. Фактически это zip архив с файлами. Инстаяллятор устанавливает тебе этот phar и настраивает систему чтобы он запускался одной командой composer (он делает это добавляя файл composer.bat). Я не люблю инсталляторы и запускаю все вручную.
Композер это средство для установки сторонних библиотек. Идея в том что ты просто пишешь в файлике какие библиотеки каких версий тебе нужны, и запускаешь композер, а он их скачивает, находит зависимости, настраивает автозагрузку, в общем все делает за тебя. Когда ты захочешь их обновить, хватит еще одной команды.
> сперва надо с помощью cd перейти в корень проекта и там уже запускать команду на скачивание?
да
> composer.json руками создавать?
быстрее наверно руками, но там есть команда require https://getcomposer.org/doc/03-cli.md#require которая добавляет имя библиотеки и версию в файл, то есть ты можешь использовать ее:
php composer.phar require vendor/package:2.×
Но по моему руками написать файл будет быстрее.
Названия пакетов и номера версий можно посмотреть тут: https://packagist.org/ — это официальный репозиторий где хранится полный список. Набери например slim и увидишь подходящие пакеты. Согласись удобнее чем руками искать, скачивать архивы, распаковывать и тд.
Название пакета обычно состоит из 2 частей вроде slim/slim. Первое это ник автора на гитхабе или другом сервисе, второе это название проекта.
Когда ставишь версию, лучше не привязываться к однйо версии, а писать например
2.× — это значит любая версия начинающаяся с 2. Это позволит обновить библиотеку когда выйдет новая версия.
Когда привыкнешь пользоваться композером, тебе что-то руками скачивать расхочется, композер гораздо удобнее.
> А когда библиотеки скачиваешь надо 'находиться' в корне проекта или в vendors?
В корне. В той же папке что и composer.json иначе он его не найдет.
>>474840
> в этой задаче не сделал исправление, слишком много надо дописывать регулярок (для каждой буквы свою же надо будет писать, да?).
А ты решал задачу про шифровку? Ты можешь сделать массив соответствия букв и заменять функцией strtr.
> Из всей тему про регулярки не понял только, как выделить все слово, в котором нашли нужную букву/часть
(граница слова)(любая буква от 0 до беск. раз) .... (любая буква от 0 до бесконеч. раз) (граница слова)
Если ты справа и слева припишешь такое, то регулярка найдет слово целиком. Граница слова это \\b — это выражение значит что с одной стороны от него должно быть буква, а с другой не быть буквы.
Есть удобный сайт чтобы применять регулярку к тексту и смотреть результат: https://regex101.com/
Если будешь им пользоваться помни что в «flavor» должно быть выбрано PCRE и что бекслеши тут не надо писать по 2 раза, то есть мы пишем в учебнике \\d, \\b а тут \d, \b
Соответственно на этом сайте ты можешь править регулярку и видеть каким частям текста она соответствует.
По поводу регулярки: я думаю, тут лучше искать по-другому. В чем особенгость слов с опечатками? Главная особенность что там либо идет латинская буква после русской либо наобророт. Значит можно составить выражение:
граница-слова любые-буквы ( русская латинская ИЛИ латинская русская ) любые буквы граница-слова
Такое выражение найдет слово с опечатками целиком. Далее, имея слова мы можем определить язык например по первой букве и заменить буквы другого алфавита с помощью strtr и массива замен.
Анон, я тебе не рассказал вчера одну вещь. Как завершить ввод? Как сказать программе что дальше читать данные не нужно?
В ОС для этого есть специальный способ. В Windows ты жмешь F6 и Enter. В Линукс жмешь Ctrl + D (работает только если перед ним нажали Enter). Это говорит системе о том что ты ввел все хотел. Система закрывает поток ввода (больше ввести и передать программе уже ничего нельзя), а программа при чтении получает уведомление что файл прочитан целиком и закончился (это можно проверить например функцией feof). То есть цикл чтения по строчно может выглядеть так:
while (!feof($file)) {
$line = fgets($file);
}
И этот код одинаково будет работать и с данными с клавиатуры и с данными из файла (в файле разумеется F6 не нужен, система и так знает где он заканчивается).
Аналогично если ты читаешь данные целиком с помощью file_get_contents() то F6/Ctrl + D как раз скажет функции о том что в потоке больше нет данных и она остановится и вернет прочитанное.
> if ($lastSymbol == 1) {
> $result = ' рубль.';
А работает ли это с числом 11? Нет:
https://ideone.com/j9bTqu
> Одиннадцать миллионов одиннадцать тысяча одиннадцать рубль. (11011011)
Числа 11-19 это исключение из общей системы.
Функция smallNumberToText($input, $isFemale) реально сложная. Там много кода и он выглядит запутанно. Я предлагаю упростить ее так:
— делаем пустой массив для слов
— если в числе есть сотни то добалвяем в массив нужные слова
— если в числе есть 10-19 то добавляем в массив слова
— иначе если в числе есть десятки то добавляем слова
— если есть единицы то добавляем слова
В конце объединяем массив в строку.
То же самое с функцией numberToText($input)
Пока в нынешнем виде код не годится даже если он правиьно работает, так как он слишком сложно написан и нереально потом его поддерживать/править и тд. Это можно и нужно сделать проще.
> Поменяем ключи и значения ключей местами, чтобы использовать функцию array_search
Это плохая идея так как ты можешь искать и в исходном массиве по ключу и поиск по ключу во много раз эффективнее array_search:
if (isset($a[$b])) или if (array_key_exists($a, $b))
Второй способ работает в 100% случаев, первый воспринимает NULL в массиве как отстутвие элемента и потому работает в 99% случаев. У тебя NULL нету так что он тоже сработает.
> $result = array_search($input, $flipped);
Чтобы взять элемент массива по ключу можно написать: $a[$b] — проходил ли ты урок про массивы? Если нет то почитай хотя бы начало, если да то плохо что у меня это не объясняется, я уже давно хочу улучшить этот урок, там конечно написано все довольно сумбурно.
> $singleDigits = mb_substr($input, 1, 1)
C числами надо работать математическими функциями, а не строчными. Это просто, вот 2 волшебных команды:
// получить последние 3 цифры числа:
echo 1234567 % 1000; // выведет 567
// получить число миллионов
echo floor(1234678 / 1000000); // выведет 12
Комбинируя их, можно получить что угодно.
> тысячь
граммар наци: мягкий знак не нужен
> if ($lastSymbol == 1) {
> $result = ' рубль.';
А работает ли это с числом 11? Нет:
https://ideone.com/j9bTqu
> Одиннадцать миллионов одиннадцать тысяча одиннадцать рубль. (11011011)
Числа 11-19 это исключение из общей системы.
Функция smallNumberToText($input, $isFemale) реально сложная. Там много кода и он выглядит запутанно. Я предлагаю упростить ее так:
— делаем пустой массив для слов
— если в числе есть сотни то добалвяем в массив нужные слова
— если в числе есть 10-19 то добавляем в массив слова
— иначе если в числе есть десятки то добавляем слова
— если есть единицы то добавляем слова
В конце объединяем массив в строку.
То же самое с функцией numberToText($input)
Пока в нынешнем виде код не годится даже если он правиьно работает, так как он слишком сложно написан и нереально потом его поддерживать/править и тд. Это можно и нужно сделать проще.
> Поменяем ключи и значения ключей местами, чтобы использовать функцию array_search
Это плохая идея так как ты можешь искать и в исходном массиве по ключу и поиск по ключу во много раз эффективнее array_search:
if (isset($a[$b])) или if (array_key_exists($a, $b))
Второй способ работает в 100% случаев, первый воспринимает NULL в массиве как отстутвие элемента и потому работает в 99% случаев. У тебя NULL нету так что он тоже сработает.
> $result = array_search($input, $flipped);
Чтобы взять элемент массива по ключу можно написать: $a[$b] — проходил ли ты урок про массивы? Если нет то почитай хотя бы начало, если да то плохо что у меня это не объясняется, я уже давно хочу улучшить этот урок, там конечно написано все довольно сумбурно.
> $singleDigits = mb_substr($input, 1, 1)
C числами надо работать математическими функциями, а не строчными. Это просто, вот 2 волшебных команды:
// получить последние 3 цифры числа:
echo 1234567 % 1000; // выведет 567
// получить число миллионов
echo floor(1234678 / 1000000); // выведет 12
Комбинируя их, можно получить что угодно.
> тысячь
граммар наци: мягкий знак не нужен
Если она тебя устраивает по времени работы то да. Но помни что факториал быстро растет и для больших чисел банально точности не хватит, результаты будут приближенные и может быть не совсем точные. Если у тебя серьезная математика/физика лучше погуглить и поискать хорошие алгоритмы.
У меня O(n) сложность вычесления. Вообще да, хватает потому, что для очень больших чисел php уже не умещает в памяти это число.
http://ideone.com/5i21ND
Не, все хитрее:
— есть небольшие целые числа, для 32 битной системы предел +- 2 млрд, для 64 битной 2 млрд в квадрате. Они хранятся точно. Это int.
— есть большие числа, от 2 млрд до 10 в 38-й (в 64 битной системе до 10 в 308-й степени). Эти числа хранятся неточно, сохраняя 7 или 14 знаков после запятой (а также есть бибиотеки которые дают большую точность). Это float.
— есть огромные числа и их стандартно php хранить не может (но есть библиотеки которые могут)
Подробнее про приближенные числа можно прочесть например тут: http://habrahabr.ru/post/112953/
Если нужна работа с огромными числами или высокая точность то в PHP есть расширения
http://php.net/manual/ru/book.gmp.php
http://php.net/manual/ru/book.bc.php
Например если ты будешь интересоваться асимметричной криптографией то там надо работать с огромными целыми числами без потери точности.
Может ты все это знаешь, тогда может этот пост еще кому-то пригодится.
Спасибо. Я летом думал RSA реализовать, думаю пригодиться.
Ты как-то переусложнил код по моему. Тут можно сделать гораздо проще.
Начнем с ООП реализации. Ты сделал один объект который представляет дерево, а внутри него куча массивов. Чувствуешь что это не совсем ООП, а попытка засунуть процедурный код с массивами внутри объекта? Ты просто взял список функций, написал вокруг них class X { ... }. Это не полноценное ООП-представление для дерева.
Ну вот недостаток твоего дерева. Как известно, в дереве, если мы возьмем какую-то ветвь то это фактически тоже дерево. Получается эта ветка должна тоже быть объектом класса Tree. У тебя ты не можешь получить ветку в виде объекта Tree.
Дерево лучше реализовать по-другому. Ты делаешь класс который представляет собой один узел (Node), у которого может быть родитель и дети. Вот и получилось дерево. Осталось написать несколько методов которые например при добавлении ребенка будут автоматически проставлять ему parent:
$parent = new Node;
$child = new Node;
$parent->addChild($child); // автоматически проставляет parent у ребенка
Если ты хочешь сделать ООП-реализацию то лучше сделать именно такой класс. Он позволяет ходить по дереву в любых направлениях и удобен в использовании.
Заметь что в задаче этого не требуют и ты вполне можешь решить ее на массивах сэкономив время. Но в этом случае твой класс это лишняя обертка которая только раздувает программу и не дает никакой дополнительной ценности. Ты можешь оставить в своем классе всего один метод (вывести дерево) или вообще сделать это на функциях, точнее на одной функции.
Фактически ключевая часть твоего алгоритма это buildAdjacencyList($template). Возьми эту функцию, допиши вторую функцию которая берет adjacency list + исходный массив и рекурсивно выводит дерево — и задача решена.
В общем мое мнение: алогритм можно сделать либо проще и без ООП, либо с нормальной реализацией ООП-дерева.
Проясни свою мысль, как именно? Если речь о группе то мне конечно нравится идея создать что-то вроде группы вконтакте так как это позволит расширить аудиторию (ибо я чувствую что на бордах мы уже выбираем всю доступную аудиторию и больше ее тут нет), но мне же круглостуточно там придется сидеть всем отвечать.
Алсо я негативно отношусь к сервисам которые требуют телефон для регистрации. Неужели Дуров жить без моего номера телефона не может?
ну, например, ты можешь вести курсы в хенгауте или трансляцией на ютуб.
Группу создавать не обязательно, можно просто беседу, что бы там общаться. Там, я думаю, будет как-то живее. Тред вялый, имхо. В беседе еще можно устраивать всякие квесты.
Можно и группу создать, но это более геморно.
У меня, как то вылетело из головы точное содержание, но суть была в то что есть таблица и в неё регулярно добавляются новые строки(заявки какие нибудь на сайте оставляют например), нужна была функция которая следит за обновлениями таблицы и обрабатывает новые строки, любой другой функцией например
есть сервисы, где можно на день взять сим в аренду. Для регистрации хватит.
По моим ощущениям текстовый урок лучше видео урока, так как ты например можешь его промотать в любую сторону, можешь читать по диагонали, смотреть в любом порядке, читать с комфортной тебе скоростью, а с видеоуроком это как минимум труднее, ты должен слушать с той скоростью с которой говорит диктор. Ну например что что рассказывают в 15-минутом уроке я бы мог наверно прочитать в текстовом виде за минуту-две.
Хотя все конечно зависит от реализации, я видел видеокурсы от западных университетов (не про PHP), они довольно хорошие. Там кстати сами видеоролики были довольно короткие и после небольших объяснений сразу же дают задачу, пока ты не забыл материал.
Ну и в плане производства наверно это труднее.
В тех видеокурсах кстати показывали лектора и формулы, а не экран (это по моему был курс по искуственному интеллекту и всяким связанным темам вроде вероятностей, байесовского фильтра и тд). То есть это не курсы уровня «перепечтаывайте мой код».
ты знаешь, что такое хенгаут? там есть живой фидбек, каждый может в ту же минуту спросить, что он не понял. По-моему, идея хорошая. Тред не удобен.
У хабра, гитхаба, твиттера и фейсбука видимо оно требовать не осмеливается? Я помню, были (довольно убедительные) аргументы про то что это помогает бороться со спамом (спама раньше и правда было довольно много, я помню), но недавно я задал себе вопрос: а как же тогда другие соцсети с большим числом пользователей умудряются жить без привязки к телефону?
Позырьте на ютабе урок от школьников как зарегать акк на малазийский фейкономер, я так сделал, зависимость есть.
> там есть живой фидбек, каждый может в ту же минуту спросить, что он не понял.
Это также значит что мы с анонами с разных стран должны в одно и то же время собраться перед экраном, не отвлекаясь ни на какие дела (а также я должен как-то проинформировать всех заранее и выяснить кому когда будет удобнее). Например сейчас я могу в разгар рабочего дня на 5 минут зайти в тред и ответить на вопрос, точно также анон может например ночью решить задачу, запостить решение и лечь спать.
Да, ты прав в том что в трансляции можно в реальном времени задать уточнящий вопрос. Это важная и полезная вещь. У нас с этим хуже, ты можешь ответ получить в худшем случае через сутки-полтора. Но нынешний вариант дает мне больше гибкости и требует от меня меньше времени.
Читать уроки через трансляцию вообще невыгодно. Сейчас я, потратив где-то день на написание урока выкладываю его и его читают может быть сотни, может тысячи человек (по моему тысячи). В случае с трансляцией я должен либо каждую неделю рассказывать одно и то же, что требует в сумме дофига времени либо мы выкладываем записанные заранее записи и теряем возможность фидбека.
Читать уроки через видеочат могут позволить только те кто получает за это деньги и кто соответственно может потратить весь день на чтение лекций, проверку решений, консультации. И то, как я знаю, многие платные курсы включают в свои программы в том числе трансляцию заранее записанных лекций.
В любом случае, спасибо за предложения, я всегда рад услышать замечания и обратную связь, я понимаю что нынешняя система не идеальна, но что касается их реализации, тут приходится считаться с возможностями и ограничениями. Мне надо как-то доносить знания максимальному числу людей с минимальными затратами времени.
Ага, а украинским анонам как я понимаю еще проще, там вообще без паспорта сим карты продают.
Ну беседу все равно можно запилить, хотя бы для общения анонов между собой на тему пхп.
define(ITEMS_PER_PAGE, 10); /где-то в конфиге/;
<?php
$arr = array('php' , 'mysql', 'html', 'css', 'javascript'); /чисто для примера. может быть массивом результата запроса к базе/
$page_num = ceil(count($arr)/ITEMS_PER_PAGE);
for($i=1; $i<=$page_num; $i++) :
?>
<a href = "<?='blabla.php?page='.$i; ?>"><?=$i; ?></a>
<? endfor; ?>
А то я смотрю уроки на ютабе там городят специальный класс Пагинатион который тоже самое делает, только смысла я не вижу получается только сложнее имхо.
\tpublic function upload($upload) {
\t$url = 'files/'.$upload->name;
\tif(move_uploaded_file($upload->tmp, $url)) {
\t
\tif($stmt = $this->db->prepare("INSERT INTO `bulletin`.`files` (`id`, `name`, `size`, `type`, `time`, `comment`, `url`) VALUES (NULL, ?, ?, ?, NULL, ?, ?)")) {
\t$stmt->execute(array($upload->name, $upload->size, $upload->type, $upload->comment, $url));
\t$id = $this->db->lastInsertId();
\tsetcookie('id', $id);
\treturn true;
\t}
\t}
\telse false;
\t}
Как я пытался проверять не срабатывает вот этот кусок:
if(move_uploaded_file($upload->tmp, $url))
Извините котаны, только что вспомнил про error при загрузке файла, всё прояснилось.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
Отвечает за модальные окна, которые нужны на этой странице, другая:
<script src="http://osvaldas.info/examples/audio-player-responsive-and-touch-friendly/audioplayer.min.js"></script>
Аудиопроигрыватель, если подключена так которая отвечает за модальные окна, криво работает та которая отвечает за аудиопроигрыватель, на странице нужно и то и то.
Яничегонепонял. Первый скрипт - это jquery, а не 'модальные окна'. Его используют многие сторонние скрипты, скорее всего, и твои 'модальные окна' тоже, но они опять же, скорее всего, реализованы в другом скрипте.
Твой аудиоплеер тоже требует jquery и по идее вообще не должен без него работать, а не конфликтовать как ты пишешь.
Олсо, ты какую-то старую версию подрубаешь.
Подключая скрипты с какого-то малоизвестного домена
> osvaldas.info
Ты дождешься того, что владелец заблокирует тебя по реферреру, его сайт ляжет, он его забросит и тд и точно также ляжет твой сайт. Не подключай скрипты со сторонних доменов особенно если они явно это не разрешают.
Решай все задачи с начала. Если у тебя есть знания то ты будешь задачи щелкать как семечки, если нет хороший повод почитать уроки.
Есть один hostinger.ru и один db4free.net
Мне нужен мускуль с внешним доступом, который используется крайне редко, но без него никак. db4free медленная и глючная хуйня и вообще с ним постоянно какие-то проблемы, а на Хостингере внешний доступ платный, а мне жалко денег.
В общем думаю изредка пиздить данные из db4free на Хостингер. Надо просто иногда брать данные оттуда и засовывать сюда. Там по сути-то всего одна таблица.
Как такое реализовать пыхо-скриптом?
db->lastInsertId()
На странице которая обрабатывает форму делаю следующий редирект:
if($mapper->upload($upload)) {
$id=$_COOKIE["id"];
header("Location: onefile.php?id=$id");
}
То есть вытаскиваю id из куки и передаю его в качестве параметра, в итоге на странице куда делается редирект id которое записано в куки на один меньше чем то id которое получено через метод GET, почему так?
Поставь echo/var_dump во всех критичных местах (где читается кука, где ставится кука, где вызывается lastInsertId). Автоматический редирект тоже замени на echo чтобы переход делался вручную. Может быть это даст тебе ответ, может нет, если нет то напиши тут результаты.
задача про номера
По регулярке:
> ([+] 7|[+]7|
Это можно записать короче как [+]\\s?7
> [(]|[)]|-|
Если у тебя тут «один из этих символов» то проще использовать квадратные скобки: [abcd]
> \\d$
Что если в конце номера есть лишний пробел? Надо либо отрезать его с помощью trim() либо предусмотреть это в регулярке.
> preg_replace($regexpTwo, '', $result);
В данном случае регулярка маленькая и удобнее ее вписать прямо в эту строку без использования переменной. Ну и старайся не делать названия вроде regexpTwo так как оно ничего не говорит. Лучше назвать например regexpToRemoveNonNumbers (да, длинно, но зато понятно).
Мелкие замечания:
— вместо for для обхода массива удобнее использовать foreach
— ты изучал функции. Ты не должен копипастить одинаковый код 2 раза, а должен сделать функцию
автозамена
Тут лучше использовать foreach вместо for. Вместо (a|b) удобнее писать [ab]. В остальном все верно.
>>474807
Поиск email
надо было конечно сделать нормально вывод email, по 1 на строчке например, а не вываливать так кучу строк включая куски email. Регулярка правильная.
Граммар наци
А программу которая пишет где и какая ошибка, ты не сделал? Надо бы сделать.
> '/(сдесь)/';
Тут скобки не нужны. зато нужен флаг u без которого регулярка может глючить на русских буквах. Ну и флаг i не помешал бы, а то слово с первой большой буквой не найдет.
автозамена
Тут лучше использовать foreach вместо for. Вместо (a|b) удобнее писать [ab]. В остальном все верно.
>>474807
Поиск email
надо было конечно сделать нормально вывод email, по 1 на строчке например, а не вываливать так кучу строк включая куски email. Регулярка правильная.
Граммар наци
А программу которая пишет где и какая ошибка, ты не сделал? Надо бы сделать.
> '/(сдесь)/';
Тут скобки не нужны. зато нужен флаг u без которого регулярка может глючить на русских буквах. Ну и флаг i не помешал бы, а то слово с первой большой буквой не найдет.
Ну ладно
между и скобками я буду ставить пробел
if ($a) { - ok
if($b) { - fail
Так же с for () {
А как быть с функциями и классами? Хули везде поразному с выставлением фигурных скобок?
Сюда вот смотрю: http://php.net/manual/en/language.oop5.decon.php
И там пиздец, в одном месте:
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}
В другом:
class A
{
function __construct()
{
Они там что совсем поохуевали? Как они смеют так делать?
Лучше всего смотреть на стандарты PSR-1/2. Их придерживается большинство авторов современных фреймворков и CMS. Ты можешь посмотреть примеры кода и описание тут:
http://www.php-fig.org/psr/psr-1/ru/
http://www.php-fig.org/psr/psr-2/ru/
> и по разному даже в мануалах на pp.net
Исторически стандарта оформления не было (как в Си) и все писали как хотели. Но относительно недавно разработчики ведущих фреймворков объединились и смогли принять стандарты оформления кода. В принципе они похожи на более ранние стандарты Zend и PEAR и если ты им следовал то для тебя почти ничего не поменяется.
Соответственно некоторые примеры в мануале могли быть написаны до принятия сообществом этих стандартов.
> между и скобками я буду ставить пробел
Да
> А как быть с функциями и классами?
Скобка на новой строке
class A
{
}
function b()
{
}
Лучше всего смотреть на стандарты PSR-1/2. Их придерживается большинство авторов современных фреймворков и CMS. Ты можешь посмотреть примеры кода и описание тут:
http://www.php-fig.org/psr/psr-1/ru/
http://www.php-fig.org/psr/psr-2/ru/
> и по разному даже в мануалах на pp.net
Исторически стандарта оформления не было (как в Си) и все писали как хотели. Но относительно недавно разработчики ведущих фреймворков объединились и смогли принять стандарты оформления кода. В принципе они похожи на более ранние стандарты Zend и PEAR и если ты им следовал то для тебя почти ничего не поменяется.
Соответственно некоторые примеры в мануале могли быть написаны до принятия сообществом этих стандартов.
> между и скобками я буду ставить пробел
Да
> А как быть с функциями и классами?
Скобка на новой строке
class A
{
}
function b()
{
}
> Пример $conditions: ['friendlist' => ' LIMIT 10', 'playlist' => ' AND genre = jazz' ]
Вот это кстати плохая идея так как тут нет подстановки параметров и мы можем словить SQL инъекцию:
$conditions = ['playlist' => "AND genre = '$genre'"]; // упс, инъекция
Лучше сделать так:
['genre' => 'jazz']
['genre' => $genre]
Тут код знает что справа может быть что угодно и корректно экранирует это.
А для limit сделать отдельный параметр.
>>474633
> его учебник не сделает из вас специалистов, нужно дохуя еще чего читать.
Это наверно так и есть, но ведь ты прошел только учебник, а там еще есть задачи после учебника, на список студентов и файлообменник, которые учат работать с базой, с формами, и т.д (ну то есть как учат, объясняют в общих чертах, а дальше сам читай статьи и мануалы). Мне кажется с ними знания получаются вполне достаточные. Что полезного есть в курсах специалиста, чего нет у нас? Я их не смотрел потому сам не знаю.
У этого метода есть недостаток: этот код неуниверсален и привязан к конкретному URL ( blabla.php, ITEMS_PER_PAGE ). Ты не можешь применит его для другой таблицы без изменений, придется копипастит, а копипаста это зло.
Также ты выводишь ссылки на все страницы, а что если надо сделать так:
Первая | ... | 3 | 4 | 5 | 6 | 7 | ... | Последняя
Твой код станет еще сложнее и запутаннее, так как ты будешь смешивать в кучу HTML и логику определения какие ссылки выводить.
Класс Pagination позволяет вынести эту логику в него, а также вынести туда формирование ссылки и упростить HTML и сдеать его универсальнм, что он подойдет к любой странице.
Ну и при выборке из базы ты не выбираешь все записи, а только нужные, а общее число получаешь отдельным запросом.
> Как я пытался проверять не срабатывает вот этот кусок:
Надо при ошибке выводить соотв. сообщение и предлагать загрузить файл еще раз (если конечно это не ошибка превышения размера).
Также надо проверять и обрабатывать ошибки при загрузке файла как описано в мануале:
https://php.net/manual/ru/features.file-upload.errors.php
https://php.net/manual/ru/features.file-upload.post-method.php
>>475126
Конкретную причину надо искать отладчиком, может он в консоль пишет текст ошибки, может нет. Отладчик открывается через Ctrl + Shift + I в браузере.
Чтобы полноценно решить эту проблему нужно знание яваскрипта разумеется.
Статья про отладчик: http://habrahabr.ru/post/143767/
>, а на Хостингере внешний доступ платный, а мне жалко денег.
Ты бы мог установить туда скрипт который выдает нужные данные наружу, только ограничить доступ не забудь.
> Как такое реализовать пыхо-скриптом?
Не понял смысл вопроса? Тебе надо чтобы скрипт написали? Это маловероятно.
- Какие задачи посоветуешь перерешать из учебника?
- Видел большую копипасту про установку Debian на виртуальную машину в старых тредах, где можно ее посмотреть? Если пропала, то какой фтп сервер лучше ставить? Чем редактировать конфиги в консоли, а то vi уж больно хардкорен.
- Как вообще обустроена рабочая среда у профессионалов? Все сидят на линуксах с копией проекта на локальном веб сервере / держат виртуальные машины / в сети есть сервер для отладки, где все стоит ? Вот у меня сейчас все в виртуальной машине, кроме phpstorm, не слишком ли это пердолинг?
> Какие задачи посоветуешь перерешать из учебника?
Никакие. Решай задачу про список студентов если не решал, и фйлообменник, а потом к Yii 2 переходи.
> редактировать конфиги в консоли,
nano очень простой, mcedit тоже довольно несложный, но более мощный
> какой фтп сервер лучше ставить?
Не надо. Если у тебя есть ssh-доступ то можно закачивать файлы по SFTP (он вдобавок в отличие от FTP шифрует все данные). Программами типа WinSCP например.
Я обычно пишу bash скрипт для таких вещей, чтобы руками ничего не делать, а просто запустил скрипт и он сам закачивает изменившиеся файлы. Для этого команда rsync может использоваться в простейшем случае. В принципе там даже можно сделать чтобы он сам обнаруживал изменения и автоматически их синхронизировал, но мне кажется это будет нагружать процессор сильнее.
> Все сидят на линуксах с копией проекта на локальном веб сервере /
Вообще по-разному, кто к чему привык тот тем и пользуется. Еще маки популярны. Я например Windows использую, для линукса есть виртуалка.
Лучше всего когда нет привязки к конкретной платформе и софту.
Но если ты где-то в корпорации работаешь то бывает конечно что выбора нет и надо пользоваться тем что одобрено.
Если ты используешь IDE изучи ее возможности, например быстрое открытие файлов/классов, множественное выделение, сниппеты для часто повторяющихся конструкций, и т.д., чтобы делать все максимально быстро. Глупо тратить время например на поиск файла в дереве каталогов.
Вот к примеру гифки некоторых возможностей Sublime: http://www.sublimetext.com/ — в PhpStorm подобных вещей должно быть еще больше. IDE это твой инструмент и его надо знать в совершенстве, а не использовать как блокнот с подсветкой.
Все рутинные вещи (вроде закачки файлов на сервер) надо стараться автоматизировать, чтобы они не тратили твое время.
git и composer надо освоить если еще не освоил.
> в сети есть сервер для отладки, где все стоит
Бывает и такое но если с ним что-то случится, работа останавливается.
Сейчас еще есть мода распространять тестовую среду в виде образа для виртуальной машины (тот же докер), но мне это не нравится, так как тебе навязывают дистрибутив, установленный софт, в нем нельзя менять настройки (то есть можно но они не сохранятся когда ты скачаешь новую версию образа). По моему это шаг назад, а не вперед.
> Вот у меня сейчас все в виртуальной машине, кроме phpstorm, не слишком ли это пердолинг?
Нет. Хотя если проект простой по моему удобнее его без виртуалки запускать, но это не всегда возможно, конечно. Раньше многие использовали виндоуз/MySQL и проекты работали везде, а сейчас конечно делают как попало.
Гайд по командной строке https://gist.github.com/codedokode/10539568
Вот собрал старые посты по виртуалбоксу и дебиану: https://gist.github.com/codedokode/420c8c12a1edae25f0ec
> Какие задачи посоветуешь перерешать из учебника?
Никакие. Решай задачу про список студентов если не решал, и фйлообменник, а потом к Yii 2 переходи.
> редактировать конфиги в консоли,
nano очень простой, mcedit тоже довольно несложный, но более мощный
> какой фтп сервер лучше ставить?
Не надо. Если у тебя есть ssh-доступ то можно закачивать файлы по SFTP (он вдобавок в отличие от FTP шифрует все данные). Программами типа WinSCP например.
Я обычно пишу bash скрипт для таких вещей, чтобы руками ничего не делать, а просто запустил скрипт и он сам закачивает изменившиеся файлы. Для этого команда rsync может использоваться в простейшем случае. В принципе там даже можно сделать чтобы он сам обнаруживал изменения и автоматически их синхронизировал, но мне кажется это будет нагружать процессор сильнее.
> Все сидят на линуксах с копией проекта на локальном веб сервере /
Вообще по-разному, кто к чему привык тот тем и пользуется. Еще маки популярны. Я например Windows использую, для линукса есть виртуалка.
Лучше всего когда нет привязки к конкретной платформе и софту.
Но если ты где-то в корпорации работаешь то бывает конечно что выбора нет и надо пользоваться тем что одобрено.
Если ты используешь IDE изучи ее возможности, например быстрое открытие файлов/классов, множественное выделение, сниппеты для часто повторяющихся конструкций, и т.д., чтобы делать все максимально быстро. Глупо тратить время например на поиск файла в дереве каталогов.
Вот к примеру гифки некоторых возможностей Sublime: http://www.sublimetext.com/ — в PhpStorm подобных вещей должно быть еще больше. IDE это твой инструмент и его надо знать в совершенстве, а не использовать как блокнот с подсветкой.
Все рутинные вещи (вроде закачки файлов на сервер) надо стараться автоматизировать, чтобы они не тратили твое время.
git и composer надо освоить если еще не освоил.
> в сети есть сервер для отладки, где все стоит
Бывает и такое но если с ним что-то случится, работа останавливается.
Сейчас еще есть мода распространять тестовую среду в виде образа для виртуальной машины (тот же докер), но мне это не нравится, так как тебе навязывают дистрибутив, установленный софт, в нем нельзя менять настройки (то есть можно но они не сохранятся когда ты скачаешь новую версию образа). По моему это шаг назад, а не вперед.
> Вот у меня сейчас все в виртуальной машине, кроме phpstorm, не слишком ли это пердолинг?
Нет. Хотя если проект простой по моему удобнее его без виртуалки запускать, но это не всегда возможно, конечно. Раньше многие использовали виндоуз/MySQL и проекты работали везде, а сейчас конечно делают как попало.
Гайд по командной строке https://gist.github.com/codedokode/10539568
Вот собрал старые посты по виртуалбоксу и дебиану: https://gist.github.com/codedokode/420c8c12a1edae25f0ec
github.com/AzerusEncole/File_Sharing
Нет, не совсем так. Первое что бросается в глаза при заходе в твой репозиторий это то, что все перемешано: твой код и код сторонних библиотек (Slim) и очень трудно понять где что. Не представляю, как потом обновлять Слим на новую версию? Это верный путь к бардаку, особенно когда ты добавишь еще несколько библиотек. Такого не должно быть.
Первый вариант решения проблемы это сделать папку, например 3rd-party и перенести в нее Слим, чтобы он был установлен примерно так:
3rd-party
---slim
-------.gitignore
-------composer.json
-------Slim
То есть внутри своей отдельной папки 3rd-party/slim
Второй, более хороший вариант это не устанавливать Слим руками вообще, а использовать композер. Композер это менеджер библиотек для php, то есть штука которая умеет сама их скачивать и устанавливать в отдельную папку (vendor). Ты создаешь специальный файл composer.json где описываешь какие библиоетки и каких версий тебе нужны, запускаешь композер и он сам их скачивает в папку vendor. при этом в репозиторий ты эту папку не коммитишь и в репозитории на гитхабе лежит только твой код.
Я советую освоить композер. Его используют везде.
Вот выше пост про него: >>474940
Композер отличная вещь, особенно когда ты подключаешь не одну, а много библиотек.
А, и еще, а ты делал задачу на список студентов? Она как бы более простая и учит работать с формами и базой данных и тоже важна. Ты можешь упустить важные знания и навыки если ее пропустишь.
3rd-party (third-party) значит «сторонний», если что. Композер для сторонних библиотек использует папку vendor (что переводится как поставщик). Тут главное что весь сторонний код лежит в отдельной папке.
Еще, данные для соединения с БД надо вынести в отдельный файл, например config.php где должны быть только настройки и ничего более. Он может выглядеть например так:
$dbUser = 'root';
$dbPass = '123';
или использовать возможности Слима:
$app->config('db.user', 'root');
$app->config('db.pass', '123');
Главное чтобы это было в отдельном файле и я мог поменять настройки на свои. Также, приложи SQL дамп твоей базы. В нем не должно быть команд создания самой базы CREATE DATABASE или выдачи прав пользователю GRANT, только команды CREATE TABLE и INSERT.
Если в phpstorme создавать composer проект, можно один какой-нибудь пакет загрузить, прямо при создании. Он почему-то вне vendor'а сохраняется. А если просто загружать их по ходу - все нормально.
Скорее всего дело либо в странных настройках либо ты не так понял что делает та или иная команда phpStorm. Я советую сначала научиться работать напрямую с композером из командной строки, потом с оберткой над ним из PhpStorm.
Возможно ты сделал вот это: https://www.jetbrains.com/phpstorm/help/using-composer-dependency-manager.html#d288366e669
Это явно не то, что требуется (хотя из их мануала я сам не понял что это такое).
На интервью конечно же
Что делал на этих технологиях, какой опыт, на каком уровне знаешь. также разумеется могут дать вопросы и задачки чтобы проверить твои знания.
По PHP советую полистать мануал, некоторые любят задавать всякие вопросы по особенностям php.
По MySQL любят спрашивать общие концепции вроде: нормализация, транзакции, внешние ключи, попросят написать какой-нибудь запрос с джойнами/группировкой.
Меня бы насторожило что ты написал jQuery но не написал JS. То есть яваскрипт (а соответственно и jQuery) ты не знаешь?
JavaScript я почти не использую и знаю его плохо, сейчас судорожно перечитываю мануал, интервью во вторник. по sql повторить кратко, что можно? особенно по нормализации, транзакции, внешних ключах, индексах(и почему поиск по индексах быстрее(слышал как задают этот вопрос, когда говорят что с индексами быстрее)).
ну то есть, я знаю про замыкания, я знаю синтаксис языка, но чисто на js почти не пишу. всегда обхожусь jquery. вот так.
Давно кодишь? Отпиши потом как прошло.
Про индексы придется почитать ссылки отсюда:
https://gist.github.com/codedokode/10539213#%D0%98%D0%BD%D0%B4%D0%B5%D0%BA%D1%81%D1%8B
там статья на ruhighload полезная. Также если есть возможность, хорошо бы самому создать табличку например с миллионом строк и поделать на ней разные запросы.
Если кратко ответить на вопрос «почему с индексом быстрее» то потому что в индексе хранятся отсортированные по возрастанию значения полей и он оптимизирован для некоторых видов поиска (но не любых). Если провести аналогию, представь русско-английский словарь где русские слова идут по алфавиту (а английские соответственно нет). Сколько времени займет найти русское слово, а сколько — английское? Или телефонный справочник отсортированный по названию организации.
> по нормализации
Это важная тема которую надо знать при проектировании БД:
http://habrahabr.ru/post/193756/
http://webadequate.ru/rabota-s-bd-mysql/15-normalizaciya-obektov-v-mysql.html
http://i-novice.net/6-normalnyx-form-bd/
> внешние ключи и связи
http://denis.in.ua/foreign-keys-in-mysql.htm
http://jtest.ru/bazyi-dannyix/sql-dlya-nachinayushhix-chast-3.html
http://ideone.com/01xYDL
Заранее спасибо!
> if (($input >= 11) && ($input <= 14)) {
> $result = $word5;
лучше наверно сразу писать return $word5 чтобы было видно что функция в этом месте завершается и не надо было смотреть куда потом идет эта переменная.
Заодно можно будет убрать большой else.
> if ($input == 0) {
> $text[] = $spelling[$input];
В этом месте наверно лучше сразу возвращать строку «ноль» через return, так как дальше все равно к нему ничего нельзя дописать.
Сумму ноль программа не выводит: http://ideone.com/iaaxDV
> На вашем счету: (0)
Проще всего для нее там сделать отдельный if + return.
Вообще, выглядит хорошо, в сравнении с прошлой версией как день и ночь.
> protected $xPoint;
Можно просто $x для краткости. Можно и как у тебя, впрочем.
> public function getPosition()
По моему это неудобно и удобнее иметь 2 метода getX и getY, хотя я в этом не уверен. Если ты используешь массив, то удобнее наверно называть элемент 'x' и 'y', хотя тут я тоже не уверен.
> public function getLabel()
>\t{
>\t return $this->label;
Ты в классе Animal обращаешься к полю которого в нем нет. Такого быть не должно и класс ничего не должен знать о своих потомках.
Тут надо просто сделать абстрактный метод:
abstract public function getLabel( );
и переопределить его в потомках.
> if($this->xPoint !== $this->field->getSize()
В таких сравнениях лучше писать «меньше», код будет надежнее.
Ну и саму функцию наверно проще было записать как-то так:
$offsets = [[-1, 0], [0, 1], ...]; // возможные смещения
foreach ($offsets as $offset) {
Если (клеточка с такими координатами существует) {
добавить ее в список ;
}
это позволило бы объединить и упростить код функций findOrthogonalSteps и findDiagonalSteps. Мне кажется надо просто в field сделать метод проверяющий существует ли клетка с указанными координатами.
> protected function findNearestEnemy(array $enemies)
> return $enemiesPrices[min(array_keys($enemiesPrices))];
А что если массив пуст? эта строка выдаст ошибку.
> if(in_array($animal->getPosition(), $availableSteps)) {
ты сравниваешь массивы через in_array. Теперь вопрос тебе, а как работает сравнение в этом случае: сравниваются только значения в массивах или значения вместе с ключами?
[1, 2] == [2, 1] ?
Описано тут: http://php.net/manual/ru/language.operators.array.php
И мне кажется, этот код надо перенести в карту и сделать метод вроде «занята ли клеточка» или «получить животное на клеточке». это явно должно быть не в Animal.
> if(in_array($animal->getPosition(), $availableSteps)) {
> \t$key = array_search($animal->getPosition(), $availableSteps, true);
>\t\t\t\tunset($availableSteps[$key]);
Тут проще не удалять ничего, а сделать новый массив и добавлять в него только хорошие ходы.
> protected function setPricesOfSteps(Animal $nearestEnemy, array $allys, array $dogs)
> $pricesOfSteps[$price] = $step;
при таком подходе ходы с одинаковым числом очков затирают друг друга. Ну и способ хранения очков и координат странный и запутанный. Я бы хранил примерно так:
['x' => 9, 'y' => 8, 'price' => 100]
Или вообще можно завести класс из 3 свойств для этого (можно и массивом впрочем).
Сам расчет стоимости хода надо бы улучшить. Ты учитываешь только расстояние до ближайшего животного, мне кажется мышь должна учитывать еще: число кошек вокруг, число выходов с клеточки. Мне кажется надо сделать метод оценки одного конкретного хода, который будет реализован по-разному в разных животных (можно сделать его абстрактным в Animal чтобы не забыли реализовать).
> protected function setPricesOfSteps(Animal $nearestEnemy, array $allys, array $dogs)
> $availableSteps = $this->findAvailableSteps($allys, $dogs);
Обращаешься к функции которой нет в Animal.
Ну и функция расчет стоимости явно должна быть не в Animal, ведь у каждого животного свои приоритеты и свой метод расчета.
> $availableSteps = $this->excludeClosedPositions(array_merge($mouses, $dogs), $availableSteps);
Непонятно почему кошки не исключены, мышка может ходить по кошкам?
> private function findVisibleField($size)
Это должно быть в карте, а не в мыши, так как это относится к работе с картой. Более того, мне кажется незачем делать этот огромный массив клеток. Проще отфильтровывать животных при поиске их на карте:
кошки = $field->найтиВсехЖивотныхВРадиусе(кошки, 4, 5, 6);
Тогда и функцию checkProtection(array $mouses) можно написать с использованием этого же метода.
Ну и в самой мышке (а можно даже в Animal c переопределением в разных животных) можно сделать метод вроде получитьВсехЖивотныхКоторыхЯВижу().
> if($counter >= 2) {
>\t\t $this->isProtected = true;
Не уверен что isProtected должно быть свойством, а не обычной переменной. Оно же устаревает каждый ход, а ты его хранишь и можно подумать что оно всегда актуально. И эту ошибку потом ведь не найти будет в коде. Я думаю, это не должно быть свойством так как никто не поддерживает его в актуальном состоянии.
> if($visibleCats) {
> } else {
Мне кажется алгоритм выбора хода можно сделать единый независимо от того видны кошки или нет. Это сделает оценку ходов более разнообразной, так как она будет учитывать не только расстояние до кошек но и другие факторы работающие при одинаковом расстоянии.
> $position = $pricesOfSteps[min(array_keys($pricesOfSteps))];
>\t\t\t$this->xPoint = $position[0];
>\t\t\t$this->yPoint = $position[1];
это скопипастено несколько раз. Надо сделать метод «выбрать лучший ход».
> if($position === $nearestEnemy->getPosition()) {
>\t\t\t\t$nearestEnemy->setState(false);
Что значит «setState»? Съесть? Название абсолютно ничего не значит и не помогает. Ну и сделать это наверно надо по-другому, например:
карта->убратьЖивотное(мышка)
Хотя решение с отметкой животного мертвым конечно имеет свои преимущества (кошка в этом случае ничего не трогает на карте), но надо тогда сделать проверку этот свойства во всех функциях получения списка животных, чтобы например другие кошки не шли к этой мертвой мышке.
Методы makeStep, мне кажется, в разных животных, можно как-то обобщить. Они очень похожие.
> private function drawSchema()
> $key = array_search($animal->getPosition(), $schema, true);
Ты как-то тут все переусложнил. Тут достаточно сначала создать 2-мерный массив пустых клеточек, а потом пройтись по животным и сделать:
$map[$a->getX()][$a->getY()] = $a->getlabel( );
После чего преобразовать ее в строку implode в цикле.
> public function getAnimalsByType($animalType)
Тут наверно короче написать с использованием array_filter и анонимной функции.
В общем, пока код какой-то сложный и надо посмотреть как его можно упростить. Мне довольно сложно было его читать. Хорошо, что программа работает, но из-за ее сложности в ней могут быть баги (ну например кошки идущие к уже съеденной мышке).
> protected $xPoint;
Можно просто $x для краткости. Можно и как у тебя, впрочем.
> public function getPosition()
По моему это неудобно и удобнее иметь 2 метода getX и getY, хотя я в этом не уверен. Если ты используешь массив, то удобнее наверно называть элемент 'x' и 'y', хотя тут я тоже не уверен.
> public function getLabel()
>\t{
>\t return $this->label;
Ты в классе Animal обращаешься к полю которого в нем нет. Такого быть не должно и класс ничего не должен знать о своих потомках.
Тут надо просто сделать абстрактный метод:
abstract public function getLabel( );
и переопределить его в потомках.
> if($this->xPoint !== $this->field->getSize()
В таких сравнениях лучше писать «меньше», код будет надежнее.
Ну и саму функцию наверно проще было записать как-то так:
$offsets = [[-1, 0], [0, 1], ...]; // возможные смещения
foreach ($offsets as $offset) {
Если (клеточка с такими координатами существует) {
добавить ее в список ;
}
это позволило бы объединить и упростить код функций findOrthogonalSteps и findDiagonalSteps. Мне кажется надо просто в field сделать метод проверяющий существует ли клетка с указанными координатами.
> protected function findNearestEnemy(array $enemies)
> return $enemiesPrices[min(array_keys($enemiesPrices))];
А что если массив пуст? эта строка выдаст ошибку.
> if(in_array($animal->getPosition(), $availableSteps)) {
ты сравниваешь массивы через in_array. Теперь вопрос тебе, а как работает сравнение в этом случае: сравниваются только значения в массивах или значения вместе с ключами?
[1, 2] == [2, 1] ?
Описано тут: http://php.net/manual/ru/language.operators.array.php
И мне кажется, этот код надо перенести в карту и сделать метод вроде «занята ли клеточка» или «получить животное на клеточке». это явно должно быть не в Animal.
> if(in_array($animal->getPosition(), $availableSteps)) {
> \t$key = array_search($animal->getPosition(), $availableSteps, true);
>\t\t\t\tunset($availableSteps[$key]);
Тут проще не удалять ничего, а сделать новый массив и добавлять в него только хорошие ходы.
> protected function setPricesOfSteps(Animal $nearestEnemy, array $allys, array $dogs)
> $pricesOfSteps[$price] = $step;
при таком подходе ходы с одинаковым числом очков затирают друг друга. Ну и способ хранения очков и координат странный и запутанный. Я бы хранил примерно так:
['x' => 9, 'y' => 8, 'price' => 100]
Или вообще можно завести класс из 3 свойств для этого (можно и массивом впрочем).
Сам расчет стоимости хода надо бы улучшить. Ты учитываешь только расстояние до ближайшего животного, мне кажется мышь должна учитывать еще: число кошек вокруг, число выходов с клеточки. Мне кажется надо сделать метод оценки одного конкретного хода, который будет реализован по-разному в разных животных (можно сделать его абстрактным в Animal чтобы не забыли реализовать).
> protected function setPricesOfSteps(Animal $nearestEnemy, array $allys, array $dogs)
> $availableSteps = $this->findAvailableSteps($allys, $dogs);
Обращаешься к функции которой нет в Animal.
Ну и функция расчет стоимости явно должна быть не в Animal, ведь у каждого животного свои приоритеты и свой метод расчета.
> $availableSteps = $this->excludeClosedPositions(array_merge($mouses, $dogs), $availableSteps);
Непонятно почему кошки не исключены, мышка может ходить по кошкам?
> private function findVisibleField($size)
Это должно быть в карте, а не в мыши, так как это относится к работе с картой. Более того, мне кажется незачем делать этот огромный массив клеток. Проще отфильтровывать животных при поиске их на карте:
кошки = $field->найтиВсехЖивотныхВРадиусе(кошки, 4, 5, 6);
Тогда и функцию checkProtection(array $mouses) можно написать с использованием этого же метода.
Ну и в самой мышке (а можно даже в Animal c переопределением в разных животных) можно сделать метод вроде получитьВсехЖивотныхКоторыхЯВижу().
> if($counter >= 2) {
>\t\t $this->isProtected = true;
Не уверен что isProtected должно быть свойством, а не обычной переменной. Оно же устаревает каждый ход, а ты его хранишь и можно подумать что оно всегда актуально. И эту ошибку потом ведь не найти будет в коде. Я думаю, это не должно быть свойством так как никто не поддерживает его в актуальном состоянии.
> if($visibleCats) {
> } else {
Мне кажется алгоритм выбора хода можно сделать единый независимо от того видны кошки или нет. Это сделает оценку ходов более разнообразной, так как она будет учитывать не только расстояние до кошек но и другие факторы работающие при одинаковом расстоянии.
> $position = $pricesOfSteps[min(array_keys($pricesOfSteps))];
>\t\t\t$this->xPoint = $position[0];
>\t\t\t$this->yPoint = $position[1];
это скопипастено несколько раз. Надо сделать метод «выбрать лучший ход».
> if($position === $nearestEnemy->getPosition()) {
>\t\t\t\t$nearestEnemy->setState(false);
Что значит «setState»? Съесть? Название абсолютно ничего не значит и не помогает. Ну и сделать это наверно надо по-другому, например:
карта->убратьЖивотное(мышка)
Хотя решение с отметкой животного мертвым конечно имеет свои преимущества (кошка в этом случае ничего не трогает на карте), но надо тогда сделать проверку этот свойства во всех функциях получения списка животных, чтобы например другие кошки не шли к этой мертвой мышке.
Методы makeStep, мне кажется, в разных животных, можно как-то обобщить. Они очень похожие.
> private function drawSchema()
> $key = array_search($animal->getPosition(), $schema, true);
Ты как-то тут все переусложнил. Тут достаточно сначала создать 2-мерный массив пустых клеточек, а потом пройтись по животным и сделать:
$map[$a->getX()][$a->getY()] = $a->getlabel( );
После чего преобразовать ее в строку implode в цикле.
> public function getAnimalsByType($animalType)
Тут наверно короче написать с использованием array_filter и анонимной функции.
В общем, пока код какой-то сложный и надо посмотреть как его можно упростить. Мне довольно сложно было его читать. Хорошо, что программа работает, но из-за ее сложности в ней могут быть баги (ну например кошки идущие к уже съеденной мышке).
Можно, я думал над этим, надо посмотреть какие там правила и дописать скрипт для деплоя. Мне тоже раздражает весь этот мусор который юкоз добавляет на сайт.
Пользователь вводит сумму, нажимает отправить и его редиректит на сайт вебмани, где собственно и осуществляется оплата. Так вот, я хочу, чтобы факт оплаты сохранялся на сайте (что-то вроде копилки), но проблема в том, что, как я уже сказал ранее, оплата осуществляется на сайте вебмани, а он уже редиректит на странички succes.html или fail.html в зависимости от результата. Допустим я хочу добавлять в таблицу факт и сумму выплаты, но при отправке формы я этого сделать не могу, так как в дальнейшем пользователь может отказаться от оплаты. Существует ли способ осуществить задуманное или с WebMerchant это невозможно?
И можно ли без потери данных увеличить раздел var?
$db = mysql_connect('сервер', 'логин','пароль');
mysql_select_db("база" ,$db);
$sql = mysql_query("SELECT `des` FROM `Index` WHERE `id` > 0" ,$db);
$res = mysql_fetch_row ($sql);
print_r($res);
По идее, код должен вывести массив со всеми значениями des. Но на практике, выводит
Array ( [0] => One )
Почему так происходит?
> И можно ли без потери данных увеличить раздел var?
Речь о реальном диске или образе виртуальной машины? Вообще, некоторые fs позволяют менять их размер: http://www.o-nix.com/pages/reshenija-v-linux/resize-ext2ext3ext4.php
Заметь что там делается все в 2 этапа: сначала переразбивается диск, выделяя больше места для одного из разделов, затем файловая система на этом разделе растягивается чтобы занять дополнительное место. Обрати внимание, что fdisk довольно низкоуровневая вещь и она легко выполнит даже опасные операции. Там расширяют раздел за счет идущей за ним неразмеченной области. Если ты хочешь расширить раздел за счет идущего за ним другого раздела то подход из статьи не поможет, ты просто затрешь начало файловой системы следующего раздела. В этом случае надо переместить или сжать следующий раздел, высвободив место для расширения, а потом только расширять предыдущий.
Прежде чем делать такие вещи, убедись что понимаешь как именно разбит диск и какие разделы в каком порядке идут. Я не знаю, как сейчас с GPT партициями, но раньше например там была какая-то хитрая схема (MBR разделы), что у тебя может быть только 3 основных раздела, а все остальные были внутри четвертого расширенного:
http://help.ubuntu.ru/manual/%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%82%D0%BA%D0%B0_%D0%B4%D0%B8%D1%81%D0%BA%D0%B0
https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB_%D0%B4%D0%B8%D1%81%D0%BA%D0%B0#.D0.92.D0.B8.D0.B4.D1.8B_.D1.80.D0.B0.D0.B7.D0.B4.D0.B5.D0.BB.D0.BE.D0.B2
Соответвенно если ты хочешь расширить раздел внутри расширенного за счет первичного то все становится сложнее.
С другой стороны если у тебя GPT разделы то написанные выше не актуально: https://ru.wikipedia.org/wiki/%D0%A2%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0_%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%BE%D0%B2_GUID
Потому если есть возможность то я бы забекапил данные, если они важны. Высока вероятность все сломать. В /var из важного, как я понимаю, базы данных MySQL?
> я снес debian-root из дев и не могу теперь зайти в рут из оболочки.
Я не очень понял, ты удалил устройство /dev/sdXXX соответствующее диску из папки /dev? Не понимаю, зачем ты это сделал, но этой папкой управляет отдельный демон и при перезагрузке он наверно пропишет туда нужные устройства заново.
> И можно ли без потери данных увеличить раздел var?
Речь о реальном диске или образе виртуальной машины? Вообще, некоторые fs позволяют менять их размер: http://www.o-nix.com/pages/reshenija-v-linux/resize-ext2ext3ext4.php
Заметь что там делается все в 2 этапа: сначала переразбивается диск, выделяя больше места для одного из разделов, затем файловая система на этом разделе растягивается чтобы занять дополнительное место. Обрати внимание, что fdisk довольно низкоуровневая вещь и она легко выполнит даже опасные операции. Там расширяют раздел за счет идущей за ним неразмеченной области. Если ты хочешь расширить раздел за счет идущего за ним другого раздела то подход из статьи не поможет, ты просто затрешь начало файловой системы следующего раздела. В этом случае надо переместить или сжать следующий раздел, высвободив место для расширения, а потом только расширять предыдущий.
Прежде чем делать такие вещи, убедись что понимаешь как именно разбит диск и какие разделы в каком порядке идут. Я не знаю, как сейчас с GPT партициями, но раньше например там была какая-то хитрая схема (MBR разделы), что у тебя может быть только 3 основных раздела, а все остальные были внутри четвертого расширенного:
http://help.ubuntu.ru/manual/%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%82%D0%BA%D0%B0_%D0%B4%D0%B8%D1%81%D0%BA%D0%B0
https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB_%D0%B4%D0%B8%D1%81%D0%BA%D0%B0#.D0.92.D0.B8.D0.B4.D1.8B_.D1.80.D0.B0.D0.B7.D0.B4.D0.B5.D0.BB.D0.BE.D0.B2
Соответвенно если ты хочешь расширить раздел внутри расширенного за счет первичного то все становится сложнее.
С другой стороны если у тебя GPT разделы то написанные выше не актуально: https://ru.wikipedia.org/wiki/%D0%A2%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0_%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%BE%D0%B2_GUID
Потому если есть возможность то я бы забекапил данные, если они важны. Высока вероятность все сломать. В /var из важного, как я понимаю, базы данных MySQL?
> я снес debian-root из дев и не могу теперь зайти в рут из оболочки.
Я не очень понял, ты удалил устройство /dev/sdXXX соответствующее диску из папки /dev? Не понимаю, зачем ты это сделал, но этой папкой управляет отдельный демон и при перезагрузке он наверно пропишет туда нужные устройства заново.
И еще, я не уверен что вообще стоит создавать кучу разделов. По моему, проще использовать один раздел для всего (или, если у тебя несколько операционных систем, то делаешь небольшой раздел для линукса, небольшой системный раздел для windows, а все данные хранишь на отдельном разделе который доступен и из линукса и из windows).
>>476321
> По идее, код должен вывести массив со всеми значениями des.
Нет. не должен. Открой мануал: http://php.net/manual/ru/function.mysql-fetch-row.php
там написано как именно работает эта функция. Она читает только одну строчку из массива результатов.
Алсо имей в виду, расширение mysql давно уже устарело и ты учишься по каким-то древним 10-летней давности учебникам.
Не знаю, наверно стоит сделать как будет быстрее.
>>476265
Я еще вот такое нашел: https://merchant.webmoney.ru/conf/guide.asp#8 — там какое-то оповещение предусмотрено.
Вот тут https://merchant.webmoney.ru/conf/guide.asp#2 написано что ты можешь указать обработчик на своем сайте который будет получать информацию о платеже.
Ты хотя бы документацию-то прочел? Прочти.
>там написано как именно работает эта функция. Она читает только одну строчку из массива результатов.
А есть какая-нибудь функция, которая дала бы весь результат, а не только первую строку без циклов?
основы я давно знаю а дальше ваще хуй знают че наверное мотивации мало сейчас у меня н
Вот получаем мы uri - /controller/action. И контроллер, и действие статичны, мы их описали.
А почему тогда, например, на хабре сделано так:
/post/137664 ?
То есть экшен динамический. Почему не /post?id=137664, например?
Ладно, если бы все так было сделано, мы бы просто изменили правило разбора экшена на этапе роутинга. Но ведь есть страницы /auth/login и /auth/register, где экшен уже статичен.
Смысл в таком подходе?
Не особо понятен твой вопрос.
> Почему не /post?id=137664, например?
Потому что это не красиво визуально и со стороны сео через get массив не рекомендуется передавать ничего. Да и вобще переменные в get это костыль.
Ютуб же делает вот так:
/watch?v=OK0L1MhMlYY
А вообще я понял, в чем дело. Похоже, у хабра нет точки входа, а просто директории, как на сосаче.
Я говорил про подход, когда мы делаем сначала переадресацию к index.php, разбираем uri и вызываем нужный контроллер.
> . Почему не /post?id=137664, например?
раньше говорили что поисковики хуже ценят такие страницы так как они думают что все адреса вроде /post это одна страница, просто с разными параметрами отображения. Хотя я не уверен что это сейчас так, я не сеошник, и например Hacker News использует такие адреса и прекрасно ищется гуглом: https://news.ycombinator.com/item?id=9521151
Ну и не очень красивые адреса. Все таки в идеологии HTTP путь (это то что идет после домена в URL) обозначает какой-то ресурс (файл или страницу) на сервере, а параметры после знака вопроса это дополнительные опции, например для сортировки, поиска или еще чего-нибудь. Ну то есть /post/123456 хорошо описывает ресурс «пост № 123456».
Потому адрес /post/123456 с точки зрения концепции HTTP лучше чем /post?id=123456
Вот мой урок с мыслями про то какие стоит делать URL: https://gist.github.com/codedokode/772a4ccc03e41d6b7cba
> Вот получаем мы uri - /controller/action. И контроллер, и действие статичны, мы их описали.
MVC это идея разделения кода на 3 части (котроллер, модель, представление). Она ничего не говорит про то как выглядит URL, это решаешь ты. Соответственно никто не говорит о том что там всегда в начале должен идти контроллер. URL может выглядеть как угодно, но лучше конечно делать их красивыми и читабельными и соответствующими концепциям HTTP.
В твоем случае никто не запрещает приписать параметры после action:
/posts/read/12345678
/posts/read?id=1234 (не так красиво и не очень соответствует идее HTTP)
Или вообще сделать словесный URL:
/new-intel-netbooks (только старайся не делать длинные URL, это вредит читабельности)
> Ладно, если бы все так было сделано, мы бы просто изменили правило разбора экшена на этапе роутинга. Но ведь есть страницы /auth/login и /auth/register, где экшен уже статичен.
Ты можешь настроить роутинг как угодно, ты не обязан придерживаться тут единого стиля.
>>476580
> Да и вобще переменные в get это костыль.
Не всегда. Это дополнительные параметры, которые идеально подходят для сортировки или поиска. Почему сортировку лучше делать через параметры? Потому что с точки зрения HTTP ресурс у нас один — список объявлений, а поиск и сортировка это просто разные представления этого ресурса:
/ads?sortBy=date&search=cars
>>476584
Я видел на Ютубе и /watch/123456 для встраивания в страницу и короткий домен youtu.be. Вообще, что касается ютуба это просто исторически так сделано и они не хотят менять чтобы не ломать ссылки. Да и зачем менять если все работает.
> . Почему не /post?id=137664, например?
раньше говорили что поисковики хуже ценят такие страницы так как они думают что все адреса вроде /post это одна страница, просто с разными параметрами отображения. Хотя я не уверен что это сейчас так, я не сеошник, и например Hacker News использует такие адреса и прекрасно ищется гуглом: https://news.ycombinator.com/item?id=9521151
Ну и не очень красивые адреса. Все таки в идеологии HTTP путь (это то что идет после домена в URL) обозначает какой-то ресурс (файл или страницу) на сервере, а параметры после знака вопроса это дополнительные опции, например для сортировки, поиска или еще чего-нибудь. Ну то есть /post/123456 хорошо описывает ресурс «пост № 123456».
Потому адрес /post/123456 с точки зрения концепции HTTP лучше чем /post?id=123456
Вот мой урок с мыслями про то какие стоит делать URL: https://gist.github.com/codedokode/772a4ccc03e41d6b7cba
> Вот получаем мы uri - /controller/action. И контроллер, и действие статичны, мы их описали.
MVC это идея разделения кода на 3 части (котроллер, модель, представление). Она ничего не говорит про то как выглядит URL, это решаешь ты. Соответственно никто не говорит о том что там всегда в начале должен идти контроллер. URL может выглядеть как угодно, но лучше конечно делать их красивыми и читабельными и соответствующими концепциям HTTP.
В твоем случае никто не запрещает приписать параметры после action:
/posts/read/12345678
/posts/read?id=1234 (не так красиво и не очень соответствует идее HTTP)
Или вообще сделать словесный URL:
/new-intel-netbooks (только старайся не делать длинные URL, это вредит читабельности)
> Ладно, если бы все так было сделано, мы бы просто изменили правило разбора экшена на этапе роутинга. Но ведь есть страницы /auth/login и /auth/register, где экшен уже статичен.
Ты можешь настроить роутинг как угодно, ты не обязан придерживаться тут единого стиля.
>>476580
> Да и вобще переменные в get это костыль.
Не всегда. Это дополнительные параметры, которые идеально подходят для сортировки или поиска. Почему сортировку лучше делать через параметры? Потому что с точки зрения HTTP ресурс у нас один — список объявлений, а поиск и сортировка это просто разные представления этого ресурса:
/ads?sortBy=date&search=cars
>>476584
Я видел на Ютубе и /watch/123456 для встраивания в страницу и короткий домен youtu.be. Вообще, что касается ютуба это просто исторически так сделано и они не хотят менять чтобы не ломать ссылки. Да и зачем менять если все работает.
> Похоже, у хабра нет точки входа, а просто директории, как на сосаче.
Ты это никак не можешь определить не имея исходников. Сервер можно настроить как угодно, можно настроить что он по URL /1.jpg будет отдавать файл 2.jpg например.
Точно также URL /posts/some-post.html не значит что там на самом деле HTML страница. Ты никак это не можешь определить снаружи (на самом деле ты можешь конечно попробовать догадаться по заголовкам, но не без 100% гарантии).
Интересно же. Раньше я просто двачевал капчу вообще без всякой пользы. И кстати как косвенный эффект, я лучше знаю всякие фреймворки и библиотеки, с которыми не работал но которые заставляю изучать анонов.
Вроде бы есть рабочий код тут http://positron-it.ru/stati/otpravlenie_soobwenij_s_vlozheniem_po_ajax/ , но у меня на локалхосте он не хочет работать почему-то. Это в нем проблема?
ucfirst вроде не работает с многобайтовыми кодировками, поэтому на странице этой функции в мануале, в комментариях люди пишут свои велосипеды. Еслиб все было так просто, этой задачи бы не было в учебнике.
Разбиваешь строку на предложения, с ними делаешь то, ты описал, затем склеиваешь обратно.
http://php.net/manual/ru/function.explode.php
Там ведь неправильно решена задача про переворачивание строки? Ведь если оперативной памяти под скрипт 70мб, а строка, которую нужно перевернуть весит 100мб, то она уже не помещается и её нужно как-то считывать по частям откуда-то и переворачивать или нет?
Открой инспектор (Ctrl + Shift + I) в браузере на вкладке Network и попробуй отправить форму. Посмотри что передается.
На стороне PHP поставь var_dump($_FILES, $_POST) чтобы увидеть что приходит.
Алсо почитай теорию https://php.net/manual/ru/tutorial.forms.php
также, я советую тебе изучить HTTP, формы и работу с ними в PHP чтобы ты мог сам написать нужный код а не копировать готовый код не понимая. Это не школа и тут списывание тебе ничем не поможет, нужно знание и понимание.
>>476999
Фрилансю и работаю удаленно.
>>477019
> Пока идея такая - выдернуть с помощью mb_substr первую букву в предложении, а потом mb_strtoupper перевести ее в верхний регистр.
Правильная идея.
> Загвоздка в том, что там еще не удалены пробелы и первую букву надо будет отсчитывать непонятно как.
функция trim отрежет пробелы с краев.
>>477023
Нет, она не работает, почитай урок https://gist.github.com/codedokode/ff99e357e9860ea169b8
>>477046
> Получается пробел в начале заменится на (заглавный?) пробел.
Пробел так и останется пробелом. заменяются только символы для которых есть заглавный аналогичный символ. Цифры и знаки, а также символы языков где нет регистра букв, не меняются.
Открой инспектор (Ctrl + Shift + I) в браузере на вкладке Network и попробуй отправить форму. Посмотри что передается.
На стороне PHP поставь var_dump($_FILES, $_POST) чтобы увидеть что приходит.
Алсо почитай теорию https://php.net/manual/ru/tutorial.forms.php
также, я советую тебе изучить HTTP, формы и работу с ними в PHP чтобы ты мог сам написать нужный код а не копировать готовый код не понимая. Это не школа и тут списывание тебе ничем не поможет, нужно знание и понимание.
>>476999
Фрилансю и работаю удаленно.
>>477019
> Пока идея такая - выдернуть с помощью mb_substr первую букву в предложении, а потом mb_strtoupper перевести ее в верхний регистр.
Правильная идея.
> Загвоздка в том, что там еще не удалены пробелы и первую букву надо будет отсчитывать непонятно как.
функция trim отрежет пробелы с краев.
>>477023
Нет, она не работает, почитай урок https://gist.github.com/codedokode/ff99e357e9860ea169b8
>>477046
> Получается пробел в начале заменится на (заглавный?) пробел.
Пробел так и останется пробелом. заменяются только символы для которых есть заглавный аналогичный символ. Цифры и знаки, а также символы языков где нет регистра букв, не меняются.
> ? Ведь если оперативной памяти под скрипт 70мб, а строка, которую нужно перевернуть весит 100мб, то она уже не помещается и её нужно как-то считывать по частям откуда-то и переворачивать или нет?
Пример конечно неудачный так как трудно найти сервер с 70 мб памяти. Но в реальности часто встречаются ситуации когда надо работать с большими объемами данных, например собранные на большом андронном коллайдере данные весят терабайты, так же огромные объемы данных выдают устройства анализа ДНК (например в биологии).
Ну или более простой пример: дампы карт разных стран открытого проекта OpenStreetMap весят гигабайты в сжатом виде.
В таких случаях мы должны как-то обрабатывать файл по частям. Например прочитать первые N Мб, обработать, записать на диск, прочитать вторые N Мб, и так далее.
В случае задачи переворачивания файла очевидно надо читать исходный файл с конца блоками, переворачивать блок и дописывать в выходной файл (это еще не точный ответ, ниже будут уточнения).
Классический пример такой задачи — это дисковая сортировка файла, который не вмещается в оперативную память. Такой файл разбивается на блоки, блоки сортируются и сохраняются во временный файл. После чего мы читаем эти блоки построчно и объядиняем: https://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0_%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5%D0%BC
Есть также обобщенный подход для реализации алгоритмов по принципу «разделяй и властвуй», он называется map-reduce. Он требует от тебя написать алгоритм в виде 2 функций, map и reduce и предполагает такой порядок работы:
— исходный файл разбивается на блоки
— каждый блок по очереди обрабатывается алгоритмом map. Этот процесс может идти как последовательно, так и параллельно если у нас много машин, причем изменение числа машин не требует изменений в алгоритме.
— затем алгоритм reduce собирает результат из блоков полученных на предыдущем этапе.
Преимущество такого подхода в том что имея функции map/reduce мы можем запускать их как в последовательном режиме, так и параллельно, например на нескольких ядрах или нескольких машинах, не меняя сам алгоритм. Более того, мы можем масштабировать систему на ходу, добавляя новые машины по мере необходимости.
map-reduce вроде бы используется в Гугл для обработки больших объеимов данных:
https://ru.wikipedia.org/wiki/MapReduce
http://habrahabr.ru/post/103467/
А теперь вернемся к предложенному мной алгоритму:
> читать исходный файл с конца блоками, переворачивать блок и дописывать в выходной файл
правильный ли он? Если у нас однобайтная кодировка и буква занимает 1 байт то да. Если у нас utf-8 или другая многобайтная кодировка то нет, этот алгоритм сломает все буквы так как буква состояшая из нескольких байт:
D0 D8
будет записана в первернутом виде
D8 D0
И разумеется файл станет нечитаемым. Что делать? Более того, если мы берем блок из файла, то нет гарантии что он будет обрезан по границам символов. мы можем в начале получить «хвост» буквы из предыдущего блока и в конце начало буквы, заканчивающейся в следующем блоке.
Тут надо менять алгоритм. utf-8 работает так, что по коду мы можем определить, первый это байт буквы, средний или последний (для них используются разные коды). Разбиваем блок на части:
(хвост буквы из пред. блока) (буква 1) (буква 2) ... ( буква N) (начало последней буквы)
Затем переворачиваем массив букв, а огрызки букв объединяем с тем, что остался от предыдущего блока. И записываем результат в выходной файл.
Если хочешь крепко поломать голову, я советую решить задачу переворачивания текста в огромном файле в utf-8. Это наверняка даст тебе какие-то полезные знания.
Для начала можешь прочесть про суму кодировку utf-8: https://ru.wikipedia.org/wiki/UTF-8
Заметь что там надо хотя бы немного понимать двоичную и шестнадцатеричную систему счисления.
Посмотреть код букв можно так:
http://ideone.com/q74IN8
> 48 65 6c 6c 6f
Это латинница
> 20
Пробел
> d0 bf d1 80 d0 b8 d0 b2 d0 b5 d1 82
Русские буквы, в utf-8 каждая кодируется ровно 2 байтами, пример как ты видишь код первого и второго байта находятся в разных диапазонах (первый d0-d1, второй 80-bf) и натренировавшись можно их различать.
> ? Ведь если оперативной памяти под скрипт 70мб, а строка, которую нужно перевернуть весит 100мб, то она уже не помещается и её нужно как-то считывать по частям откуда-то и переворачивать или нет?
Пример конечно неудачный так как трудно найти сервер с 70 мб памяти. Но в реальности часто встречаются ситуации когда надо работать с большими объемами данных, например собранные на большом андронном коллайдере данные весят терабайты, так же огромные объемы данных выдают устройства анализа ДНК (например в биологии).
Ну или более простой пример: дампы карт разных стран открытого проекта OpenStreetMap весят гигабайты в сжатом виде.
В таких случаях мы должны как-то обрабатывать файл по частям. Например прочитать первые N Мб, обработать, записать на диск, прочитать вторые N Мб, и так далее.
В случае задачи переворачивания файла очевидно надо читать исходный файл с конца блоками, переворачивать блок и дописывать в выходной файл (это еще не точный ответ, ниже будут уточнения).
Классический пример такой задачи — это дисковая сортировка файла, который не вмещается в оперативную память. Такой файл разбивается на блоки, блоки сортируются и сохраняются во временный файл. После чего мы читаем эти блоки построчно и объядиняем: https://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0_%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5%D0%BC
Есть также обобщенный подход для реализации алгоритмов по принципу «разделяй и властвуй», он называется map-reduce. Он требует от тебя написать алгоритм в виде 2 функций, map и reduce и предполагает такой порядок работы:
— исходный файл разбивается на блоки
— каждый блок по очереди обрабатывается алгоритмом map. Этот процесс может идти как последовательно, так и параллельно если у нас много машин, причем изменение числа машин не требует изменений в алгоритме.
— затем алгоритм reduce собирает результат из блоков полученных на предыдущем этапе.
Преимущество такого подхода в том что имея функции map/reduce мы можем запускать их как в последовательном режиме, так и параллельно, например на нескольких ядрах или нескольких машинах, не меняя сам алгоритм. Более того, мы можем масштабировать систему на ходу, добавляя новые машины по мере необходимости.
map-reduce вроде бы используется в Гугл для обработки больших объеимов данных:
https://ru.wikipedia.org/wiki/MapReduce
http://habrahabr.ru/post/103467/
А теперь вернемся к предложенному мной алгоритму:
> читать исходный файл с конца блоками, переворачивать блок и дописывать в выходной файл
правильный ли он? Если у нас однобайтная кодировка и буква занимает 1 байт то да. Если у нас utf-8 или другая многобайтная кодировка то нет, этот алгоритм сломает все буквы так как буква состояшая из нескольких байт:
D0 D8
будет записана в первернутом виде
D8 D0
И разумеется файл станет нечитаемым. Что делать? Более того, если мы берем блок из файла, то нет гарантии что он будет обрезан по границам символов. мы можем в начале получить «хвост» буквы из предыдущего блока и в конце начало буквы, заканчивающейся в следующем блоке.
Тут надо менять алгоритм. utf-8 работает так, что по коду мы можем определить, первый это байт буквы, средний или последний (для них используются разные коды). Разбиваем блок на части:
(хвост буквы из пред. блока) (буква 1) (буква 2) ... ( буква N) (начало последней буквы)
Затем переворачиваем массив букв, а огрызки букв объединяем с тем, что остался от предыдущего блока. И записываем результат в выходной файл.
Если хочешь крепко поломать голову, я советую решить задачу переворачивания текста в огромном файле в utf-8. Это наверняка даст тебе какие-то полезные знания.
Для начала можешь прочесть про суму кодировку utf-8: https://ru.wikipedia.org/wiki/UTF-8
Заметь что там надо хотя бы немного понимать двоичную и шестнадцатеричную систему счисления.
Посмотреть код букв можно так:
http://ideone.com/q74IN8
> 48 65 6c 6c 6f
Это латинница
> 20
Пробел
> d0 bf d1 80 d0 b8 d0 b2 d0 b5 d1 82
Русские буквы, в utf-8 каждая кодируется ровно 2 байтами, пример как ты видишь код первого и второго байта находятся в разных диапазонах (первый d0-d1, второй 80-bf) и натренировавшись можно их различать.
Посредством fpdf, пытаюсь собрать pdf из нескольких выбранных файлов(multiple). Все изображения заливаются в корень с index.php. Но fpdf упорно ругается что не может их открыть. Что я делаю не так?
Warning: getimagesize(08kNmVUXGAJM.jpg) [function.getimagesize]: failed to open stream: No such file or directory in A:\home\test1.ru\www\m\fpdf.php on line 1213
FPDF error: Missing or incorrect image file: 08kNmVUXGAJM.jpg
Ты не указал полный путь к картинке значит она ищется только в текущем каталоге. Увидеть текущий каталог можно через echo getcwd(), поменять через chdir.
Проверь какой у тебя текущий каталог и есть ли в нем файл картинки. проверь также существует ли файл который ты пытаешься добавить как картинку через file_exists.
Вот, сделал:
http://ideone.com/Jiufhp
Не очень понял как добавить ? и ! в explode/ b еще у тебя в примере довольно большая функция fixText, а у меня совсем короткая получилась.
Алсо, уже можно перекатываться на какое-нибудь IDE, а то ideone не очень удобен (частенько хочется отменить действие например).
Ой, что-то сделал следующую задачу и понял, что первую сделал криво.
Проверь пока вторую, первую я скоро переделаю:
http://ideone.com/K84ggh
Нужен хостинг, чтобы проверить одну штуку. На локалке это нереально реализовать.
Попробуй хостингер, вроде неплохой вариант. Год назад пользовался, вполне себе на уровне для бесплатного хостинга.
Вот сейчас зарегистрировался на myfreehosting.ru, залил пару файлов на фтп, но они не открываются, выдает 404. То-ли я что не так делаю, то-ли сервис мозги ебёт. Причем для редактирования файлы открываются, а по прямой ссылке типа sitename.myfreehosting.ru/test.html нет.
http://phpclub.ru/mysql/doc/example-maximum-column-group-row.html
Код вроде элементарный, а какой тут должен быть ход мыслей, чтобы решить задание, не улавливаю.
Что здесь возвращает подзапрос (массив значений?), каким макаром он ссылается на алиас из внешнего запроса?
В общем, если кто сможет объяснить на пальцах, в какой последовательности сервер обрабатывает подобную инструкцию, буду благодарен.
SELECT article, dealer, price
FROM shop s1
WHERE price=(SELECT MAX(s2.price)
FROM shop s2
WHERE s1.article = s2.article);
Есть ли смысл сначала писать без фреймворков, а потом портировать? В целях самообучения.
> Не очень понял как добавить ? и ! в explode
explode не умеет разбивать строку по нескольким символам сразу. Для этого нужна более мощная функция preg_split, которая принимает регулярное выражение. Она находит в строке все места, соответствующие выражению, вырезает их и возвращает массив оставшихся (не соответствующих выражению) кусочков.
Мануал http://php.net/manual/ru/function.preg-split.php
> еще у тебя в примере довольно большая функция fixText, а у меня совсем короткая получилась.
Ну хорошо, если короткая.
> уже можно перекатываться на какое-нибудь IDE, а то ideone не очень удобен
Да, конечно. Выбери какой-нибудь редактор или IDE и пользуйся ими. Ты также можешь установить себе PHP чтобы запускать программы без ideone (а в том же PhpStorm их вообще можно прямо в IDE запускать одной кнопкой).
Вот урок по установке PHP https://gist.github.com/codedokode/7054af4a03865c4cc863
> не понял, почему надо писать &перед переменной, взял из мануала
Это плохо, что не понял. По умолчанию в PHP переменные в функцию передаются по значению, то есть передается не сама переменная, а копия ее значения. Если функция меняет эту копию, то сама исходная переменная не меняется. Знак & говорит что надо передавать не копию, а саму исходную переменную по ссылке, так, что ее можно изменить внутри функции.
Если ты не поставишь & то ты меняешь только копию переменной внутри функции, а сам элемент массива останется неизменным.
Вот тут описана передача по ссылке: http://php.net/manual/ru/functions.arguments.php#functions.arguments.by-reference
Мануал про ссылки: http://php.net/manual/ru/language.references.php
Я не советую копировать код если ты не понял, как он работает. Чтобы убедиться, что ты все понял, давай переделаем array_walk на array_map (она к тому же не требует использования ссылок).
В остальном все хорошо, программа работает верно.
> Не очень понял как добавить ? и ! в explode
explode не умеет разбивать строку по нескольким символам сразу. Для этого нужна более мощная функция preg_split, которая принимает регулярное выражение. Она находит в строке все места, соответствующие выражению, вырезает их и возвращает массив оставшихся (не соответствующих выражению) кусочков.
Мануал http://php.net/manual/ru/function.preg-split.php
> еще у тебя в примере довольно большая функция fixText, а у меня совсем короткая получилась.
Ну хорошо, если короткая.
> уже можно перекатываться на какое-нибудь IDE, а то ideone не очень удобен
Да, конечно. Выбери какой-нибудь редактор или IDE и пользуйся ими. Ты также можешь установить себе PHP чтобы запускать программы без ideone (а в том же PhpStorm их вообще можно прямо в IDE запускать одной кнопкой).
Вот урок по установке PHP https://gist.github.com/codedokode/7054af4a03865c4cc863
> не понял, почему надо писать &перед переменной, взял из мануала
Это плохо, что не понял. По умолчанию в PHP переменные в функцию передаются по значению, то есть передается не сама переменная, а копия ее значения. Если функция меняет эту копию, то сама исходная переменная не меняется. Знак & говорит что надо передавать не копию, а саму исходную переменную по ссылке, так, что ее можно изменить внутри функции.
Если ты не поставишь & то ты меняешь только копию переменной внутри функции, а сам элемент массива останется неизменным.
Вот тут описана передача по ссылке: http://php.net/manual/ru/functions.arguments.php#functions.arguments.by-reference
Мануал про ссылки: http://php.net/manual/ru/language.references.php
Я не советую копировать код если ты не понял, как он работает. Чтобы убедиться, что ты все понял, давай переделаем array_walk на array_map (она к тому же не требует использования ссылок).
В остальном все хорошо, программа работает верно.
Пришлось довольствоваться утешительным призом
>давай начнем с более простой задачи: просто выведи 5 самых популярных пользователей.
Хорошо, хоть это получилоьс.
https://gist.github.com/anonymous/72887bb131a8fb88a657
Реквестирую подсказку полного решения этой задачи.
Нехило я психанул сегодня. >>477538
Это тоже мои потуги.
Пойду попробую поспать.
Я тут заметил, что недосыпание негативно влияет на усмтвенные способности.
Не знаю, а чем он хуже? По моему это фреймворки одного уровня, разница может в том что Silex использует компоненты симфони.
Мы используем, например в задаче про файлообменник. В дикой природе он используется для простых маленьких одностраничных сайтов и всяких API.
>>477225
Хороший, серьезный фреймворк, стоит изучать, хотя если ты начинающий то лучше сначала решить наши задачи про студентов и файлообменник.
>>477239
> $arr[$i] = $newParts; //составляем новый массив из предложений
Можно писать просто $arr[] = ... это автоматически проставит подходящий числовой индекс для элемента.
> foreach ($parts as &$value)
Зачем тут & ? Не пиши то, что не понимаешь сам. Ты должен каждый символ в коде понимать.
> krsort($words); //сортируем массив из слов по значениям в обратном порядке
Для переворачивания больше подходит специально для этого придуманная функция array_reverse.
> foreach ($parts as &$value) {
Вторую переменную логично назвать $part. value ничего не значит и только сбиваетс толку. А еще лучше использовать тут слова foreach ($sentences as $sentence) ...
> array_walk(
Давай на array_map попробуем заменить? Чтобы без ссылок обойтись.
> добавляем точку в конце (коряво?)
Сойдет
> //все буквы в нижний регистр
Это называется комментарий Капитана Очевидность. В комментариях стоит писать не что делает функция (это любой может прочесть в мануале), а зачем и почему ты это делаешь, например «после переворачивания строки последнее слово будет с заглавной буквы. Исправим это, переведя все буквы в нижний регистр». Видишь, в чем разница? Я объясняю для какой цели тут использована эта функция и почему ее нельзя убрать.
Такие комментарии стоит ставить в не очень очевидных местах.
Соответственно комментарий вроде «//объединяем слова в предложения» особого смысла не несет, так как это просто описание функции implode.
Не знаю, а чем он хуже? По моему это фреймворки одного уровня, разница может в том что Silex использует компоненты симфони.
Мы используем, например в задаче про файлообменник. В дикой природе он используется для простых маленьких одностраничных сайтов и всяких API.
>>477225
Хороший, серьезный фреймворк, стоит изучать, хотя если ты начинающий то лучше сначала решить наши задачи про студентов и файлообменник.
>>477239
> $arr[$i] = $newParts; //составляем новый массив из предложений
Можно писать просто $arr[] = ... это автоматически проставит подходящий числовой индекс для элемента.
> foreach ($parts as &$value)
Зачем тут & ? Не пиши то, что не понимаешь сам. Ты должен каждый символ в коде понимать.
> krsort($words); //сортируем массив из слов по значениям в обратном порядке
Для переворачивания больше подходит специально для этого придуманная функция array_reverse.
> foreach ($parts as &$value) {
Вторую переменную логично назвать $part. value ничего не значит и только сбиваетс толку. А еще лучше использовать тут слова foreach ($sentences as $sentence) ...
> array_walk(
Давай на array_map попробуем заменить? Чтобы без ссылок обойтись.
> добавляем точку в конце (коряво?)
Сойдет
> //все буквы в нижний регистр
Это называется комментарий Капитана Очевидность. В комментариях стоит писать не что делает функция (это любой может прочесть в мануале), а зачем и почему ты это делаешь, например «после переворачивания строки последнее слово будет с заглавной буквы. Исправим это, переведя все буквы в нижний регистр». Видишь, в чем разница? Я объясняю для какой цели тут использована эта функция и почему ее нельзя убрать.
Такие комментарии стоит ставить в не очень очевидных местах.
Соответственно комментарий вроде «//объединяем слова в предложения» особого смысла не несет, так как это просто описание функции implode.
> $parts = trim($parts);
Ты передаешь в функцию trim массив вместо строки, о чем она возмущенно пишет ниже:
> PHP Warning: trim() expects parameter 1 to be string, array given in /home/2WwBpt/prog.php on line 24
Так как она не может обработать массив, то возвращает null в знак протеста.
Адрес в URL не обязан соответствовать папкам и файлам на сервере. Если в адресе написано /catalog/8 это на значит что на сервере есть папка с таким именем. Ты можешь настроить правила обработки URL как угодно (какой скрипт вызывать или какой файл отдавать при обращении по такому-то URL).
Вот мой урок про виды URL: https://gist.github.com/codedokode/772a4ccc03e41d6b7cba
> Откуда берутся эти дополнительные страницы? Как они генерируются?
Там все обращения по адресам вроде /catalog/XXX перенаправлены на один и тот же скрипт, например catalog.php. Он анализирует какая цифра указана в URL и соответственно при выборке из базы добавляет конструкцию LIMIT которая говорит пропустить первые N строк.
> И как ставится нумеровка снизу, указывающая на количество доступных страниц и текущую страницу?
Циклом for может быть?
Если будешь делать нашу задачу про список студентов (в первом посте) то там тоже надо сделать постраничную навигацию.
Есть бесплатные PHP хостинги, причем западные даже без рекламы.
Юкоз я бы вообще никому не советовал. Там куча рекламы и ограничений и он расчитан на создание сайтов через их конструктор, а не программированием.
>>477507
Посмотри логи на сервере (если они есть в панели управления), посмотри что пишет инспектор в браузере (Ctrl + sHift + I) на вкладке Network. Из твоего описания вообще ничего не ясно.
>>477538
Подзапросы это плохая вещь в общем, они плохо читаются, плохо оптимизируются. Лучше использовать джойны.
> Что здесь возвращает подзапрос (массив значений?), каким макаром он ссылается на алиас из внешнего запроса?
Здесь подзапрос использован внутри WHERE. Это значит что MySQL выбирает по очереди строки из таблицы shop s1 и каждую строку проверяет на соответсвие условию WHERE. Так как там подзапрос то это значит что для каждой строки по очереди выполняется отдельный запрос с подстановкой туда конкретного значения, например, если у нас в таблице такие значения:
s1.article
----------
10
20
30
То для первой строчки будет выполнен подзапрос
SELECT MAX(s2.price) FROM shop s2 WHERE 10 = s2.article
Для второй
SELECT MAX(s2.price) FROM shop s2 WHERE 20 = s2.article
И так далее.
Ну и потом в выборке останутся только те строчки где условие истинно.
Так как подзапрос использован в равенстве
WHERE x = (...)
То он должен вернуть ровно 1 значение, иначе будет ошибка. Подзапросы можно использовать в таких местах:
— сравнения или выражения (подзапрос должен вернуть одно значение):
WHERE x = (...)
WHERE x > (...)
— IN (подзапрос может вернуть много строк с 1 колонкой)
WHERE x IN (select ...)
— FROM/JOIN (подзапрос может вернуть много строк и колонок)
SELECT ... FROM (SELECT ...) ...
Я не советую использовать подзапросы, этот запрос лучше и быстрее будет работать с группировкой.
Мануал:
http://www.mysql.ru/docs/gruber/mg10.html
http://www.mysql.ru/docs/gruber/mg11.html
Также у нас есть хорошие задачи на MySQL (в первом посте), я советую решить там задачку про лайки. Это минимум по базам данных, который должен знать любой программист.
Есть бесплатные PHP хостинги, причем западные даже без рекламы.
Юкоз я бы вообще никому не советовал. Там куча рекламы и ограничений и он расчитан на создание сайтов через их конструктор, а не программированием.
>>477507
Посмотри логи на сервере (если они есть в панели управления), посмотри что пишет инспектор в браузере (Ctrl + sHift + I) на вкладке Network. Из твоего описания вообще ничего не ясно.
>>477538
Подзапросы это плохая вещь в общем, они плохо читаются, плохо оптимизируются. Лучше использовать джойны.
> Что здесь возвращает подзапрос (массив значений?), каким макаром он ссылается на алиас из внешнего запроса?
Здесь подзапрос использован внутри WHERE. Это значит что MySQL выбирает по очереди строки из таблицы shop s1 и каждую строку проверяет на соответсвие условию WHERE. Так как там подзапрос то это значит что для каждой строки по очереди выполняется отдельный запрос с подстановкой туда конкретного значения, например, если у нас в таблице такие значения:
s1.article
----------
10
20
30
То для первой строчки будет выполнен подзапрос
SELECT MAX(s2.price) FROM shop s2 WHERE 10 = s2.article
Для второй
SELECT MAX(s2.price) FROM shop s2 WHERE 20 = s2.article
И так далее.
Ну и потом в выборке останутся только те строчки где условие истинно.
Так как подзапрос использован в равенстве
WHERE x = (...)
То он должен вернуть ровно 1 значение, иначе будет ошибка. Подзапросы можно использовать в таких местах:
— сравнения или выражения (подзапрос должен вернуть одно значение):
WHERE x = (...)
WHERE x > (...)
— IN (подзапрос может вернуть много строк с 1 колонкой)
WHERE x IN (select ...)
— FROM/JOIN (подзапрос может вернуть много строк и колонок)
SELECT ... FROM (SELECT ...) ...
Я не советую использовать подзапросы, этот запрос лучше и быстрее будет работать с группировкой.
Мануал:
http://www.mysql.ru/docs/gruber/mg10.html
http://www.mysql.ru/docs/gruber/mg11.html
Также у нас есть хорошие задачи на MySQL (в первом посте), я советую решить там задачку про лайки. Это минимум по базам данных, который должен знать любой программист.
По моему, ты либо потратишь время зря (то есть быстрее сразу писать на фрйемворке), либо бросишь и так и не перенесешь. Но ты можешь попробовать, если хочешь. Я готов прокомментировать код и дать советы. Но только тогда надо писать с использованием ООП и MVC, быдлокод на функциях и массивах в стиле PHP4 сейчас никому не нужен.
Также, я бы советовал сначала решить одну-две задачи из верхнего поста (про студентов и файлообменник), они дадут тебе полезные знания. Если ты учился по плохим устаревшим учебникам или видеокурсам то они помогут тебе переучиться на современнный нормальный стиль программирования. Хотя бы комментарии к задаче про студентов почитай.
> Задача про лайки. Взорвала мой мозг.
Не беда, я могу дать подсказки. Там достаточно 2 джойнов + группировка по моему в самом оптимальном варианте. Но если ты можешь сделать еще проще, то я не против.
Код лучше постить на sqlfiddle так как там он выполняется и можно увидеть результат.
Советы и замечания:
> name VARCHAR(50)
Для обязательных к заполнению полей принято ставить NOT NULL. NULL значит «неизвестно, не указано», а ведь у нас вряд ли разрешены пользователи без имени.
Между таблицами надо проставить связи через внешние ключи: http://denis.in.ua/foreign-keys-in-mysql.htm
Это во-первых, делает твою базу данных гораздо понятнее так как видны связи, во-вторых защищает тебя от вставки неправильных значений (а это экономит время на исправление последствий ошибок).
who/whom плохо различаются и легко сделать опечатку, лучше писать from/to или from_user/to_user или from_id/to_id.
> PRIMARY KEY (who, whom)
Это ты правильно сделал, это защищает от повторной вставки записи с теми же значениями
По поводу подсказки:
Вот ты сделал число полученных лайков. А теперь попробуй добавить 1 джойн так, чтобы выводилось еще и число отданных лайков.
При этом ты можешь наткнуться на проблему, что при джойне 2 таблиц у тебя получаются большие числа (так как одно и то же id учитывается несколько раз). Допустим юзер 1 поставил лайки 3 и 5 и получил от 4, 5, 6. Получается такая картина:
юзер кому_поставил от_кого_получил
юзер1 3 4
юзер1 3 5
юзер1 3 6
юзер1 5 4
юзер1 5 5
юзер1 5 6
То есть джойн дает нам все возможные комбинации id с учетом условий ON/WHERE (это называется полное декартовое произведение).
На таком массиве данных COUNT покажет число 6 для обоих колонок (полученных/отданных лайков), и это не то, что нам надо.
Решение этой проблемы ты можешь найти если изучишь агрегатные функции (группирующие строки, вроде COUNT) получше:
http://www.mysql.ru/docs/gruber/mg06.html
http://phpclub.ru/mysql/doc/group-by-functions.html
В общем, я бы хотел чтобы эта задача научила тебя мыслить именно джойнами: мы джойним таблицы, добавляем условия для отсеивания записей, группируем и получаем нужные нам данные. джойны очень важная штука и используются повсеместно.
> Задача про лайки. Взорвала мой мозг.
Не беда, я могу дать подсказки. Там достаточно 2 джойнов + группировка по моему в самом оптимальном варианте. Но если ты можешь сделать еще проще, то я не против.
Код лучше постить на sqlfiddle так как там он выполняется и можно увидеть результат.
Советы и замечания:
> name VARCHAR(50)
Для обязательных к заполнению полей принято ставить NOT NULL. NULL значит «неизвестно, не указано», а ведь у нас вряд ли разрешены пользователи без имени.
Между таблицами надо проставить связи через внешние ключи: http://denis.in.ua/foreign-keys-in-mysql.htm
Это во-первых, делает твою базу данных гораздо понятнее так как видны связи, во-вторых защищает тебя от вставки неправильных значений (а это экономит время на исправление последствий ошибок).
who/whom плохо различаются и легко сделать опечатку, лучше писать from/to или from_user/to_user или from_id/to_id.
> PRIMARY KEY (who, whom)
Это ты правильно сделал, это защищает от повторной вставки записи с теми же значениями
По поводу подсказки:
Вот ты сделал число полученных лайков. А теперь попробуй добавить 1 джойн так, чтобы выводилось еще и число отданных лайков.
При этом ты можешь наткнуться на проблему, что при джойне 2 таблиц у тебя получаются большие числа (так как одно и то же id учитывается несколько раз). Допустим юзер 1 поставил лайки 3 и 5 и получил от 4, 5, 6. Получается такая картина:
юзер кому_поставил от_кого_получил
юзер1 3 4
юзер1 3 5
юзер1 3 6
юзер1 5 4
юзер1 5 5
юзер1 5 6
То есть джойн дает нам все возможные комбинации id с учетом условий ON/WHERE (это называется полное декартовое произведение).
На таком массиве данных COUNT покажет число 6 для обоих колонок (полученных/отданных лайков), и это не то, что нам надо.
Решение этой проблемы ты можешь найти если изучишь агрегатные функции (группирующие строки, вроде COUNT) получше:
http://www.mysql.ru/docs/gruber/mg06.html
http://phpclub.ru/mysql/doc/group-by-functions.html
В общем, я бы хотел чтобы эта задача научила тебя мыслить именно джойнами: мы джойним таблицы, добавляем условия для отсеивания записей, группируем и получаем нужные нам данные. джойны очень важная штука и используются повсеместно.
Конечно я буду использовать ООП и MVC. Все твои статьи уже прочитал, довольно полезны.
Дело в том, что я уже кодил раньше, но на плюсах и питоне. Синтаксис и библиотеку самого пхп практически не знаю, планирую выучить по ходу дела. Мне главное понять архитектуру сайта и бд, как все устроено. Больше сейчас читаю про паттерны, а не сам язык.
Я посмотрел, как работает перловская wakaba. Фактически буду переписывать ее на пхп по всем правилам ООП и MVC.
Уже, кстати, появился вопрос.
Очевидно, при запросе страницы треда (/thread/214723.html) мы не будем генерировать ее каждый раз. Только при добавлении или удалении поста. В остальных случаях просто будем отдавать уже готовую.
1) Что будет, если во время отдачи страницы одному пользователю, другой - добавит пост в тред и перегенерирует ее? То есть кэширует ли пхп или апач страницы во время отдачи автоматически или это надо делать явно?
2) Допустим, нам надо отдать статичный файл (ту же страницу или картинку). Можно ли делать это на этапе роутинга, не прибегая к контроллеру? В идеале можно даже не использовать RewriteCond %{REQUEST_FILENAME} !-f в .htaccess. Запрашивать напрямую. Вопрос в том, соответствует ли это паттерну MVC?
3) Где лучше делать экранирование данных: в моделе или контроллере?
> Очевидно, при запросе страницы треда (/thread/214723.html) мы не будем генерировать ее каждый раз. Только при добавлении или удалении поста. В остальных случаях просто будем отдавать уже готовую.
Это довольно спорное решение и имеет как минимум такие недостатки:
— нельзя сделать страницу кастомизированной для пользователя, например подсвечивать его посты и скрывать те, что не нравятся, нельзя выводить время в стиле «5 минут назад»
— когда постов много, приходится пересоздавать огромный файл
— это более сложное решение чем просто брать посты из базы и выводить. Зачем делать сложнее если можно делать проще?
Потому если ты хочешь простой вариант то логичнее именно генерировать каждый раз.
В вакабе сделано так потому, что это изначально примитивный набор скриптов на Перле, железо тогда было дохлое, MVC не использовали и приходилось возиться с файлами и их генерацией. В наше время наверно проще и удобнее все в Бд хранить и генерировать на лету (с другой стороны, вакаба интересный пример того, как примитивными средствами сделать относительно сложную систему).
Интересно, кстати, они при добавлении поста перегенерируют весь тред? Ведь можно (если бы там не было футера) было просто дописать новый пост в конец треда, что гораздо быстрее. Ну или как-то зная длину футера, откусывать его.
Тут конечно есть вопрос, как это потянет высокую нагрузку, файлы с диска конечно можно отдавать с намного большей скоростью чем динамический контент. Хотя я не уверен, высокая нагрузка плохо сочетается с гигантскими тредами по 500 постов, если ты хочешь высокую нагрузку то выгоднее как-то все это частями грузить. Ну и динамическая генерация дает гораздо больше возможностей. Ну и при активном постинге вакаба должна неслабо грузить файловую систему постоянными перезаписями огромных файлов (тут конечно надо мерять, а не гадать).
Что я хочу сказать, что надо внимательно изучить преимущества и недостатки каждого подхода. Я лично сторонник не делать преждевременных оптимизаций и делать как проще.
> Что будет, если во время отдачи страницы одному пользователю, другой - добавит пост в тред и перегенерирует ее? То есть кэширует ли пхп или апач страницы во время отдачи автоматически или это надо делать явно?
Вот, ты правильно мыслишь. По умолчанию в линуксе файлы никак не блокируются и если один пользователь запросит страницу в то время как другой пишет в нее, то первый получит оборванный файл.
Апач тут не при чем, он не кеширует если явно не попросить, php тем более (так как он не участвует в отдаче файла).
Решения проблемы с параллельным доступом как миниум две:
— блокировки файлов. При записи мы берем эксклюзивную блокировку на файл, заставляя других ждать ее снятия при попытке прочитать его.
— атомарная операция переписывания файла (тут этот вариант выгоднее так как не требует ничего блокировать). Мы пишем во временный файл, а потом атомарно командой rename() помещаем временный файл на место файла с тредом.
Чтение:
linux: man flock, man fcntl
http://linux.die.net/man/2/fcntl
http://linux.die.net/man/2/flock
http://linux.die.net/man/3/fcntl
windows: msdn createfile https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx (обрати внимание на флаг dwShareMode)
> Допустим, нам надо отдать статичный файл (ту же страницу или картинку). Можно ли делать это на этапе роутинга, не прибегая к контроллеру?
Зачем доводить это до этапа роутинга? Это надо делать на уровне nginx/Апача не запуская PHP
> В идеале можно даже не использовать RewriteCond %{REQUEST_FILENAME} !-f в .htaccess.
А в чем проблема с этим условием? И я сомневаюсь что ты сможешь «запрашивать напрямую» при использовании mod_rewrite, по моему ты не очень понял как он работает.
> . Вопрос в том, соответствует ли это паттерну MVC?
паттерн MVC определяет разделение кода, который генерирует динамические страницы. Про статические он ничего не говорит, так что можно так делать.
> Где лучше делать экранирование данных: в моделе или контроллере?
Там где они используются, чтобы с первого взгляда было видно что все безопасно. Соответственно экранирование HTML спецсимволов надо делать в шаблоне (либо использовать шаблоизатор с автоэкранированием), а экранирование данных для БД в той функции где выполняется запрос, причем не стоит делать это руками, используй плейсхолдеры.
Вот мои уроки по XSS и XSRF:
https://github.com/codedokode/pasta/blob/master/security/xss.md
https://github.com/codedokode/pasta/blob/master/security/xsrf.md
Ну и насчет «изучить PHP в процессе», я бы советовал все же найти время и порешать задачки с моего учебника. Они научат тебя самым часто используемым функциям, да и с опытом программирования ты их сможешь быстро решить. А то в сети можно найти кучу неправильных и устаревших примеров.
И да, почитай еще сайт phptherightway, он как раз борется с устаревшими подходами.
> Очевидно, при запросе страницы треда (/thread/214723.html) мы не будем генерировать ее каждый раз. Только при добавлении или удалении поста. В остальных случаях просто будем отдавать уже готовую.
Это довольно спорное решение и имеет как минимум такие недостатки:
— нельзя сделать страницу кастомизированной для пользователя, например подсвечивать его посты и скрывать те, что не нравятся, нельзя выводить время в стиле «5 минут назад»
— когда постов много, приходится пересоздавать огромный файл
— это более сложное решение чем просто брать посты из базы и выводить. Зачем делать сложнее если можно делать проще?
Потому если ты хочешь простой вариант то логичнее именно генерировать каждый раз.
В вакабе сделано так потому, что это изначально примитивный набор скриптов на Перле, железо тогда было дохлое, MVC не использовали и приходилось возиться с файлами и их генерацией. В наше время наверно проще и удобнее все в Бд хранить и генерировать на лету (с другой стороны, вакаба интересный пример того, как примитивными средствами сделать относительно сложную систему).
Интересно, кстати, они при добавлении поста перегенерируют весь тред? Ведь можно (если бы там не было футера) было просто дописать новый пост в конец треда, что гораздо быстрее. Ну или как-то зная длину футера, откусывать его.
Тут конечно есть вопрос, как это потянет высокую нагрузку, файлы с диска конечно можно отдавать с намного большей скоростью чем динамический контент. Хотя я не уверен, высокая нагрузка плохо сочетается с гигантскими тредами по 500 постов, если ты хочешь высокую нагрузку то выгоднее как-то все это частями грузить. Ну и динамическая генерация дает гораздо больше возможностей. Ну и при активном постинге вакаба должна неслабо грузить файловую систему постоянными перезаписями огромных файлов (тут конечно надо мерять, а не гадать).
Что я хочу сказать, что надо внимательно изучить преимущества и недостатки каждого подхода. Я лично сторонник не делать преждевременных оптимизаций и делать как проще.
> Что будет, если во время отдачи страницы одному пользователю, другой - добавит пост в тред и перегенерирует ее? То есть кэширует ли пхп или апач страницы во время отдачи автоматически или это надо делать явно?
Вот, ты правильно мыслишь. По умолчанию в линуксе файлы никак не блокируются и если один пользователь запросит страницу в то время как другой пишет в нее, то первый получит оборванный файл.
Апач тут не при чем, он не кеширует если явно не попросить, php тем более (так как он не участвует в отдаче файла).
Решения проблемы с параллельным доступом как миниум две:
— блокировки файлов. При записи мы берем эксклюзивную блокировку на файл, заставляя других ждать ее снятия при попытке прочитать его.
— атомарная операция переписывания файла (тут этот вариант выгоднее так как не требует ничего блокировать). Мы пишем во временный файл, а потом атомарно командой rename() помещаем временный файл на место файла с тредом.
Чтение:
linux: man flock, man fcntl
http://linux.die.net/man/2/fcntl
http://linux.die.net/man/2/flock
http://linux.die.net/man/3/fcntl
windows: msdn createfile https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx (обрати внимание на флаг dwShareMode)
> Допустим, нам надо отдать статичный файл (ту же страницу или картинку). Можно ли делать это на этапе роутинга, не прибегая к контроллеру?
Зачем доводить это до этапа роутинга? Это надо делать на уровне nginx/Апача не запуская PHP
> В идеале можно даже не использовать RewriteCond %{REQUEST_FILENAME} !-f в .htaccess.
А в чем проблема с этим условием? И я сомневаюсь что ты сможешь «запрашивать напрямую» при использовании mod_rewrite, по моему ты не очень понял как он работает.
> . Вопрос в том, соответствует ли это паттерну MVC?
паттерн MVC определяет разделение кода, который генерирует динамические страницы. Про статические он ничего не говорит, так что можно так делать.
> Где лучше делать экранирование данных: в моделе или контроллере?
Там где они используются, чтобы с первого взгляда было видно что все безопасно. Соответственно экранирование HTML спецсимволов надо делать в шаблоне (либо использовать шаблоизатор с автоэкранированием), а экранирование данных для БД в той функции где выполняется запрос, причем не стоит делать это руками, используй плейсхолдеры.
Вот мои уроки по XSS и XSRF:
https://github.com/codedokode/pasta/blob/master/security/xss.md
https://github.com/codedokode/pasta/blob/master/security/xsrf.md
Ну и насчет «изучить PHP в процессе», я бы советовал все же найти время и порешать задачки с моего учебника. Они научат тебя самым часто используемым функциям, да и с опытом программирования ты их сможешь быстро решить. А то в сети можно найти кучу неправильных и устаревших примеров.
И да, почитай еще сайт phptherightway, он как раз борется с устаревшими подходами.
>— нельзя сделать страницу кастомизированной для пользователя, например подсвечивать его посты и скрывать те, что не нравятся, нельзя выводить время в стиле «5 минут назад»
Почему не JS?
>— когда постов много, приходится пересоздавать огромный файл
Так в любом случае его генерировать. Только сравни: 500 человек сидят в ридонли и только открывают странички, и 10 - в это время постят. Так мы генерируем страницу не 500 раз в секунду, а 10.
>— это более сложное решение чем просто брать посты из базы и выводить. Зачем делать сложнее если можно делать проще?
Чем же? Мне кажется, даже проще. Например, получаем запрос GET /pr/res/468317.html - сразу отдаем nginx'ом.
В функциях добавления и удаления поста пишем в конце что-то типа rebuild_cache($thread_id).
Насколько я тебя понял, все упирается в нагрузку файловой системы. Можно тогда хранить снапшот треда в памяти и так же его обновлять при постинге.
А вообще, написав все это, я вспомнил, что в основном люди используют автоподгрузку, а не полное обновление страницы. Так и нагрузка на сервер выходит намного меньше. Если это принять во внимание, то количество постов в секунду будет даже больше, чем количество полных обновлений. И тогда станет выгоднее использовать динамическую выдачу.
В общем, сделаю динамически и пока только с апачем. Потом, может, прикручу nginx чисто для статики.
Спасибо за ссылки, гляну.
Вообще, не ценишь ты свое время, бро. Такой пост - это же где-то час писанины.
> Почему не JS?
Потому что это костыли. Ведь если бы мы не усложняли себе жизнь и делали динамическую генерацию, то все бы работало само собой.
Ну и на JS такие вещи мало кто может сделать нормально. На практике получится что-то, что будет прыгать, срабатывать с задержкой и тд.
> Так в любом случае его генерировать. Только сравни: 500 человек сидят в ридонли и только открывают странички, и 10 - в это время постят. Так мы генерируем страницу не 500 раз в секунду, а 10.
В одном случае мы создаем только сетевой трафик + нагрузку на CPU, а в другом нагрузку на запись на диски. И неизвестно какого ресурса у тебя больше, CPU или дискового трафика. Это надо делать измерения и тесты, а не пытаться угадать (что ты явно пытаешься сделать и это неправильно).
> В функциях добавления и удаления поста пишем в конце что-то типа rebuild_cache($thread_id).
Вот, это и есть нездоровый подход. Ты не знаешь, нужен тебе кеш или нет, ты даже не прикинул что на что мы меняем в случае его использования, ты не делал тесты, а уже закладываешь его в архитектуру. Так дела не делаются. Ты должен делать выводы на основе расчетов, предыдущего опыта, измерений а не потому что тебе так кажется.
> , я вспомнил, что в основном люди используют автоподгрузку, а не полное обновление страницы. Так и нагрузка на сервер выходит намного меньше.
В случае с вакабой автоподгрузка сводится к запросу всей страницы и отдачей либо 304 либо 200. Если добавлен один комментарий в тред из 500 постов мы скачиваем 501 пост.
Вообще да, это общепринятый подход, обновлять комментарии аяксом, на Хабре тоже так делают, это экономит сетевой трафик как минимум.
> это же где-то час писанины.
Шутишь? Час на такой пост? Максимум минут 15-20, я же опытный. Алсо, я стараюсь отвечать на посты когда ем что-нибудь, чтобы зря время не терять.
Вообще, лучше всего было бы если бы ты набросал простой скрипт (для статической/динамической генерации страниц) и попробовал померять: дисковый трафик, сетевой, число запросов в секунду и тд.
>В случае с вакабой автоподгрузка сводится к запросу всей страницы и отдачей либо 304 либо 200. Если добавлен один комментарий в тред из 500 постов мы скачиваем 501 пост.
Лол, и правда. Это же крайне тупо. Мы же можем послать номер поста, на котором остановились, и получить только разницу. Подводные камни - если были удалены какие-то посты.
>>477650
Я не знаю реальную статистику, сколько людей загружает полную страницу, а сколько автообновляет. От этой пропорции все и зависит.
> Это же крайне тупо
Это потому что автоподгрузка изначально была в каком-то расширении к браузеру (некий «куклоскрипт» или «вишмастер» может быть?), и ее авторы не имели возможности поменять что-то на сервере.
> Я не знаю реальную статистику, сколько людей загружает полную страницу, а сколько автообновляет.
Можно взять статистику с потолка. Ну или рассмотреть 2 экстремальных сценария: только загрузка полной страницы (человек просто тыкает во все треды подряд?) или 1 загрузка + 100 обновлений + 20 отправок сообщения аяксом (активный участник треда).
Для начала ты можешь просто на бумаге прикинуть, сколько и каких операций будет выполнено в каком случае. И соответственно будет видно, что мы меняем на что.
> Подводные камни - если были удалены какие-то посты.
Это тоже реально реализовать:
— ведя на сервере что-то вроде журнала событий и присылая записи этого «журнала» на клиент.
— помечая удаленный пост флагом deleted = 1 вместо удаления и ставя дату обновления. При наличии поля «дата обновления» мы можем также присылать на клиент информацию об удаленных или отредактированных постах начиная с момента времени t.
Кстати, еще одна плохо продуманная вещь в бордах — это постраничный список тредов. В активных разделах можно легко не увидеть некоторые треды, так как они будут вытеснены более активными в процессе обновления:
Заходим на первую страницу, видим посты A, B, C, (на второй в это время D, E, F)
Аноны постят в D, E, F выталкивая их вверх
Заходим на вторую страницу раздела и снова видим A, B, C!!!
На реддите если ты посмотришь кнопку следующая/предыдущая страница, используется более хитрый подход, там не передается номер страницы. Ну и вот еще пост в тему: http://use-the-index-luke.com/no-offset (я не согласен что надо всегда отказываться от LIMIT. Нужно смотреть по ситуации).
Ну и сложная вещь тут не обновления треда. Гораздо сложнее обновлять страницы со списком тредов, так как если ты хочешь сделать их на файлах, то тебе надо при каждом постинге обновлять все HTML-файлы и это уже гораздо более тяжелая операция.
https://github.com/AzerusEncole/File_Sharing
Дамп
http://rghost.ru/7TSk8M57j
Чтобы добавить вычисления для чисел с плавающей точкой, надо регулярку делать или можно как-то is_float обойтись?
Ага, кажется начинаю догонять. Исправьте, если где-то неправильно понял.
Я догадываюсь, что WHERE работает наподобие цикла (наверняка оно там внутри так и реализовано), то есть выполнение этого запроса происходит в следующем порядке:
0. Допустим, таблица выглядит следующим образом:
article-----dealer----price
0001------A----------3.45
0001------B----------3.99
0002------A----------10.99
0003------B----------1.45
0003------C----------1.69
0003------D----------1.25
1. Сначала интерпретатор отмечает себе только что выбрать и откуда:
SELECT article, dealer, price
FROM shop s1;
Это будет первая инструкция к серверу, чтобы он выбрал все содержимое указанных полей данной таблицы.
2. Теперь, заметив предикат/клаузулу WHERE (кстати, в чем конкретно разница между п. и к.?), интерпретатор заставит сервер пройтись в цикле по всем записям, попавшим в массив/объект (или в какой там форме возвращает данные mysql).
В каждой итерации будет участвовать одна запись целиком (т.е. не только один столбец, WHERE s1.price = ... не означает, что будет взята только цена; все указанные в п.1 поля текущей записи так сказать под рукой в текущей итерации).
В первую итерацию попадет запись
0001-----A-----3.45
Я знаю что в бд все хранится в "куче", но предполагаю, что записи будут перебираться в том порядке, в каком они заносились. Впрочем, порядок неважен в данном случае.
3. Итак, на первой итерации результирующий запрос будет выглядеть след.образом:
SELECT article, dealer, price
FROM shop s1
WHERE 3.45 = stmt;
, где stmt - это подзапрос, возвращающий одно значение.
Значит, сначала выполнится подзапрос, которому между прочим будут доступны поля текущей записи
s1.article = 0001
s1.dealer = A
s1.price = 3.45
SELECT max(s2.price)
FROM shop s2
WHERE s2.article = 0001;
Этот подзапрос выберет все записи для товара 0001 и найдет максимальное значение поля price для него.
Теперь это значение (равное 3.99, см. п.0) подставится уже в наружный запрос, и мы получим
SELECT article, dealer, price
FROM shop s1
WHERE 3.45 = 3.99;
3.45 != 3.99, значит в результирующий набор эта запись (0001 - A - 3.45) не попадет.
4. На последующих итерациях будут повторяться действия, описанные в п.3, но в качестве текущего объекта будут браться значения уже из второй, третьей и т.д. записи.
Например, на второй итерации получим следующие операции:
s1.article = 0001
s1.dealer = B
s1.price = 3.99
SELECT article, dealer, price
FROM shop s1
WHERE 3.99 = ( SELECT max(s2.price)
FROM shop s2
WHERE s2.article = 0001 );
, что сведется к следующему:
SELECT article, dealer, price
FROM shop s1
WHERE 3.99 =3.99;
(3.99 === 3.99) ? добавляем в результирующий набор : пропускаем
5. Повторить пункт 3 для каждой записи таблицы shop.
Итого:
1) Подзапросы действительно не очень удобны для чтения и понимания.
Мне вот пришлось написать такое полотно с анализом, чтобы понять что собственно тут происходит.
Возможно, опытные программисты уже на автомате пишут такие запросы, не задумываясь.
А пока приходится напрягаться, чтобы понять логику кода.
2) Неоптимизированный код. Для каждой записи выполняется подзапрос, насколько я понял.
Это все равно что написать в php
for ($i = 0; $i < count($array); $i ++){}
ОК, вечером попробую написать этот же запрос джойнами, попытаюсь наконец решить задачу про лайки. >>477628
Ага, кажется начинаю догонять. Исправьте, если где-то неправильно понял.
Я догадываюсь, что WHERE работает наподобие цикла (наверняка оно там внутри так и реализовано), то есть выполнение этого запроса происходит в следующем порядке:
0. Допустим, таблица выглядит следующим образом:
article-----dealer----price
0001------A----------3.45
0001------B----------3.99
0002------A----------10.99
0003------B----------1.45
0003------C----------1.69
0003------D----------1.25
1. Сначала интерпретатор отмечает себе только что выбрать и откуда:
SELECT article, dealer, price
FROM shop s1;
Это будет первая инструкция к серверу, чтобы он выбрал все содержимое указанных полей данной таблицы.
2. Теперь, заметив предикат/клаузулу WHERE (кстати, в чем конкретно разница между п. и к.?), интерпретатор заставит сервер пройтись в цикле по всем записям, попавшим в массив/объект (или в какой там форме возвращает данные mysql).
В каждой итерации будет участвовать одна запись целиком (т.е. не только один столбец, WHERE s1.price = ... не означает, что будет взята только цена; все указанные в п.1 поля текущей записи так сказать под рукой в текущей итерации).
В первую итерацию попадет запись
0001-----A-----3.45
Я знаю что в бд все хранится в "куче", но предполагаю, что записи будут перебираться в том порядке, в каком они заносились. Впрочем, порядок неважен в данном случае.
3. Итак, на первой итерации результирующий запрос будет выглядеть след.образом:
SELECT article, dealer, price
FROM shop s1
WHERE 3.45 = stmt;
, где stmt - это подзапрос, возвращающий одно значение.
Значит, сначала выполнится подзапрос, которому между прочим будут доступны поля текущей записи
s1.article = 0001
s1.dealer = A
s1.price = 3.45
SELECT max(s2.price)
FROM shop s2
WHERE s2.article = 0001;
Этот подзапрос выберет все записи для товара 0001 и найдет максимальное значение поля price для него.
Теперь это значение (равное 3.99, см. п.0) подставится уже в наружный запрос, и мы получим
SELECT article, dealer, price
FROM shop s1
WHERE 3.45 = 3.99;
3.45 != 3.99, значит в результирующий набор эта запись (0001 - A - 3.45) не попадет.
4. На последующих итерациях будут повторяться действия, описанные в п.3, но в качестве текущего объекта будут браться значения уже из второй, третьей и т.д. записи.
Например, на второй итерации получим следующие операции:
s1.article = 0001
s1.dealer = B
s1.price = 3.99
SELECT article, dealer, price
FROM shop s1
WHERE 3.99 = ( SELECT max(s2.price)
FROM shop s2
WHERE s2.article = 0001 );
, что сведется к следующему:
SELECT article, dealer, price
FROM shop s1
WHERE 3.99 =3.99;
(3.99 === 3.99) ? добавляем в результирующий набор : пропускаем
5. Повторить пункт 3 для каждой записи таблицы shop.
Итого:
1) Подзапросы действительно не очень удобны для чтения и понимания.
Мне вот пришлось написать такое полотно с анализом, чтобы понять что собственно тут происходит.
Возможно, опытные программисты уже на автомате пишут такие запросы, не задумываясь.
А пока приходится напрягаться, чтобы понять логику кода.
2) Неоптимизированный код. Для каждой записи выполняется подзапрос, насколько я понял.
Это все равно что написать в php
for ($i = 0; $i < count($array); $i ++){}
ОК, вечером попробую написать этот же запрос джойнами, попытаюсь наконец решить задачу про лайки. >>477628
В связи с вышесказанным, возникает подозрение, что у меня и большинства людей, испытывающих сложности с программированием, проблема заключается именно в слабом понимании алгоритмов.
Иногда может мешать еще слабо развитая фантазия и память (сложно представить себе последовательность действий, плюс удержать это все в оперативной памяти головы). Но это решается при помощи ручки и листа бумаги, куда можно записывать промежуточные вычисления.
Не подкинете ли хороших материалов по составлению алгоритмов, анализу и решению задач?
Да, я слышал про гугл, воспользуюсь. Но форум как бы и предназначен для экономии времени, не каждый результат поиска может быть полезным.
Общий порядок выполнения запроса такой:
— FROM/JOIN (MySQL присматривается из какой таблички мы выбираем строчки, если есть JOIN то выбираем из нескольких сразу)
— отсеивание всех строк через WHERE в цикле (условие WHERE применяется по очереди к каждой строке и соответственно на каждом шаге цикла выполняется подзапрос)
— группировка строк через GROUP BY если он есть
— отсеивание строк через HAVING если он есть
— сортировка через ORDER BY если он есть
— отсечение через LIMIT если он есть
— выборка полей указанных после SELECT и возврат результата пользователю
Разумеется это общий план запроса. MySQL старается применять оптимизации, но они применяются только так, чтобы не влиять на результат.
> кстати, в чем конкретно разница между п. и к
Предикат это условие, CLAUSE это часть запроса, например FROM, WHERE, ORDER
> Сначала интерпретатор отмечает себе только что выбрать и откуда:
Ты ничего не путаешь? Интерпретатор PHP не выполняет SQL запросы, он просто передает запрос базе, а она возвращает результат (в своем особом формате который PHP превращает в массив или что ты его попросишь).
> интерпретатор заставит сервер пройтись в цикле по всем записям, попавшим в массив/объект
запрос целиком выполняется на стороне MySQL. PHP не делает никаких циклов, он просто передает текст запроса никак не интерпретируя его. Иначе был бы бардак, каждый должен заниматься своим делом.
> Неоптимизированный код. Для каждой записи выполняется подзапрос, насколько я понял.
База данных может делать оптимизации если она достаточно умная, а запрос дает такую возможность. Причем разные базы данных (MSSQl, Oracle, MySQL, Postgresql) обладают разным уровнем оптимизаций. Проверить это можно командой EXPLAIN которая покажет план и порядок выполнения запроса. Так наугад сказать нельзя, оптимизируется это или нет.
Общий порядок выполнения запроса такой:
— FROM/JOIN (MySQL присматривается из какой таблички мы выбираем строчки, если есть JOIN то выбираем из нескольких сразу)
— отсеивание всех строк через WHERE в цикле (условие WHERE применяется по очереди к каждой строке и соответственно на каждом шаге цикла выполняется подзапрос)
— группировка строк через GROUP BY если он есть
— отсеивание строк через HAVING если он есть
— сортировка через ORDER BY если он есть
— отсечение через LIMIT если он есть
— выборка полей указанных после SELECT и возврат результата пользователю
Разумеется это общий план запроса. MySQL старается применять оптимизации, но они применяются только так, чтобы не влиять на результат.
> кстати, в чем конкретно разница между п. и к
Предикат это условие, CLAUSE это часть запроса, например FROM, WHERE, ORDER
> Сначала интерпретатор отмечает себе только что выбрать и откуда:
Ты ничего не путаешь? Интерпретатор PHP не выполняет SQL запросы, он просто передает запрос базе, а она возвращает результат (в своем особом формате который PHP превращает в массив или что ты его попросишь).
> интерпретатор заставит сервер пройтись в цикле по всем записям, попавшим в массив/объект
запрос целиком выполняется на стороне MySQL. PHP не делает никаких циклов, он просто передает текст запроса никак не интерпретируя его. Иначе был бы бардак, каждый должен заниматься своим делом.
> Неоптимизированный код. Для каждой записи выполняется подзапрос, насколько я понял.
База данных может делать оптимизации если она достаточно умная, а запрос дает такую возможность. Причем разные базы данных (MSSQl, Oracle, MySQL, Postgresql) обладают разным уровнем оптимизаций. Проверить это можно командой EXPLAIN которая покажет план и порядок выполнения запроса. Так наугад сказать нельзя, оптимизируется это или нет.
По моему опыту проблема у людей в том, что они не изучают материал последовательно, а пропускают темы или вовсе не изучают основы. Это бывает, когда люди учатся по плохим учебникам, или например поиском находят статьи которые им пока рановато читать. Если учить человека последовательно с основ (и у него есть желание учиться), обычно результат получается хороший.
Не всегда конечно есть что посоветовать для последовательного изучения той или иной темы. По PHP или яваскрипту есть учебники, а вот с SQL чуть сложнее, приходится просто разные статьи с интернета собирать.
В твоем случае, могу предложить тебе переписать запрос джойнами, а также решить задачку про лайки (из задач на MySQL, ссылка в первом посте, там же кроме задач есть пара ссылок на теорию).
Ну и чем больше задач на SQL ты решаешь, тем лучше его знаешь. Первый раз сложно, а потом привыкнешь и будешь на автомате джойны строить. Если ты представляешь как таблицы связаны, то это не так и сложно.
> Не подкинете ли хороших материалов по составлению алгоритмов, анализу и решению задач?
Это обширная тема. Гуглить надо по словам «алгоритмы и структуры данных».
Вот я нашел какие-то видеолекции (хотя я сам не очень люблю такой формат да и лекторы там не идеальные):
https://www.lektorium.tv/course/22823
http://habrahabr.ru/company/abbyy/blog/251561/
Есть такая книга но она очень старая, не уверен, стоит ли на нее время тратить: http://snilit.tspu.ru/uploads/files/default/virt.pdf
Есть такая интересная книга, может быть ее стоит почитать: http://aliev.me/runestone/ — она использует Питон для примеров, но это не принципиально, ты можешь те же алгоритмы на любом языке реализовать.
Также, ты можешь поискать книги про решение олимпиадных задач. Там тоже разные алгоритмы описываются.
Но вообще я не думаю, что тебе это так уж нужно.
Говорит
"Oops! Something went wrong.
Try it again..."
Хром версия 41.0.2272.76 Ubuntu 14.04
update
под виндовс то же самое
Ну и ладно.
Бывает, попробуй чуть попозже зайти. У меня сейчас работает: http://sqlfiddle.com/#!9/348ce/1
Или ты что-то не то ввел туда, может какой-нибудь вечный запрос?
Да, уже работает, иногда тормозит почему-то.
>>477628
Там был подвох в дистинкте.
Плюс я плохо представлял, как происходит джойн и группировка.
Сегодня просидел полдня, почитал мануалы. Потом разобрал этот запрос поэтапно, то есть сначала с одним джойном и без группировки.
Затем добавил второй джойн, и убрав группировку стало ясно, что проблему вызывают повторяющиеся строки.
Их убрал дистинктом.
http://sqlfiddle.com/#!9/c6cea/32
ОК, этот пример помог лучше понять принцип объединения таблиц, а также поведал о тонкостях агрегационных функций.
Если бы все это было менее нервозатратно, было бы вообще зашибись.
А то я за эти полтора дня выработал такой рейдж, что хватило бы на маленькую атомную станцию.
А, черт, там же еще нужно вывести взаимные лайки.
Ладно, мне же больше нечем этой ночью заняться.
>Чтобы убедиться, что ты все понял, давай переделаем array_walk на array_map (она к тому же не требует использования ссылок).
Я понял, спасибо тебе за хорошее объяснение.
1-я задача, исправленная:
http://ideone.com/UwUKmc
>>477620
2-я задача, исправленная (убрал лишние комментарии и все что ты написал сделал):
http://ideone.com/V9kcYW
>>477656
>>477657
>Ну и сложная вещь тут не обновления треда. Гораздо сложнее обновлять страницы со списком тредов, так как если ты хочешь сделать их на файлах, то тебе надо при каждом постинге обновлять все HTML-файлы и это уже гораздо более тяжелая операция.
Да, ты прав. Тогда приходит на ум следующее.
Используем что-то типа memcached. И при постинге не ребилдим кэш треда, а только изменяем его иннер стейт. И страницу генерируем только при непосредственном запросе.
Получается система:
1) Юзер запрашивает полную страницу треда.
2) Смотрим, изменен ли он с последнего кэширования.
3) Если да, ребилдим и отдаем юзеру. Если нет, отдаем прямо из памяти.
Похоже, идеальный вариант.
Со списком тредов нужно работать отдельно. Чтобы не было повторений на следующих страницах, придется хранить порядок тредов на момент запроса страницы для каждого юзера.
А вообще, что-то мне подсказывает, здесь без хардкорного JS не обойтись. Как вконтакте, только сложнее, т.к. при нажатии "Обновить треды" придется их мозайкой перекладывать и добавлять новые, а не просто подгружать посты в текущей структуре.
> Используем что-то типа memcached
Я не понимаю, при чем тут мемкеш. В memcache не надо будет перестраивать данные? проблема была в том, что в архитектуре вакабы надо обновлять большое число файлов при каждом постинге, и от того что ты их перенесешь в кеш, ситуация не поменяется. Ты просто предлагаешь наугад всякие бессмысленные идеи никак их не обосновывая.
Ты даже не посчитал например какой объем памяти потребуется.
И кстати почему ты думаешь что отдача полмегабайтного треда из мемкеша будет быстрее отдачи полмегабайта постов из базы? Я не сравнивал, но на первый взгляд это одинаково неэффективные действия.
Кстати нгинкс умеет отдавать страницы напрямую из мемкеша.
> Со списком тредов нужно работать отдельно. Чтобы не было повторений на следующих страницах, придется хранить порядок тредов на момент запроса страницы для каждого юзера.
Что значит на момент запроса? Каждый переход на новую страницу это запрос.
Вообще, делай как хочешь. Мне кажется, мы сейчас делаем другую ошибку — слишком много обсуждаем и слишком мало делаем.
это тред какого-то левого человека, заселенный троллями чуть менее чем полностью. Я не думаю, что стоит туда переходить, лучше наверно создать нормальный отдельный тред и дать тому утонуть самому по себе.
Надо сначала только тут все непроверенный задачки проверить.
Начну с простого http://pastebin.com/2mDjEcgP
> что я делаю не так
Смешиваешь в одном файле HTML-код и работу с базой. Получается нечитаемая каша. Надо разносить это в разные файлы как описано тут http://www.phpinfo.su/articles/practice/shablony_v_php.html
Используешь <?. Он может быть отключен где-то и потому надо исплоьзовать толкьо <?php и <?=
Вместо <? echo надо писать <?=
Функции mysql_ давно устарели. Ты мануал открывал? Там большими буквами это написано. Переходи на PDO.
А что именно не так? Код выдает ошибку какую-то? Алсо
>mysql_query
Этим давно уже не пользуются, я хоть и не ОП, но он всегда советует изучать PDO, вот http://habrahabr.ru/post/137664/
Алсо если в каком-то учебнике описывают функции mysql_ то это значит что он тоже устарел и лучше всего взять что-то поновее. Ничему хорошему там тебя не научат.
Задачу про лайки таки осилил.
Выкладываю пока сырье гистом.
https://gist.github.com/anonymous/02c00de98f837d07c34c
Вместо COUNT(IF) который полагается на обработку NULL лучше использовать SUM который складывает нолики и единицы.
Более так как операция сравнения = возвращает 0 или 1, мы можем даже обойтись без IF (но лучше не надо, так как с IF код гораздо понятнее и читабельнее).
А так, все верно решено. Не хочешь еще задачку про календарь или расписание решить попробовать?
В html поставил meta charset="utf8"?
После того как установишь соединение с базой, установи кодировку соединения:
mysqli_query("SET NAMES UTF8");
В PDO это делается при создании объекта:
$link = new PDO("mysql:host=localhost;dbname=DB;charset=UTF8");
Правильно настрой сервер mysql:
в my.ini в секции [mysqld] прописать
skip-character-set-client-handshake
character-set-server = utf8
init-connect='SET NAMES utf8'
collation-server=utf8_general_ci
Также желательно установить кодировку для клиента и mysqldump. Для этого в секциях [client] и [mysqldump] необходимо добавить строчку:
default-character-set=utf8
И не используй ублюдочный phpmyadmin, работай через командную строку или workbench на худой конец.
Я хер знает если честно, где тут my.ini, я ведь не на локалхосте работаю, а на говнохостинге. Тут с мускулем можно только через myAdmin работать, я бы и рад по другому.
>Между таблицами надо проставить внешние ключи
А это разве не ключ:
from_user INT UNSIGNED NOT NULL REFERENCES user (id)
Или там есть нюансы типа индексации или ограничений (constraints)?
Правильнее ли будет писать так:
FOREIGN KEY(from_user) REFERENCES user (id) ?
Индекс и ограничитель на внешний ключ вешается автоматически (в первом и втором варианте)? Или надо дописывать руками?
>>478191
Беспредельно желаю.
Уже смотрел на задание про базу кинотеатра. Пока мысленно набросал структуру, проверь насколько правильно продумано.
Из сущностей (таблиц, объектов):
1) Фильм Film. Имеет свойства/поля:
- id
- name
- duration enum(60, 90, 120, 150, 180)
Возможно, стоит добавить дополнительную колонку для коэффициента ценовой категории. Но для упрощения будем считать, что цена рассчитывается только в зависимости от времени начала сеанса.
2) Билет ticket:
- id
- seance_id references seance(id)
3) Seance:
- id
- datetime
- film_id references film(id)
4) Самая интересная таблица, в которой указывается цена на сеанс в зависимости от типа временного отрезка (комбинация дня и времени H:i:s). Не знаю даже, как назвать эту таблицу.
- DayOfWeek enum(1,2,3,4,5,6,7)
- TimeOfDay tinyint references timeOfDay(id)
- price float
Первичный ключ - комбинация всех трех полей.
5) Приходится вынести таблицу времен суток в отдельную сущность:
- id tinyint
- period varchar(100) -- например "утренний (9:00-11:00)"
>Между таблицами надо проставить внешние ключи
А это разве не ключ:
from_user INT UNSIGNED NOT NULL REFERENCES user (id)
Или там есть нюансы типа индексации или ограничений (constraints)?
Правильнее ли будет писать так:
FOREIGN KEY(from_user) REFERENCES user (id) ?
Индекс и ограничитель на внешний ключ вешается автоматически (в первом и втором варианте)? Или надо дописывать руками?
>>478191
Беспредельно желаю.
Уже смотрел на задание про базу кинотеатра. Пока мысленно набросал структуру, проверь насколько правильно продумано.
Из сущностей (таблиц, объектов):
1) Фильм Film. Имеет свойства/поля:
- id
- name
- duration enum(60, 90, 120, 150, 180)
Возможно, стоит добавить дополнительную колонку для коэффициента ценовой категории. Но для упрощения будем считать, что цена рассчитывается только в зависимости от времени начала сеанса.
2) Билет ticket:
- id
- seance_id references seance(id)
3) Seance:
- id
- datetime
- film_id references film(id)
4) Самая интересная таблица, в которой указывается цена на сеанс в зависимости от типа временного отрезка (комбинация дня и времени H:i:s). Не знаю даже, как назвать эту таблицу.
- DayOfWeek enum(1,2,3,4,5,6,7)
- TimeOfDay tinyint references timeOfDay(id)
- price float
Первичный ключ - комбинация всех трех полей.
5) Приходится вынести таблицу времен суток в отдельную сущность:
- id tinyint
- period varchar(100) -- например "утренний (9:00-11:00)"
>на хостинге можно только через phpmyadmin
Не только. Спокойно можно подключаться через консоль, например:
mysql -u username -p mypasswordis1234qwerty -h mysite.ru
Ну неважно. Просто в phpmyadmin ты не понимаешь, что происходит, тыкаешь на кнопки, и "оно само" работает. Только вот не всегда так как ты хочешь.
Кодировка для баз на бесплатном хостинге скорее всего latin1 (шведская, устанавливается по умолчанию).
Соответственно и таблицы, которые ты создаешь, тыкая на кнопки, получаются в latin1.
Можно выбрать таблицу слева в дереве базы, потом кликнуть на панель sql (третья сверху, после "обзор" и "структура").
В появившемся поле вбить
alter table mytable character set utf8 collate utf8_general_ci;
и нажать ОК.
И так для каждой таблицы.
Но лучше было бы создать нормальную базу на локальном сервере, потом сделать дамп (можно через тот же phpmyadmin: панель "экспорт" вверху), и залить ее на хостинг через панель импорт.
Ну или каждый раз при соединении писать set names utf8. Кому как больше нравится.
> Но для упрощения будем считать, что цена рассчитывается только в зависимости от времени начала сеанса.
Нет, цена задается произвольно менеджерами для каждого сеанса. Никакой формулы нет. Надо просто добавить колонку в таблицу.
> duration enum(60, 90, 120, 150, 180)
Не уверен что тут надо делать ENUM. ENUM должен быть строкой, а с числами там есть баги:
http://www.mysql.ru/docs/man/ENUM.html
> Если вы вставляете число в столбец ENUM, это число воспринимается как индекс, и в таблицу записывается соответствующее этому индексу значение перечисления. (Однако, это не будет работать с LOAD DATA, который воспринимает все входящие данные как строки.) Не рекомендуется сохранять числа в перечислении, т.к. это может привести к излишней путаннице.
Более того так как ENUM это строка, ты не можешь использовать ее в математических вычислениях.
> Самая интересная таблица, в которой указывается цена на сеанс в зависимости от типа временного отрезка
Нет, надо просто указывать цену для сеанса так как ее назначает менеджмент и она зависит не только от времени, но и от фильма, от предыдущих показов, фильмов у конкурентов и даже от погоды. Нет формулы.
Сразу предупрежу что не хочу разводить холивар, но все же какую IDE ты используешь, анон? Есть дикое желание перекатиться из Sublime Text, ну или не совсем перекатиться, в сторону чего-то более мощного, функционального. Что посоветуешь? Интересуют кроссплатформенные решения, но главное под Windows и Linux, хотя если что-то действительно годное, хватит и под Linux.
Спасибо, пожалуйста.
— PhpStorm (вроде есть возможность официально использовать бесплатно какую-то тестовую версию)
— Вроде еще Netbeans PHP и Zend Studio были, но я ими не пользовался
— Алсо есть еще Eclipse PDT, она совсем бесплатная но не очень навороченная
> PhpStorm
Вроде как 30 дней триалка, лицензия стоит $199
> Netbeans PHP и Zend Studio
Попробую посмотреть, но что касается Zend, думаю он тоже стоит как самолет.
> Eclipse PDT
Сегодня установил, пикрелэйтэд. Есть баги, тормоза, не очень радует.
> Есть баги,
какие интересно? Уверен что это баг а не ошибка в настройке или непонимание, как оно работает? Тогда наверно стоит о нем в багтрекер написать чтобы его исправили.
> тормоза
Ты не в виртуалке запускаешь? Памяти достаточно?
Тормоза там бывают если куча плагинов включена. Их можно по моему отключить. Также, меня там больше раздражали не тормоза, а избыточное число всплывающих подсказок которые мешают работать и которые в большинстве случаев не нужны. Ну и интерфейс перегруженный в сравнении в саблаймом.
> Уверен что это баг а не ошибка в настройке
Может и не баги, может с иксами проблема, но ui иногда распидорашивает.
> Тормоза там бывают если куча плагинов включена
Я имею ввиду некоторые узкие места, например фильтры, файловый менеджер.
Файловый менеджер это панель с файлами слева? По моему от нее пользы нет, если у тебя в проекте больше 10 файлов то удобнее открывать их через быстрое открытие по имени, а не тыкаться в эту крошечную область экрана и искать там файлы вложенный в 5 папок.
Все сделал вроде, все равно не работает. Теперь вообще любой текст содержащий русские символы не видно в таблице, просто пустые ячейки. Хотя вручную нормально вставляется а пхп скриптом нихуя. Не пойму почему.
> Спокойно можно подключаться через консоль, например: mysql -u username -p mypasswordis1234qwerty -h mysite.ru
Большинство хостингов запрещают подключаться снаружи. Только если у тебя есть ssh-доступ и ты можешь подсоединяться через него (или сделав туннеь тем же ssh).
> в секции [mysqld] прописать
> init-connect='SET NAMES utf8'
Ты уверен что это правильно? Почему ты запрос пишешь в секции для сервера, а не для клиента? И зачем делать SET NAMES если там есть какая-то опция вроде character_set_client или как-то так.
>>478392
1) PHP-скрипт сохранен в кодировке utf-8?
2) чем работаешь с базой в скрипте? PDO? mysqli? Ты так и не написал
3) при соединении с базой через PDO ты указал charset=utf8 (без минуса)?
4) в HTML-коде c формой (если открыть в браузере через Ctrl + U) стоит meta charset utf-8 или отдается кодировка в заголовке Content-Type?
Также, есть сообщения об ошибках? Данные не могут просто так исчезнуть, наверняка он пишет в лог какие-то ошибки, а ты их не читаешь. Найди логи на хостинге, в файлах или панели управления и почитай.
Вот ссылки на тему кодировок MySQL, можешь пока почитать (плохо что предыдущий анон не дал тебе информации, а просто дал магические заклиняния которые начинающему малопонятны):
http://fstrange.ru/coder/mysql/kodirovka-krakozyably.html
http://gahcep.github.io/blog/2013/01/05/mysql-utf8/
http://phpfaq.ru/charset
Все, я понял в чем была проблема. Вроде бы менеджер файлов, через который я загружал файл на фтп сохранял его не в той кодировке. Сейчас залил через Filezilla и все работает.
А хотя нет, все равно баг остался. Если я в bindValue буду передавать просто строку на русском языке, то она в таблице отображается. А вот если туда передавать POST переменные со строкой на русском, то ничего не вставиться.
Ты не ответил на этот вопрос:
> 4) в HTML-коде c формой (если открыть в браузере через Ctrl + U) стоит meta charset utf-8 или отдается кодировка в заголовке Content-Type?
У меня POST данные от стороннего сайта принимаются, я могу их посмотреть только уже в базе данных, сам скрипт ничего не выводит. Я для этого и запилил хостинг, потому-что на локалку не получится принять данные.
>>478465
Да, как раз собирался об этом написать. Форма передает данные на сайт вебмерчант, а тот в свою очередь присылает их обратно мне на пхп скрипт. Пикрелейтед. Я так понял все дело в accept-charset=windows-1251? То есть он принимает их в такой кодировке и в такой-же передает обратно, а они должны приходить в utf-8?
https://gist.github.com/anonymous/8d25f5ed7fa65a77a853
В рот ебал sqlfiddle. Теперь 5.6 возвращает empty set, а 5.5 говорит Error writing file './db_2_e59db/film.frm' (Errcode: 28)
Ничего не знаю, у меня на локалке 5.5 и все работает.
Пруфы пикрелейтед.
А, я забыл скопипастить инсерт второй таблицы...
С 11 утра сижу, щас стены кодить начну.
http://sqlfiddle.com/#!9/7107f3/1
И кто его просил менять формт даты с 2015-05-15 на May 15, 2015?
Мы здесь таких не любим.
Если sqlfiddle не работает, то ладно, не надо с ним мучаться тогда. Но я не думаю, что с ним какие-то проблемы. Проблемы в твоем коде.
> Теперь 5.6 возвращает empty set,
Ну так запрос может неверный?
Вот я вставил твои команды и все заработало. Уже второй раз у меня все работает, а у тебя нет. Может тебя какой-то плохой сервер обслуживает?
Таблицы обычно называют во множественном числе, tickets, films.
> duration TINYINT UNSIGNED
Не экономь байты и ставь минимум SMALLINT, а то вдруг найдется фильм длиннее 4 часов (у TINYINT максимум 255)
Алсо, для таких не очень очевидных колонок стоит добавлять комментарий про то, что в них хранится, со словом COMMENT, который сохранится в базе и поможет другим разработчикам разобраться в твоей таблице: http://stackoverflow.com/a/22444161
Для длительности, думаю, не стоит задавать значение по умолчанию так как не очень понятно почему там именно 120.
> price FLOAT NOT NULL,
Для денег есть специальный тип DECIMAL который в отличие от флоат гарантирует сохранность всех знаков после запятой. Тут конечно можно найти обсуждение где большинство высказывется за INT: https://toster.ru/q/23947 — по моему там автор вопроса не очень адекватен. Если есть специальный тип для таких целей, глупо городить костыли с интами и домножением.
На SO высказываются за DECIMAL:
http://stackoverflow.com/questions/628637/best-data-type-for-currency-values
http://stackoverflow.com/a/22444161
Более того упоминается какие-то Generally Accepted Accounting Principles которые это рекомендуют.
> FOREIGN KEY (film_id) REFERENCES film(id),
тут стоило бы дописывать ON DELETE/ON UPDATE хотя бы чтобы попрактиковаться. ты должен подумать и выбрать подходящее действие для этих случаев.
> WHERE s2.date BETWEEN s1.date + 1 AND s1.date + INTERVAL (f1.duration) MINUTE;
Это ведь не обнаружит ситуацию когда 2 фильма начинаются в одно и то же время? А надо обнаруживать.
Вот пример: http://sqlfiddle.com/#!9/c8606/1 — не обнаруживает пересечение
Если мы сдвинем время хотя бы на 1 секунду, то обнаруживает: http://sqlfiddle.com/#!9/0db2e/1
Если ты хочешь защититься от джойна фильма самого на себя или от вывода пары по 2 раза то можно поставить условие что первый id должен быть больше второго.
Если sqlfiddle не работает, то ладно, не надо с ним мучаться тогда. Но я не думаю, что с ним какие-то проблемы. Проблемы в твоем коде.
> Теперь 5.6 возвращает empty set,
Ну так запрос может неверный?
Вот я вставил твои команды и все заработало. Уже второй раз у меня все работает, а у тебя нет. Может тебя какой-то плохой сервер обслуживает?
Таблицы обычно называют во множественном числе, tickets, films.
> duration TINYINT UNSIGNED
Не экономь байты и ставь минимум SMALLINT, а то вдруг найдется фильм длиннее 4 часов (у TINYINT максимум 255)
Алсо, для таких не очень очевидных колонок стоит добавлять комментарий про то, что в них хранится, со словом COMMENT, который сохранится в базе и поможет другим разработчикам разобраться в твоей таблице: http://stackoverflow.com/a/22444161
Для длительности, думаю, не стоит задавать значение по умолчанию так как не очень понятно почему там именно 120.
> price FLOAT NOT NULL,
Для денег есть специальный тип DECIMAL который в отличие от флоат гарантирует сохранность всех знаков после запятой. Тут конечно можно найти обсуждение где большинство высказывется за INT: https://toster.ru/q/23947 — по моему там автор вопроса не очень адекватен. Если есть специальный тип для таких целей, глупо городить костыли с интами и домножением.
На SO высказываются за DECIMAL:
http://stackoverflow.com/questions/628637/best-data-type-for-currency-values
http://stackoverflow.com/a/22444161
Более того упоминается какие-то Generally Accepted Accounting Principles которые это рекомендуют.
> FOREIGN KEY (film_id) REFERENCES film(id),
тут стоило бы дописывать ON DELETE/ON UPDATE хотя бы чтобы попрактиковаться. ты должен подумать и выбрать подходящее действие для этих случаев.
> WHERE s2.date BETWEEN s1.date + 1 AND s1.date + INTERVAL (f1.duration) MINUTE;
Это ведь не обнаружит ситуацию когда 2 фильма начинаются в одно и то же время? А надо обнаруживать.
Вот пример: http://sqlfiddle.com/#!9/c8606/1 — не обнаруживает пересечение
Если мы сдвинем время хотя бы на 1 секунду, то обнаруживает: http://sqlfiddle.com/#!9/0db2e/1
Если ты хочешь защититься от джойна фильма самого на себя или от вывода пары по 2 раза то можно поставить условие что первый id должен быть больше второго.
Я подозреваю c 5.5 там какой-то баг. 5.6 все работает — я проверил только что.
> И кто его просил менять формт даты с 2015-05-15 на May 15, 2015?
Дата это дата, а не строка.
Я думал, что ее можно временно поменять сделав SET something = 'something' но я не нашел, что надо туда вписать, можешь сам посмотреть настройки в этом списке:
https://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html
Видимо преобразование даты делается не в MySQL, а в коде который получает данные из базы и выводит их на сайте. Тогда ты повлиять не можешь на формат.
Также ты можешь использовать в запросе функции превращающие дату в строку как тебе удобно (но лучше не делать, мне кажется, дата и так вполне читабельна): http://stackoverflow.com/a/4596536
И анон который сделал https://github.com/Si0n/register3 я тоже помню.
И еще, аноны, что будем делать с новым тредом? Сделаем новый или перейдем в >>475689 ? С одной стороны там неправильная шапка, много неприятных невоспитанных недружелюбных троллей и нет напоминания про оформление кода (самый важный пост в треде!), с другой стороны готовый тред уже есть и картинок много.
>Таблицы называют во множ.числе
Я привык, что потом это название переносится на класс объекта в php. В yii так принято, например.
>DECIMAL вместо FLOAT
Я где-то читал, что это абслоютно одно и то же, все эти типы вместе с даблом какбы оставили только для совместимости, мол раньше между ними была разница в спообе хранения данных, сейчас нет.
Но может быть я неправильно понял, или автор был не совсем компетентен.
Ага, вспомнил. Никитин из "Специалиста" это сказал. Курсы правда 2011 года, может что-то и поменялось.
27:42 https://www.youtube.com/watch?v=mENlTJ4UoD4
Ладно, я слегка удручен, пойду посплю.
Разница есть.
DOUBLE это числа с плавающей точкой, по моему там 15 значащих цифр из числа сохраняется и вычисления приближенные.
NUMERIC это точный формат, хранит и вычиcляет числа с указанной точностью.
Вот мануал на англ:
https://dev.mysql.com/doc/refman/5.6/en/precision-math.html
> Precise calculations: For exact-value numbers, calculations do not introduce floating-point errors. Instead, exact precision is used. For example, MySQL treats a number such as .0001 as an exact value rather than as an approximation, and summing it 10,000 times produces a result of exactly 1, not a value that is merely “close” to 1
Для сравнения DOUBLE: https://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html
> Because floating-point values are approximate and not stored as exact values, attempts to treat them as exact in comparisons may lead to problems. They are also subject to platform or implementation dependencies. For more information see Section B.5.5.8, “Problems with Floating-Point Values”
Вот по русски пост на тему: http://tarlyun.com/blog/2011/03/22/xranenie-ne-celyx-chisel-v-mysql/
Ты либо не так понял, либо знания лектора из Специалиста устарели.
И даже если бы в MySQL не было разницы, эта разница есть в других движках БД и потому надо знать смысл и назначение этого типа (а не только особенности реализации).
Лучше создать новый, а мочератора попросить удалить фейковый.
Доступ к строке гет-запроса (query string) можно получить через массив SERVER, например
action="<?php echo $_SERVER['REQUEST_URI'].'&newquery=123';?>"
https://php.net/manual/ru/reserved.variables.server.php
Но ты уверен, что такое "присобачивание" параметров это правильное решение?
Скажи, что именно хочешь сделать. А лучше выложи код на гитхаб или пастбин.
Наверняка можно придумать более грамотный подход к формированию строки запроса.
а значение 123 как передать в action?
я делаю фильтр, присобачивание параметров определенно то, что нужно. я написал свой парсер url который отлично работает. И функцию, которая из get переменных формирует запрос к бд https://ideone.com/tw5KHg
Все работало как следует, пока мне не пришлось использовать форму, которая нагло удаляет предыдущие get переменные.
Надо дописать эти параметры как скрытые input в форму, чтобы они передавались вместе с ней.
По твоему коду, в нем куча ошибок и такая уязвимсоть как SQL инъекция.
> foreach($url as $key => $val){
Непонятно зачем ты руками разбираешь URL когда PHP сделает это сам и складывает результаты в $_GET
Также, у тебя SQL инъекция. Злоумышленник может видоизменить SQL запрос как угодно и как минимум получить все данные из базы, как максимум получить полный контроль над сайтом.
https://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_SQL-%D0%BA%D0%BE%D0%B4%D0%B0
http://forum.antichat.ru/threads/43966/
Для борьбы с ней надо экранировать по правилам SQL все данные, а также сделать белый список разрешенных полей для фильтрации и не принимать другие значения.
> if(is_string($val)){
Эта проверка бессмысленна так как parse_str возвращает только строки. Даже если это строка вида '123', она все равно считается строкой. Обрати внимание:
$x = '123'; // строка из 3 символов
$y = 123; // число 123
Если у тебя есть возможность, я бы тебе советовал порешать задачи из первого поста, про студентов и файлообменник например, так как уровень твоего кода пока очень низкий. Ты можешь запостить потом написанный код и я дам замечания и советы по улучшению. По моему, сейчас ты пишешь код, который содержит ошибки и уязвимости и который невозможно читать и поддерживать. Ты зачем-то пишешь свои аналоги того, что у же есть в PHP. Если кому-то потом придется поддерживать такой код, он вряд ли будет рад.
(если ты новичок в нашем треде, то знай что я обычно комментирую весь код который сюда постят, и по каждому примеру у меня обычно есть замечания)
Надо дописать эти параметры как скрытые input в форму, чтобы они передавались вместе с ней.
По твоему коду, в нем куча ошибок и такая уязвимсоть как SQL инъекция.
> foreach($url as $key => $val){
Непонятно зачем ты руками разбираешь URL когда PHP сделает это сам и складывает результаты в $_GET
Также, у тебя SQL инъекция. Злоумышленник может видоизменить SQL запрос как угодно и как минимум получить все данные из базы, как максимум получить полный контроль над сайтом.
https://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_SQL-%D0%BA%D0%BE%D0%B4%D0%B0
http://forum.antichat.ru/threads/43966/
Для борьбы с ней надо экранировать по правилам SQL все данные, а также сделать белый список разрешенных полей для фильтрации и не принимать другие значения.
> if(is_string($val)){
Эта проверка бессмысленна так как parse_str возвращает только строки. Даже если это строка вида '123', она все равно считается строкой. Обрати внимание:
$x = '123'; // строка из 3 символов
$y = 123; // число 123
Если у тебя есть возможность, я бы тебе советовал порешать задачи из первого поста, про студентов и файлообменник например, так как уровень твоего кода пока очень низкий. Ты можешь запостить потом написанный код и я дам замечания и советы по улучшению. По моему, сейчас ты пишешь код, который содержит ошибки и уязвимости и который невозможно читать и поддерживать. Ты зачем-то пишешь свои аналоги того, что у же есть в PHP. Если кому-то потом придется поддерживать такой код, он вряд ли будет рад.
(если ты новичок в нашем треде, то знай что я обычно комментирую весь код который сюда постят, и по каждому примеру у меня обычно есть замечания)
да, над безопасностью надое еще поработать, благо то, что я пишу будет доступно только кругу лиц незаинтересованных в том, чтобы навредить(там провекрка есть).
Руками я разбираю затем, чтобы если в юрл уже есть значение переменной, к примеру
?user=4638, а я хочу изменить фильтр еще раз и тогда к запросу просто еще раз дописалось бы &user=4639, к примеру url станет вида ?user=4638&user=4639.
Поэтому в функции я пишу все переменные в массив и если у get переменной уже есть значение то оно перезаписывается( $url["$var"] = $val;).
По поводу is_string да, сам недавно исправил на is_numeric
Спасибо!
> Руками я разбираю затем, чтобы если в юрл уже есть значение переменной, к примеру
?user=4638, а я хочу изменить фильтр еще раз и тогда к запросу просто еще раз дописалось бы &user=4639, к примеру url станет вида ?user=4638&user=4639.
Незачем разбирать руками, все это уже разобранное лежит в GET и можно писать foreach ($_GET as $key => $value)
parse_str (если я не путаю) работает точно так же и в твоем случае второй user затрет первый. В массиве не может быть 2 элемента с одинаковым ключом. Ты пробовал сделать такой URL?
И если ты в SQL запросе напишешь WHERE user =1 AND user = 2 он ничего не найдет так как оба условия никогда одновременно не выполняются. Надо писать WHERE user IN (...)
Чтобы передать несколько id для юзера через строку запроса в URL, есть массивы: http://php.net/manual/ru/faq.html.php#faq.html.arrays
Не изобретай велосипед а пользуйся тем что уже есть.
да в том то и дело, что мне не нужно несколько id, а только один. поэтому и хочу, чтобы терло.
Ага, на счет гет понял, можно не разбивать на части, а просто пропарсить сам гет. спасибо
PHP разбирает параметры из строки запроса и складывает их в массив _GET. Ты бы мог взять их из него, но вместо этого ты пытаешься разбирать URL самостоятельно. Это и есть велосипед.
Ну и массив _GET в самом начале мануала по PHP упомянут вообще-то.
а, ну правильно. да я знаю его и пользуюсь все время, просто чето тупанул.
Для начала про считывания файлы. Я так понял, что лучше всего его считывать побайтно и представлять ввиде строки, правильно?
Архив будет собой представлять файл в котором данные записанные в формате json, а именно массив со строками, это нормально?
Объект будет выглядеть примерно, как:
объект.
и два публичных метода, создать архив и распаковать архив.
+приватный считать файл.
все ок?
Как мне считать таким образом картинку и почему мне сказали обратить внимания на чтения бинарных файлов?
Хорошо, что ты тут спрашиваешь. У тебя пока план не идеален и я попробую описать, что именно не так.
Также, ты не запостил код. Как я тебе могу сказать что именно у тебя неправильно если я не вижу кода. Ты думаешь, я телепат?
> Я так понял, что лучше всего его считывать побайтно и представлять ввиде строки, правильно?
Файл это набор байтов на диске с именем. В плане скорости выгоднее читать большими кусками, например по 100 Кб так как в этом случае получается в 100 000 раз меньше вызовов функции чем если читать по 1 байту за раз и код работает гораздо быстрее.
Строка в PHP это тоже набор байтов, только в памяти. Ты можешь узнать число байт в строке с помощью strlen (обрати внимание, что именно число байт, а не символов. 1 символ не равен 1 байту), ты можешь получить N-й байт (не символ!) начиная с нулевого, с помощью $str[100] и ты можешь получить его код в виде числа от 0 до 255 с помощью ord($str[100]). Также, ты можешь преобразовать число в строку из 1 байта с помощью chr: chr(64) вернет строку "@" состояющую из 1 байта с кодом 64 (который соответствует символу @ как ты уже догадался наверно).
Также в PHP есть еще 2 функции, pack и unpack, для упаковывания чисел в последовательность байтов и обратной распаковки. Они тебе пригодятся, так что почитай про них.
Соответственно функции fread, file_get_contents делают как раз то, что тебе нужно: перемещают байты из файла на диске в строку в памяти.
> Архив будет собой представлять файл в котором данные записанные в формате json, а именно массив со строками, это нормально?
Нет. Преимущество JSON это некоторая читаемость человеком (в этой задаче она не требуется) и возможность легкой обработки яваскриптом (тут тоже не требуется). Следовательно JSON не дает тут никаких преимуществ, а только увеличивает вес получившегося файла за счет лишних символов. JSON это формат для веба, а не архивов.
Я рекомендую для начала изучить существующие форматы сжатия. Прежде чем что-то изобретать, посмотри на существующие решения.
Заметь, что большинство форматов бинарные. Это значит, что они хранят данные не как читабельный набор символов, а просто набор байт. Это позволяет максимально компактно записывать данные. Ну например 9-разрядное число требует 9 цифр (и 9 байт) для записи в текстовом виде, а в бинарном всего 4 байта.
Потому тебе придется изучить как хранить число (например, длину файла) в виде нескольких байт. Вот кое-что на эту тему:
http://www.5byte.ru/11/0008.php
http://kuzelenkov.narod.ru/mati/book/inform/inform5.html
http://habrahabr.ru/post/233245/
(статьи не очень качественные так что задавай вопросы, если что)
Если кратко, то мы записываем число в двоичном виде, затем разбиваем на группы по 8 бит (на байты) и сохраняем эти байты. Это почти самый компактный способ. Заметь, что тебе не придется делать это руками, функции pack/unpack умеют это делать.
Для отрицательных чисел используется специальный дополнительный код, когда нули заменяются на единицы. Такой сложный подход используется исторически так как сложение/вычитание таких чисел не требовало изменений в схеме сумматора в микропроцессоре, а другие способы (например обозначать знак только первым битом не трогая остальные) усложнили бы его конструкцию.
По сжатым файлам я рекомендую почитать такие вещи:
— gzip — открытый формат сжатия. Он не поддерживает упаковку нескольких файлов, а только сжатие одного файла. В линукс, если тебе надо сжать несколько файлов, ты сначала объединяешь из в один командой tar (опять же, изучи формат tar-файла), а потом жмешь tar через gzip. Это дает более хорошее сжатие чем если сжимать по отдельности, но имеет тот недостаток что для чтения списка файлов в архиве ты должен распаковать gzip-архив целиком (прочитав целиком его с диска, что может занять время), достать из него tar-файл и прочитать список файлов в немю
Описание форматов:
https://ru.wikipedia.org/wiki/Gzip
https://tools.ietf.org/html/rfc1952 (на англ, но зато подробно)
http://www.opennet.ru/docs/RUS/tar/tar-11.html
http://cvs.savannah.gnu.org/viewvc/checkout/tar/tar/src/tar.h?content-type=text/plain
Там упоминается CRC. CRC это контрольная сумма, которая считается из байтов по определенной формуле и используется, чтобы обнаружить ошибки при передаче или хранении файла — в этом случае сумма в файле не совпадет с вычисленной.
— zip — не открытый формат сжатия. zip-файл состоит из 2 частей: оглавления, хранящего список и информацию о файлах в архиве и тела, которое хранит сами сжатые файлы. За счет того, что zip файл содержит оглавление, ты можешь просмотреть список файлов в архиве не распаковывая его целиком, только прочитав начало файла.
Описание:
http://en.wikipedia.org/wiki/Zip_(file_format)
https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html
https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
С форматами разобрались, пора поговорить про само сжатие.
Хорошо, что ты тут спрашиваешь. У тебя пока план не идеален и я попробую описать, что именно не так.
Также, ты не запостил код. Как я тебе могу сказать что именно у тебя неправильно если я не вижу кода. Ты думаешь, я телепат?
> Я так понял, что лучше всего его считывать побайтно и представлять ввиде строки, правильно?
Файл это набор байтов на диске с именем. В плане скорости выгоднее читать большими кусками, например по 100 Кб так как в этом случае получается в 100 000 раз меньше вызовов функции чем если читать по 1 байту за раз и код работает гораздо быстрее.
Строка в PHP это тоже набор байтов, только в памяти. Ты можешь узнать число байт в строке с помощью strlen (обрати внимание, что именно число байт, а не символов. 1 символ не равен 1 байту), ты можешь получить N-й байт (не символ!) начиная с нулевого, с помощью $str[100] и ты можешь получить его код в виде числа от 0 до 255 с помощью ord($str[100]). Также, ты можешь преобразовать число в строку из 1 байта с помощью chr: chr(64) вернет строку "@" состояющую из 1 байта с кодом 64 (который соответствует символу @ как ты уже догадался наверно).
Также в PHP есть еще 2 функции, pack и unpack, для упаковывания чисел в последовательность байтов и обратной распаковки. Они тебе пригодятся, так что почитай про них.
Соответственно функции fread, file_get_contents делают как раз то, что тебе нужно: перемещают байты из файла на диске в строку в памяти.
> Архив будет собой представлять файл в котором данные записанные в формате json, а именно массив со строками, это нормально?
Нет. Преимущество JSON это некоторая читаемость человеком (в этой задаче она не требуется) и возможность легкой обработки яваскриптом (тут тоже не требуется). Следовательно JSON не дает тут никаких преимуществ, а только увеличивает вес получившегося файла за счет лишних символов. JSON это формат для веба, а не архивов.
Я рекомендую для начала изучить существующие форматы сжатия. Прежде чем что-то изобретать, посмотри на существующие решения.
Заметь, что большинство форматов бинарные. Это значит, что они хранят данные не как читабельный набор символов, а просто набор байт. Это позволяет максимально компактно записывать данные. Ну например 9-разрядное число требует 9 цифр (и 9 байт) для записи в текстовом виде, а в бинарном всего 4 байта.
Потому тебе придется изучить как хранить число (например, длину файла) в виде нескольких байт. Вот кое-что на эту тему:
http://www.5byte.ru/11/0008.php
http://kuzelenkov.narod.ru/mati/book/inform/inform5.html
http://habrahabr.ru/post/233245/
(статьи не очень качественные так что задавай вопросы, если что)
Если кратко, то мы записываем число в двоичном виде, затем разбиваем на группы по 8 бит (на байты) и сохраняем эти байты. Это почти самый компактный способ. Заметь, что тебе не придется делать это руками, функции pack/unpack умеют это делать.
Для отрицательных чисел используется специальный дополнительный код, когда нули заменяются на единицы. Такой сложный подход используется исторически так как сложение/вычитание таких чисел не требовало изменений в схеме сумматора в микропроцессоре, а другие способы (например обозначать знак только первым битом не трогая остальные) усложнили бы его конструкцию.
По сжатым файлам я рекомендую почитать такие вещи:
— gzip — открытый формат сжатия. Он не поддерживает упаковку нескольких файлов, а только сжатие одного файла. В линукс, если тебе надо сжать несколько файлов, ты сначала объединяешь из в один командой tar (опять же, изучи формат tar-файла), а потом жмешь tar через gzip. Это дает более хорошее сжатие чем если сжимать по отдельности, но имеет тот недостаток что для чтения списка файлов в архиве ты должен распаковать gzip-архив целиком (прочитав целиком его с диска, что может занять время), достать из него tar-файл и прочитать список файлов в немю
Описание форматов:
https://ru.wikipedia.org/wiki/Gzip
https://tools.ietf.org/html/rfc1952 (на англ, но зато подробно)
http://www.opennet.ru/docs/RUS/tar/tar-11.html
http://cvs.savannah.gnu.org/viewvc/checkout/tar/tar/src/tar.h?content-type=text/plain
Там упоминается CRC. CRC это контрольная сумма, которая считается из байтов по определенной формуле и используется, чтобы обнаружить ошибки при передаче или хранении файла — в этом случае сумма в файле не совпадет с вычисленной.
— zip — не открытый формат сжатия. zip-файл состоит из 2 частей: оглавления, хранящего список и информацию о файлах в архиве и тела, которое хранит сами сжатые файлы. За счет того, что zip файл содержит оглавление, ты можешь просмотреть список файлов в архиве не распаковывая его целиком, только прочитав начало файла.
Описание:
http://en.wikipedia.org/wiki/Zip_(file_format)
https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html
https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
С форматами разобрались, пора поговорить про само сжатие.
Мне не нужно сжимать данные, мне нужно создать из них архив, извини, что не уточнил.
Я скину код, как допишу, сегодня вечером думаю, мне до понедельника надо закончить
>>478842
Код покажи, телепатов в треде нет.
>>478832
Еще, если у тебя не учебная задача по написанию архиватора. то не пиши его, а используй готовые. PHP умеет и распаковывать и запаковывать файлы в нескольких форматах.
Поговорим про сжатие. У тебя есть на входе набор байт, надо получить набор из меньшего количества байт на выходе, как ты собираешься это делать?
Сжатие тесно связано с количеством информации (энтропией). Надо искать какие-то закономерности в информации и записывать их в более компактном виде. Ну например, если в файле идет 100 раз подряд байт с кодом 46, мы можем вместо 100 одинаковых байтов записать так:
(число повторений, например 100) (повторяющийся байт, например 46)
как видишь, мы записали 2 байтами то, что занимало 100 байт. Это называется RLE:
https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B4%D0%BB%D0%B8%D0%BD_%D1%81%D0%B5%D1%80%D0%B8%D0%B9
Чуть улучшенная версия RLE позволяет кодировать не только повторы одного символа, но и нескольких. Она была использована в древнем алгоритме LZ77:
https://ru.wikipedia.org/wiki/LZ77
http://habrahabr.ru/post/141827/
LZ77 может неплохо сжимать, например тексты программ, где много пробелов и повторяющихся слов. Также она жмет примитивные черно-белые картинки с большим числом пустых областей.
Вот LZMA, это по сути то же самое, только с более сложным (но более компактным) способом кодирования данных: https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Markov_chain_algorithm
Это разумеется самый простой случай. Другой относительно простой способ сжатия — это определение, какие байты (или последовательности байт) встречаются чаще, а какие реже, и кодирование более часто встречающихся меньшим числом бит:
https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%B4_%D0%A5%D0%B0%D1%84%D1%84%D0%BC%D0%B0%D0%BD%D0%B0
Есть и более сложные техники, но для начала освоить эти было бы неплохо. Тебе также стоит изучить простые древние форматы сжатия, использовавшие эти подходы:
http://habrahabr.ru/post/231177/
https://ru.wikipedia.org/wiki/Deflate
http://compression.ru/download/articles/lz/mihalchik_deflate_decoding.html (кстати это годный сайт про алгоритмы сжатия)
Это были общие алгоримты для сжатия произвольных файлов. Но некоторые файлы (картинки, музыка, видео) гораздо сильнее можно сжать если учитыват их структуру и применять особые алгоритмы оптимизированные под это. Например, в видео мы можем использовать тот факт, что многие кадры представляют собой чуть измененный или сдвинутый предыдущий кадр и кодировать только разницу между ними. В фотографиях мы можем учесть, что есть большие области почти однородного цвета (небо например) и тоже более оптимально их кодировать.
Вот алгоритмы оптимизированные под конкретный вид данных:
https://ru.wikipedia.org/wiki/PNG
https://www.artlebedev.ru/tools/technogrette/img/png-1/
http://netghost.narod.ru/gff2/graphics/summary/png.htm
http://compression.ru/book/part2/part2__3.htm (алгоритм JPEG использует сжатие с потерями, пытаясь отбросить детали которые глаз видит хуже всего)
http://algolist.manual.ru/compress/image/jpeg.php
https://ru.wikipedia.org/wiki/JPEG
Для кодирования последовательностей чисел (например междленно меняющихся сигналов или id документов в поисковом индексе) есть https://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BB%D1%8C%D1%82%D0%B0-%D0%BA%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5
Для аудио есть FLAC, MP3, OGG Vorbis
Для видео ты наверно и сам слышал названия разных алгоритмов начиная с древнего MPEG и заканчивая H.264, VP9 и подобными.
Ты хоть осознаешь во что ты ввязался со своим заданием?
>>478842
Код покажи, телепатов в треде нет.
>>478832
Еще, если у тебя не учебная задача по написанию архиватора. то не пиши его, а используй готовые. PHP умеет и распаковывать и запаковывать файлы в нескольких форматах.
Поговорим про сжатие. У тебя есть на входе набор байт, надо получить набор из меньшего количества байт на выходе, как ты собираешься это делать?
Сжатие тесно связано с количеством информации (энтропией). Надо искать какие-то закономерности в информации и записывать их в более компактном виде. Ну например, если в файле идет 100 раз подряд байт с кодом 46, мы можем вместо 100 одинаковых байтов записать так:
(число повторений, например 100) (повторяющийся байт, например 46)
как видишь, мы записали 2 байтами то, что занимало 100 байт. Это называется RLE:
https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B4%D0%BB%D0%B8%D0%BD_%D1%81%D0%B5%D1%80%D0%B8%D0%B9
Чуть улучшенная версия RLE позволяет кодировать не только повторы одного символа, но и нескольких. Она была использована в древнем алгоритме LZ77:
https://ru.wikipedia.org/wiki/LZ77
http://habrahabr.ru/post/141827/
LZ77 может неплохо сжимать, например тексты программ, где много пробелов и повторяющихся слов. Также она жмет примитивные черно-белые картинки с большим числом пустых областей.
Вот LZMA, это по сути то же самое, только с более сложным (но более компактным) способом кодирования данных: https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Markov_chain_algorithm
Это разумеется самый простой случай. Другой относительно простой способ сжатия — это определение, какие байты (или последовательности байт) встречаются чаще, а какие реже, и кодирование более часто встречающихся меньшим числом бит:
https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%B4_%D0%A5%D0%B0%D1%84%D1%84%D0%BC%D0%B0%D0%BD%D0%B0
Есть и более сложные техники, но для начала освоить эти было бы неплохо. Тебе также стоит изучить простые древние форматы сжатия, использовавшие эти подходы:
http://habrahabr.ru/post/231177/
https://ru.wikipedia.org/wiki/Deflate
http://compression.ru/download/articles/lz/mihalchik_deflate_decoding.html (кстати это годный сайт про алгоритмы сжатия)
Это были общие алгоримты для сжатия произвольных файлов. Но некоторые файлы (картинки, музыка, видео) гораздо сильнее можно сжать если учитыват их структуру и применять особые алгоритмы оптимизированные под это. Например, в видео мы можем использовать тот факт, что многие кадры представляют собой чуть измененный или сдвинутый предыдущий кадр и кодировать только разницу между ними. В фотографиях мы можем учесть, что есть большие области почти однородного цвета (небо например) и тоже более оптимально их кодировать.
Вот алгоритмы оптимизированные под конкретный вид данных:
https://ru.wikipedia.org/wiki/PNG
https://www.artlebedev.ru/tools/technogrette/img/png-1/
http://netghost.narod.ru/gff2/graphics/summary/png.htm
http://compression.ru/book/part2/part2__3.htm (алгоритм JPEG использует сжатие с потерями, пытаясь отбросить детали которые глаз видит хуже всего)
http://algolist.manual.ru/compress/image/jpeg.php
https://ru.wikipedia.org/wiki/JPEG
Для кодирования последовательностей чисел (например междленно меняющихся сигналов или id документов в поисковом индексе) есть https://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BB%D1%8C%D1%82%D0%B0-%D0%BA%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5
Для аудио есть FLAC, MP3, OGG Vorbis
Для видео ты наверно и сам слышал названия разных алгоритмов начиная с древнего MPEG и заканчивая H.264, VP9 и подобными.
Ты хоть осознаешь во что ты ввязался со своим заданием?
КАК СЧИТАТЬ ЛЮБОЙ ФАЙЛ в строку, а не только текстовый?!
Напишу, покажу код, скажешь ОП, что не так, хорошо?
Я потом расскажу, что и зачем надо было.
Советы и замечания по файлообменнику https://github.com/AzerusEncole/File_Sharing
Дамп SQL надо положить в репозиторий. Вот захочет кто-то скачать проект, как он его запустит без дампа? И структура БД это часть твоего проекта, какой смысл хранить её отдельно? Разумеется, в этом случае дамп должен содержать только структуру таблицы, без самих данных в ней.
Папки .idea, файлы внутри thumbs, uploads, vendor не должны загружаться в репозиторий. Содержимое папки vendor скачает композер, ну а файлы пользователь может закачать сам. Изучи как работает .gitignore и сделай примерно так:
— перенеси папки и файлы куда-нибудь в другое место
— закоммить проект (гит подумает что файлы удалены)
— внеси изменения в gitignore и закоммить
— верни файлы на место. теперь гит их не увидит.
Мануал по gitignore: https://git-scm.com/book/ru/v1/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B-Git-%D0%97%D0%B0%D0%BF%D0%B8%D1%81%D1%8C-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9-%D0%B2-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9#Игнорирование-файлов
Насчет файла composer.json, хватило бы только require, остальные поля заполняют только если ты хочешь сделать отдельную библиотеку, ну да ладно, хуже не будет.
Ручная загрузка классов через require усложнит тебе жизнь когда файлов станет больше. Советую изучить возможности композера по автозагрузке и сделать автозагрузку классов через файл composer.json. Есть такая статья http://habrahabr.ru/post/149678/ но она старая и не описывает формат PSR-4, он описан только в официальной доке:
https://getcomposer.org/doc/01-basic-usage.md#autoloading
https://getcomposer.org/doc/04-schema.md#autoload
> ini_set('display_errors', 1);
Это наверно лучше делать в php.ini на твоем компьютере, так как непраивльно показывать ошибки на боевом сервере, их незачем видеть пользователям, они должны идти только в лог.
сами команды error_reporting, ini_set надо перенести ниже чем use, так как use принято писать первыми в файле.
> https://github.com/AzerusEncole/File_Sharing/blob/master/index.php#L23
параметры соединения с БД надо вынести в отдельный файл config.php чтобы можно было их менять не трогая сам код.
> if ($file === NULL || !$file->isValid()) {
> return $app['twig']->render('main.twig');
Надо выводить сообщение об ошибке, можно без подробностей, а не показывать молча главную страницу и ставить пользователя в тупик.
> $app->get('/download/{id}', function ($id) use ($app) {
Скачивать файлы через PHP довольно неэффективно — Апач, а тем более nginx делают это с затратой горазло меньших ресурсов и могут выдержать гораздо больше пользователей (а ведь на файлообменнике скачивание это основная функция).
Есть такие варианты:
— настроить переписывание в htaccess так, чтобы при обращении по URL /download/realname-1234/желаемое-имя.txt отдавался бы файл /upload/realname-1234 (при этом браузер пользователя берет имя файла из последнего сегмента URL)
Статья: http://habrahabr.ru/company/sprinthost/blog/129560/ + есть официальная дока на сайте Апача на английском
Подход с htaccess работает только в Апаче. Для nginx придется написать отдельное правило с регулярным выражением. Зато в скачивании никак не участвует PHP.
— использовать специальный заголовок X-SendFile. В этом случае при попытке скачивания PHP скрипт отдает вместо файла этот заголовок и завершается, а веб-сервер перхватывает его и отдает реальный файл. Такой подход позволяет PHP скрипту только инициировать скачивание и может использоваться для ограничения доступа, защиты от прямого скачивания, учета числа скачиваний, и т.д. Инфа: http://habrahabr.ru/post/151795/
Для nginx используется аналогичный подход, только заголовок называется по другому, X-Accel-Redirect. Чтобы это работало, надо установить в Апач/nginx соотв. модуль.
У нас еще есть вопрос по безопасности. Что если злоумышленник закачает на твой сервер файл evil.php с вредоносным кодом, а затем попробует его скачать по прямой ссылке через папку /uploads/123evil.php ? Скрипт выполнится? Тогда твой сервер взломан.
Второй вариант взлома: закачиваем файл .htaccess (этот файл задает настройки Апача для папки) и им меняем настройки сервера на нужные нам, разрешая запускать любые файлы как php скрипт. Этот способ взлома может сработать там, где например запрещают загружать файлы с расширением php (что недостаточно так как есть несколько способов обхода этого).
защита:
— с помощью htaccess и php_flag engine 0 отключить выполнение PHP скриптов в папке загрузки. Подвох: на некоторых хостингах эта директива отключена и ты окажешься беззащитен
— переименовывать файлы с гарантированно безопасным расширением вроде txt при сохранении
Обрати внимание, что у Апача есть опасная особенность: в некоторых конфигурациях если он видит незнакомое расширение у файла, то смотрит на предыдущее. Ну например evil.php.lalala он может воспринять как PHP файл и выполнить.
Последнее замечание по коду работы с базой. Вот это вот ужасное изобретение:
> public function execute($sql, $params) {
Во-первых глядя на функцию мы не можем увидеть откуда в нее приходит запрос и гарантировать что она безопасна. Во-вторых, ты с ее помощью размазал работу с базой по всей своей программе вместо того, чтобы сконцентрировать в одном классе. Вот как должно быть:
public function insertFile(...)
public function findAllFiles(...)
public function searchFileByName(...)
Посмотри насколько это лучше читается, чем твой зашифрованный и запутанный код. Не делай так.
Также, заметь что у тебя в функции добавления файла аж 6 аргументов. Это много и легко забыть их порядок. А функция поиска файлов возвращает массив непонятно какого вида. Я предлагаю чуть усложнить код и добавить объект File представляющий информацию о файле. Мы сможем передавать этот объект для вставки и возвращать функциями выборки из базы. Это чуть увеличит объем кода, но все равно без этого более сложное приложение нормально не написать, так что лучше учиться сейчас.
Вот урок на тему работы с БД: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Файлы в этой папке https://github.com/AzerusEncole/File_Sharing/tree/master/templates лучше все называть c расширением twig чтобы не было путаницы.
Если есть какие-то вопросы, задавай.
После исправления посмотрим на код еще раз.
Вот еще от робота замечания:
------------
В файле ./classes/Connection.php:
> catch (PDOException $e) {
> echo $e->getMessage();
> }
Здесь ты ловишь исключение и самостоятельно его выводишь. Это плохая идея, так как об исключении узнает пользователь сайта (и ничего не поймет), а ты не узнаешь, так как оно не пишется в логи. Не надо ловить и выводить исключения через echo, PHP сам по умолчанию ловит все непойманные исключения, записывает их в лог (который ты можешь позже прочесть) и, если включен display_errors в php.ini, выводит информацию об исключении на экран (а если выключен то выводится белая страница, что конечно не очень красиво).
Если ты хочешь выводить красивую страницу-заглушку для пользователя при возникновении исключения, удобнее всего задействовать обработчик непойманных исключений, который задается функцией set_exception_handler (мануал: http://php.net/manual/ru/function.set-exception-handler.php ). Не забудь выдавать HTTP-код вроде 500, это положено по стандарту HTTP и это подскажет поисковикам не индексировать страницу ошибки. (примечание: Силекс скорее всего содержит свой способ для создания такой страницы-заглушки и надо использовать его).
Урок про исключения: https://gist.github.com/codedokode/65d43ca5ac95c762bc1a
Задал ли ты кодировку соединения с базой при работе через PDO? Это можно сделать либо параметром charset= при соединении либо командой SET NAMES в PDO::MYSQL_ATTR_INIT_COMMAND. Мануал: http://php.net/manual/ru/ref.pdo-mysql.connection.php
Кажется, ты не используешь строгий режим в MySQL. Зря. MySQL более тщательно проверяет твои запросы и вместо предупреждений (которые ты не увидишь) выдает ошибки при попытке вставить неправильные данные. Ну к примеру, если у тебя есть колонка типа varchar(200) и ты попытаешься вставить в нее строку из 300 символов, в нестрогом режиме MySQL молча отрежет лишнее (и в базе окажется обрезанная строка), а в строгом выдаст ошибку.
Использование строгого режима экономит твое время на исправление неправильно вставленных данных. Статья на хабре: http://habrahabr.ru/post/116922/
Включить строгий режим можно сделав при соединении с БД запрос SET sql_mode='STRICT_ALL_TABLES'. В PDO это удобно сделать опцией PDO::MYSQL_ATTR_INIT_COMMAND при создании объекта PDO.
В файле ./templates/file.twig:
Слеш в конце одиночного тега (<img src="./thumbs/{{ file.id …=""/>) используется только в XHTML и XML, в HTML он не ставится.
Советы и замечания по файлообменнику https://github.com/AzerusEncole/File_Sharing
Дамп SQL надо положить в репозиторий. Вот захочет кто-то скачать проект, как он его запустит без дампа? И структура БД это часть твоего проекта, какой смысл хранить её отдельно? Разумеется, в этом случае дамп должен содержать только структуру таблицы, без самих данных в ней.
Папки .idea, файлы внутри thumbs, uploads, vendor не должны загружаться в репозиторий. Содержимое папки vendor скачает композер, ну а файлы пользователь может закачать сам. Изучи как работает .gitignore и сделай примерно так:
— перенеси папки и файлы куда-нибудь в другое место
— закоммить проект (гит подумает что файлы удалены)
— внеси изменения в gitignore и закоммить
— верни файлы на место. теперь гит их не увидит.
Мануал по gitignore: https://git-scm.com/book/ru/v1/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B-Git-%D0%97%D0%B0%D0%BF%D0%B8%D1%81%D1%8C-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9-%D0%B2-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9#Игнорирование-файлов
Насчет файла composer.json, хватило бы только require, остальные поля заполняют только если ты хочешь сделать отдельную библиотеку, ну да ладно, хуже не будет.
Ручная загрузка классов через require усложнит тебе жизнь когда файлов станет больше. Советую изучить возможности композера по автозагрузке и сделать автозагрузку классов через файл composer.json. Есть такая статья http://habrahabr.ru/post/149678/ но она старая и не описывает формат PSR-4, он описан только в официальной доке:
https://getcomposer.org/doc/01-basic-usage.md#autoloading
https://getcomposer.org/doc/04-schema.md#autoload
> ini_set('display_errors', 1);
Это наверно лучше делать в php.ini на твоем компьютере, так как непраивльно показывать ошибки на боевом сервере, их незачем видеть пользователям, они должны идти только в лог.
сами команды error_reporting, ini_set надо перенести ниже чем use, так как use принято писать первыми в файле.
> https://github.com/AzerusEncole/File_Sharing/blob/master/index.php#L23
параметры соединения с БД надо вынести в отдельный файл config.php чтобы можно было их менять не трогая сам код.
> if ($file === NULL || !$file->isValid()) {
> return $app['twig']->render('main.twig');
Надо выводить сообщение об ошибке, можно без подробностей, а не показывать молча главную страницу и ставить пользователя в тупик.
> $app->get('/download/{id}', function ($id) use ($app) {
Скачивать файлы через PHP довольно неэффективно — Апач, а тем более nginx делают это с затратой горазло меньших ресурсов и могут выдержать гораздо больше пользователей (а ведь на файлообменнике скачивание это основная функция).
Есть такие варианты:
— настроить переписывание в htaccess так, чтобы при обращении по URL /download/realname-1234/желаемое-имя.txt отдавался бы файл /upload/realname-1234 (при этом браузер пользователя берет имя файла из последнего сегмента URL)
Статья: http://habrahabr.ru/company/sprinthost/blog/129560/ + есть официальная дока на сайте Апача на английском
Подход с htaccess работает только в Апаче. Для nginx придется написать отдельное правило с регулярным выражением. Зато в скачивании никак не участвует PHP.
— использовать специальный заголовок X-SendFile. В этом случае при попытке скачивания PHP скрипт отдает вместо файла этот заголовок и завершается, а веб-сервер перхватывает его и отдает реальный файл. Такой подход позволяет PHP скрипту только инициировать скачивание и может использоваться для ограничения доступа, защиты от прямого скачивания, учета числа скачиваний, и т.д. Инфа: http://habrahabr.ru/post/151795/
Для nginx используется аналогичный подход, только заголовок называется по другому, X-Accel-Redirect. Чтобы это работало, надо установить в Апач/nginx соотв. модуль.
У нас еще есть вопрос по безопасности. Что если злоумышленник закачает на твой сервер файл evil.php с вредоносным кодом, а затем попробует его скачать по прямой ссылке через папку /uploads/123evil.php ? Скрипт выполнится? Тогда твой сервер взломан.
Второй вариант взлома: закачиваем файл .htaccess (этот файл задает настройки Апача для папки) и им меняем настройки сервера на нужные нам, разрешая запускать любые файлы как php скрипт. Этот способ взлома может сработать там, где например запрещают загружать файлы с расширением php (что недостаточно так как есть несколько способов обхода этого).
защита:
— с помощью htaccess и php_flag engine 0 отключить выполнение PHP скриптов в папке загрузки. Подвох: на некоторых хостингах эта директива отключена и ты окажешься беззащитен
— переименовывать файлы с гарантированно безопасным расширением вроде txt при сохранении
Обрати внимание, что у Апача есть опасная особенность: в некоторых конфигурациях если он видит незнакомое расширение у файла, то смотрит на предыдущее. Ну например evil.php.lalala он может воспринять как PHP файл и выполнить.
Последнее замечание по коду работы с базой. Вот это вот ужасное изобретение:
> public function execute($sql, $params) {
Во-первых глядя на функцию мы не можем увидеть откуда в нее приходит запрос и гарантировать что она безопасна. Во-вторых, ты с ее помощью размазал работу с базой по всей своей программе вместо того, чтобы сконцентрировать в одном классе. Вот как должно быть:
public function insertFile(...)
public function findAllFiles(...)
public function searchFileByName(...)
Посмотри насколько это лучше читается, чем твой зашифрованный и запутанный код. Не делай так.
Также, заметь что у тебя в функции добавления файла аж 6 аргументов. Это много и легко забыть их порядок. А функция поиска файлов возвращает массив непонятно какого вида. Я предлагаю чуть усложнить код и добавить объект File представляющий информацию о файле. Мы сможем передавать этот объект для вставки и возвращать функциями выборки из базы. Это чуть увеличит объем кода, но все равно без этого более сложное приложение нормально не написать, так что лучше учиться сейчас.
Вот урок на тему работы с БД: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Файлы в этой папке https://github.com/AzerusEncole/File_Sharing/tree/master/templates лучше все называть c расширением twig чтобы не было путаницы.
Если есть какие-то вопросы, задавай.
После исправления посмотрим на код еще раз.
Вот еще от робота замечания:
------------
В файле ./classes/Connection.php:
> catch (PDOException $e) {
> echo $e->getMessage();
> }
Здесь ты ловишь исключение и самостоятельно его выводишь. Это плохая идея, так как об исключении узнает пользователь сайта (и ничего не поймет), а ты не узнаешь, так как оно не пишется в логи. Не надо ловить и выводить исключения через echo, PHP сам по умолчанию ловит все непойманные исключения, записывает их в лог (который ты можешь позже прочесть) и, если включен display_errors в php.ini, выводит информацию об исключении на экран (а если выключен то выводится белая страница, что конечно не очень красиво).
Если ты хочешь выводить красивую страницу-заглушку для пользователя при возникновении исключения, удобнее всего задействовать обработчик непойманных исключений, который задается функцией set_exception_handler (мануал: http://php.net/manual/ru/function.set-exception-handler.php ). Не забудь выдавать HTTP-код вроде 500, это положено по стандарту HTTP и это подскажет поисковикам не индексировать страницу ошибки. (примечание: Силекс скорее всего содержит свой способ для создания такой страницы-заглушки и надо использовать его).
Урок про исключения: https://gist.github.com/codedokode/65d43ca5ac95c762bc1a
Задал ли ты кодировку соединения с базой при работе через PDO? Это можно сделать либо параметром charset= при соединении либо командой SET NAMES в PDO::MYSQL_ATTR_INIT_COMMAND. Мануал: http://php.net/manual/ru/ref.pdo-mysql.connection.php
Кажется, ты не используешь строгий режим в MySQL. Зря. MySQL более тщательно проверяет твои запросы и вместо предупреждений (которые ты не увидишь) выдает ошибки при попытке вставить неправильные данные. Ну к примеру, если у тебя есть колонка типа varchar(200) и ты попытаешься вставить в нее строку из 300 символов, в нестрогом режиме MySQL молча отрежет лишнее (и в базе окажется обрезанная строка), а в строгом выдаст ошибку.
Использование строгого режима экономит твое время на исправление неправильно вставленных данных. Статья на хабре: http://habrahabr.ru/post/116922/
Включить строгий режим можно сделав при соединении с БД запрос SET sql_mode='STRICT_ALL_TABLES'. В PDO это удобно сделать опцией PDO::MYSQL_ATTR_INIT_COMMAND при создании объекта PDO.
В файле ./templates/file.twig:
Слеш в конце одиночного тега (<img src="./thumbs/{{ file.id …=""/>) используется только в XHTML и XML, в HTML он не ставится.
Точно так же как и текстовый: командами file_get_contents, fread
>>478898
Тебе сначала надо определиться с форматом архива. Посмотри на архив tar, это как раз архив без сжатия. Также в zip можно класть файлы без сжатия. Ну и подумай сам, что лучше: создавать файл который может прочесть большинство архиваторов или придумывать странный формат который никто кроме твоей программы не читает. Тебе же самому будет удобнее с таром.
И что значит «сохранить в архив»? Архив это такой же файл и запись в него делается теми же командами что и любой другой файл: fil_put_contents (записать целиком), fwrite (по частям). Я думаю fwrite тут удоюнее, так как она не требует держать все файлы в памяти.
>>478918
напиши больше подробностей, кто когда и зачем. Или ты тот анон что архиватор пишет? Я советовал обратить внимание так как все архиваторы использую бинарный формат.
> надо регулярку делать или можно как-то is_float обойтись?
is_float проверяет хранит ли переменная значение типа float. Ты читаешь строку по символьно и каждый символ это тоже маленькая строка, потому эта функция тут бесполезна.
Чтобы в нынешнем виде добавить поддержку дробей, надо завести дополнительную переменную-флаг которая значит «в числе встретилась точка/запятая». Затем проверять этот флаг при сборке числа из цифр, если ранее встретилась запятая то прибавлять цифры к числу не умножением на 10, а по другому алгоритму. Ну например можно сделать отдельную переменную с названием «множитель для дробных цифр» и добавлять цифру к числу с ее учетом: первый раз как число десятых, второй раз как число сотых, третий как число тысячных и тд. (ну или множитель можно получить как 10 в степени «минус номер цифры», например для третьей цифры после запятой это 10 в минус третьей или одна тысячная).
В общем, надо немного усложнить код. Советую попробовать это сделать, хорошее упражнение.
> if ($op == '+') {
> if ($op == '-') {
Это лучше писать в виде блока if/elseif чтобы было видно что все варианты взаимоисключающие.
Сама программа работает верно.
> 1-я задача, исправленная:
> $value = trim($value);
>\treturn($value);
тут лучше сразу писать return trim($value); А когда напишешь, остановиться на секунду и спросить себя: чем твоя функция отличается от функции trim? Если ничем то стоит ли ее создавать вообще? Не проще ли в array_map передать саму trim?
Комментарии к функциями принято писать не справа, а перед ними. Есть даже специальный формат, если ты хочешь дать описание аргументам и возвращаемому результату, PhpDoc:
http://habrahabr.ru/sandbox/22836/
http://habrahabr.ru/post/134916/ (тут с картинками)
Если ты используешь php Doc то такие комментарии во-первых будут распознавать IDE (и показывать например при наведении на функцию), во-вторых из них можно автоматически генерировать документацию к проекту, вот пример такой документации:
http://www.doctrine-project.org/api/orm/2.2/class-Doctrine.ORM.EntityManager.html
А вот исходный файл с которого она сгенерирована, с комментариями:
http://www.doctrine-project.org/api/orm/2.2/source-class-Doctrine.ORM.EntityManager.html (если ты будешь хорошо учиться то потом сможешь понимать такой код)
В остальном, все верно.
> 2-я задача, исправленная
> $i = 0;
Неиспользуемая переменная. Их надо убирать, так как они сбивают с толку читающего. Это как рецепт в котором в начале написано «помойте горсть риса», при этом сам рис больше нигде не используется.
В остальном все правильно.
> 1-я задача, исправленная:
> $value = trim($value);
>\treturn($value);
тут лучше сразу писать return trim($value); А когда напишешь, остановиться на секунду и спросить себя: чем твоя функция отличается от функции trim? Если ничем то стоит ли ее создавать вообще? Не проще ли в array_map передать саму trim?
Комментарии к функциями принято писать не справа, а перед ними. Есть даже специальный формат, если ты хочешь дать описание аргументам и возвращаемому результату, PhpDoc:
http://habrahabr.ru/sandbox/22836/
http://habrahabr.ru/post/134916/ (тут с картинками)
Если ты используешь php Doc то такие комментарии во-первых будут распознавать IDE (и показывать например при наведении на функцию), во-вторых из них можно автоматически генерировать документацию к проекту, вот пример такой документации:
http://www.doctrine-project.org/api/orm/2.2/class-Doctrine.ORM.EntityManager.html
А вот исходный файл с которого она сгенерирована, с комментариями:
http://www.doctrine-project.org/api/orm/2.2/source-class-Doctrine.ORM.EntityManager.html (если ты будешь хорошо учиться то потом сможешь понимать такой код)
В остальном, все верно.
> 2-я задача, исправленная
> $i = 0;
Неиспользуемая переменная. Их надо убирать, так как они сбивают с толку читающего. Это как рецепт в котором в начале написано «помойте горсть риса», при этом сам рис больше нигде не используется.
В остальном все правильно.
Да, я тот анон, что архиватор пишет. Больше подробностей будет в начале следующей недели, отпишусь, как и зачем, и почему истерил, лол.
Спасибо, я уже понял. Работает все. Задание учебное и надо просто сохранять архив в формате json, потому, что готовые решения запрещенны.
Наступило лето, все гуляют наверное, лол.
Твой совет к сожалению не несет объяснения, откуда проблемы. И не предупреждает что base 64 увеличивает вес на 33%. Это уже не архиватор, а какой-то расширятор и лишнее-место-на-диске-поглощатор.
Я подозреваю что ошибка анона в том что он читает бинарный файл и пытается его закодировть как строку в JSON. Но в JSON строки должны быть в кодировке utf-8. Если почитать вики по этой теме, то ясно что в JSON может содержаться не любая последоваетльность байт, а только определенные стандартом. Ну а в бинарном файле разумеется байты могут быть любые, и стоит хоть одной неправильной последовательности появиться, как кодировщик ломается.
так что выход, да, действительно, закодировать тело файла в base64 превратив его тем самым в валидную текстовую строку из 64 видов символов. И увеличив размер на 33%.
Я не понимаю, что делает анон, но архиватром это при всем желании нельзя назвать. Почему нельзя сделать бинарную упаковку наподобие tar? Я же не написал взять программу tar, я имел в виду изучить этот формат и создавать архивы в нем. Тогда архивы будут распаковываться программами вроде 7zip и будут весить примерно как исходные файлы, а не на треть больше. Это какое-то наркоманское задание, я не знаю кто его придумал, но оно скорее учит неправильным вещам чем полезым. таких заданий быть не должно.
По поводу 33% веса думаю надо исправить формат, иначе ничего не выйдет. Надо просто дописать две функции, которые будут кодировать строку и декодировать
Оп-кун, я допилял архиватор. Я не знаю, как эта хрень работает, но архив занимает чуть меньше 3кб , а файлы сами по себе около 4.
все в фейкотред
Погоди, погоди, там еще у анона надо код проверить. Если у тебя вопрос какой-то то можешь пока тут его задать.
Что если нам передадут, например, миллиард переменных через GET? Как php будет реагировать?
GET переменые передаются через адресную строку, в URL. Веб-сервер (например Апач) имеет ограничение на длину URL которое задается директивой http://httpd.apache.org/docs/2.4/mod/core.html#limitrequestline и по умолчанию она равно 8190 байт то есть максимум 8190 латинских символов. Так как каждая GET переменная должна содержать хотя бы имя, знак = и знак & то получается около 2500 переменных
Если прислать более длинную строку URL то веб-сервер отвергнет запрос с какоим-то кодом 4xx, не помню точно с каким.
Заметь что это ограничение не действует на POST так как там переменные передаются в теле запроса которое может иметь размер в несколько мегабайт.
Потому в PHP есть второе ограничение, на макисмальное число GET/POST/COOKIE переменных: http://php.net/manual/ru/info.configuration.php#ini.max-input-vars — по умолчанию оно равно 1000.
Таким образом миллиард переменных не дойдет. Также, если ты исплоьзуешь GET ты должен помнить про ограничение на длину URL. Также, так как форма отправляется из браузера то браузер тоже ограничивает длину URL и по моему безопасным значением считается 2000 байт, если больше то в каких-то браузерах твоя форма не будет работать или данные будут отрезаться. Для больших объемов данных используй POST, а не GET.
ОП, что можешь сказать о симфони2? насколько сложно изучить? учитывая, что я его в глаза не видел, есть ли шансы за неделю изучить и выполнить тестовое задание(немного шарю в yii):
Реализовать каталог товаров на symfony2 без использования дополнительных бандлов, не идущих в стандартной поставке симфони.
1. Реализовать EAV модель. (Создать доктриновские сущности, связать их и построить на их основе структуру БД)
2. Реализовать фильтрацию по значениям характеристик товара. (построить фильтр на чекбоксах, как в интернет-магазинах) http://screencloud.net/v/4t5M
3. Фильтр должен работать как AJAXово, так и при нажатии на кнопку «Подобрать» с перезагрузкой страницы.
4. Реализуйте сортировку по цене (убывание, возрастание) с хранением пользовательского выбора в куках. При фильтрации выбранная сортировка сбиваться не должна.
5. Реализуйте фасеты для значений фильтра (как на скриншоте).
Спасибо.
Симфони сложный, так что это маловероятно, если ты и сделаешь задание то все равно знания будут обрывочные и недостаточные чтобы полноценно пользоваться фреймворком.
> . Я не знаю, как эта хрень работает
Это плохо
> if ($handler = opendir($path)){
>\t\t\twhile (false !== ($file = readdir($handler))){
Это старый и неудобный подход. Гораздо удобнее рекурсивно читать каталог через рекурсивный итератор файловой системы:
http://php.net/manual/ru/class.recursivedirectoryiterator.php
Итератор это объект по которому можно пройтись циклом foreach. Рекурсивный итератор это когда некоторые элементы итератора сами являются итераторами (папка содержит другие файлы).
> echo "Something went wrong in function readFolder. Sorry. \n";
Ты не пишешь причину ошибки, это плохо, првада PHP выводит ее сам в виде предупреждения, так что тут это не проблема. Но в таких случаях принято использовать исключения: https://gist.github.com/codedokode/65d43ca5ac95c762bc1a
Если у тебя есть код
if (условие) {
50 строк
} else {
2 строки
}
То его надо попробовать перевернуть, чтобы было
if (не выполняется условие) {
2 строки
} else {
50 строк
}
А еще лучше
if (не выполняется условие) {
выкинуть исключение;
}
50 строк;
То есть твои ифы надо перевернуть хотя бы, чтобы сначала шла более короткая ветка.
> while ($byte = fread($inputStream, 1024*1024)){
Это неудачное решение. Во-первых, если их файла прочитается строка "0" то она будет воспринята как false и цикл завершится. Во-вторых, функция fread возвращает false при ошибке или пустую строку "" при достижении конца файла и эти ситуации надо бы различать.
Ну и наконец тут незачем городить цикл, так как файл целиком можно прочесть одной командой file_get_contents.
> $file['type'] = filetype($path);
А зачем это?
> echo "No such file or directory"."\n";
это неверное сообщение. Причина ошибки может быть в другом.
> но архив занимает чуть меньше 3кб , а файлы сами по себе около 4.
Значит там ошибка и часть данных теряется. Ты бы мог глазами посмотреть JSON и попробовать найти чего там не хватает. Должно получаться на 33% больше чем исходные файлы + немного оверхеда от самого JSON.
> . Я не знаю, как эта хрень работает
Это плохо
> if ($handler = opendir($path)){
>\t\t\twhile (false !== ($file = readdir($handler))){
Это старый и неудобный подход. Гораздо удобнее рекурсивно читать каталог через рекурсивный итератор файловой системы:
http://php.net/manual/ru/class.recursivedirectoryiterator.php
Итератор это объект по которому можно пройтись циклом foreach. Рекурсивный итератор это когда некоторые элементы итератора сами являются итераторами (папка содержит другие файлы).
> echo "Something went wrong in function readFolder. Sorry. \n";
Ты не пишешь причину ошибки, это плохо, првада PHP выводит ее сам в виде предупреждения, так что тут это не проблема. Но в таких случаях принято использовать исключения: https://gist.github.com/codedokode/65d43ca5ac95c762bc1a
Если у тебя есть код
if (условие) {
50 строк
} else {
2 строки
}
То его надо попробовать перевернуть, чтобы было
if (не выполняется условие) {
2 строки
} else {
50 строк
}
А еще лучше
if (не выполняется условие) {
выкинуть исключение;
}
50 строк;
То есть твои ифы надо перевернуть хотя бы, чтобы сначала шла более короткая ветка.
> while ($byte = fread($inputStream, 1024*1024)){
Это неудачное решение. Во-первых, если их файла прочитается строка "0" то она будет воспринята как false и цикл завершится. Во-вторых, функция fread возвращает false при ошибке или пустую строку "" при достижении конца файла и эти ситуации надо бы различать.
Ну и наконец тут незачем городить цикл, так как файл целиком можно прочесть одной командой file_get_contents.
> $file['type'] = filetype($path);
А зачем это?
> echo "No such file or directory"."\n";
это неверное сообщение. Причина ошибки может быть в другом.
> но архив занимает чуть меньше 3кб , а файлы сами по себе около 4.
Значит там ошибка и часть данных теряется. Ты бы мог глазами посмотреть JSON и попробовать найти чего там не хватает. Должно получаться на 33% больше чем исходные файлы + немного оверхеда от самого JSON.
filetype что бы знать папка ли это или нет в массиве
Вы видите копию треда, сохраненную 4 июня 2015 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.