Это копия, сохраненная 6 октября 2016 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Это не чат! Пожалуйста старайтесь постить только вопросы, решения и ответы по теме. Сколько лет вы не можете найти работу никому не интересно. Высказывайтесь одним большим постом, а не цепочкой мелких
Это тред для начинающих. Не написал за свою жизнь ни одной программы? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Netbeans PHP или PhpStorm (с ним будет удобнее).
Предыдущий (неофициальный) тред был тут: >>807538 (OP)
и тут:
https://2ch.hk/pr/res/807538.html (М)
Что самое главное для программиста? Умение аккуратно оформлять код (читай второй пост прежде чем писать код).
Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко.
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП заходит редко, где-то раз в 2-3 дня, у него мало времени, не жди его, решай задачки дальше. ОП отвечает на все вопросы по его задачкам и учебнику, а вот насчет каких-то других вещей - только если останется время. Но в треде немало анонимных экспертов разного уровня, так что вряд ли вопрос останется без ответа.
У нас есть уроки по основам PHP, они собраны и выложены по адресу http://archive-ipq-co.narod.ru/ Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь, то надо начать с него. Он простой и понятный (по крайней мере в начале). Там есть задачи, их надо решать обязательно (чтобы стать программистом, надо писать код — иначе никак). Пости ссылки на решения в тред, мы их проверим, напишем замечания и дадим советы по улучшению.
Если не знаешь как решать, запости код, напиши в каком месте остановился и попроси подсказку.
Ты прошел весь учебник? Молодец, но это были лишь основы языка PHP, этого недостаточно. Вот что в идеале надо изучить еще: ООП, как работает веб-сервер, HTML/CSS, SQL, PDO, работа с таблицами в БД, работа с формами, MVC, git, composer, JS, фреймворки, автоматизированное тестирование.
Надо переходить к более серьезным задачкам, которые научат тебя всему этому.
- для начала прочти урок https://github.com/codedokode/pasta/blob/master/soft/web-server.md
- установи Апач + PHP (советы выше и ниже) и читай туториал http://php.net/manual/ru/tutorial.php
- Учи HTML/CSS и SQL, PDO, хотя бы основы
- Далее простая, но полезная задача сделать список студентов, в ней много полезных советов: https://github.com/codedokode/pasta/blob/master/student-list.md
- Более сложная задача сделать файлообменник на микрофреймворке Slim: https://gist.github.com/codedokode/9424217
- Еще более сложная и долгая задача на Yii/Yii2: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование
- Если ты все решил, переходи к Symfony 2/Doctrine 2
- Почитать про паттерны http://designpatternsphp.readthedocs.org/ru/latest/README.html (если ты не изучил ни одного фреймворка, то это будет рановато), тут с примерами кода http://designpatternsphp.readthedocs.org/ru/latest/README.html . Имей в виду что без примеров использования их учить бесполезно - не поймешь, хочешь увидеть примеры использования паттернов - ковыряй исходники Симфони, например Symfony Forms. Не заучивай паттерны - смотри код и думай, зачем тут они использованы.
Чтобы делать эти задания, тебе надо установить Апач + PHP (можно заодно сразу и MySQL) на компьютер. Вот полезные инструкции:
https://github.com/codedokode/pasta/blob/master/soft/php-install.md
https://github.com/codedokode/pasta/blob/master/soft/apache-install.md
Может тебе понадобится пользоваться командной строкой, вот гайд https://github.com/codedokode/pasta/blob/master/soft/cli.md
Решения задач лучше показать мне, особенно на ООП,так как сам ты вряд ли увидишь все ошибки. Пости свой код на гитхаб и вкидывай ссылку в тред по мере решения. Я прокомментирую и укажу на ошибки.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания JS/HTML/CSS/SQL. Решай их параллельно с задачами выше.
- HTML/CSS: https://github.com/codedokode/pasta/blob/master/html/html.md
- JS: https://gist.github.com/codedokode/ce30e7a036f18f416ae0
- SPA (сложно): https://github.com/codedokode/pasta/blob/master/js/spa.md
- Проверялка решений на JS: http://dkab.github.io/jasmine-tests/
- MySQL: https://gist.github.com/codedokode/10539213
Что почитать
- Мануал по PHP — http://www.php.net/manual/ru/langref.php
- Сайт phptherightway (перевод на русский: http://getjump.me/ru-php-the-right-way/ )
- По PHP: Профессиональное программирование на PHP Джордж Шлосснейгл
- По PHP: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- JS: learn.javascript.ru
- Про Git: https://git-scm.com/book/ru/v1
Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию.
Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета)
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет.
Подскажи сайты для поиска работы, я не умею гуглить? — hh.ru, geekjob.ru, moikrug.ru (склеен с brainstorage.me), fl.ru, upwork.com (бывший одеск). Имей в виду, что кроме фриланса есть еще постоянная удаленная работа (remote job) когда тебе не надо тратить время на поиск заказов и переговоры с неадекватными заказчиками.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
http://roem.ru
http://vc.ru
https://events.yandex.ru/events/yasubbotnik/
Доп контент для чтения.
Я бы поставил лойс вашему треду, если бы это было возможно. Просто лудший.
нуфажная си-блядь
И как это работает? При возврате на предыдущую страницу ты видишь чистую форму? Или заполненную, но которую просто не примет сервер при попытке отправить? Если второе, то хуйня собачья, я тоже так могу, а хочется красивое решение.
Неа, Прату читаю. Из кс 50 выписал все проблем сеты, примерное, скачал lecture notes, все проекты тоже буду пилить. Практики мало не бывает
Понятно, ну успехов тогда. А я как раз перешел к части про пыху и веб, теперь в этом треде буду сидеть.
Часов 8, но до этого я реализовал почти все структуры данных по sections.
Я уже решил.
Тоже ошибка не показывалась, но
$dbh = new PDO('mysql:host=localhost;dbname=databasename', _USER_NAME_, _DB_PASSWORD);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Решило проблему.
вроде да, на место вопросов подставляются параметры: имя колонки и порядок ASC|DESC, которые биндятся строкой ниже. А что именно тебя смутило?
Бесплатный, если хочешь, можешь купить сертификат.
Через плейсхолдеры можно подставлять только числа или строки (в кавычках), но не имена полей и таблиц.
<?php
View::$cb = function(){echo '1';};
View::render();
class View {
static public $cb;
static function render() {
self::$cb();
}
}
Зачем статика?
http://ideone.com/lO2his
Я для вью использовал ob_start, extract, ob_get_clean, это проще.
Сделал что-то вот такое http://ideone.com/fwF60F
При сабмите редиректит на 405 not allowed. Я вообще в бэк-технологиях не рублю и не знаю как это решать. Помогите.
Вот верстка http://codepen.io/sashaslow/pen/OXZkEE
Спасибо, что откликнулись.
>При сабмите редиректит на 405 not allowed
Это значит что твой сервер не разрешает отправлять пост запросы на данный адрес.
Настраивай сервер, если не можешь - пиши хостинг провайдеру о том что у тебя проблемы. Я бы начал с банальной проверки, установлен ли вообще PHP на сервере.
https://ideone.com/Cc6pdv
такой вопрос: в какой версии пхп он будет выкидывать ошибку о необъявлённой переменной $flag?
как ОП-пики связаны с пыхой?
Нет, она может быть обьявлена в другом файле, поэтому ошибки не должно быть вплоть до 3.2 наверное.
пхп
Как надо так и связаны.
Ну вот допустим у меня есть три варианта пользовательского поведения.
1) юзер просто заходит на мой http://yo.ba
2) юзер кормит странице http://yo.ba ссылку постом через форму на этой же странице что бы получить короткую
3) и последнее: юзер проходит по укороченной ссылке вида:
http://yo.ba/asdhj1j23b1j
Прост сделать 1 контроллер с ифами в нем? Я не представляю как начать работать в этом направлении в ООП, помогите пожалуйста.
я хочу что бы урлы были вида: examp.le/1
examp.le/2
examp.le/3
Но всё это обращалось к индекс.пхп и при этом ему еще передавался второй кусок урла
Может быть дело в том что передаю с http, а принимаю на https?
http://pastebin.com/zBYgH7FA
да, дело оказалось в этом
Как этого избежать, чтоб страница просто рефрешилась, например?
Сделал аяксом, полет, тьфу тьфу тьфу, блять, вроде пока более-менее нормальны.
У меня во всем /pr/ такой баг.
Легко ли будеть овладеть вордпрессом? (приглашают на работу, но гвоорят чтобы WP изучил). И посоветуй какиенить годные видеоуроки по оному
Вот собственно пробдлема, которая меня смутила, это модель MVC, роутер, стоит ли (думаю да) юзать и чтобы не изобретать велосипед чем мне пользоваться, или лучше все писать самому? слышал про slim, может кто-то что-то подскажет?
Собственно как соединить админку и клиента с использованием MVC, писать самому все или использовать микрофреймворк, закотирует ли работодатель юзание микрофреймворка?
https://lumen.laravel.com/
Алсо, нашел вот такой еще микрофреймворк. Но в них совсем зеленый. Может кто пояснит, но что смотреть в первую очередь, что требовать от него.
PHP?
(сам знаю Java, занимаюсь мобильной разработкой под ведроид)
в 2016 году есть Фронтэнд ( HTML, JS, CSS и библиотеки это вот всё ) Бэкэнд (PHP, гитхабы, Джэйсоны, XML, и вот это всё ) Выбирай. Сразу говорю, фриланс требует и то и то.
Уже курю http://nginx.org
эти микро в основном что бы делать быстрые RESTFUL API сервисы можешь на них не смотреть
оно по дефолту добавлено в gitignore потому не хуярит на хаб , а те которые ты юзаешь будут генерироваться
> Прост сделать 1 контроллер с ифами в нем?
Похоже что да. Я так и сделал, решая задачу про студентов из оп-поста.
А еще можешь замутить класс Роутер, который будет анализировать входящие запросы и направлять куда следует. Вот пример реализации https://habrahabr.ru/post/31270/
Я сначала не совсем понимал как именно строить архитектуру и как классы должны взаимодействовать между собой. Потом за день написал тестовое приложение, костылями реализовав контроллер и представление. Затем где-то дня 2 - 3 потратил на рефакторинг кода.
Cтатья почти 10-и летней (!) давности.
Как альтернатива, прочитать: https://toster.ru/q/321702
Там нет готовой реализации, зато ответы дадут понимание, что важнее.
Примеры сущностей в роутинге можно посмотреть здесь: http://symfony.com/doc/current/components/routing.html
А лучше не велосипедить и взять готовый: https://packagist.org/search/?q=router
>>829307
Ответ будет очень сильно варьироваться от уровня твоих знаний, который нам неизвестен. Если ты писал сайты на процедурном PHP, то тебе, скорее всего, приходила в голову мысль о хоть каком-то разделении PHP и HTML кода.
>Ответ будет очень сильно варьироваться от уровня твоих знаний,
Я знаю как писать функции, как писать классы, выносил функции в отдельный файл.
>Если ты писал сайты на процедурном PHP, то тебе, скорее всего, приходила в голову мысль о хоть каком-то разделении PHP и HTML кода.
Ну да. Но ведь тут совсем хардкор. Первое впечатление такое, что отправленная переменная например, проходит овер 9000 обработок, прежде чем дойти до бд. Что Запрашиваемая страница, как франкенштейн собирается из 9000 осколочков страниц раскиданный по модели. При этом по началу что-то изменить невероятно трудно.
Да нет же, контроллер берет запрос от пользователя, идет в модель и забирает данный, затем выстреливает или в виде вьюхе, изи же. В cs50 за 10 минут все рассказали.
ну я не так тебя понял просто анон , например функцию сверки пароля мы реализуем в моделе , но вызываем её да в контроллере
Начни с задач ОПа. МВЦ не сложна, когда начнёшь пользоваться и осознаешь ее - будешь думать в этой концепции.
Я вот пример опа сейчас смотрю. Он чёт неработает.
Ты про задачу про студентов? Пытаюсь её разобрать.
Урок читал? https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Что именно в нем непонятно?
>>830049
Что именно не работает? Какую ошибку пишет?
>>829343
Не знаю, где ты нашел эти 9000 обработок. Модель напрямую вообще никак не взаимодействует с пользователем, а только получает данные через аргументы из других функций.
>>827711
Такой класс обычно называют Front Controller. Он анализирует запрос и вызывает нужный контроллер для его обработки.
>>829264
Архитектура там сомнительного качества.
>>828823
Начинать надо с проектирования БД. И я подозреваю что будет не очень простая система.
Ну и в сервисах по продаже билетов их никто вручную не вбивает, там есть интерфесы для взаимодействия с авиакопманиями и бронирования мест.
Урок читал? https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Что именно в нем непонятно?
>>830049
Что именно не работает? Какую ошибку пишет?
>>829343
Не знаю, где ты нашел эти 9000 обработок. Модель напрямую вообще никак не взаимодействует с пользователем, а только получает данные через аргументы из других функций.
>>827711
Такой класс обычно называют Front Controller. Он анализирует запрос и вызывает нужный контроллер для его обработки.
>>829264
Архитектура там сомнительного качества.
>>828823
Начинать надо с проектирования БД. И я подозреваю что будет не очень простая система.
Ну и в сервисах по продаже билетов их никто вручную не вбивает, там есть интерфесы для взаимодействия с авиакопманиями и бронирования мест.
>Что именно не работает? Какую ошибку пишет?
this
Но я уже бругой гайд подобрал, так что буду по нему работать пока. Просто странно, вроде не у кого проблем с примером небыло. Либо только у меня проблемы, либо один я решил рассказать о проблемах.
Да, ошибка в коде. Но справедливости ради, там же очевидно в чем ошибка и исправляется она за 2 минуты.
>Что именно в нем непонятно?
Всё непонятно. Как получить начальную страницу? Как передать данные для логина? Как таким макаром с сессиями работать? Как передать переменную до контроллера, а затем получить данные из БД, ещё и аяксом? Возможно я просто слишком тупой для MVC.
Вот интересно, про MVC ты еще не знаешь, а логины и сессии изучил. Может логичнее наоборот было делать?
> Как получить начальную страницу?
В смысле, главную страницу? Для нее нужен как минимум контроллер (IndexController) и HTML-шаблон.
> Как передать данные для логина?
Откуда и куда передать? насколько я понимаю, там есть страница логина, значит у нее есть свой контроллер. Соответственно нужна модель, которая отвечает за список пользователь и может проверить пароль, а также класс, который отвечает за авторизацию, то есть проверку и выдачу кук (или сессий если хочется усложнить систему).
> Как таким макаром с сессиями работать?
как обычно
В том, что list.php лежит в дебрях вместо корня. Его либо надо вызывать индеском, либо выводить наверх.
>Не знаю, где ты нашел эти 9000 обработок.
Идём на сайт primer.com\index.php
В индексе реквайр на core\core.php
там реквайр на 4 файла, в них ещё реквайры в них ещё реквайры а там реквайр твига, а твиг реквайрит шаблоны, а шаблоны содержат маркеры, а маркеры запршиваются из переменных...
И это только одна страница, а сервер бегает, и собирает эдакого франкенштэйна из всего, что только указано. Я просто пытаюсь отследить какой путь пройдёт ОДНА переменная передаваемая через $_POST. А теперь представь, что у тебя хотя-бы форум. Это как магия. Ты вроде что-то отсылаешь, а оно теряется в дебрях кода.
Начни с основ PHP, решай задачки с сайта в ОП-посте. Как решишь - можешь пробовать делать задачу на список студентов. Я думаю это займет у тебя еще месяца 3 - 4, так что приготовься там.
Уже читаю php параллельно гугля html, но у меня от силы неделя наверно, после которой начнутся адовые вопли типа "че не работаешь?!" а объяснить что я занимаюсь это анриал т.к. в моей семье сидеть за компом=играться в игрушечки.
Спасибо за поддержку анон, а то совсем руки опустились уже.
ОС 10
Сервер apache на сборке openserver
при установке composer просит указать путь к php.exe.
путь указываю C:\OpenServer\modules\php\PHP-5.6\php.exe
после чего выдает мне ошибки:
The PHP exe file you specified did not run correctly:
C:\OpenServer\modules\php\PHP-5.6\php.exe
The php.ini used by your command-line PHP is: C:\OpenServer\modules\php\PHP-5.6\php.ini
A setting in your php.ini could be causing the problem: Either the 'extension_dir' value is incorrect or the dll does not exist.
Program Output:
Warning: PHP Startup: Unable to load dynamic library 'c:/openserver/modules/php/PHP-5.6/ext/php_imagick.dll' - Не найден указанный модуль.
in Unknown on line 0
Тебе же написало в чем суть ошибки - не может найти библиотеку imagick. Тебе даже написало причину, почему такое может происходить. Или значение extension_dir неправильное, или такой библиотеки просто не существует.
Я бы для начала проверил, существует ли вообще папка с библиотеками, и существует ли та библиотека которая не найдена. От этого уже можно отталкиваться и гуглить почему php её не находит, обычно на винде это происходит из-за неправильно указных путей.
я уже устал от этих проблем. во всех туториалах все легко устанавливается, а тут как начнешь, так на каждом шагу яма с дерьмом.
Очевидно ты установил васянскую сборку, не понимаешь как она настроена и что в ней вообще есть, и чего там нет. А потом еще и удивляешься почему что-то не работает. Я бы тебе посоветовал начать с установки чистого апача, потом прикрутить к нему php, это не так уж и сложно.
Есть сайт http://tunezone.net , есть его дев-версия tunezone.dev.ephyros.com .
Я делаю пост вк и добавляю в него ссылочку на сайт - подгружает в превью картинку-заглушку, которая была на нем пару месяцев назад.
После добавленяи og: тегов через раз оно пытается подрузить изображение из og:image, но безуспешно.
В фейсбуке все работает замечательно.
С дев сервером все работает замечательно.
Кэш вк через vk.com/dev/pages.clearCache чистил.
нужна ваша помощь пояснить мне за общий принцип.
Поставил слим, почитал доки, полазил в ядре, по туториолам вывел Hello World, а дальше не пойму как с ним работать!
не пойму как создавать новый контроллер, может есть у кого-то примеры работ со слимом, или просто может кто пояснит?
наверно папки /controller /model /view ???
а вообще зайти на гитхаб любого фреймворка не судьба
Пиши свой шаблонизатор, который будет инклюдить шапку и футер к каждой странице
прочитаю про Smarty сейчас, но все же нет ли более простого способа?
нужно прочитать про шаблониатор , только не про смарти (господи прости ) а про Blade хотя-бы и понять как работает система layout
поехавший велосепедостройшик идика ты на...
можно ли запускать подготовленное выражение для UPDATE в петле с изменением имени поля и его значения, например как-нибудь так:
$stmt = $db->prepare("UPDATE table SET ? WHERE id =?");
$values = array('field1'=>0, 'field2'=>2);
foreach ($values as $field => $id) {
$stmt->execute();
}
?
Нет, нельзя. Через плейсхолдеры можно подставлять только значения, но не имена колонок или таблиц.
Подставляй напрямую, только не забывай что эти данные нужно экранировать самому, иначе получается SQL инъекция.
Перебрал фореачем, нашел все ошибки, вывел их и исправил. Как теперь эти самые исправления вернуть в сам текст, а не выводить их по отдельности?
> Для поиска по всем колонкам можно применить оператор LIKE к соединенным через пробел значениям столбцов.
Что это значит, и как и что соединять?
Я понял это вот так:
SELECT * FROM `abiturients` WHERE id name lastName gender groupNum email egePoints dateOfBirth registry userPassword LIKE '%jo%';
Конечно оно не работает. Нагуглил варианты только с AND (OR) и вот такой крутой, но совсем не понятный алгоритм:
http://phpclub.ru/detail/article/mysql_search
>Что это значит, и как и что соединять?
То и значит. Несколько колонок нужно соединить для поиска. Сделать это можно с помощью CONCAT. Запрос
SELECT CONCAT(name, ' ', lastName, ' ', groupNum) FROM 'abiturients';
Выдаст тебе одну колонку с соединенными через пробел столбцами. Чтобы лучше понять как работает - можешь этот запрос запустить в командной строке и посмотреть на результат.
Я вижу что ты знаешь как использовать LIKE, так что запрос поиска попробуй составить сам, я думаю это будет нетрудно.
Попробуй include или require файлы с твоими сайдбаром и тд
Спасибо большое, я не знал про CONCAT, я еще не до конца прочитал "Учебное пособие по MySQL" из оп-поста.
В чем может быть соль?
Ну у нас люди пишут задачу про студентов с использованием MVC и ООП и как-то справляются. А затем они пишут файлообменник на микрофреймворке, который еще сложнее. Если понимать, какая у приложения структура, то все становится понятно.
Хотя может пример, который ты смотрел, переусложнен. Или же возможно у тебя пока слишком мало знаний, чтобы понимать такие сложные системы.
Я конечно не видел этот пример, но в архитектуре MVC с $_POST может работать только контроллер, так что обходить десятки файлов не требуется и достаточно изучить только код одного класса-контроллера. Вот видишь, как полезно использовать принцип MVC и как он упрощает жизнь.
Переменные в твиг в MVC тоже передает контроллер, так что искать их по всему коду не требуется.
Мало ходить на курсы. Надо изучать каждый урок до тех пор пока тебе каждое слово в нем не будет понятно. По времени это конечно может затянуться, особенно на первых порах, но ничего не поделать. И соответственно ты должен самостоятельно, не подглядывая в ответы, решить все задачи которые там дают.
Ну то есть для тебя обучение должно быть чем-то вроде работы, на которой ты трудишься 8 часов в сутки несмотря на то что неохота, лень итд.
Придется наверно самостоятельно гуглить и искать информацию. Можно в соответствующем треде вопросы задать (не факт конечно что ответят).
У нас тут пхп тред, но по у нас есть задачки по HTML и по SQL (в ОП посте), которые вроде как пересекаются с тем, что тебе нужно. Ну например, если ты прорешаешь все наши 13 задач по HTML то у тебя будет достаточный минимальный уровень знаний этого языка.
Если ты действительно изучаешь Яву (а я что-то сомневаюсь что за 3 месяца реально изучить все что нужно для работы, ведь там надо кроме языка учить еще ООП, паттерны, библиотеки, фреймворки) то даже тут наш тред может немного тебе помочь. У нас есть в ОП посте задача про студентов - любой начинающий Ява-программист должен уметь ее решить. К ней идет большое число пояснений, и хотя часть из них относится только к PHP, но часть универсальна и не зависит от языка.
>>830159
За 4 месяца вряд ли, если только он не будет ежедневно по хардкору учить.
>>830161
Ну как доказательство покажи какой-нибудь сайт, который ты сделал или предложи написать код.
>>830258
Для установки композера надо иметь установленный и настроенный php (логично по моему). У тебя - сборка, это не то же самое что чистый пхп. Там пхп настроен так, чтобы работать в составе сборки и не факт что его так просто можно использовать отдельно.
Насколько я помню, в некоторых сборках надо сначала открыть консоль (в которой все настроено как надо) и соответственно уже из этой консоли запускать композер или его установщик, хотя я могу ошибаться.
Если что, в ОП посте есть инструкция по установке пхп вручную.
В любом случае, тебе было бы полезно обойтись без сборок и поставить все руками. Не факт что это получится без ошибок, но там я хотя бы или кто-то еще сможет тебе подсказать, что делать.
>>830459
Работа со Слимом делается так: создаем public/index.php, в нем пишем:
- подключение и настройка Слима
- подключение и настройка твоих классов, загрузка конфига, если он есть
- определяем маршруты и функции-обработчики для них
- запускаем Слим через $app->run();
Собственно функции-обработчики и играют роль контроллеров из MVC. Разумеется, они должны быть небольшими по объему, чтобы index.php не распухал.
А остальное ты делаешь как обычно, ну то есть делаешь классы для работы с базой, для валидации, делаешь шаблоны twig для вывода страниц. И вызываешь все это из функций-обработчиков.
В принципе, Слим это по большому счету роутер (штука которая анализирует УРЛ и выбирает обработчик) + middleware + обработчик ошибок + DI контейнер + полезные классы для представления Запроса, Ответа. А что касается работы с базой, валидации - это уже не задача Слима, он тут ничего не делает.
>>830579
В этой статье есть бесполезные вещи, например бессмысленный класс Connect . Зачем он нужен когда есть PDO? Автору этого показалось мало, он еще и Фабрику там сделал. Такое ощущение что человек только открыл для себя паттерны и решил использовать каждый паттерн из книги.
Автор на ровном месте сделал 2 класса вместо того чтобы написать
$pdo = new PDO(...);
А потом кто-то посмотрит на это и будет говорить что ООП это переусложнение, давайте писать все лапшой без классов.
Мало ходить на курсы. Надо изучать каждый урок до тех пор пока тебе каждое слово в нем не будет понятно. По времени это конечно может затянуться, особенно на первых порах, но ничего не поделать. И соответственно ты должен самостоятельно, не подглядывая в ответы, решить все задачи которые там дают.
Ну то есть для тебя обучение должно быть чем-то вроде работы, на которой ты трудишься 8 часов в сутки несмотря на то что неохота, лень итд.
Придется наверно самостоятельно гуглить и искать информацию. Можно в соответствующем треде вопросы задать (не факт конечно что ответят).
У нас тут пхп тред, но по у нас есть задачки по HTML и по SQL (в ОП посте), которые вроде как пересекаются с тем, что тебе нужно. Ну например, если ты прорешаешь все наши 13 задач по HTML то у тебя будет достаточный минимальный уровень знаний этого языка.
Если ты действительно изучаешь Яву (а я что-то сомневаюсь что за 3 месяца реально изучить все что нужно для работы, ведь там надо кроме языка учить еще ООП, паттерны, библиотеки, фреймворки) то даже тут наш тред может немного тебе помочь. У нас есть в ОП посте задача про студентов - любой начинающий Ява-программист должен уметь ее решить. К ней идет большое число пояснений, и хотя часть из них относится только к PHP, но часть универсальна и не зависит от языка.
>>830159
За 4 месяца вряд ли, если только он не будет ежедневно по хардкору учить.
>>830161
Ну как доказательство покажи какой-нибудь сайт, который ты сделал или предложи написать код.
>>830258
Для установки композера надо иметь установленный и настроенный php (логично по моему). У тебя - сборка, это не то же самое что чистый пхп. Там пхп настроен так, чтобы работать в составе сборки и не факт что его так просто можно использовать отдельно.
Насколько я помню, в некоторых сборках надо сначала открыть консоль (в которой все настроено как надо) и соответственно уже из этой консоли запускать композер или его установщик, хотя я могу ошибаться.
Если что, в ОП посте есть инструкция по установке пхп вручную.
В любом случае, тебе было бы полезно обойтись без сборок и поставить все руками. Не факт что это получится без ошибок, но там я хотя бы или кто-то еще сможет тебе подсказать, что делать.
>>830459
Работа со Слимом делается так: создаем public/index.php, в нем пишем:
- подключение и настройка Слима
- подключение и настройка твоих классов, загрузка конфига, если он есть
- определяем маршруты и функции-обработчики для них
- запускаем Слим через $app->run();
Собственно функции-обработчики и играют роль контроллеров из MVC. Разумеется, они должны быть небольшими по объему, чтобы index.php не распухал.
А остальное ты делаешь как обычно, ну то есть делаешь классы для работы с базой, для валидации, делаешь шаблоны twig для вывода страниц. И вызываешь все это из функций-обработчиков.
В принципе, Слим это по большому счету роутер (штука которая анализирует УРЛ и выбирает обработчик) + middleware + обработчик ошибок + DI контейнер + полезные классы для представления Запроса, Ответа. А что касается работы с базой, валидации - это уже не задача Слима, он тут ничего не делает.
>>830579
В этой статье есть бесполезные вещи, например бессмысленный класс Connect . Зачем он нужен когда есть PDO? Автору этого показалось мало, он еще и Фабрику там сделал. Такое ощущение что человек только открыл для себя паттерны и решил использовать каждый паттерн из книги.
Автор на ровном месте сделал 2 класса вместо того чтобы написать
$pdo = new PDO(...);
А потом кто-то посмотрит на это и будет говорить что ООП это переусложнение, давайте писать все лапшой без классов.
MVC ничего не говорит про папки и файлы. Ты наверно ищешь PSR-4 про который у меня есть урок где-то тут: https://github.com/codedokode/pasta/blob/master/php/autoload.md
>>830829
В фреймворке как раз самих моделей и контроллеров обычно нет.
>>830893
В случае с твигом проще всего использовать наследование шаблонов. В случае не с твигом проще всего в начале шаблона поставить <?php require шапка.phtml ?>
>>830905
Наверно эта система layout в Blade где-то описана и есть документация ведь?
>>830957
Нет. Тебе надо вручную собирать выражение вида field = :field и при этом гарантировать безопасность.
>>830991
С помощью preg_replace заменить ошибочное выражение на правильное и записать это обратно в ту же переменную, где был текст.
>>831049
1) старье
2) переусложненный синтаксис, накопившийся за долгое время
3) по моему нет автоэкранирования -> будут XSS
>>831163
Может где-то в скрипте бесконечный цикл? Лог ошибок не смотрел? Для начала попробуй поставить max_exucution_time на 1 секунду и посмотреть в каком месте буде ошибка что время истекло.
Еще есть вероятность что верия пхп какая-то кривая или криво собрана.
>>831314
Не понял что именно нужно. Группировать по разным наборам полей одновременно не получится, однако можно из 2 этих полей сделать выражение в том числе с использованием IF и группировать по нему.
MVC ничего не говорит про папки и файлы. Ты наверно ищешь PSR-4 про который у меня есть урок где-то тут: https://github.com/codedokode/pasta/blob/master/php/autoload.md
>>830829
В фреймворке как раз самих моделей и контроллеров обычно нет.
>>830893
В случае с твигом проще всего использовать наследование шаблонов. В случае не с твигом проще всего в начале шаблона поставить <?php require шапка.phtml ?>
>>830905
Наверно эта система layout в Blade где-то описана и есть документация ведь?
>>830957
Нет. Тебе надо вручную собирать выражение вида field = :field и при этом гарантировать безопасность.
>>830991
С помощью preg_replace заменить ошибочное выражение на правильное и записать это обратно в ту же переменную, где был текст.
>>831049
1) старье
2) переусложненный синтаксис, накопившийся за долгое время
3) по моему нет автоэкранирования -> будут XSS
>>831163
Может где-то в скрипте бесконечный цикл? Лог ошибок не смотрел? Для начала попробуй поставить max_exucution_time на 1 секунду и посмотреть в каком месте буде ошибка что время истекло.
Еще есть вероятность что верия пхп какая-то кривая или криво собрана.
>>831314
Не понял что именно нужно. Группировать по разным наборам полей одновременно не получится, однако можно из 2 этих полей сделать выражение в том числе с использованием IF и группировать по нему.
Ты перед реактом Js выучил? Прототипы и замыкания понимаешь? Если нет - у нас в ОП посте есть задачи по JS которые должен уметь за 5 минут решить любой фронтендер. Также, там есть задача "сапер с использованием MVC" которую ты можешь попробовать решить с использованием реакта.
>>831602
Сделай нормальный, хороший класс-логгер, соответствующий стандарту. Затем испорти его, сделав его синглтоном. Затем доломай окончательно, написав класс-фабрику по созданию таких логгеров-синглтонов. У нас в Оп посте есть ссылка на сайт про паттерны, код можно скопировать оттуда.
Я сам не очень понимаю, зачем тут фабрика. Но раз требуют - надо делать.
>С помощью preg_replace заменить ошибочное выражение на правильное и записать это обратно в ту же переменную, где был текст.
может я дебил? Заранее прошу прощения
То есть твой вопрос сводится к "как собрать одну строку из массива отдельных слов, добавив пробелы"? Есть функция для этого.
Алсо, для поиска ошибок разбивать строку на слова не требуется. Мощь регулярных выражений позволяет делать поиск и замену сразу в строке.
Не не, нужно чтоб цикл исправления в строку возвращал
Вот еще вопрос, есть реглярка
$regexp /(хуй)(пизда)/
Как прегматчем взять на проверку первую часть регулярки?
Пробовал смотреть на пхп.су подробные мануалы, так там в отличии от учебника ОПа какаято непонятная хуйня везде написана
Все правильно сказал
>Ну у нас люди пишут задачу про студентов с использованием MVC и ООП и как-то справляются.
Даже с моим уровнем знаний, видно что при решении местные аноны тупо копируют весь код в app\view а потом тыкают в него роутером и контроллером. Тоесть в итоге у них никакого MVC и в помине нет.
>Ты наверно ищешь PSR-4 про который у меня есть урок где-то тут: https://github.com/codedokode/pasta/blob/master/php/autoload.md
require_once __DIR__ . '/Some/Class.php';
require_once __DIR__ . '/Some/Other/Class.php';
Хочешь много проблем? Используй в коде __DIR__ !
Не понял тебя. Без __DIR__ или аналогичной константы имя будет относительное и искаться будет вообще непонятно где в зависимости от многих факторов (текущего каталога в процессе ОС, например).
>>831704
Ну нет. В app/view идут только HTML-шаблоны страниц.
Никогда не использовал DIR, впервые от тебя слышу что есть какие-то проблемы. Что-то ты делаешь не так.
Точнее будет так. Лет 10 назад использовал DIR в первых пректах, но потом отказался из-за вечных проблем с путями.
http://pastebin.com/jdSgqG8d
>SELECT '' FROM users WHERE login = $e_login
Что это за запрос такой? Что он по твоему вернет? Так же в твоих SQL запросах присутствуют SQL инъекции, нужно использовать экранирование или prepared statements. Расширение mysql уже давно устарело, вместо него есть mysqli или PDO.
По поводу того что возвращает ошибку - у тебя запрос скорее всего не выбирает никаких колонок, поэтому элемента password в массиве просто не существует. Перед сравнением сделай var_dump переменной $user_data и посмотри что у тебя там.
>Что это за запрос такой? Что он по твоему вернет?
Я рассчитываю, что он заглянет в табличку и найдёт юзера с таким логином, а потом сверит пасс и всё будет отлично.
>По поводу того что возвращает ошибку.
Я не правильно выразился, он выдаёт "echo 'Wrong password!';" при любых вариантах.
Я тебе написал почему он выдает что неправильный пароль. Запрос написан неправильно, попробуй выполнить его в командной строке и посмотреть на результат.
SELECT * FROM users WHERE login = $e_login
или
SELECT password FROM users WHERE login = $e_login
дело в том, что я делаю кастомные записи с помощью register_post_type().
заполняя аргументы я дошел до 'support'
выбрал только нужные мне поля (титул, изображение)
и вот тут-то и наткнулся на трудность. Мне нужно добавить еще одно поле, но только свое, кастомное, которого нет в стандартной сборке.
Можно ли зарание зарегестрировать новое поле и потом уже добавить его в 'support' ?
если да, то как его добавить?
но и как говорил анон с верху используй PDO или mysqli , учись сразу делать хорошо
на самом деле нет, и ссылка выглядит очень подозрительно. Я бы поставил минимум 2 антивируса прежде чем переходить по ней.
лучше маня через виртуалку запустил , анитивирус шлак
Если это значит, что после...
var test1_3 = partialAny(test, 1);
console.log(test1_3(5, 3));
console.log(test1_3(2));
...должно выводиться в консоли...
a=1,b=5,c=3
a=1,b=2,c=undefined
...то у меня все именно так! Подскажите в чем причина, только не спойлерите решение.
И ещё, мелочи:
1) Вместо typeof args == 'undefined' лучше использовать args === undefined
2) Вместо
args = args2[j];
j++
проще сразу
args = args2[j++]
Ещё можно последовательно доставать элементы из args2 с помощью функции shift(), тогда вообще не нужно со счётчиком j возиться. Однако shift(), наверное, будет, чуть-чуть медленне, чем подход со счётчиком.
> проще сразу
> args = args2[j++]
на мой взгляд в виде 2 строк последовательность действий понятнее.
Задачи с гитхаба опа, так что отстань, мелкая буква.
Стоит ли вкатываться дебилу в 27 лет? Программа максимум - получать 250-300 баксов в месяц (для беларуси это овердохуище)
Мне нихуя не очевидно, что деалет этот код. То ли присваиваем args2[j] args и после этого инкрементируем j, то ли инкементируем j и уже после этого присваиваем args2[j] args.
мимотольковкатился
Это потому, что ты не понимаешь разницы между постфиксным и префиксным инкрементом. Почитать можно тут: http://learn.javascript.ru/operators#инкремент-декремент
Для каждого языка разница своя. Код для людей же пишут, а не для машин.
Знать, несомненно, такое полезно, но в своем коде я таких конструкций все-таки постараюсь сторониться. В
j++;
args = args2[j];
или
args = args2[j];
j++;
Смысл ясен с первого взгляда.
вкатывайся
> Код для людей же пишут, а не для машин.
Так можно дойти до того, что i = i + 1 гораздо нагляднее, чем i++, смысл первого выражения ясен с первого взгляда.
Индекс-инкремент/декремент используется часто, например в реализациях стека на основе массива: https://www.cs.bu.edu/teaching/c/stack/array/download/stack.c
Строки:
> stackP->contents[++stackP->top] = element;
> return stackP->contents[stackP->top--];
В методах push и pop соответственно.
Вот ещё с learn.javascript.ru:
Решение задачи "Глобальный счётчик":
http://learn.javascript.ru/closures
> return currentCount++;
Решение задачи "Фильтрация массива "на месте""
http://learn.javascript.ru/array-methods
> arr.splice(i--, 1);
Ну и реализация счётчика:
http://learn.javascript.ru/closures-usage
Как видишь, такой лаконичный подход активно применяется, просто ты пытаешься использовать только то, к чему привык. Даже ценой избыточного кода.
Не вопрос. Однако замечу, что трактовать двояко
> i++;
>i = i + 1;
нельзя, в отличие от
> arr.splice(i--, 1);
или
>args = args2[j++]
Да, запись короче, но новичку, вроде меня, не очевидно, что автор решил сократить arr.splice(i, 1);i--;
Эта часть кода
> return currentCount++;
приведена в отрыве от контекста: currentCount находится вне текущего скоупа и не уничтожается при выходе из функции. Переписать ее, как в предыдущем случае, просто не выйдет без создания дополнительной переменной - после return-а идет выход из функции.
По поводу
>stackP->contents[++stackP->top] = element;
Ну, это C. Тут априори предполагается, что программист точно знает, что он делает. JS, PHP, да даже тот же C++ - уровнем повыше, и по мне так лучше делать код чуть длинее и читабельнее, нет?
Я все же думаю, лучше писать отдельно. А то создается впечатление, что автор не пишет понятный и поддерживаемый код, а хочет похвастаться тем что вчера на хабре прочел статью про разницу пост- и префиксного инкремента. А так, синтаксис языка вообще позволяет полпрограммы в одну строчку записать, если прочитать остальные статьи на хабре.
У тебя такое впечатление создается только из-за того, что ты сам вчера прочитал про это.
С другой стороны, в питоне например отказались от этой фичи
> А то создается впечатление, что автор не пишет понятный и поддерживаемый код, а хочет похвастаться тем что вчера на хабре прочел статью про разницу пост- и префиксного инкремента.
То есть, по твоему, Кантор, который контрибьютил в PostgreSQL, Redis, Google Closure Compiler, "хвастается тем, что прочитал вчера на хабре"?
Разницу между пост/префикс инкрементами обязан понимать любой новичок, это очень простая вещь. В доказательство - эта тема рассмотрена Кантором именно в "основах JavaScript". Хотя это основы программирования вообще.
А и че бы не запостить:
http://ideone.com/1llE14
Повторяю, это очень старый пример из закромов, можешь обосрать. Но у меня работало, лол.
HTML+CSS+PHP+JS + пара фреймворков попопулярней, в общем и целом ничего сложного.
>И я не понимаю, зачем вообще нужен max-width. Ведь блочный элемент, если у него не заданы width и max-width, по умолчанию растягивается (с учетом бордеров и паддингов) на всю ширину родителя (урок https://github.com/codedokode/pasta/blob/master/html/shrink-to-fit.md ). Значит, тут надо просто вместе с display block поставить для width/max-width значения auto и все. Точнее, только для max-width, так как width нигде не задан.
>И я не понимаю, зачем вообще нужен max-width.
Чтобы вкладки с большим названием не растягивались на всё занимаемое им пространство.
Почему-то свойство max-width не переопределяться и вкладки сжимаются до 200px: https://jsfiddle.net/obdgvxfk/ . Если его не использовать то всё работает отлично https://jsfiddle.net/obdgvxfk/1/ .
>И еще одно замечание. Текст в блоках с содержимым вкладки примыкает к бордеру. Это некрасиво, создается ощущение что тексту там тесно. Ведь во всех книгах и журналах у текста есть поля справа и слева. Так же должно быть и тут - у тела вкладки должны быть поля, хотя бы 5-10px и у заголовка вкладки тоже должны быть небольшие поля, чтобы буквы не прилипали к краю вплотную. Если присмотреться, поля вокруг текста есть везде - и на кнопках, и в полях ввода.
Добавил поля: https://jsfiddle.net/tb7k9dw9/8/
>Насчет удаления атрибутов с id, я придумал такой вариант:
>Можно попробовать для связи табов и вкладок использовать их номера. CSS3 позволяет указать в стилях что-то вроде "N-й по счету элемент типа E" - и соответственно можно попробовать привязать первый инпут и первый label к первому телу вкладки, второй ко второму итд.:
Сделал: https://jsfiddle.net/1ax6k6hw/
>Тогда data-tab нам не нужен и нужен только id. Как от него избавиться, я увы, не знаю. Если положить input внутрь label то не получится использовать селектор ~.
Я тоже об этом говорю. Такой способ хорош только в случаях если мы в ручную делаем шаблон, а не генерируем его динамически.
>>833459
>Я имел в виду что можно написать например так:
>
>.content {
>width: 100%; / старые браузеры второе правило проигнорируют и возьмут первое /
>width: calc(...); / новые прочитают оба правила, но возьмут второе /
>}
Ах да, я забыл что свойства выполняются в приоритете.
>.content {
>width: 100%; / старые браузеры второе правило проигнорируют и возьмут первое /
>width: calc(...); / новые прочитают оба правила, но возьмут второе /
>}
>
>> Пусть пока будет так:https://jsfiddle.net/0u3x5rsw/3/
>> <div class="wrapper">
>Враппер не нужен - минимальную ширину можно задать на элементе html или body.
>Также, если присмотреться, левая граница меню не совпадает с левой границей заголовка - меню сдвинуто на 5-10px вправо, некрасиво.
Исправил: https://jsfiddle.net/0u3x5rsw/6/ https://jsfiddle.net/xx82knpf/2/
>>833459
>Задача про Короля Лир
>
>> Исправил: https://jsfiddle.net/tsuh4gjw/8/
>Что-то ничего не исправилось. Заголовок по прежнему вырван влево. Тут надо просто поставить на body поля слева и все.
Исправил: https://jsfiddle.net/tsuh4gjw/9/
>>833459
>> Неисчезающие инпуты
>> До сих пор не понял в чём смысл: https://jsfiddle.net/gpysbfar/4/
>> Контент находиться за пределами лейбла, но не скрывается.
>О, еще одна хорошая головоломка, но я нашел ответ. label по умолчанию имеет display: inline, а как известно, у inline-элементов нет собственных размеров - их размер и форма совпадает с содержимым. Потому инлайн-элемент не имеет форму прямоугольника, а может например растягиваться на несколько строк в вслед за содержащимся в нем текстом.
А почему лейбел не растягивается на ширину сместившихся влево инпута?
>> Applies to: block containers and boxes that establish a formatting context
>> Применяется к: блокам-контейнерам (?) и боксам, задающим контекст форматирования
Если что, я умею читать английский.
>>830052
>Задача про радиокнопки
>> .inputbox > input {
>> position: absolute;
>> left: -10000px;
>Вот у этого метода конечно есть подвох - что если завтра кто-то поставит наш элемент на гигантскую страницу которая шире этих 10 000 px? Может лучше сделать родителя c overflow: hidden и тогда не придется так далеко загонять? Или все же не стоит? Я в принципе не настаиваю.
Исправил: https://jsfiddle.net/fp8o0exq/6/
>>833459
>> Вроде получилось: https://jsfiddle.net/fp8o0exq/7/
>тут есть проблема: если двигать по кнопкам синюю рамку, то ширина кнопки при появлении рамки меняется и правая кнопка сдвигается на 1-2 пикслея вправо. Это некрасиво. Надо либо компенсировать лишний пиксель в бордере отрицательным маргином, либо сделать у всех кнопок border 1px и отрицательный маргин, чтобы они слегка наезжали друг на друга.
Исправлено: https://jsfiddle.net/fp8o0exq/9/
>>833459
>Тень вроде выглядит похоже. Единственная проблема - если кнопка вжата, на ней есть тень, и на ней кликнуть, то тень пропадает, пока на кнопке фокус. Видимо потому, что тень переиспользуется для рамки. но у элемента может быть несколько теней - надо просто грамотно прописать правила.
Я тоже увидел это, но не уделил этому нужного внимания. Исправил: https://jsfiddle.net/fp8o0exq/8/
Еще я подумал, что можно было задать несколько теней с помощью box-sizing, но у них тогда бы был общий радиус теней https://jsfiddle.net/L69xmtwh/
>Или же можно попробовать сделать рамку с помощью свойства outline: http://htmlbook.ru/css/outline
Но ему же нельзя задать смещение по оси или размытие, или радиус углов если мы хотим использовать его за место бордера.
>И я не понимаю, зачем вообще нужен max-width. Ведь блочный элемент, если у него не заданы width и max-width, по умолчанию растягивается (с учетом бордеров и паддингов) на всю ширину родителя (урок https://github.com/codedokode/pasta/blob/master/html/shrink-to-fit.md ). Значит, тут надо просто вместе с display block поставить для width/max-width значения auto и все. Точнее, только для max-width, так как width нигде не задан.
>И я не понимаю, зачем вообще нужен max-width.
Чтобы вкладки с большим названием не растягивались на всё занимаемое им пространство.
Почему-то свойство max-width не переопределяться и вкладки сжимаются до 200px: https://jsfiddle.net/obdgvxfk/ . Если его не использовать то всё работает отлично https://jsfiddle.net/obdgvxfk/1/ .
>И еще одно замечание. Текст в блоках с содержимым вкладки примыкает к бордеру. Это некрасиво, создается ощущение что тексту там тесно. Ведь во всех книгах и журналах у текста есть поля справа и слева. Так же должно быть и тут - у тела вкладки должны быть поля, хотя бы 5-10px и у заголовка вкладки тоже должны быть небольшие поля, чтобы буквы не прилипали к краю вплотную. Если присмотреться, поля вокруг текста есть везде - и на кнопках, и в полях ввода.
Добавил поля: https://jsfiddle.net/tb7k9dw9/8/
>Насчет удаления атрибутов с id, я придумал такой вариант:
>Можно попробовать для связи табов и вкладок использовать их номера. CSS3 позволяет указать в стилях что-то вроде "N-й по счету элемент типа E" - и соответственно можно попробовать привязать первый инпут и первый label к первому телу вкладки, второй ко второму итд.:
Сделал: https://jsfiddle.net/1ax6k6hw/
>Тогда data-tab нам не нужен и нужен только id. Как от него избавиться, я увы, не знаю. Если положить input внутрь label то не получится использовать селектор ~.
Я тоже об этом говорю. Такой способ хорош только в случаях если мы в ручную делаем шаблон, а не генерируем его динамически.
>>833459
>Я имел в виду что можно написать например так:
>
>.content {
>width: 100%; / старые браузеры второе правило проигнорируют и возьмут первое /
>width: calc(...); / новые прочитают оба правила, но возьмут второе /
>}
Ах да, я забыл что свойства выполняются в приоритете.
>.content {
>width: 100%; / старые браузеры второе правило проигнорируют и возьмут первое /
>width: calc(...); / новые прочитают оба правила, но возьмут второе /
>}
>
>> Пусть пока будет так:https://jsfiddle.net/0u3x5rsw/3/
>> <div class="wrapper">
>Враппер не нужен - минимальную ширину можно задать на элементе html или body.
>Также, если присмотреться, левая граница меню не совпадает с левой границей заголовка - меню сдвинуто на 5-10px вправо, некрасиво.
Исправил: https://jsfiddle.net/0u3x5rsw/6/ https://jsfiddle.net/xx82knpf/2/
>>833459
>Задача про Короля Лир
>
>> Исправил: https://jsfiddle.net/tsuh4gjw/8/
>Что-то ничего не исправилось. Заголовок по прежнему вырван влево. Тут надо просто поставить на body поля слева и все.
Исправил: https://jsfiddle.net/tsuh4gjw/9/
>>833459
>> Неисчезающие инпуты
>> До сих пор не понял в чём смысл: https://jsfiddle.net/gpysbfar/4/
>> Контент находиться за пределами лейбла, но не скрывается.
>О, еще одна хорошая головоломка, но я нашел ответ. label по умолчанию имеет display: inline, а как известно, у inline-элементов нет собственных размеров - их размер и форма совпадает с содержимым. Потому инлайн-элемент не имеет форму прямоугольника, а может например растягиваться на несколько строк в вслед за содержащимся в нем текстом.
А почему лейбел не растягивается на ширину сместившихся влево инпута?
>> Applies to: block containers and boxes that establish a formatting context
>> Применяется к: блокам-контейнерам (?) и боксам, задающим контекст форматирования
Если что, я умею читать английский.
>>830052
>Задача про радиокнопки
>> .inputbox > input {
>> position: absolute;
>> left: -10000px;
>Вот у этого метода конечно есть подвох - что если завтра кто-то поставит наш элемент на гигантскую страницу которая шире этих 10 000 px? Может лучше сделать родителя c overflow: hidden и тогда не придется так далеко загонять? Или все же не стоит? Я в принципе не настаиваю.
Исправил: https://jsfiddle.net/fp8o0exq/6/
>>833459
>> Вроде получилось: https://jsfiddle.net/fp8o0exq/7/
>тут есть проблема: если двигать по кнопкам синюю рамку, то ширина кнопки при появлении рамки меняется и правая кнопка сдвигается на 1-2 пикслея вправо. Это некрасиво. Надо либо компенсировать лишний пиксель в бордере отрицательным маргином, либо сделать у всех кнопок border 1px и отрицательный маргин, чтобы они слегка наезжали друг на друга.
Исправлено: https://jsfiddle.net/fp8o0exq/9/
>>833459
>Тень вроде выглядит похоже. Единственная проблема - если кнопка вжата, на ней есть тень, и на ней кликнуть, то тень пропадает, пока на кнопке фокус. Видимо потому, что тень переиспользуется для рамки. но у элемента может быть несколько теней - надо просто грамотно прописать правила.
Я тоже увидел это, но не уделил этому нужного внимания. Исправил: https://jsfiddle.net/fp8o0exq/8/
Еще я подумал, что можно было задать несколько теней с помощью box-sizing, но у них тогда бы был общий радиус теней https://jsfiddle.net/L69xmtwh/
>Или же можно попробовать сделать рамку с помощью свойства outline: http://htmlbook.ru/css/outline
Но ему же нельзя задать смещение по оси или размытие, или радиус углов если мы хотим использовать его за место бордера.
спосибо
>>>830052
>>Задача про радиокнопки
>>> .inputbox > input {
>>> position: absolute;
>>> left: -10000px;
>>Вот у этого метода конечно есть подвох - что если завтра кто-то поставит наш элемент на гигантскую страницу которая шире этих 10 000 px? Может лучше сделать родителя c overflow: hidden и тогда не придется так далеко загонять? Или все же не стоит? Я в принципе не настаиваю.
>Исправил: https://jsfiddle.net/fp8o0exq/6/
Что-то ничего не сохранилось. Должно быть так: https://jsfiddle.net/a6wk2qng/
> $this->mainCode();
> Название функции должно начинаться с глагола. И обозначать что она делает.
Ничего лучше prepareForView() не придумал
> Надо бы запретить сортировать по hash. Это дает информацию о том в каком порядке они идут. Хотя это не получится использовать для взлома, но все равно, надо внимательнее относиться к безопасности.
Сделал, но совсем не понял зачем и как это связано с безопасностью
> throw new ConfigException("Ошибка в $filename");
>Сюда надо бы добавить подробности ошибки разбора JSON
А как?
> $reg=preg_quote("/$find/ui");
> Неправильно - preq_quote заэкранирует слеши и превратит их в \/. preg_quote экранирует все символы, которые могли бы иметь специальное значение, и ограничители (слеши) конечно тоже заэкраниурет.
Судя по http://php.net/manual/ru/function.preg-quote.php как раз таки слеш / он не экранирует
Остальное исправил. Осталось комментарии расписать по коду. Жду оценки: https://github.com/TheSidSpears
Сама себе злобная буратина.
Весь див не вариант, потому что там стоят вместе с ним реквест разбана и автообновление. Пикрилейтед.
>>834568
А чего конкретно ты хочешь добиться? Если ты хочешь оставить что-то и удалить все остальное, то скорее всего без юзерскрипта тут не обойтись. Ставь tampermonkey.
Ладно, спасибо за ответ. Постараюсь с юзерскриптом поработать.
Про такой вид инкремента пишут чуть ли не на второй странице любой книги под названием "Изучи ПХП за 12 часов".
>автор не пишет понятный и поддерживаемый код
No comments
> Сделал, но совсем не понял зачем и как это связано с безопасностью
Допустим у нас хеш состоит из 40 символов от a до z (условно). Допустим злоумышленник зарегистраировал много аккаунтов и знает хеш каждого. Теперь он сортирует список по хешу и например видит между 2 своими аккаунтами с хешами A и B другого студента. Теперь он знает что его хеш находится в промежутке от A до B. Меньше вариантов для подбора.
Если злоумышленник сам не может задать себе хеш, то это сокращает число вариантов перебора в N раз, где N число аккаунтов злоумышленника. На практике хеш из 40 символов дает такое большое число вариантов что это не сильно поможет.
Но если злоумышденник вдобавок может назначать своему аккаунту хеш (например из-за плохо сделанной формы редактирования или регистрации, позволяющей задать хеш) то злоумышленник может подобрать хеш любого пользователя методом деления пополам.
Потому возможность сортировки по хешу вместе с возможностью задавать хеш для совего аккаунта позволяют подобрать любой хеш. То есть сортировка - это по сути половина уязвимости.
Кстати теоретически такие баги ведь могут быть и на других сайтах, кажется я открыл новый метод взлома паролей.
И кстати, вторую половину уязвимости - возможность при редактировании поменять себя хеш - я тоже видел у некоторых анонов в коде. Сами по себе эти половинки ничего не дают, но вместе - взлом любого аккаунта.
Потому важно обращать внимание на такие вещи.
Если ты впихиваешь 2 действия в одну строку, то приходится останавливаться и задумываться, в правильно ли порядке они выполняются.
Когда они написаны в 2 строки - сомнений нет.
Есть разница между написанием учебной программы из 20 строк и необходимостью копаться в коде из сотен тысяч строк. Во втором случае на мой взгляд чем очевиднее, тем удобнее.
Цели соревноваться кто впихнет действия в меньшее число строк нету.
Сишники вообще не пример, у них нет даже стандарта оформления кода, не говоря о коллекциях, менеджерах пакетов, нормальной стандартной библиотеке. Они там живут в каменном веке на диком западе и они последние с кого стоит брать пример.
Так я эту строку уже в разных позах исхуярил - нет, то не так, это не так. Я просто не знаю, что я там неправильно сделал.
Пиздос у меня в падике за такие вопросы убивают нахуй. У тебя неправильно написан elseif и далее нужно заменить оператор присваивания на оператор сравнения. Это уже сам нагуглишь. Пиздос блять с такими скиллами поиска решений тебе лучше забить на эту хуйню.
Всмысле он неправильно написан? Про оператор я уже понял, сам бы потом заметил и исправил. И всё таки - как это он неправильно написан, уточнить можешь? И почему вы тут такие высокомерные? В оп-посте прям так и написано, что недалёких недопрогеров тут и обучат и поднимут, а от первого же вопроса я, блять, как /b попал
Ну хз, когда приходит анон и говорит, что не отличает elseif от esleif и ты понимаешь что это не тралинг, то както хз даже.
А, погоди, понял, буква не там
Отличаю ващет. Но не будем о плохом, ведь программа работает, а ты молодец,
Далеко не всегда стоит разделять абсолютно все на атомарные кусочки кода. Иногда наоборот, все становится более очевидным, когда более-менее объединено. Например, читаешь код из 10-ти строк и только потом, дочитав до конца и мысленно интерпретировав этот код в мозгу, попутно возвращаясь обратно то на 5 строку, то на 10, понимаешь, что делает этот блок. Вместо того, чтобы организовать его в три строки так, что при быстром взгляде становится понятно какая логическая операция совершается.
Однозначного ответа как именно делать - нет. Все зависит от контекста выполняемой задачи и текущей операции. В обоих вариантах можно дойти до маразма.
> Сишники вообще не пример,
Ну вот на Java, в Java есть соглашения по оформлению кода, пакетный менеджер и что там ещё интересует.
http://cs.lmu.edu/~ray/notes/stacks/
Смотреть реализацию стека на основе массива, строки:
> array[size++] = item;
> array[--size] = null;
Просто сишный пример я нагуглил быстрее. На PHP вообще ничего нормального не было, только читерские реализации с использованием array_push/array_pop.
> то приходится останавливаться и задумываться
Вот в упор не понимаю, где там нужно "задумываться".
В общем, если можно объединить операции, не потеряв читаемости и очевидности, то стоит это сделать. Плодить ассемблерную логику ничуть не лучше, чем впихивать невпихумое в одну строку.
Это может быть какая-то микрооптимизация. Там как минимум надо проверять size на выход за границы массива (если он не динамиечский).
>>834815
Да всё уже решили, читай ветку, перед тем как отвечать. И, да, про сравнение - я этого не спрашивал, я бы заметил и исправил, НЕ НАДО тыкать меня в неё, ладно? И я НЕ намеренно допустил ошибку в else. Но ты хоть сразу написал в чём дело, всё равно спасибо.
Про сравнение ты бы ничего не заметил, потому что оно там уже ни к чему, лалка.
Но я не лалка. Сегодня я потерялся в трёх соснах, а завтра я буду наставлять на путь таких же заблудших душ. И я никогда не буду советовать ему бросать это дело
У max-width есть специальное значение, обозначающее "отсутствие ограничений": https://developer.mozilla.org/ru/docs/Web/CSS/max-width#Values
(если сложно читать англ то можно найти это свойство в htmlbook и посмотреть там).
В таких случаях стоит всегда сначала лезть в мануал и проверять, нет ли у свойства какой-то нужной нам особенности.
> А почему лейбел не растягивается на ширину сместившихся влево инпута?
Понятия не имею. Видимо этот момент в спецификациях четко не описан и потому производители браузеров его реализуют как хотят. Но факт в том что у инлайн-элементов нет своих размеров. И overflow нельяз применять к ним.
> Если что, я умею читать английский.
Это же отлично. Тогда в случае совмнений можно первым делом лезть смотреть мануалы на MDN и читать спецификацию на w3.org. Она конечно довольно сложная, особенно поначалу, но может там найдется ответ на какой-то вопрос или какая-то полезная информация.
ну вот например эти разделы
https://www.w3.org/TR/REC-CSS2/visuren.html
https://www.w3.org/TR/REC-CSS2/visudet.html
я бы рекомендовал попробовать почитать. Так как в них по сути и описаны разные особенности позиционирования, которые мы изучаем в задачах.
>>834510
>>Сюда надо бы добавить подробности ошибки разбора JSON
> А как?
Почитать внимательно офиц мануал по json_decode http://php.net/manual/ru/function.json-decode.php
У max-width есть специальное значение, обозначающее "отсутствие ограничений": https://developer.mozilla.org/ru/docs/Web/CSS/max-width#Values
(если сложно читать англ то можно найти это свойство в htmlbook и посмотреть там).
В таких случаях стоит всегда сначала лезть в мануал и проверять, нет ли у свойства какой-то нужной нам особенности.
> А почему лейбел не растягивается на ширину сместившихся влево инпута?
Понятия не имею. Видимо этот момент в спецификациях четко не описан и потому производители браузеров его реализуют как хотят. Но факт в том что у инлайн-элементов нет своих размеров. И overflow нельяз применять к ним.
> Если что, я умею читать английский.
Это же отлично. Тогда в случае совмнений можно первым делом лезть смотреть мануалы на MDN и читать спецификацию на w3.org. Она конечно довольно сложная, особенно поначалу, но может там найдется ответ на какой-то вопрос или какая-то полезная информация.
ну вот например эти разделы
https://www.w3.org/TR/REC-CSS2/visuren.html
https://www.w3.org/TR/REC-CSS2/visudet.html
я бы рекомендовал попробовать почитать. Так как в них по сути и описаны разные особенности позиционирования, которые мы изучаем в задачах.
>>834510
>>Сюда надо бы добавить подробности ошибки разбора JSON
> А как?
Почитать внимательно офиц мануал по json_decode http://php.net/manual/ru/function.json-decode.php
https://m.habrahabr.ru/post/309102/comments/#comment_9786844
Вот в очередной раз наткнулся: руби-приложение ничего не делает, а ест 840 Мб памяти и порождает кучу процессов. Ну зато не умирает после каждого запроса, как наш PHP. Может лучше бы было если умирало...
>У max-width есть специальное значение, обозначающее "отсутствие ограничений": https://developer.mozilla.org/ru/docs/Web/CSS/max-width#Values
Поменял значение у width: https://jsfiddle.net/tb7k9dw9/9/
Лучше с них и начинать, для меня описание некоторых вещей было намного понятней чем в статьях на русском. Видимо английский язык действительно больше подходит для изучения программирования.
Если больше нет замечаний значит ли то что всё остальное правильно?
https://dev.mysql.com/worklog/
вот пример задачи из него:
https://dev.mysql.com/worklog/task/?id=411
Как видите, все довольно подробно описано.
Понял, спс
Немогу разобраться с неймспейсами
Пишу
class ConfigException extends Exception { }
вижу
Fatal error: Class 'StudentList\Exceptions
\Exception' not found
Ну тут всё понятно
Пишу
class ConfigException extends \Exception { }
вижу
Fatal error: Uncaught Error: Class 'StudentList\Exeptions\ConfigException' not found
Почему?
http://hello-site.ru/share/ydNBvqu0mGQvrLt/
После поверхностного ознакомления:
- Роутеру URI должен приходить "сверху", роутер не должен знать, что запрашиваемый путь хранится в $_SERVER. Что если я захочу использовать роутер для консольного приложения, там ведь нет $_SERVER? Ну или просто хочу протестировать роутер искусственным запросом-строкой вроде "user/1", не запуская сервер.
- Валидировать реквест - не задача роутера.
- Нет внятного отчёта об ошибках, возвращение false означает, что роутер будет просто молча не работать, если пользователь что-то не так сделает. Что если я перепутаю и передам аргументы в последовательности не "контроллер-метод", а "метод-контроллер"? Мне нужно ковыряться в исходнике, чтобы понять как пользоваться твоим роутером, а я хочу установить его, попрописывать маршруты, их обработчики и всё.
- Не соблюдаются PSR 1-2 (оформление кода).
И чего сорс не на гитхабе, а на каком-то сомнительном сайте?
Алсо что за задание, прям так и сказали "велосипедируй роутер, плевать какой"? Может требования какие-то, типо плейсхолдеры, возможность использования коллбеков?
> Uncaught Error: Class 'StudentList\Exeptions\ConfigException'
МБ в этом дело? Алсо ты не показал composer.json
я спросил у них можно ли юзать готовый роутер или микрофреймворк, или нужно написать все самому, а мне ответили, что пиши сам все как ты это понимаешь, потом посмотрим.
Не знаю что с этим делать. правильно ли выбрал маршрут.
Но все же спасибо.
тобиш первым делом мне стоит заменить SERVER на GET и распаристь его parse_url()
-а вот с валидацией я так и не понял. если роутер принимает запрос, то валидацию лучше сделать в другом классе и просто воспользоваться методом валидации в роутере?
-насчет ошибок я тоже задумывался, но подумал что не знаю как лучше реализовать в случае ошибки писать exit('Код ошибки') ?
- код оформлю, спасибо.
>тобиш первым делом мне стоит заменить SERVER на GET и распаристь его parse_url()
Нет. Это не решит проблемы, ты меняешь один велосипед на другой. Очевидно тот анон писал тебе о том что URI в роутер должен передаваться аргументом, в конструктор например. А вообще, настоящим ООП подходом будет выбросить глобальные переменные типа $_SERVER и $_GET и использовать свою реализацию PSR-7. Но это наверное слишком сложно для тебя будет.
>насчет ошибок я тоже задумывался, но подумал что не знаю как лучше реализовать в случае ошибки писать exit('Код ошибки')
Писать exit при ошибке это неправильно, это не ООП подход. Если у тебя там стоит exit это на самом деле еще хуже чем возвращать false, потому что в таком случае при использовании твоего роутера скрипт может просто внезапно завершить работу, не дав программе сделать какие-то действия для обработки ошибки (записать её в лог, например). Лучшим решением будет бросать исключение с кодом и текстом ошибки.
С твоим уровнем познаний об ООП тебе конечно очень сложно на собеседовании придется.
Sure he is, sugar cube.
Анончики, как реализовать перекат на другие страницы не загружая заново сайт? Я так понимаю тут по скрину при переходе на другую страницу загружается все заново.
https://github.com/Phrlog/StudentList
https://www.youtube.com/watch?v=R-yQux1S63w
Вот очень хороший курс от Гикбрейнс.
Я сейчас на третьем занятии всего лишь, но уже научился устанавливать свои темы, работать с формами.
Короче, догоняй меня и пошли вместе.
Я дам тебе полезных советов от 32-летнего филолога и пришлю файл с шагами для установки той же темы - я это всё расписываю для удобства и логики (преподаватель часто скачет, чувствую себя афроамериканцем на этой вот гифке).
Пытаюсь регуляркой выделить предложения с preg_split, но почему-то выделяет только точки. Проверял на regex101, там правильно выделяет (пикрилейтед).
Что тут не так?
http://ideone.com/ijgnbQ
имеет смысл подготавливать запрос, в котором нет плейсхолдеров:
$sql = "SELECT FROM abiturients WHERE id = " . $id;
$sth = $this->dbh->prepare($sql);
Или можно сразу execute'ить?
$sth = $this->dbh->prepare("SELECT FROM abiturients WHERE id = " . $id);
Предложение до точки, не включая саму точку.
Алсо, уже разобрался - неправильно использовал preg_split. Через preg_match_all эта регулярка норм проходит и все работает как надо.
Логическая ошибка
Есть неплохой пак задач Regex Tuesday - http://callumacrae.github.io/regex-tuesday/ но я бы не назвал его хардкорным.
Только на английском, правда (русского перевода я не встречал).
Спасибо
$app['controllers'] - откуда мне знать, инстанс какого это класса и есть ли он там вообще? Также перестает работать автодополнение IDE.
Получить ID массивом
Очистить от повторений
Сравнить массив ID с ID в файле (если он есть. Если нет - создать его)
Оставить в массиве только те ID, которых нет в файле
Получить данные по этим ID
Разослать почту по полученным данным
Занести ID из массива в файл
*/
Есть такая задача. Стоит ли под каждый таск делать метод класса или это говнокод будет? Как правильно сделать по ООП
А где смысл-то? Буду бесконечно прописывать $class->method() (только одна чередность, никаких вариантов других нет, по сути логичнее в один метод все сунуть)? Я вот думаю сделать метод не под каждый таск а под каждое важное событие например вот: (каждый пропуск новый метод)
/
Получить ID массивом
Очистить от повторений
Сравнить массив ID с ID в файле (если он есть. Если нет - создать его)
Оставить в массиве только те ID, которых нет в файле
Получить данные по этим ID
Разослать почту по полученным данным
Занести ID из массива в файл
/
}
можно и так , просто всё в 1 не логично и трудоёмко что то менять , а так выходит ты можешь либо поменять 1 действие в цепочке , либо изменить его так что бы нечего не сломалось
Исправил, но реакция такая же.
Вот коммит с ошибкой специально переименован файл router.json : https://github.com/TheSidSpears/Students/commit/d92857aad12d721bb65543046656abb7acca2f27
Ну просто это простенький скрипт за 500 руб. Тут явно ничего не будет меняться.
имхо надо писать хорошо и продуманно везде , домашние проекты, скрипты по 500р etc как минимум можешь его добавить к себе в приват репу на хабе и давать работодателю по запросу
Бесплатные приватные репы, все модно и удобно. Норм если я проекты заказчиков там буду держать? В чем подвох?
ну я так и делаю , да и не только я , а и все остальные фирмы , за что пояснять то ?
$array = [
[<------------------------ второй столбец строчки на пике (11)
'name' => 'test',
'phone' => 'test', Это 3-и столбцы строчки по id 11
'email' => 'test',
'message' => 'test',
],
[<------------------------ второй столбец строчки на пике (будет другой ид, например 12)
'name' => 'test',
'phone' => 'test',
'email' => 'test', Это 3-и столбцы строчки по id (будет другой ид, например 12)
'message' => 'test',
],
['name' => 'test',
'phone' => 'test',
'email' => 'test',
'message' => 'test',
]
];
Помогите пожалуйста, всю голову сломал
Да. Учи PHP->ООП->MVC->yii2 (потом symfony2 и zend если захочешь)
Мелкий проект с говнофриланса примерно 20-30к. Делается неделю примерно
ну естессна востребовано и т.д
Сделал. Ебаный mysqli. Соснул какой раз уже у пдо.
Почему? Ведь в случае, когда я сам собираю запрос, подставляя переменные, метод prepare не поможет избежать SQL инъекцию, и я не собираюсь использовать запрос повторно? В чем тогда заключается необходимость использования?
А зачем ты переменную вставляешь прямо в запрос? используй плейсхолдеры.
И вообще, тебе бы конечно теорию не мешало сначала изучить, такое ощущение что ты не совсем понимаешь, что делает та или иная функция. Для начала можно например перечитать мануал по ним.
> А зачем ты переменную вставляешь прямо в запрос? используй плейсхолдеры.
Как быть с именем поля в выражении с ORDER BY? Ведь через плейсхолдеры можно подставлять только числа и строки, а имя полей нельзя.
В посте >>835962 подставляется именно переменная. Имена полей - другое дело, их приходится подставлять напрямую после проверки.
>>836483
Но если выражение $sql = "SELECT '*' FROM abiturients ORDER BY {$colum}", зачем использовать prepare?
В мануале написано, что он нужен для того чтобы избежать sql инъекций, и для того чтобы оптимизировать многократное повторение запроса с разными переменными. Но я не собираюсь использовать выражение повторно, и проверяю значение переменной самостоятельно, т.к. не использую плейсхолдер, в данном случае.
>>819083
>> Следует ли её переименовать в связи тем что она теперь может получать не всех студентов?
>Можно просто назвать getStudents или FindStudents.
>
>>>$limit = 2147483647
>> В мануале по mysql сказано о том что если нужно получить все записи, то нужно указать какое-то большое число
>А ты его по памяти написать сможешь когда тебе будут нужны все студенты? лучше сделать условие что если $limit = 0 то конструкция LIMIT не вставляется. Правда, это не даст нам указывать OFFSET без LIMIT, но это почти никогда и не нужно.
>
>> Для борьбы с sql инъекциями, я решил что удобней будет делать проверку на разрешенные колонки в методе получения запроса, чем в методе выполнения этого запроса. Так ведь тоже можно?
>Это неправилньо. Вот я смотрю на код функции getAllStudents() https://github.com/someApprentice/Students/blob/master/app/Model/Gateway/StudentGateway.php#L10 и хочу проверить, безопасна ли она.
Исправлена функция getAllStudents: https://github.com/someApprentice/Students/commit/363d244b1ded5a4801b03053e0e3538192a094e3 полное описание коммита внутри
>>819083
>> extract($this->getSortQuery());
>Вот это плохая идея, так как глядя на код непонятно какие переменные создаются. Где гарантия что эта строчка не перезапишет одну из переменных, которая определена выше? ну например я правлю твой код и хочу добавить переменную, как мне выбрать ее имя чтобы она не конфликтовала с теми, что создаются в этой строчке? А что если завтра кто-то добавит в возвращаемый getSortQuery() массив новое поле и оно по имени будет совпадать с одной из переменных? Получается мы можем сломать контроллер, добавив новое поле в массив совсем в другом месте.
Исправлено: https://github.com/someApprentice/Students/commit/c6f3e8282b0a190e97f86b460cf60485eac81f45
>>819083
>Также, у тебя в контроллере стоит 4 вызова render. Ты выносишь знание о структуре страницы в контроллер, но удобнее просто подключить один шаблон, а он уже пусть подключает что ему нужно.
Исправлено: https://github.com/someApprentice/Students/commit/54c05e525d22b9136ab3cf4d2e8df52574359d2c
>>819083
>> Следует ли её переименовать в связи тем что она теперь может получать не всех студентов?
>Можно просто назвать getStudents или FindStudents.
>
>>>$limit = 2147483647
>> В мануале по mysql сказано о том что если нужно получить все записи, то нужно указать какое-то большое число
>А ты его по памяти написать сможешь когда тебе будут нужны все студенты? лучше сделать условие что если $limit = 0 то конструкция LIMIT не вставляется. Правда, это не даст нам указывать OFFSET без LIMIT, но это почти никогда и не нужно.
>
>> Для борьбы с sql инъекциями, я решил что удобней будет делать проверку на разрешенные колонки в методе получения запроса, чем в методе выполнения этого запроса. Так ведь тоже можно?
>Это неправилньо. Вот я смотрю на код функции getAllStudents() https://github.com/someApprentice/Students/blob/master/app/Model/Gateway/StudentGateway.php#L10 и хочу проверить, безопасна ли она.
Исправлена функция getAllStudents: https://github.com/someApprentice/Students/commit/363d244b1ded5a4801b03053e0e3538192a094e3 полное описание коммита внутри
>>819083
>> extract($this->getSortQuery());
>Вот это плохая идея, так как глядя на код непонятно какие переменные создаются. Где гарантия что эта строчка не перезапишет одну из переменных, которая определена выше? ну например я правлю твой код и хочу добавить переменную, как мне выбрать ее имя чтобы она не конфликтовала с теми, что создаются в этой строчке? А что если завтра кто-то добавит в возвращаемый getSortQuery() массив новое поле и оно по имени будет совпадать с одной из переменных? Получается мы можем сломать контроллер, добавив новое поле в массив совсем в другом месте.
Исправлено: https://github.com/someApprentice/Students/commit/c6f3e8282b0a190e97f86b460cf60485eac81f45
>>819083
>Также, у тебя в контроллере стоит 4 вызова render. Ты выносишь знание о структуре страницы в контроллер, но удобнее просто подключить один шаблон, а он уже пусть подключает что ему нужно.
Исправлено: https://github.com/someApprentice/Students/commit/54c05e525d22b9136ab3cf4d2e8df52574359d2c
Ок, видимо можно обойтись без prepare в подобных случаях.
Видать лучше оставить как есть, пусть не так красивенько и современно, зато практичней
Я симлинки бросал
>Неправильный путь: всегда использовать объектно ориентированное программирование
>Неправильный путь: следовать стандартам PHP-FIG, за исключением PSR-1 и PSR-2
Я сам явапримат но есть фреланс где я одмин апсерверов и дба по совместительству.
Так вот. Бакенд на yii2.
Задача сейчас в разнесении субд и аппсервера.
Ну по своей макакопривычке спрашиваю что у вас там с кенекшен пулерами кешами препаред стейтментов и прочего. В итогепосле обьяснений что это я узнаю что yii2 не может в конекшен пулинг. И самое главное цитата:
@cebe is right in that opening database connections is usually a very cheap operation. Doesn't say there is no room for improvement
Посаны у нас в яве за такое обоссывают рили.
Вопрос поясните как понизить/не повысить латенси в yii2 и/или пхп при разнесении бэкенда и субд.
Пулы нужны были для тех БД у которых медленный процесс установки соединения. С пулами не все так просто, так как например процесс может изменить какие-то глобальные настройки соединения и потом это вызовет ошибки в другом месте.
То есть пул оправдан когда время установки нового соединения заметно в сравнении с другии частями обработки запроса.
Та же mysql очень быстро устанавливает соединение и по идее там пул и не нужен. Для ограничения числа соединений есть настройки в mysql.
Вопрос немного в другом.
При разнесении серверов из-за сетевой составляющей растет латенси. Как ее снизить в случае с пхп/юии2.
Как с помощью регэкспов найти подстроку где есть '123 ... 321', но обязательно между ними нет '666'.
Проблема в том, что это подстрока, и я не могу понять как указать "сначала 111, потом что угодно кроме 666, потом 321" в части "потом что угодно кроме 666". Для односимвольных подходит [^6]*, а тут подстрока.
данные
111олпорао555ролр321 -> подходит
111олпорао666ролр321 -> нет
Про загрузку файлов в Slim ( >>820849 )
Я практически ничего не вынес из твоего ответа, поэтому начнём с малого.
Есть код http://ideone.com/B77xqx . Из него нужно убрать $this->view->render($response, 'download.html', $args), потому что мне вообще не нужно никакую web-страницу возвращать. во всех примерах в get в return прописано рендерить страницу, и я не знаю как туда можно прописать что-то еще
А возвращать он должен файл (бог с ней, с закачкой пока)
Как?
Лол, нет. Вместо букв может быть реально что угодно, а вместо 666 конкретная подстрока (например, </locations>).
Кстати, а может просто использовать <a download>? http://htmlbook.ru/html/a/download
Другое...
Есть ссылка при нажатии на которую заполняется форма: <a onclick="document.getElementById('parentId').value={{ comment.comment.getId() }}">
Проблема, что так она отображается как текст, но если добавить href='#', то как ссылка, но теперь скачет при нажатии на верх экрана. Что прописать в href, чтобы никак не реагировало и никуда не переходило?
>>837061
https://regex101.com/r/jL3gG0/2
Вот конкретика. Нужно найти записи item, где есть остатки в точке 2703, и проигнорить те где их нет.
>а вместо 666 конкретная подстрока (например, </locations>)
Сразу напиши все условия, и пример конкретный, и для чего это тебе нужно?
Это неплохо, но я решаю задачу на поиск через regexp (хотя бы потому, что это - куча лог-файлов, по которым я ищу grep-ом).
Что-то мне не верится, что она не решаема стандартными условиями.
Пик 1. Первые 2 подстроки матчатся, а 3-я нет, так как содержит 666.
>>837070
> остатки в точке 2703
Сам понял?
<item (\w="(\w[:\-\/\^])\w" ?)><locations><location id="2703">(<\w>\w\.?\w<\/\w>)<expected_date\/><\/location><\/locations><\/item>
Закрывающие теги переводить лень
А как ее снижают в случае Явы? Я сомневаюсь, что тут есть какая-то разница и что ты можешь компенсировать задержку в передаче данных. да и времена там должны быть меньше миллисекунды если сервера в одном дата центре.
> о всех примерах в get в return прописано рендерить страницу
Надо не только смотреть примеры, а надо разбираться как именно работает фреймворк. Ты не обязан вызывать render, и нигде такого не написано. Обработчик просто должен сформировать HTTP-ответ в объекте $response и вернуть его.
Также, надо немного представлять как работает протокол HTTP.
Вот теперь подумай, какой HTTP-ответ должен получиться и как его сформировать в объекте $response. То есть надо задать нужные заголовки (как минимум задающие тип содержимого и то, что это файл для скачивания), а в качестве тела ответа передать содержимое файла.
Изучи код или документацию объекта Response в Слиме.
В моем посте было написано несколько вариантов, как это сделать, начиная с наименее оптимального способа.
Есть есть еще какие-то конкретные вопросы, задавай.
>>837066
Нет, полезнее разобраться в протоколе HTTP и том как работает фреймворк. Плюс этот атрибут download еще в старых браузерах не работает вроде бы.
1. html <table>
2. css display:table
3. css display:inline-block || block
4. css display:flex
5. bootstrap col-xs-6 etc
>1. html <table>
Для таблиц исключительно. Например, таблица с параметрами товара, где в первой колонке - название параметра, во второй - сам параметр. Или прайс-лист, например.
>2. css display:table
Норм, но всё равно не то. Можно использовать, но есть несколько подводных камней. Обходятся на stackoverflow
>3. css display:inline-block || block
Это уже какие-то хаки.
>4. css display:flex
Норм. Есть много подводных камней. Так себе поддерживается в MSIE 9. Особый мирок в css (скорее, комнатка)
>5. bootstrap col-xs-6 etc
Норм, но есть много ограничений (макет должен учитывать bootstrap не реализуешь в контексте bootstrap колонку шириной в 14.88%, например). Если освоить, то можно делать красивые адаптивные странички очень быстро. Для посвященного только в css выглядит как магия.
ОП же в этом треде обитает? Хотелось бы проверки студентов.
За счет коннекшен пулов. За счет дистрибьютед кешей.
Так вот я пробовал.
Если просто прописывают в настройках юии новый айпи то латенси стает нереальная. Запросы по 0.4 с. Как они там профилируют я хз
Ты что хтмл или хмл собрался регекспами парсить лол?
ОП посмотри, пожалуйста, моих студентов. Мне кажется, что все очень плохо. Направь меня на путь истинный.
https://github.com/anotherCodeMunkey/studentsList
В settings.php настройки для подключения к базе данных.
Я не ОП, но:
Пагинацию лучше делать через модель и лимит/оффсет прямо в запросе, если я правильно понял у тебя для каждой страницы запрашиваются ВСЕ студенты из базы.
Что у тебя в классе Pager делают студенты?
Зачем столько использований сессий? Можно же вполне без них обойтись.
Есть повторения в коде, например account.php login.php main.phpregister.php search.php имеют одну шапку, футер. Сделай base.php и в него инклудь контент.
Буду благодарен, если так же на мой код взглянешь, я >>835694 анон. Может вместе лучше сделаем.
http://arhivach.org/thread/193343/#835046 - задача про кредит
http://arhivach.org/thread/193343/#835049 - ООО Вектор с антикризисными мерами
http://arhivach.org/thread/193343/#835052 - палиндром
http://arhivach.org/thread/193343/#835053 - поиск ошибок в тексте
> Ответ на любой вопрос
Верно
> Генератор имён готовый
Правильно.
> В тылу врага - шифровка
Тут тоже верно
> 1337 - шифровка
Все правильно.
> На словах ты Лев Толстой
Верно.
> Разбиение строки на символы
А тут вроде ничего не разбивается. Забыл сохранить?
> Палиндром
> $textLow = mb_strtolower($text); //переводим всё в нижний регистр
> $textLowTogether = str_replace(' ','', $textLow);
Тут незачем заводить новые переменные, логично класть результат назад в $text
Увы, программа не всегда определяет результат правильно: http://ideone.com/HBN7W4 - она считает слово палиндромом, если последние сравниваемые символы (в центре) совпадают.
> Айпад в кредит
Ты скопировал код 3 раза. Но идея функций в том что ты можешь один раз написать функцию, а затем вызвать ее с 3 разными аргументами (с разным процентом, комиссией и тд). То есть решение неправильное.
> Ответ на любой вопрос
Верно
> Генератор имён готовый
Правильно.
> В тылу врага - шифровка
Тут тоже верно
> 1337 - шифровка
Все правильно.
> На словах ты Лев Толстой
Верно.
> Разбиение строки на символы
А тут вроде ничего не разбивается. Забыл сохранить?
> Палиндром
> $textLow = mb_strtolower($text); //переводим всё в нижний регистр
> $textLowTogether = str_replace(' ','', $textLow);
Тут незачем заводить новые переменные, логично класть результат назад в $text
Увы, программа не всегда определяет результат правильно: http://ideone.com/HBN7W4 - она считает слово палиндромом, если последние сравниваемые символы (в центре) совпадают.
> Айпад в кредит
Ты скопировал код 3 раза. Но идея функций в том что ты можешь один раз написать функцию, а затем вызвать ее с 3 разными аргументами (с разным процентом, комиссией и тд). То есть решение неправильное.
>>823666
> Сначала не совсем понимал как сделать ответы к вопросом
Вопросы бывают разных типов и содержат разные наборы полей - это типичный случай наследования таблиц. У Фаулера описаны 3 паттерна, 3 способа реализации наследовния: Single Table Inheritance, Class Ti, Concrete TI. Советую погуглить и изучить. Сравнить преимущества и недостатки, выбрать более удобный.
Также надо убедиться что выбранный тобой фреймворк или библиотека поддерживает схему.
Также желательно выбирать такую схему, которая защищает от вставки неправильных данных.
Кстати, ответы на вопросы тоже могут получиться разных типов.
> Таблица attemts сделана для того чтобы зарегистрированные пользователи не могли бесконечно проходить тест, каждому тесту можно задать максимальное количество раз пересдачи. Тут я не уверен, нужен ли такой функционал вообще?
Нужен так как вроде там где-то в статистике выводится информация по числу пересдач
> И как быть с незарегистрированными пользователями?
Можно выдавать им идентификационную куку и идентифицировать по ней. Можно например создать "анонимного" пользователя, который отличается от натоящего только отсутствием части данных. Если пользователь зарегистрируется то надо сохранять его результаты в аккаунте. В случае "анонимного" пользователя регистрация просто добавляет ему имя и пароль.
> Для поиска по тестам хочу Sphinx взять, он получше и быстрее обычного поиска через LIKE.
Можно попробовать, почему бы и нет. У меня был урок по sphinx: https://gist.github.com/codedokode/10539366
> Про индексы знаю, тут они нужны будут в основном для внешних ключей?
надо смотреть еще, какие запросы будут делаться, по каким условиям записи выбираться. Так трудно сказать. Во многих случаях, да, как раз внешний ключ подойдет - напимер выбрать все вопросы по id теста.
Вот кстати, ты используешь PostresQL, а там есть полезная конструкция CHECK которая позволяет добавлять на колонки и их сочетания произвольные условия для проверки (например что число должно быть четное или что сумма двух колонок должна быть меньше третьей). Советую пройтись и поставить такие условия, где это возможно. Так ты защитишься от неправильных данных в БД.
> password character varying(500) NOT NULL,
зачем 500 символов? И ты надеюсь сам пароль не собираешься хранить?
> FOREIGN KEY (author_id) REFERENCES users (id)
А там не надо писать ON UPDATE/ON DELETE?
> test_id integer NOT NULL,
Тут внешнего ключа не хватает
> CREATE TABLE answers (
Тут нет внешних ключей
>>823666
> Сначала не совсем понимал как сделать ответы к вопросом
Вопросы бывают разных типов и содержат разные наборы полей - это типичный случай наследования таблиц. У Фаулера описаны 3 паттерна, 3 способа реализации наследовния: Single Table Inheritance, Class Ti, Concrete TI. Советую погуглить и изучить. Сравнить преимущества и недостатки, выбрать более удобный.
Также надо убедиться что выбранный тобой фреймворк или библиотека поддерживает схему.
Также желательно выбирать такую схему, которая защищает от вставки неправильных данных.
Кстати, ответы на вопросы тоже могут получиться разных типов.
> Таблица attemts сделана для того чтобы зарегистрированные пользователи не могли бесконечно проходить тест, каждому тесту можно задать максимальное количество раз пересдачи. Тут я не уверен, нужен ли такой функционал вообще?
Нужен так как вроде там где-то в статистике выводится информация по числу пересдач
> И как быть с незарегистрированными пользователями?
Можно выдавать им идентификационную куку и идентифицировать по ней. Можно например создать "анонимного" пользователя, который отличается от натоящего только отсутствием части данных. Если пользователь зарегистрируется то надо сохранять его результаты в аккаунте. В случае "анонимного" пользователя регистрация просто добавляет ему имя и пароль.
> Для поиска по тестам хочу Sphinx взять, он получше и быстрее обычного поиска через LIKE.
Можно попробовать, почему бы и нет. У меня был урок по sphinx: https://gist.github.com/codedokode/10539366
> Про индексы знаю, тут они нужны будут в основном для внешних ключей?
надо смотреть еще, какие запросы будут делаться, по каким условиям записи выбираться. Так трудно сказать. Во многих случаях, да, как раз внешний ключ подойдет - напимер выбрать все вопросы по id теста.
Вот кстати, ты используешь PostresQL, а там есть полезная конструкция CHECK которая позволяет добавлять на колонки и их сочетания произвольные условия для проверки (например что число должно быть четное или что сумма двух колонок должна быть меньше третьей). Советую пройтись и поставить такие условия, где это возможно. Так ты защитишься от неправильных данных в БД.
> password character varying(500) NOT NULL,
зачем 500 символов? И ты надеюсь сам пароль не собираешься хранить?
> FOREIGN KEY (author_id) REFERENCES users (id)
А там не надо писать ON UPDATE/ON DELETE?
> test_id integer NOT NULL,
Тут внешнего ключа не хватает
> CREATE TABLE answers (
Тут нет внешних ключей
>Буду благодарен, если так же на мой код взглянешь
Ну я еще не настолько шарю, чтобы давать какие-то советы по улучшению кода, поэтому просмотрев твой код я ничего не могу тебе посоветовать, лол.
Насчет сессий распиши поподробней как без них можно обойтись.
-----------------
->site_folder
-->classes // папка для классов и в ней сами классы
--Class1.class.php
--Class2.class.php
-->assets // css, js библиотеки
--jquery.js
--bootstrap.js
-->scripts // папка для ява-скриптов
--script1.js
--script2.js
-->templates // папка для шаблонов
--base.template.html
--index.template.html
--footer.template.html
// тут подключаем шаблон, нужные классы и пишем код, в шаблоны только выводим данные
index.php
Я не ОП и не тот анон, но это первое что бросилось в глаза:
__autoload устарел и скоро будет deprecated, вместо неё лучше использовать spl_autoload_register http://php.net/manual/en/function.spl-autoload-register.php или автозагрузку по стандарту PSR-4 (к которой уже есть готовые автозагрузчики, например тот что предоставляет composer).
https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L155
>if ($_POST) {
Наверное лучше и надежнее будет проверять метод через $_SERVER['REQUEST_METHOD'].
Выборку и поиск студентов можно объединить в один запрос, если искать по соединенным через пробел столбцам.
Не понятно зачем ты в контроллере смешал верстку и текст ошибок, вот тут https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L121
Это для того чтобы выводить сообщения об ошибке\успехе в одном месте? Мне кажется это не задача контроллера расставлять классы в HTML тегах.
После отправки заголовков Location лучше завершать функцию через return, она все равно ничего полезного уже не сделает.
Валидатор сделан не очень удобно, функция валидации не возвращает результат, и с первого взгляда можно не понять как его использовать. Мне кажется ты переусложняешь и ограничиваешь валидатор тем, что в аргументах передаешь ему тип формы register или edit.
https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L189
А что если я выберу пол, но руками в разметке поменяю значение инпута на любую рандомную строку? Я правильно понимаю что у меня получится отправить форму без ошибок? То же самое с location.
https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L97 Вот эта функция явно часть представления, никак не валидации.
Там еще проблемы есть, но я думаю что ОП получше объяснит.
Бамп вопросу.
Как понизить латенси связи с базой в пхп юии2.
Не верю что не придумали в пхп конекшен пулинга.
Или мнение создателя юии2 о том что создание конекшенов это легковесная операция является догмой в мирке пхп?
Про объединение выборки и поиска в один метод ты прав, да, так же можно объединить методы index и search в контроллере.
Верстку уберу и просто сообщение об ошибке оставлю, но не думаю что от этого мое решение будет более элегантным, как лучше сделать не знаю пока.
Валидатор параметром register или edit никак не ограничивается, это просто наскоро сделанный костыль лол. Проверяется только в этом месте: $method == 'register' ? $this->validateEmail($student->email) : ''; Но нужно что-нибудь другое придумать.
С полом и location ты прав, нужно исправить.
Насчет метода applyTags в классе Validator - он используется только там, и причем довольно часто, не думаю что будет рационально относить его к другому классу.
Энивей, спасибо что посмотрел, приму к сведению.
>>826200
> >>824851
> ОП, проверь пожалуйста, вроде все исправил.
>>829509
> Доделываю студентов осталась пагинация и мне кажется, что все очень плохо. ОП, что я делаю не так? нужны твои советы.
Не хочется в чатик превращать, напиши в телеграмм если есть @phrlog
У тебя все свалено в публичную папку. Также, название classes довольно бессмысленное, и писать .class. в имени файла не приянято - погугли PSR-4 или почитай мой урок по нему.
Советую почитать комментарии из задачи про студентов и делать как там написано.
>>837504
Ты по моему не прочитал то, о чем я писал. Получиться 0.4с только из-за переноса базы на соседний сервер вряд ли может (если только у тебя там не 10 000 запросов). Причина в другом. Начни с просмотра разных топов, также можно посмотреть отладочный вывод Юи который пишет время выполнения каждого запроса.
Коннекшен пулы тут вообще не при чем.
> Или мнение создателя юии2 о том что создание конекшенов это легковесная операция является догмой в мирке пхп?
Сделай тест и померяй. На одном хайлоад сайте мы прекрасно жили без всяких пулов. Скорее медленный коннект это реальность всяких дорогих ентерпрайзных баз данных от известных компаний.
Но если тебе так надо усложнить себе жизнь то в php есть persistent connect.
ОП, тебе огромное спасибо! Я помню твой первый тред в Б и далее. По ним начинал заниматься - дошел до регекспов кажется.
Благодаря также твоим тредам понял что пр это легко. Сейчас я успешный джава программист 300кккк в наносекунду. Еще раз Спасибо!
Передавать строки edit/register плохо. Вместо них надо использовать либо константы либо true/false.
привязывать валидатор к форме (а register это название формы) тоже не очень соответствует MVC. ПРаивльнее переименовать в create_user/update_user.
> Валидатор параметром register или edit никак не ограничивается, это просто наскоро сделанный костыль лол. Проверяется только в этом месте: $method == 'register' ? $this->validateEmail($student->email) : ''; Но нужно что-нибудь другое придумать.
А при редактировании email не надо проверять на уникальность?
И анон тебе все верно написал, HTML кода в контроллере быть не должно.
Использование класса вроде App или Core мне всегда не очень нравилось. В твоем случае класс App должен назваться Front Controller.
https://github.com/Phrlog/StudentList/blob/master/app/App.php#L18
не очень хорошо писать код, выполняющий какое-то действие, прямо в конструкторе. Задача конструктора только подготовить объект к работе, а не выполнять ее. Не стоит исплоьзовать объект как функцию - вместо этого проще использовать настоящую функцию или статический метод.
https://github.com/Phrlog/StudentList/blob/master/app/App.php#L24
Что если нужного класса или метода не найдется?
Я тут подумал, надо бы начать собирать такие отзывы, а то часто люди приходят, спрашивают чего можно добиться, есть ли смысл итд.
мануал: json_encode, json_decode, погугли про сам формат
Код на PDO сам по себе получается довольно большой:
$s = $pdo->prepare(...);
$s->bind(....);
$s->execute(...);
$result = $s->fetchAll(...);
Если библиотека позволяет это записать например в виде
$result = $lib->fetchAll("SELECT ???", [$a, $b, $c]);
То в принцип она полезна. Но лучше искать не малоизвестную, а какую-нибудь популярную библиотеку, которую поддерживают и развивают, исправляют баги, пишут документацию.
Насчет safemysql - напоминает другую малоизвестную библиотек DbSimple. То есть проблема просто в том, что про них никто особо не знает, у них может быть мало документации и некому чинить в них баги. В общем, применять можно, но на свой страх и риск.
Из известных есть Doctrine DBAL, вот пример, как в ней делается запрос: http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#fetcharray
Также ты можешь унаследовать ее и добавить свои функции.
Если тебе интересны другие библиотеки, можно поискать их тут
http://phptrends.com/
http://pronskiy.com/php-digest/
Слова для поиска: db, dbal, database, pdo
> Причем в нижнем варианте, при отправке запроса не появляется ошибка, просто не выполняется сортировка
Если ты посдставляешь имя колонки через плейсхолдер по типу "ORDER BY ? " то оно подставляется в кавычках и воспринимается как строка, а не имя поля. Так как эта строка одинаковая дял всех записей то она никак не влияет на порядок сортировки.
Я тут подумал, надо бы начать собирать такие отзывы, а то часто люди приходят, спрашивают чего можно добиться, есть ли смысл итд.
мануал: json_encode, json_decode, погугли про сам формат
Код на PDO сам по себе получается довольно большой:
$s = $pdo->prepare(...);
$s->bind(....);
$s->execute(...);
$result = $s->fetchAll(...);
Если библиотека позволяет это записать например в виде
$result = $lib->fetchAll("SELECT ???", [$a, $b, $c]);
То в принцип она полезна. Но лучше искать не малоизвестную, а какую-нибудь популярную библиотеку, которую поддерживают и развивают, исправляют баги, пишут документацию.
Насчет safemysql - напоминает другую малоизвестную библиотек DbSimple. То есть проблема просто в том, что про них никто особо не знает, у них может быть мало документации и некому чинить в них баги. В общем, применять можно, но на свой страх и риск.
Из известных есть Doctrine DBAL, вот пример, как в ней делается запрос: http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#fetcharray
Также ты можешь унаследовать ее и добавить свои функции.
Если тебе интересны другие библиотеки, можно поискать их тут
http://phptrends.com/
http://pronskiy.com/php-digest/
Слова для поиска: db, dbal, database, pdo
> Причем в нижнем варианте, при отправке запроса не появляется ошибка, просто не выполняется сортировка
Если ты посдставляешь имя колонки через плейсхолдер по типу "ORDER BY ? " то оно подставляется в кавычках и воспринимается как строка, а не имя поля. Так как эта строка одинаковая дял всех записей то она никак не влияет на порядок сортировки.
Чтобы не городить много ифов, можно как-то попробовать разбивать код на функции или переставить команды местами.
> когда подгружаем список студентов, нужно еще отслеживать сортировку
Получается да, надо
> А еще получается, что нужно проверять авторизацию пользователя в обоих контроллерах index.php и register.php.
Возможно, что да.
> И каждый раз, как обновляется страница, например при сортировке. Значить дублируется код, и + запрос в базу каждое обновление страницы.
Дублирование кода решается вынесением в функцию. запрос к базе, думаю, не проблема.
> Нужно ли действительно проверять авторизацию пользователя в каждом контроллере?
Если тебе это нужно для чего-то, то да.
> Как избавиться от запроса в базу при каждый раз при обновлении страницы?
Не избавляться. Некоторые испльзуют сессию как кеш, но база сама умеет кешировать запросы, тем более такие простые.
> Как обойти трехуровневую вложенность if?
Попробовать переставить код или вынести что-то в функцию. Если не получитя, то оставить вложенность.
> $2 и $4 заключены в квадратные скобки, но значения в них могут быть пустыми и в этом случае в тексте будут появлятся лишние "[]". Поэтому, я решил после этой строки вставить чистку пустых квадратных скобок, но мне кажется, есть, наверное, более красивое решение этого.
Может быть можно было бы как-то через "или" сделать, по типу preg_replace("/([a-z])([а-я])|([а-я])([a-z])/", '$1$2[$3$4]', ...) - тогда пустых скобок бы не было.
Чтобы не городить много ифов, можно как-то попробовать разбивать код на функции или переставить команды местами.
> когда подгружаем список студентов, нужно еще отслеживать сортировку
Получается да, надо
> А еще получается, что нужно проверять авторизацию пользователя в обоих контроллерах index.php и register.php.
Возможно, что да.
> И каждый раз, как обновляется страница, например при сортировке. Значить дублируется код, и + запрос в базу каждое обновление страницы.
Дублирование кода решается вынесением в функцию. запрос к базе, думаю, не проблема.
> Нужно ли действительно проверять авторизацию пользователя в каждом контроллере?
Если тебе это нужно для чего-то, то да.
> Как избавиться от запроса в базу при каждый раз при обновлении страницы?
Не избавляться. Некоторые испльзуют сессию как кеш, но база сама умеет кешировать запросы, тем более такие простые.
> Как обойти трехуровневую вложенность if?
Попробовать переставить код или вынести что-то в функцию. Если не получитя, то оставить вложенность.
> $2 и $4 заключены в квадратные скобки, но значения в них могут быть пустыми и в этом случае в тексте будут появлятся лишние "[]". Поэтому, я решил после этой строки вставить чистку пустых квадратных скобок, но мне кажется, есть, наверное, более красивое решение этого.
Может быть можно было бы как-то через "или" сделать, по типу preg_replace("/([a-z])([а-я])|([а-я])([a-z])/", '$1$2[$3$4]', ...) - тогда пустых скобок бы не было.
Да - собирай. Ты еще по емейл помню отвечал на вопросы. Ну и я еще джавараша навернул помнится. Так лень туда постить свою сукцсесс историю. Со 2го раза поступил на курсы ЕПАМа. Они хорошо учат, хотя относятся как к говну.
Точно, про проверку при обновлении я и забыл.
Если не найдется нужного метода, там указан index по умолчанию: public $method = 'index';
А такой метод в валидаторе это нормально? https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L97
Если нет, то даже не знаю куда его отнести, во View не очень хочется, тем более придется инджектить чтобы в валидаторе использовать.
А если не найдется контроллера? Вообще странная логика, для несуществующих страниц надо отдавать 404.
Непраивльно. Валидатор вообще никакого отношения к View иметь не должен. И к HTML тегам. Его задача проверять, а не выводить надписи.
Проебать гет на такое гавно....
>Людям не нужны фреймворки общего назначения. Ни у кого нет общих проблем, каждый пытается решать очень специфические проблемы.
Что, простите? Из крайности в крайность. То есть их петушиный Zend нужен был для чего-то, а остальные фреймворки не нужны?
Сделал бы сам нормальный и удобный фреймворк - не было бы разговора.
Javalab EPAM.
А что там понимать? Метод getBody() возвращает какой-то объект, который в свою очередь содержит метод write().
Посмотрите, помогите советом. http://ideone.com/1KAdTb
Пробую реализовать это
> Или пойти еще дальше и настроить правила в .htaccess так, чтобы файл отдавался по ссылке сразу вообще без обращения к PHP.
В папке files создал .htaccess со строкой ForceType application/octet-stream , но файл всё-равно открывается в браузере, а не загружается апач перезагружал В чем может быть дело?
Если таки не должна, значит куки ей передает контроллер, он же получает от Аутенфикатора новое значение куки и устанавливает их. Толстоватый контроллер получается, нет?
Закончи колледж только, мой тебе совет. Потом устраивайся джуном и на заочку в вузик.
А ты проверь в браузере в отладчике (Ctrl + Shift + I), с какими заголовками он отдается.
Да и отдавать только одним этим правилом вряд ли получится. Мы ведь в целях безопасности переименовываем часть файлов.
Чтобы стать программистом, нужно родиться с мозгами заточенными на это. Лучше даже не пытайся
Я пиздец застрял на этом говне, сажусь на 10 минут каждый день, пишу нихуя, опускаются руки и я возвращаюсь к игорам.
Может кто мне помочь с этим? Я вообще прекрасно понимаю логику всего что я хочу, прекрасно понимаю как и что должно быть. Но я нифига не могу представить как это всё оформить в ооп-стиле. Я аж плачу, может мне кто-нибудь помочь?
Тебе написали про класс Front Controller и роутер. Про первый есть инфа в книге Мэтт Зандстра из оп поста.
Если тебе вообще сложно с ооп - у опа есть урок и несколько задач на эту тему, рекомендую.
Выполение (просто прохождение всех assert'ов): https://jsfiddle.net/t23j9k14/
В виде гиста читабельней: https://gist.github.com/anonymous/43a22302ac120787ec490fb177e74435
Иду строго по Кантору, до прототипов ещё не дошёл. Приватность полей и методов организовываю с помощью var/function declaration в объекте.
Ещё в JS из коробки нет expectations (есть в PHP 7), поэтому проверка на выброшенное исключение у меня странноватая.
> HamburgerException: invalid size 'TOPPING_SAUCE'
Вот такого нет, так как вместо SIZE можно в теории передать что угодно, хоть пустой объект. Поэтому у меня "Invalid size. Only ... are available."
> Цена или калории не являются свойствами так как они вычисляются из других свойств и хранить их не надо.
А по-моему это помогло уменьшить код. Вычисление из других свойств требует больше действий.
Легко. Можешь в колледж даже не ходить, сразу изучать программирование. Но всё равно числиться в колледже лучше чем нет.
Кто-то ранее спрашивал, как удобно можно для вывести строку с посимвольной индексацией, так вот, если еще кому-то нужно, я пользуюсь такой:
https://ideone.com/tY3iMe
Мне это немного помогает во время отладки строковых методов.
Алсо, если нужно вызвать str_echo более одного раз в коде, то следует вынести ___my_getspace в код отдельно от str_echo.
дорогой анончик, помоги, направь в нужную сторону. Есть апач2, есть убунту 14.04, PHP 5.5.9-1. PHP работает все нормально пока я не сделал простую форму и не нажал кнопку сабмит. Как видно из скрина - в адресной строке появляется пхп файл обработчик, но ничего не происходит. Скрины кода прилагаются
Это значит что все работает, твой код записывает данные в файл, но не выводит их на экран. Разобрался бы сначала в том что скопипастил.
HTTP/1.1 302 Found
Но почему? С переименовыванем я еще разберусь, хочу понять почему ForceType не работает
Нет, а Content-Type у отдаваемого содержимого (которое открывается в браузере вместо скачивания) какой? Тебе по моему надо для начала разобраться в отладчике в браузере и изучить внимательно заголовки HTTP-ответа.
Ну или ты можешь взять какую-то программу, позволяющую отправлять произвольные HTTP запросы, и ей отправлять запрос на получение файла и смотреть что придет в ответ.
Без этой информации ответить на твой вопрос трудно. Надо смотреть что именно отдает сервер, а не гадать почему что-то не работает.
http://ideone.com/CvrlHo
Ну, а теперь сделай еще запрос к URL, указанному в Location и проверь его заголовки. Редирект ведь запускает новый HTTP запрос.
Также, я вижу ошибку - в URL содержится пробел (запрещено). Кстати еще не должно быть русских букв (в заголовке нельзя использовать не-латиницу). При составлении URL запрещенные символы надо кодировать процентным кодированием (и при этом не закодировать случайно слеш).
Прочитай-ка урок про URL https://github.com/codedokode/pasta/blob/master/network/urls.md
Алгоритмы сортировки https://www.google.ru/search?q=алогритмы+сортировки&newwindow=1&gbv=1&sei=xErTV_HxBemD6QThn634Dw
Большое О: http://aliev.me/runestone/AlgorithmAnalysis/BigONotation.html
Если кратко - большое О показывает, как изменится время выполнения или расход памяти при изменении объема входных данных. Ну например функция count() в PHP имеет сложность O(1) то есть время выполнения не зависит от размера массива, а in_array - O(N), то есть на в 10 раз большем массиве она будет работать в 10 раз дольше.
Алгоритмы вообще: http://aliev.me/runestone/index.html
Если этого мало, почитай еще SICP: http://newstar.rinet.ru/~goga/sicp/sicp.pdf
Регулярку удобно тестировать на regex101, если что
> [(]?\\s?\\d
Это подразумевает что всегда сначала идет скобка, а за ней пробел. Но ведь может быть и наоборот. Лучше не закладывать определенный порядок в котором идут дополнительные символы.
>>839212
Для начала надо вкатиться в CSS и JS (у нас есть задачи в ОП посте). Тогда потом можно будет и за SPA браться - и на эту тему у нас тоже есть задача.
>>839209
Да, только прежде чем учить фреймворки, надо сначала научиться без них делать задачи, например, задачу про студентов. И фреймворки это не готовое решение задачи, попробуй сам потом изучить фреймворк и увидишь.
>>839099
Во-первых, включи отображеие ошибок либо найди и смотри логи, куда они пишутся.
Во-вторых, натыкай в своем коде echo или var_dump чтобы видеть, какие команды выполняются, и что вообще происходит.
По коду гадать никто не будет. Надо садиться и отлаживать.
Неправильно объявлять функции внутри другой. PHP это не JS. ты даешь свой код как пример, но пример не должен содержать таких грубых ошибок. Также, 2 подчеркивания зарезервированы и их не стоит использовать в своих функциях. Да и вообще не стоит с подчеркивания начинать названия.
Также, может быть проще просто разбить исходную строку на группы по 5 символов и отсчитывать на глаз?
Для добивания пробелов не нужен логарифм, есть str_pad (не работает с кирилицей) или str_repeat + mb_strlen.
> до прототипов ещё не дошёл.
Надо бы уже дойти, эта задача как раз про них. И кстати в ES5 (или ES6) добавили синтаксис для классов. Но сначала конечно надо научиться руками прототипы писать.
> Приватность полей и методов организовываю с помощью var/function declaration в объекте.
Это довольно неэффективно, так как при каждом создании объекта все методы создаются заново. Плюс, конструктор растягивается и становится гигантским и запутанным. В твоем случае, там например, стена кода и куча переменных.
> Hamburger.SIZE_SMALL = { .... }
В константах обычно не хранят свойства, а просто используют их как обозначение. То есть значение константы по идее не должно ничего решать. Просто число или строка.
.... далее ответ тут (спам лист, как же надоело): http://pastebin.ru/ZLsd1J6K
.....
> var invalidSizeErrorMessage = 'Invalid size. Only ' + availableSizes.map(size => size.desc).join`, ` + ' sizes are available';
> validatePriceCaloriesConst(availableSizes, size, invalidSizeErrorMessage);
Код должен быть более прямолинейным. В твоем случае очевидно тут замаскирован обычный if:
if (!isValidSize(size, allowedSizes)) {
throw new HamburgerException("Something...");
}
Либо же
validateSize(size);
На мой взгляд так проще чем разносить в одну функцию текст ошибки, а в другую выброс исключения с этим текстом.
> if (givenConst
> && consts i .price === givenConst.price
> && consts i .calories === givenConst.calories
Вот на этом примере видно почему плохо в константах хранить данные: неудобно сравнивать.
> if (e instanceof HamburgerException) {
> console.assert('Duplicate topping MAYO' === e.errorMessage);
Это не то чтобы ошибка, но плохая идея, анализировать человеческие тексты. Завтра текст чуть поменяют (добавят запятую например) и код перестанет работать. Сравнивать надо либо какие-то коды, либо использовать исключения разных классов. Ну и можно даже не проверять содержимое исключения, на мой взгляд оно того не стоит.
> Ещё в JS из коробки нет expectations
Интересно, впервые про них услышал. В JS насколько я знаю, есть какие-то библиотеки для тестов с ассертами (jasmine, mocha итд). Правда отключать их нельзя, как в пхп, но для тестов это и не требуется.
Кстати, раз ты тесты умеешь писать, не хочешь сюда тест для 12-й задачи добавить? https://github.com/codedokode/jasmine-tests
>> Цена или калории не являются свойствами так как они вычисляются из других свойств и хранить их не надо.
> А по-моему это помогло уменьшить код. Вычисление из других свойств требует больше действий.
За это придется расплачиваться проблемами, если мы например захотим разрешать убирать компоненты. Или менять цену до расчета. не стоит хранить вычисляемые данные. И я не уверен что код в одном случае будет меньше, в любом случае механизм расчета где-то будет.
> if (~t_o_p_p_ingsS_e_t_ted . in_d_e_xOf(t_o_p_ping . desc)) {
Вот по моему > = 0 смотрится понятнее.
Имей только в виду, что некоторые компании смотрят на бумаги. Ну и чтобы пойти джуниором надо иметь определенный уровень знаний, желания мало.
>>837921
Ты имеешь в виду класс со страницы 336? class Cookie { private $created.... ?
Ну, это не совсем модель, как мне кажется. Обычно модель не отвечает за работу с куками, так как куки это часть взаимодействия с пользователем, и это не задача модели.
Я бы этот класс никуда не относил. Просто вспомогательный класс для реализации авторизации.
Если надо отнести, то это компонент, который отвечает за авторизацию через протокол HTTP, и это можно считать частью слоя контроллеров. Но это конечно очень натянуто получается. Лучше будет сказать "помощник контроллера". То есть не контроллер, но класс который ими используется.
Работать с куками вполне нормально. Это пример инкапсуляции, когда мы скрываем внутри класса конкретный механизм авторизации, а наружу выставляем только методы вроде login/logout(). мы даже можем поменять механизм, не трогая остальной код.
Ну и по коду там конечно мне не все нравится. Подчеркивания - плохо. Публичные статические поля - нехорошо.
И еще мне не очень нравится реализация, когда в конструктор передается id пользователя. Я бы сделал конструктор без аргументов, а также методы loginAs($userId)/logout()/getUserId(). То есть сделал бы многоразовый класс, который умеет логиниться под разными логинами, а не заточен на работу с одним заданным в конструкторе id.
Еще мне не нравится использование исключений, я бы просто сделал чтобы метод getUserId() возвращал null если авторизация не подтверждена.
Еще, мне кажется, тут многовато полей. И название не очень удачное, это ведь не класс представляющий куку. Правильнее назвать AuthroizationHelper или CookieAuthorizationHelper, как-то так.
Сама схема с использованием шифрования неплохая, позволяет проверять авторизацию без обращения к БД. Правда есть и небольшой недостаток - при смене пользователем пароля куки не теряют свою силу. Лучше бы чтобы теряли, чтобы при похищении кук они бы переставали работать при смене пароля.
>>837904
>>837187
Вообще, странный выбор вариантов. А где флоаты (в бутстрапе видимо)? На них и стоит делать.
Советую подучить CSS, у нас в Оп посте как раз хорошие задачки по теме.
flex не везде работает, более того по моему есть разные его версии.
display: table нужно в редких случаях, когда надо например выровнять блоки по сетке или сделать их одинковой высоты и растягивающимися.
>>837893
У тебя когда долг становится меньше 5000, на него не начисляется процент и комиссия. Потому и меньше выходит наверно.
>>837866
getBody() возвращает объект и на нем сразу же вызывается метод write() без сохранения объекта в переменную. Так же как можно писать $x = func() + 2 , можно писать и func()->something().
Иногда еще делают чтобы каждый метод возвращал $this - это цепочечные вызовы. Мне не очень нравится.
>>836666
Просто люди любят всему перечить. Ну например, известно что ООП популярная технология, используемая около 30 лет в коммерческой разработке. Но тут приходят функциональщики и начинают свое "ООП не единственный путь, есть и другие подходы". Но попроси их объяснить как сделать дерево DOM, дерево AST, графический интерфейс с виджетами, представление данных из БД, на их функицональщине или хотя бы организовать код в большом проекте - и им нечего ответить. ООП идеально подходит для таких ситуаций, его и придумали в ходе решения подобных задач.
Нередко критикуют ООП те, кто его толком не понимает либо понимает не так. Ну например услышал про паттерны, и думает что в программе надо обязательно все их использовать вместо простого кода. И думает, что ООП это переусложнение кода.
Или некоторые например говорят, что модули (как в Питоне) это как ООП только проще и лучше. Но на самом деле это хуже, это как неполноценный объект который нельзя создать больше чем в одном экземпляре. И у которого все зависимости и настройки жестко прописаны (в отличие от ООП где есть DI).
Или вот есть Го, где нет ООП потому что он не нужен. А что же у них есть? Синтаксис, который позволяет на функциях имитировать объекты и писать что-то вроде:
user := users.NewUser()
user.setName('Ivan')
Ну совсем на ООП непохоже! То есть видимо им ООП использовать хочется, но признавать что это ООП не хочется.
> А PHP Расмус Лердорф изначально создавал как набор инструментов, написанных на С, позволяющих легко и быстро разрабатывать динамический HTML. PHP таким задумывался и таким остался, он сам и есть фреймворк.
Может это и так, но чистый php - это довольно плохой и негибкий фреймворк. Ну к примеру в нем есть шаблоны, но нет автоэкранирования данных для защиты от XSS. В нем есть нефатальные (!) ошибки. В нем есть суперглобальные переменные вроде $_POST что побуждает людей использовать их в любом месте кода. В нем нельзя повлиять на цикл обработки запроса (например сделать что-то еще до прихода HTTP запроса или после его обработки).
Да, я помню, Лердорф даже привел пример приложения без фреймворка: https://toys.lerdorf.com/archives/38-The-no-framework-PHP-MVC-framework.html
Предлагаю вам самим изучить этот код и посмотреть.
> В мире Python и Ruby создание веб-сайтов с нуля — занятие довольно утомительное, поскольку эти языки для создания веб-сайтов не предназначались.
Бред же. В руби по моему HTTP сервер одной строчкой создается, если кому-то хочется писать на "чистом" руби. То есть создание сайтов на числом php и ruby одинково неудобно на мой взгляд.
> Чётко уясните: строк кода в любом проекте должно быть как можно меньше, чтобы код стал как можно чище и читабельнее!
Ну без фреймворка их придется написать больше.
> Но дело в том, что так называемое объектно ориентированное программирование очень часто бывает излишне сложным!
А бывает и нет.
> одно из главных преимуществ PHP — поддержка императивной, функциональной,
справедливости ради функциональный код на нем пиать довольно громоздко и неудобно. Ну-ка скажите мне как кратко записать функцию, получающую на вход объект и возвращающую его свойство name? function ($o) { return $o->name; }. Чтобы PHP поддерживал функциональный подход, это должно писаться в 4 раза короче.
> Так что объект, по сути, — это просто набор функций и переменных, которые теперь называют методами и атрибутами.
Нет. Принципиальная разница что в случае обектов мы можем создать несколько объектов с разными значениями полей, а в случае с функциями + переменными - не можем. Переменные есть только в одном экземпляре. Ну и в ООП есть наследование, инкапсуляция.
> Если вы решили следовать стандартам PHP-FIG, то вы должны понимать, что некоторые из них — например, стандарты автозагрузчика PSR-0 и PSR-4 и ряд других — напрямую влияют на то, как вы пишете код ПО.
А аворы намекают что надо не следовать PSR-4, а придумывать в каждом проекте свой способ называт файлы? Спасибо, не надо.
И еще, про другие стандарты вроде стандарта на логгер. Авторы видимо тоже считают что не надо ему следовать, а надо написать свой логгер. С которым не смогут из коробки взаимодействовать все другие библиотеки. Стандарты для того и пишутся чтобы библиотеку A можно было исплоьзовать с библиотекой B без переходников.
В общем есть подозрение что авторы манифеста в больших проектах не работали и просто не понимают, зачем нужны ООП, фреймворки или стандарты.
Имей только в виду, что некоторые компании смотрят на бумаги. Ну и чтобы пойти джуниором надо иметь определенный уровень знаний, желания мало.
>>837921
Ты имеешь в виду класс со страницы 336? class Cookie { private $created.... ?
Ну, это не совсем модель, как мне кажется. Обычно модель не отвечает за работу с куками, так как куки это часть взаимодействия с пользователем, и это не задача модели.
Я бы этот класс никуда не относил. Просто вспомогательный класс для реализации авторизации.
Если надо отнести, то это компонент, который отвечает за авторизацию через протокол HTTP, и это можно считать частью слоя контроллеров. Но это конечно очень натянуто получается. Лучше будет сказать "помощник контроллера". То есть не контроллер, но класс который ими используется.
Работать с куками вполне нормально. Это пример инкапсуляции, когда мы скрываем внутри класса конкретный механизм авторизации, а наружу выставляем только методы вроде login/logout(). мы даже можем поменять механизм, не трогая остальной код.
Ну и по коду там конечно мне не все нравится. Подчеркивания - плохо. Публичные статические поля - нехорошо.
И еще мне не очень нравится реализация, когда в конструктор передается id пользователя. Я бы сделал конструктор без аргументов, а также методы loginAs($userId)/logout()/getUserId(). То есть сделал бы многоразовый класс, который умеет логиниться под разными логинами, а не заточен на работу с одним заданным в конструкторе id.
Еще мне не нравится использование исключений, я бы просто сделал чтобы метод getUserId() возвращал null если авторизация не подтверждена.
Еще, мне кажется, тут многовато полей. И название не очень удачное, это ведь не класс представляющий куку. Правильнее назвать AuthroizationHelper или CookieAuthorizationHelper, как-то так.
Сама схема с использованием шифрования неплохая, позволяет проверять авторизацию без обращения к БД. Правда есть и небольшой недостаток - при смене пользователем пароля куки не теряют свою силу. Лучше бы чтобы теряли, чтобы при похищении кук они бы переставали работать при смене пароля.
>>837904
>>837187
Вообще, странный выбор вариантов. А где флоаты (в бутстрапе видимо)? На них и стоит делать.
Советую подучить CSS, у нас в Оп посте как раз хорошие задачки по теме.
flex не везде работает, более того по моему есть разные его версии.
display: table нужно в редких случаях, когда надо например выровнять блоки по сетке или сделать их одинковой высоты и растягивающимися.
>>837893
У тебя когда долг становится меньше 5000, на него не начисляется процент и комиссия. Потому и меньше выходит наверно.
>>837866
getBody() возвращает объект и на нем сразу же вызывается метод write() без сохранения объекта в переменную. Так же как можно писать $x = func() + 2 , можно писать и func()->something().
Иногда еще делают чтобы каждый метод возвращал $this - это цепочечные вызовы. Мне не очень нравится.
>>836666
Просто люди любят всему перечить. Ну например, известно что ООП популярная технология, используемая около 30 лет в коммерческой разработке. Но тут приходят функциональщики и начинают свое "ООП не единственный путь, есть и другие подходы". Но попроси их объяснить как сделать дерево DOM, дерево AST, графический интерфейс с виджетами, представление данных из БД, на их функицональщине или хотя бы организовать код в большом проекте - и им нечего ответить. ООП идеально подходит для таких ситуаций, его и придумали в ходе решения подобных задач.
Нередко критикуют ООП те, кто его толком не понимает либо понимает не так. Ну например услышал про паттерны, и думает что в программе надо обязательно все их использовать вместо простого кода. И думает, что ООП это переусложнение кода.
Или некоторые например говорят, что модули (как в Питоне) это как ООП только проще и лучше. Но на самом деле это хуже, это как неполноценный объект который нельзя создать больше чем в одном экземпляре. И у которого все зависимости и настройки жестко прописаны (в отличие от ООП где есть DI).
Или вот есть Го, где нет ООП потому что он не нужен. А что же у них есть? Синтаксис, который позволяет на функциях имитировать объекты и писать что-то вроде:
user := users.NewUser()
user.setName('Ivan')
Ну совсем на ООП непохоже! То есть видимо им ООП использовать хочется, но признавать что это ООП не хочется.
> А PHP Расмус Лердорф изначально создавал как набор инструментов, написанных на С, позволяющих легко и быстро разрабатывать динамический HTML. PHP таким задумывался и таким остался, он сам и есть фреймворк.
Может это и так, но чистый php - это довольно плохой и негибкий фреймворк. Ну к примеру в нем есть шаблоны, но нет автоэкранирования данных для защиты от XSS. В нем есть нефатальные (!) ошибки. В нем есть суперглобальные переменные вроде $_POST что побуждает людей использовать их в любом месте кода. В нем нельзя повлиять на цикл обработки запроса (например сделать что-то еще до прихода HTTP запроса или после его обработки).
Да, я помню, Лердорф даже привел пример приложения без фреймворка: https://toys.lerdorf.com/archives/38-The-no-framework-PHP-MVC-framework.html
Предлагаю вам самим изучить этот код и посмотреть.
> В мире Python и Ruby создание веб-сайтов с нуля — занятие довольно утомительное, поскольку эти языки для создания веб-сайтов не предназначались.
Бред же. В руби по моему HTTP сервер одной строчкой создается, если кому-то хочется писать на "чистом" руби. То есть создание сайтов на числом php и ruby одинково неудобно на мой взгляд.
> Чётко уясните: строк кода в любом проекте должно быть как можно меньше, чтобы код стал как можно чище и читабельнее!
Ну без фреймворка их придется написать больше.
> Но дело в том, что так называемое объектно ориентированное программирование очень часто бывает излишне сложным!
А бывает и нет.
> одно из главных преимуществ PHP — поддержка императивной, функциональной,
справедливости ради функциональный код на нем пиать довольно громоздко и неудобно. Ну-ка скажите мне как кратко записать функцию, получающую на вход объект и возвращающую его свойство name? function ($o) { return $o->name; }. Чтобы PHP поддерживал функциональный подход, это должно писаться в 4 раза короче.
> Так что объект, по сути, — это просто набор функций и переменных, которые теперь называют методами и атрибутами.
Нет. Принципиальная разница что в случае обектов мы можем создать несколько объектов с разными значениями полей, а в случае с функциями + переменными - не можем. Переменные есть только в одном экземпляре. Ну и в ООП есть наследование, инкапсуляция.
> Если вы решили следовать стандартам PHP-FIG, то вы должны понимать, что некоторые из них — например, стандарты автозагрузчика PSR-0 и PSR-4 и ряд других — напрямую влияют на то, как вы пишете код ПО.
А аворы намекают что надо не следовать PSR-4, а придумывать в каждом проекте свой способ называт файлы? Спасибо, не надо.
И еще, про другие стандарты вроде стандарта на логгер. Авторы видимо тоже считают что не надо ему следовать, а надо написать свой логгер. С которым не смогут из коробки взаимодействовать все другие библиотеки. Стандарты для того и пишутся чтобы библиотеку A можно было исплоьзовать с библиотекой B без переходников.
В общем есть подозрение что авторы манифеста в больших проектах не работали и просто не понимают, зачем нужны ООП, фреймворки или стандарты.
Слишком много кода. Решение должно быть раза в 3 меньше по объему. Ты просто копипастишь куски одинакового кода, а надо умело вставить в код условия, чтобы он в разных ситациях вел себя по-разному.
Также, цифры вроде 1.03 скопипастены много раз. Как их менять? Неудобно же.
> elseif ($creditSum = $creditSum - 5000) {
Ты перепутал = и ==. = не сравнивает, а просто сохраняет значение в переменную.
> PHP Notice: Undefined variable: obshayaSumma in /home/TSraa6/prog.php on line 6
Это надо исправить.
Часто они плохо реализованы и дергаются. На небольшом экране они мешают.
Но есть и другие недостатки. Например, когда перемещаешься по полям ввода, браузер прокручивает страницу, чтобы курсор всегда был виден. но если на странице есть закрепленные элементы, курсор может оказаться под ним и будет не виден.
Также, при перемещении к якорю (ссылка вида http://example.com#anchor ) якорь может оказаться скрыт закрепленной шапкой.
В общем, я против закрепленных элементов.
Яндекс вообще отличился - на мобильной версии поиска у них треть экрана занимает кнопка с предложением уcтановить какое-то бекдороподобное придожение, чтобы яндекс мог следить не только за твоими запросами, но и за твоим местоположением по GPS, подслушивать тебя, смотреть список контактов, установленных приложений, спамить нотификациями. Не факт что они все это делают, но вряд ли они просто так за бесплатно написали приложение.
Калькулятор: http://ideone.com/a3O2X9
Навигатор (поиск кратчайшего пути): http://ideone.com/VNWZl0 [Как сделать с рекурсией не догнал, поэтому решил алгоритмом Дейскры]
Числа прописью: http://ideone.com/Vap7Ez
Компания "Вектор" ооп: http://ideone.com/Vd0MKA [в комментах несколько вопросов, на которые хотелось бы получить ответ :)]
На пхп написан
Почитай Макконнелла. Функции нужно разбивать, чтоб не более 30 строк и соблюдался SRP. Вложенные циклы тоже разбивай. Сложные условия в ифах выносятся в отдельные функции. Это только беглый взгляд.
А мне припекает от глистов у этих кошатниц и оттого что я так и не научился ставить знаки препинания.
Не особо. Но бомбит от того, что кто-то такой дегенерат, что не додумался открыть пикчу в полном размере прежде чем скачать, когда пересоздавал тред.
Какая разница?
что за безумие
Хм, теперь предлагает скачать, хотя я ничего в этом направлении не делал. Ну ладно
так долго пыхтел с подключением getID3, а он оказывается есть в репозитории композера. На офф. сайте об этом ни слова. Аргх!
Что значит:
$a = &$b;
$a &= $b;
Сам я на нем обосрался, и решил вот вам принести.
$a &= $b Это короткая запись $a = $a & $b, то есть это побитовый оператор "И". Устанавливаются только те биты, которые установлены и в $a, и в $b.
http://php.net/operators.bitwise
>>839875
$a =& $b присваивает $a как ссылку на $b.
http://php.net/references
Хм. Теперь, как я понял, я должен с помощью .htaccess сделать так, что-бы сервер нашел файл по такому кривому адресу (заменил + на пробелы и исправил десятичное шифрование кириллицы).
Я не представляю как это делается, нагуглить не могу. Подскажите, в какую сторону копать
Ты разделом ошибся. Картинки в высоком разрешении ищи не в программаче.
дрочи , потом в сеньёры вкатишься с фаворит языком
Ребзи, банальный вопрос, что означает -> и => в PHP ?
$this->zxc as $x => $y
Как это понимать ?
Ознакомься с принципами работы логических операций в Computer Science.
https://ru.wikipedia.org/wiki/Битовые_операции
Первое используют для обращения к свойствам объекта
class A{public $a = 2;};
echo (new A)->a; // 2
Второе используют при описании массивов.
echo ['a' => 2]['a'] // 2
https://gist.github.com/anonymous/5720757075a77a8c903674284b90c6f6
https://jsfiddle.net/mLd25zLz/
> не хочешь сюда тест для 12-й задачи добавить?
Рано мне пока. Но я буду очень рад принести пользу треду.
> плохая идея, анализировать человеческие тексты. Завтра текст чуть поменяют (добавят запятую например) и код перестанет работать.
Это верно, я мог бы и догадаться, когда сам правил сообщения исключений, из-за того, что assert не проходил из-за опечатки.
Ещё там была проблема: если HamburgerException не выбрасывалось (а должно), то скрипт молчал.
> В константах обычно не хранят свойства, а просто используют их как обозначение.
Да, это была просто попытка избежать стены if'ов (взамен обзаведясь стеной проверок).
Если давать константам ничего не значащие значения (как, безусловно, и должно быть), то нужно где-то выставить соответствие между константой и массивом вида { price: N, calories: Y ... } + нужно держать это всё в согласованности, так как можно добавить вверху Hamburger.NEW_CONST, но забыть прописать её соответствие в if'ах. Хотя я наверно утрирую. Cделал метод, возвращающий соответствия.
Сейчас значения констант влияют только на текст сообщений в исключениях, больше ни на что.
Забыл в прошлый пост добавить, касательно пасты: https://gist.github.com/codedokode/ce30e7a036f18f416ae0#Задачки-на-js
> Такая функция есть в lodash: http://lodash.com/docs#pluck
Я искал эту функцию в исходниках, но не нашёл. Зато нашёл вот что:
https://github.com/lodash/lodash/wiki/Changelog
> Removed _.pluck in favor of _.map with iteratee shorthand
И вопрос по 11-й задаче:
> Можешь взять оттуда первые 5-10 городов и перенести в код.
Что значит "можешь взять и перенести"? Спарсить? Или задание заключается только в том, чтобы отсортировать массив объектов и вывести N первых из них?
https://gist.github.com/anonymous/5720757075a77a8c903674284b90c6f6
https://jsfiddle.net/mLd25zLz/
> не хочешь сюда тест для 12-й задачи добавить?
Рано мне пока. Но я буду очень рад принести пользу треду.
> плохая идея, анализировать человеческие тексты. Завтра текст чуть поменяют (добавят запятую например) и код перестанет работать.
Это верно, я мог бы и догадаться, когда сам правил сообщения исключений, из-за того, что assert не проходил из-за опечатки.
Ещё там была проблема: если HamburgerException не выбрасывалось (а должно), то скрипт молчал.
> В константах обычно не хранят свойства, а просто используют их как обозначение.
Да, это была просто попытка избежать стены if'ов (взамен обзаведясь стеной проверок).
Если давать константам ничего не значащие значения (как, безусловно, и должно быть), то нужно где-то выставить соответствие между константой и массивом вида { price: N, calories: Y ... } + нужно держать это всё в согласованности, так как можно добавить вверху Hamburger.NEW_CONST, но забыть прописать её соответствие в if'ах. Хотя я наверно утрирую. Cделал метод, возвращающий соответствия.
Сейчас значения констант влияют только на текст сообщений в исключениях, больше ни на что.
Забыл в прошлый пост добавить, касательно пасты: https://gist.github.com/codedokode/ce30e7a036f18f416ae0#Задачки-на-js
> Такая функция есть в lodash: http://lodash.com/docs#pluck
Я искал эту функцию в исходниках, но не нашёл. Зато нашёл вот что:
https://github.com/lodash/lodash/wiki/Changelog
> Removed _.pluck in favor of _.map with iteratee shorthand
И вопрос по 11-й задаче:
> Можешь взять оттуда первые 5-10 городов и перенести в код.
Что значит "можешь взять и перенести"? Спарсить? Или задание заключается только в том, чтобы отсортировать массив объектов и вывести N первых из них?
Начну с View.
Нужно отобразить следующие данные:
Сумму всех заказов;
Сумму заказов со статусом ORDER_DELIVERED ;
Сумму заказов со статусом ORDER_PAID;
Таблицу с описанием заказов, у которых статус !только! ORDER_PAID. (GridView::widget)
И график (Highcharts::widget), где отображены заказы ORDER_DELIVERED и ORDER_PAID.
Ну и, конечно же, надо добавить временной срез типа "неделя", "месяц", "год".
Соответственно, самое простое - тупо для каждого типа данных запилить по запросу, что как-то глупо, ибо 5 запросов, которые по сути одни и те же данные вытягивают или это окей?
Потом я почитал про ActiveDataProvider и нихуя не понял, но он нужен для таблицы (gridview). Где я обосрался?
Не могу разобраться уже со вторым плагином drug'n'drop загрузки. Залил, скрипт сам завёлся. Но при перетаскивании файла в лог пишет ошибку Failed to Upload file #0: Internal Server Error
В настройках плагина action правильный указал, в форме в <input type=file> правильный name указал. Нужно ли чще что-то менять в router.php?
В общем, я не знаю куда копать уже
>Failed to Upload file #0: Internal Server Error
Очевидно какая-то ошибка в бэкэнде. Попробуй обычной формой сабмитить и смотри что получается.
Веб-сервер раскодирует процентную кодировку автоматически при поиске файла. Насчет плюса - не уверен.
Что касается кириллицы - надо быть осторожным, кодировка в URL должна совпадать с используемой кодировкой в файловой системе (в линуксе это обычно utf-8, в винде win-1251). То есть кирилицу в именах файлов на диске лучше вообще не использовать, даже вместе с процентным кодированием.
Вообще, чтобы не мучаться с именами, лучше сделать так: давать файлам на диске простые имена (только латиница и цифры например). А URL для них делать такой:
http://example.com/download/file123.txt/скачиваемое-имя.php
СОответственно в htaccess мы приписываем правило чтобы такой УРЛ перенаправлялся на файл
/download/file123.txt
А последняя часть в УРЛ нужна для того, чтобы браузер взял из нее нужное имя для сохранения файла.
Также, есть еще вариант с X-sendfile. Это расширение Апача которое может само отдавать файлы (в том числе из непублинчых папок). Там схема такая: запрос поступает к php, который определяет реальное имя файла, отдает апачу заголовок с ним и завершается. А Апач уже отдает этот файл.
То есть в таком сценарии можно сделать например УРЛ
/download/1234/имя-для-скачивания.txt
А файл реально хранится где-то в /storage/1234-some-name.txt
В любом случае принцип тут такой: браузер берет в качестве имени последний сегмент в УРЛ (то что идет от последнего слеша до конца УРЛ), при этом хранить файл мы можем под другим именем. Но в этом случае мы должны придумать механизм, как получить это настоящее имя из УРЛ. Либо оно как-то там заложено, либо там заложен id файла, а имя мы берем из базы.
При выборе имени для хранения на диске надо учесть такие моменты:
- скачиваемый файл хорошо бы сохранять с тем же именем что и закачиваемый
- разные пользователи могут загрузить 2 файла с одинаковым именем, и надо чтобы они не затерли друг друга
- если мы сохраним файл с именем x.php на диск то есть риск его выполнения. То же самое относится к файлам с именами .htaccess которые позволяют переопределить настройки сервера. Значит, нам надо гарантировать сохранение на диск с безопасным расширением
- если использовать имена в кирилице, может возникнуть проблема с тем, что в УРЛ используется utf-8, а на диске (в винде например) - другая кодировка
- желательно распределять файлы по папкам, чтобы в одной папке было не более 1000 файлов, а то трудно будет оиентироваться
- желательно делать понятные имена на диске, чтобы администратору было проще разбираться, есть что
Попробуй придумать схему, которая бы удовлетворяла этим критериям. Ну и конечно не забудь про безопасность.
Веб-сервер раскодирует процентную кодировку автоматически при поиске файла. Насчет плюса - не уверен.
Что касается кириллицы - надо быть осторожным, кодировка в URL должна совпадать с используемой кодировкой в файловой системе (в линуксе это обычно utf-8, в винде win-1251). То есть кирилицу в именах файлов на диске лучше вообще не использовать, даже вместе с процентным кодированием.
Вообще, чтобы не мучаться с именами, лучше сделать так: давать файлам на диске простые имена (только латиница и цифры например). А URL для них делать такой:
http://example.com/download/file123.txt/скачиваемое-имя.php
СОответственно в htaccess мы приписываем правило чтобы такой УРЛ перенаправлялся на файл
/download/file123.txt
А последняя часть в УРЛ нужна для того, чтобы браузер взял из нее нужное имя для сохранения файла.
Также, есть еще вариант с X-sendfile. Это расширение Апача которое может само отдавать файлы (в том числе из непублинчых папок). Там схема такая: запрос поступает к php, который определяет реальное имя файла, отдает апачу заголовок с ним и завершается. А Апач уже отдает этот файл.
То есть в таком сценарии можно сделать например УРЛ
/download/1234/имя-для-скачивания.txt
А файл реально хранится где-то в /storage/1234-some-name.txt
В любом случае принцип тут такой: браузер берет в качестве имени последний сегмент в УРЛ (то что идет от последнего слеша до конца УРЛ), при этом хранить файл мы можем под другим именем. Но в этом случае мы должны придумать механизм, как получить это настоящее имя из УРЛ. Либо оно как-то там заложено, либо там заложен id файла, а имя мы берем из базы.
При выборе имени для хранения на диске надо учесть такие моменты:
- скачиваемый файл хорошо бы сохранять с тем же именем что и закачиваемый
- разные пользователи могут загрузить 2 файла с одинаковым именем, и надо чтобы они не затерли друг друга
- если мы сохраним файл с именем x.php на диск то есть риск его выполнения. То же самое относится к файлам с именами .htaccess которые позволяют переопределить настройки сервера. Значит, нам надо гарантировать сохранение на диск с безопасным расширением
- если использовать имена в кирилице, может возникнуть проблема с тем, что в УРЛ используется utf-8, а на диске (в винде например) - другая кодировка
- желательно распределять файлы по папкам, чтобы в одной папке было не более 1000 файлов, а то трудно будет оиентироваться
- желательно делать понятные имена на диске, чтобы администратору было проще разбираться, есть что
Попробуй придумать схему, которая бы удовлетворяла этим критериям. Ну и конечно не забудь про безопасность.
Дописал еще в задачу комментарии про скачивание.
>>839909
Для пост + тег - связь многие-ко-многим. А чем категория отличается от тега?
> Или можно как-то подставить в поле тегов у поста массив id-шников тегов из таблицы
Тебе надо прочитать про нормализацию баз данных и виды связей прежде чем проектировать базу. Тогда такой вопрос даже не возникнет. У нас в ОП посте есть задачи по SQL и там есть ссылки на статьи, где это описано. Давай еще раз напомню ключевые слова:
- нормализация базы данных
- виды связей в базах данных
>>839954
Хватит флудить.
>>840032
Любое число можно представить в двоичном виде (то есть записать в виде 0 и 1). Битовые операции И, ИЛИ, ИСКЛЮЧАЮЩЕЕ ИЛИ, НЕТ, СДВИГ работают с двоичными представлениями чисел.
Ну например 5 | 3 дает 7. Попробуй почитать про двоичную систему счисления, и про битовые операции и понять почему.
И сложно ли будет осваивать пхп после нода?
> this._size = this._hamburgerSizeEnum[sizeIndex];
Не очень понятно, почему нельзя написать this._size = size ?
> } else if (this._toppings[toppingIndex]) {
Это явно неправильно - toppingIndex это индекс в другом массиве.
Чуть-чуть от дублирование информации можно попробовать избавиться, заведя массивы размеров, добавок такого вида:
toppings[Hamburger.TOPPING_MAYO] = { price: ..., calories ... };
Этот массив можно использовать и для расчета цены, и для проверки существования такой добавки. Но небольшое дублирование (надо отдельно объявить константу, отдельно написать ее свойства) остается, что поделать.
Также, функцию получения этого массива можно сделать статической (Hamburger._getToppingsList), так как он не привязан к конкретному объекту гамбургера.
> Removed _.pluck
Спасибо, поправил. Как все быстро меняется в нашей сфере.
> Что значит "можешь взять и перенести"? Спарсить? Или задание заключается только в том, чтобы отсортировать массив объектов и вывести N первых из них?
Задание в том чтобы отсортировать и вывести, откуда они берутся - неважно.
В SQL можно попробовать получить данные одновременно селектом вроде
SELECT SUM(IF(status = 'x', 1, 0)) AS sumX, SUM(...) AS sumY ...
Но не факт что это будет быстрее и сможет использовать индексы. Не видя запроса и структуры таблиц, не понять.
> Потом я почитал про ActiveDataProvider и нихуя не понял, но он нужен для таблицы (gridview). Где я обосрался?
Как я понимаю, это попытка абстрагировать источник данных для грида. Ведь не факт что ты хочешь выводить данные именно из базы? Потому они делают интерфейс поставщика данных ( http://www.yiiframework.com/doc-2.0/yii-data-dataproviderinterface.html ), и реализующий его ПоставщикДанныхИзБазыЧерезActiveRecord. Я думаю, именно ради заменяемости.
И ты соответственно можешь всегда заменить источник данных, например написав ПоставщикДанныхИзФайла или ПоставщикДостовернойСтатистикиСПотолка.
А как бы ты сделал заменяемость источника данных для грида?
>>840296
> Но при перетаскивании файла в лог пишет ошибку Failed to Upload file #0: Internal Server Error
Во-первых, открой отладчик в браузере и посмотри POST запрос и ответ на него, во-вторых, смотри логи PHP на сервере.
>>840325
Сравни запросы которые отправляет форм и плагин. Если надо, воткни пару вардампов в код загрузчика на сервере.
Также посмотри исходники загрузчика и что он делает с файлами.
В SQL можно попробовать получить данные одновременно селектом вроде
SELECT SUM(IF(status = 'x', 1, 0)) AS sumX, SUM(...) AS sumY ...
Но не факт что это будет быстрее и сможет использовать индексы. Не видя запроса и структуры таблиц, не понять.
> Потом я почитал про ActiveDataProvider и нихуя не понял, но он нужен для таблицы (gridview). Где я обосрался?
Как я понимаю, это попытка абстрагировать источник данных для грида. Ведь не факт что ты хочешь выводить данные именно из базы? Потому они делают интерфейс поставщика данных ( http://www.yiiframework.com/doc-2.0/yii-data-dataproviderinterface.html ), и реализующий его ПоставщикДанныхИзБазыЧерезActiveRecord. Я думаю, именно ради заменяемости.
И ты соответственно можешь всегда заменить источник данных, например написав ПоставщикДанныхИзФайла или ПоставщикДостовернойСтатистикиСПотолка.
А как бы ты сделал заменяемость источника данных для грида?
>>840296
> Но при перетаскивании файла в лог пишет ошибку Failed to Upload file #0: Internal Server Error
Во-первых, открой отладчик в браузере и посмотри POST запрос и ответ на него, во-вторых, смотри логи PHP на сервере.
>>840325
Сравни запросы которые отправляет форм и плагин. Если надо, воткни пару вардампов в код загрузчика на сервере.
Также посмотри исходники загрузчика и что он делает с файлами.
>А как бы ты сделал заменяемость источника данных для грида?
Да тут проблема в другом. Вот смотри, я сделал этот ActiveDataProvider, который тупо тянет информацию из БД.
Все окей.
А вот дальше полный пиздец. Я запилил MyModelSerivce, который подсчитывает некоторые величины, например, сумму значений заказов со статусом "ORDERS_PAID". Не спрашивай, анон, почему я не использую sum() метод.
Хуйня в том, что когда я передаю моему охуенному сервису массив данных, то он дублирует строки. Например, если в выборке был один заказ, то сумму он посчитает как два.
Да не пять цифр. Я же говорю, что помимо total, order_paid, order_delivered нужно запилить табличку, которая содержит в себе только order_delivered заказы
У меня единственная мысль - это только 5 запросов к базе.
Вытащить и посчитать total, order_paid, order_delivered
Потом вытащить только order_delivered значения
И потом только order_paid
Ты делаешь что-то странное. Для 5 цифр грид и дата провайдер не нужен. Для таблички с заказами надо использовать встроенные в Юи средства.
А чего так дрочат на ларавел? Фреймворк, как фреймворк, немного упоротый в плане абстракции, но это хуета же. Судя по бенчмаркам лаганутый пиздец, но это как бы тоже не привыкать. Ну еще один фреймворк в мире уже трещащем по швам от этого говна, в чем профит то у него?
Удобный для домохозяек. Смотри на пик, всё на статике, даже думать не нужно, привычный процедурный подход.
Да я писал на нем в рамках ознакомления. Обдристать все фасадами можно на любом фреймворке изи, так что не может же это быть причиной его повальной популярности.
Ну а ты много фреймворков видел, в которых из коробки столько фасадов? Laravel позволяет крудить даже толком не понимая ООП. У меня нет более логичного объяснения хайпу.
Ну чтож, пока это единственная причина установленная и это грустно конечно. Может со временем еще что скажут.
Алсо, подниму тогда такую тему, я тут фалконом увлекся, прямо жара, тонкий слой абстракции под себя, эх красота. Полет нормальный. У кого еще опыт есть? Интересно мнения почитать.
>Вроде в России всегда Yii2 был более популярным, а в мире - Symphony.
Недавно инфа проскакивала, что ларавел обогнал юи на новых проектах.
>В Yii2 есть Gii - генератор всевозможных крудов и прочего. Это то же самое - фасады? Что вы с аноном вкладываете в это понятие?
Это кодогенерация, инструменты разработчика обыкновенные. В любом уважающем себя фреймворке есть. Фасад грубо говоря это такой паттерн простейший, когда сложную логику инкапсулируют в какой-нибудь класс, вероятнее всего статический, чтобы потом чистенько вызвать эту кучу говна в одну строчку. Нет никакой сложности самому фасады накидать по желанию, да и вообще в ходе разработки даже не зная паттернов все это делают то и дело и без знания паттерна самого.
Все известные пехепе фреймворки начиная с кейка - клоны рельс, большей или меньшей степени хуёвости.
А разве ларавел при этом не хуже юи? Юи 2 особенно. Ларавел разве что няшностью особой похвастаться может, а так больше ничем.
Пишут эйчарки чаще всего и так, что к реальной работе там это отношения не имеет никакого в итоге. Ну можно расчитывать разве что, что раз пишут пхп, значит будет пхп, а уж какой стек лучше и не пытаться угадывать.
Ну и выделенного тобой это тоже касается скорее всего, пишут просто они исходя из того, чтобы выглядело сурьезно.
>Вроде в России всегда Yii2 был более популярным, а в мире - Symphony.
ZF2 был всегда популярнее
>>838079
Ну вот допустим я написал кусочек класса который встречает пользователя.
Ну допустим даже использовал конструктор как мне кажется правильным. Как мне дальше то с этим всем работать?
Мне не помогает знание как решить кошки мышки и вектор в работе с такой неведомой хуйней как полноценное приложение, тут ведь нет кошек и собачек и других удобных существ, которые можно бы было объектами обозвать. Я хз как дальше всё это связывать.
Мои методы должны возвращать другие объекты, или просто их вызывать? И так цепочка до бесконечности?
Я кажется где-то выше писал про паттерн Front Controller. В надежде что ты погуглишь. Суть его в том что он получает информацию о запросе, анализирует ее, выбирает и запускает нужный контроллер для его обработки.
Что касается ООП то стоит сначала не писать код, а решить, чем будет заниматься каждый класс и соответственно, какие у него будут методы. Так будет лучше видно, логичная ли у тебя получилась схема или нет.
Ну например, КонтроллерНовостей отвечает за вывод страницы списка новостей и страницы новости. Соответственно, у него есть 2 метода, listNewsAction() и viewNewsAction(). В фреймворках обычно подобные методы реализуются так:
listNewsAction(Request $r): Reponse
или например так
listNewsAction(Request $req, Response $res)
То есть действие контроллера получает на вход HTTP-запрос и либо отдает HTTP-ответ либо заполняет предоставленный объект с ответом.
Но если у тебя простое приложение, это может быть переусложнением и проще параметры запроса брать из суперглобальных переменных, и выводить ответ сразу, без заполнения объекта Response.
что касается фронт-контроллера. Очевидно, у него есть только один метод - обработатьЗапрос() и его задача также получить запрос и выдать ответ (используя один из конкретных контроллеров). Но опять же, в простом приложении можно обойтись без объектов запроса/ответа и например сделать так:
handle($method, $url)
Или пойти дальше и сделать фронт-контроллер просто функцией.
В общем, конечно использование суперглобальных переменных не очень красиво, так как получается что класс откуда-то из воздуха берет какие-то данные, а не получает их явно.
Еще можно посмотреть как это реализовано в фреймворках, например, Slim, Yii2, Symfony 2.
Задачи про собачек как раз должны были научиться сначала проектировать классы, а не бросаться писать код.
Я кажется где-то выше писал про паттерн Front Controller. В надежде что ты погуглишь. Суть его в том что он получает информацию о запросе, анализирует ее, выбирает и запускает нужный контроллер для его обработки.
Что касается ООП то стоит сначала не писать код, а решить, чем будет заниматься каждый класс и соответственно, какие у него будут методы. Так будет лучше видно, логичная ли у тебя получилась схема или нет.
Ну например, КонтроллерНовостей отвечает за вывод страницы списка новостей и страницы новости. Соответственно, у него есть 2 метода, listNewsAction() и viewNewsAction(). В фреймворках обычно подобные методы реализуются так:
listNewsAction(Request $r): Reponse
или например так
listNewsAction(Request $req, Response $res)
То есть действие контроллера получает на вход HTTP-запрос и либо отдает HTTP-ответ либо заполняет предоставленный объект с ответом.
Но если у тебя простое приложение, это может быть переусложнением и проще параметры запроса брать из суперглобальных переменных, и выводить ответ сразу, без заполнения объекта Response.
что касается фронт-контроллера. Очевидно, у него есть только один метод - обработатьЗапрос() и его задача также получить запрос и выдать ответ (используя один из конкретных контроллеров). Но опять же, в простом приложении можно обойтись без объектов запроса/ответа и например сделать так:
handle($method, $url)
Или пойти дальше и сделать фронт-контроллер просто функцией.
В общем, конечно использование суперглобальных переменных не очень красиво, так как получается что класс откуда-то из воздуха берет какие-то данные, а не получает их явно.
Еще можно посмотреть как это реализовано в фреймворках, например, Slim, Yii2, Symfony 2.
Задачи про собачек как раз должны были научиться сначала проектировать классы, а не бросаться писать код.
Вероятно, твой класс не должен лезть в глобальные переменные $_POST и $_SERVER, их значение нужно передавать через аргумент, например конструктора. Но я не настоящий сварщик.
>>Мне не помогает знание как решить кошки мышки и вектор
А студентов ты сделал?
>>возвращать другие объекты, или просто их вызывать?
Если пользователя встречает конструктор, то ему можно вернуть объект, с которым он будет дальше работать. Например запросит шаблон и передаст ему данные для отображения.
>>или просто их вызывать? И так цепочка до бесконечности?
Объекты ведь не только хранят данные, но могут их обрабатывать с помощью методов. Например обновлять базу данных.
12 задача: https://ideone.com/U2fJ2w
11 задача: https://ideone.com/ntoBFC (не знаю, как бороться с копипастой здесь)
> функцию получения этого массива можно сделать статической (Hamburger._getToppingsList)
А этот статический метод будет при каждом вызове новый массив создавать или возвращать уже существующий? Если второе, то я тогда не совсем понимаю, зачем приватный геттер, если можно в статических свойствах массивы хранить. (сейчас так сделал).
Спасибо, стало понятно.
Ну, Laravel самый популярный, россияне подтянулись, наверное.
>>840633
Вроде нет, но у меня устарелые данные из этой статьи: https://habrahabr.ru/post/254277/
>>840577
Yii2 вроде бы показывал лучшие результаты при тестах, но там уровня Hello world тесты.
Они все равно достаточно показательны. К примеру ларавел, как ни отрубали там все лишнее для тестах на хелоуворлде, и даже вносили пару правок в сам движок, все равно оказался значительнее медленее и юи1 и юи2 без всяких оптимизаций. Это говорить может о том, что, скажем так, "ядро" фреймворка лучше в юйке, а его из песни не выкинешь. На больших коммерческих проектах есть дядя с денюжкой и соответственно масштабировать можно как хочешь, но если ресурсы огранчиены сильно, то производительность фреймворка очень важна. Но без хелоуворлда может вполне оказаться, что ларавел на тяжелом проекте быстрее юи потому, что он допустим лучше работает при наличии множества компонентов. Я таких тестов не видел правда. Если не ошибюсь в юи1 di нету, так что всякое может быть.
php -m в списке есть mbstring.
apache2.4 -> phpinfo() mbstring нет.
php.ini настроен и находится в одном месте в системе.
apache тоже настроен, обычные php скрипты обрабатывает нормально.
Куда копать дальше, что делать?
В виндус постоянно были проблемы с подключением библиотек. Если её нет в списке, значит очевидно библиотеку не удалось загрузить. Смотри логи апача, там обычно пишет по какому пути он пытается найти библиотеку. Так же проверь другие библиотеки, я помню у меня не только mbstring отваливался.
Спасибо тебе добрый человек.
Оказывается в php.ini нужно указывать полный путь до каталога с расширениями.
for ($month = 1; $month <= 20; $month ++)
такое условие? Откуда взялись 20 месяцев? Мое, конечно, вырвиглазное условие в цикле, но оно, как мне кажется, логично. Нет?
for($price=40000,$mounth=1; $price>=0; $price=$price-($payment-$price*0.03-$komissiya))
>$price>=0
Нет, так цикл будет вечным.
Подумай, как должно быть, тут почти правильно.
А ОП, да, почему-то так и не поменял этот момент. Там явно надо исходить из того, осталось ли что-то для выплаты или нет.
Нет, выражение $price=$price-($payment-$price*0.03-$komissiya же
Зачем ты пихаешь всё в одну нечитабельную строку?
Переменная $percent есть, но не используется
mounth - это "рот"
Хвалю твое стремление к уменьшению кода, на начальных этапах это полезно. Но вообще такие конструкции плохо читаются.
записи оно добавляет но при каждой перезагрузке странице, что может быть не так ?
> Про плагин drug'n'drop
Получилось. Ошибки все найдены и исправлены. Да, в итоге всё оказалось не таким очевидным.
Доброго вечера. Подскажите пожалуйста, как из json-кода вытащить только то, что я от него хочу ?
Например есть json-код:
[first] = stdClass Object (
[id] => 1
[name] => Username
[value] => XYZ
)
[second] = stdClass Object (
[id] => 2
[name] => Username2
[value] => XYZ2
)
Как вытащить отсюда только имена ? Или например только айди ?
Можно название, либо ссылочку, если есть на торрентах?
Братишка, помоги. Я честно только-что выебал себе мозг, но так и не понял как это правильно сделать...
Твое условие правильное. Единственное что шапка стала слишком сложной и нечитаемой и вычисление $price лучше перенести в тело цикла.
Но у решения ОПа есть своя хитрость. Допустим что ты укажешь такие данные что долг увеличивается быстрее чем выплачивается. Твой код зависнет в вечной цикле, а мой остановится.
Единственное, что 20 конечно это мало так как могут быть кредиты на 10-20 лет. Я бы поставил там лет 500, чтобы гарантированно было больше жизни заемщика, но не позволяло сделать цикл вечным (или как альтернатива, можно сделать определение факта бесконечного роста долга).
В руководстве по написанию кода в НАСА например запрещается использовать циклы, где не задано максимальное число итераций.
Твой код устарел лет на 20 (когда же кончатся устаревшие учебники по PHP4?). Срочно переписывать. Как вариант, можешь почитать phptherightway или комментарии к задаче про студентов из ОП поста.
Ты не представляешь как быстро все меняется в нашей сфере. Я иногда читаю свои уроки написанные пару лет назад и вижу что что-то пора менять, так как оно может не заработать на современных версия программ.
>>840922
Имеет. Чтобы не задумваться, какие символы встречаются в твоей строке и можно ли их считать без mbstring.
>>840929
Циклом. Тебе стоит вернуться к основам языка и изучить циклы и работу с объектами (хотя я бы распаковывал джейсон в массивы: с ними удобнее).
Отлаживать программу:
- https://ru.wikipedia.org/wiki/Отладка_программы
- https://netbeans.org/kb/docs/php/debugging_ru.html
- https://learn.javascript.ru/debugging-chrome
Отладчик позволяет останавливать выполнение программы в заранее поставленных точках, выполнять инструкции по шагам, просматривать значения переменных. Иногда можно еще и вмешиваться в работу программы, меняя значения переменных или вызывая какую-то функцию, пока программа стоит на паузе.
в пыхе везде юзают var_dump() либо exit(var_dump());
но интерпрайз бляди ешё юзают xdebug %хотя хз как он работает в фреймворках %
Тоже через дамп все делаю, но пришел какой-то вузоблядок на работу и давай мне затирать, типа заебись было бы дебажить
Так то в фреймворках удобные средства для профайлинга и дебага часто имеются. Сам я правда дикар ьи как-то мимо этого прохожу пока что, но народу нравится вроде.
public/index.php и public/register.php
https://github.com/enotocode/Studentslist
Аноны, помогите плз написать простой скрипт или логически его построить:
Как сделать сохранение в куках нажатых чекбоксов? Знаю что это жс, но все же
+Как запомнить на пыхе чекбоксов сервером базой склайт
>Сумма кредита — 40000 р., банк в начале каждого месяца (включая первый) начисляет 3% от остатка долга за пользование кредитом и 1000 р. комиссии
И все таки не могу понять это условие. Если срок кредита не указан, то теоретически можно платить ровно 3% от долга + 1000 р постоянно независимо от времени? Какой-то просто странный кредит, хотя я в них не особо разбираюсь. Получается если заплатить только за месяц, то получится 40К*0.03+40К+1К? Я думал что кредит это "МОЛОДОЙ ЧЕЛОВЕК ПЕРЕПЛАТА БУДЕТ 45% ЗА ГОД ДАВАЙ ПАСПОРТ ПОШЛИ ОФОРМЛЯТЬСЯ" и платишь сумму покупки + 45% раскиданные на каждый месяц.
> Как сделать сохранение в куках нажатых чекбоксов?
Так же, как и сохранение в куках любых данных, пришедших из формы.
> Как запомнить на пыхе чекбоксов сервером базой склайт
Так же, как и запоминать в базе любые данные, пришедшие из формы.
Короче, разберись с тем, как работать с данными, пришедшими из форм, тогда и вопросы о чекбоксах отпадут сами собой.
Если всё правильно, то я сохраню статичные чекбоксы так:
<!DOCTYPE HTML>
<html>
<head>
<title>Untitled</title>
<meta charset="utf-8">
</head>
<body>
<input type="checkbox" class="stat" name="column-1" value="1" checked >
<input type="checkbox" class="stat" name="column-2" value="2" checked >
<script>
(function(b) {
var e = document.querySelectorAll("." + b),
a = localStorage.getItem(b),
a = (a = JSON.parse(a)) || (a = {}, localStorage.setItem(b, JSON.stringify(a)));
[].forEach.call(e, function(c, d) {
void 0 !== a[d] && (c.checked = a[d]);
c.addEventListener("change", function() {
a[d] = c.checked;
localStorage.setItem(b, JSON.stringify(a))
})
})
})("stat");
</script>
</body>
</html>
Теперь надо замутить динамичное нажатие до 50-100 выгружаемых сообщений с чекбоксами. хм.
Если всё правильно, то я сохраню статичные чекбоксы так:
<!DOCTYPE HTML>
<html>
<head>
<title>Untitled</title>
<meta charset="utf-8">
</head>
<body>
<input type="checkbox" class="stat" name="column-1" value="1" checked >
<input type="checkbox" class="stat" name="column-2" value="2" checked >
<script>
(function(b) {
var e = document.querySelectorAll("." + b),
a = localStorage.getItem(b),
a = (a = JSON.parse(a)) || (a = {}, localStorage.setItem(b, JSON.stringify(a)));
[].forEach.call(e, function(c, d) {
void 0 !== a[d] && (c.checked = a[d]);
c.addEventListener("change", function() {
a[d] = c.checked;
localStorage.setItem(b, JSON.stringify(a))
})
})
})("stat");
</script>
</body>
</html>
Теперь надо замутить динамичное нажатие до 50-100 выгружаемых сообщений с чекбоксами. хм.
Всё, понял, оказалось проще простого: задал в value ид комментария, генерируемый серверной частью.
на второй картинке не то подчеркнул, но суть ясна
Нет. Каждый месяц, независимо от того, выплачиается долг или нет, к остатку долга прибавляется 3% + 1000.
Соответственно чем дольше платишь тем больше набежит.
Потому заемщик стремится выплатить как можно быстрее, но у него есть только 5000 в месяц.
>>841344
> (function(b) {
> })("stat");
Чем это лучше, чем var b = ".stat" ? И что за имя странное у переменной?
Событие change можно ловить не на каждом чекбоксе отдельно, а сразу на форме.
>>841513
А почему оно должно интерпретироваться? Браузер загружает скрипт отдельным запросом и сервер отдает его как статический файл. Тебе стоит перечитать про основы HTTP например тут https://github.com/codedokode/pasta/blob/master/soft/web-server.md
Твиг ведь не встраивается в веб-сервер и не обрабатывает все отдаваемые файлы. Он обрабатывает только тот файл который ему указывается при вызове render.
[code]
<?php
$filename = "base.db"; //Имя файла для прикрепления
$to = " @gmail.com"; //Кому
$from = " @gmail.com"; //От кого
$subject = "Еженедельное сохранение базы данных b"; //Тема
$message = "Сохранено, хозяин."; //Текст письма
$boundary = "---"; //Разделитель
/ Заголовки /
$headers = "From: $from\nReply-To: $from\n";
$headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\"";
$body = "--$boundary\n";
/ Присоединяем текстовое сообщение /
$body .= "Content-type: text/html; charset='utf-8'\n";
$body .= "Content-Transfer-Encoding: quoted-printablenn";
$body .= "Content-Disposition: attachment; filename==?utf-8?B?".base64_encode($filename)."?=\n\n";
$body .= $message."\n";
$body .= "--$boundary\n";
mail($to, $subject, $body, $headers); //Отправляем письмо
?>
[/code]
Этот код будет работать?
Пытаюсь написать скрипт, который отправлял бы мне раз в неделю бэкапы базы данных.
Не, я бы хотел сделать маленьким скриптом, возможно датабазу придётся аварийно перемещать.
1) в почтовых сервисах есть ограничения на размер вложений, порядка 10-20 Мб.
2) boundary надо выбирать тщательнее так как есть вероятность что он встретится в файле
3) в тему стоит добавить дату и укоротить ее
4) для сборки письма по частям и кодирования лучше бы использовать библиотеку либо функции из расширения для работы с почтой
5) в теле письма нет самого файла, если я не ошибаюсь, а только его имя. Ты уверен что это верно?
Я бы советовал тебе для начала почитать про MIME-кодирование писем и вложений, ан е копировать код бездумно.
Анон, помоги запилить скрипт:
Нужно чтобы при редиректе параметр $_GET["q"] подставлялся
в ссылку http://link.com/?q=, тем самым прописывая в ней параметры, которые она получила в гет. То есть, если я зашел на страницу по запросу, например
http://site.ru/?q=test, то меня должно перенаправить на
http://link.com/?q=test и присвоить ей этот параметр, test. Вот как бы это сделать?
Иными словами:
Я пришел на страницу http://site1/?q=test, а далее редирект на http://site2/?q=test. Причем test нужно достать из $_GET и подставить в http://site2/?q=
Ну ты заходишь на свою страницу по ссылке например:
http://site.ru/?name1=value1&name2=value2&name3=value3
Далее тебе надо спарсить гет-часть этой ссылки.
Всё это лежит в массиве $_GET в виде
["name1" => "value1", "name2" => "value2", "name3" => "value3"]
Я ничего не придумал кроме как в два действия склеить всё
Сначала клеим имена и значения в одномерный массив:
$tempArray = [];
foreach ($_GET as $name => $value) {
$tempArray[] = $name . "=" . $value;
}
Там получилось чет типа:
["name1=value1", "name2=value2", "name3=value3"];
Далее ты берешь и имплодом склеиваешь массив в строку используя & как склеиватель:
$getPart = implode("&", $tempArray);
В $getPart теперь лежит строка: "name1=value1&name2=value2&name3=value3"
Далее всё что тебе нужно это склеить новую ссылку для редиректа:
берешь значит свой новый линк: http://link.com/ добавляешь у нему ? и нашу гет-часть
$newLink = "http://link.com/" . "?" . $getPart;
Теперь там лежит то что нужно:
http://link.com/?name1=value1&name2=value2&name3=value3
Го редиректить:
header("Location: $newLink");
Вот собственно и всё
Ну ты заходишь на свою страницу по ссылке например:
http://site.ru/?name1=value1&name2=value2&name3=value3
Далее тебе надо спарсить гет-часть этой ссылки.
Всё это лежит в массиве $_GET в виде
["name1" => "value1", "name2" => "value2", "name3" => "value3"]
Я ничего не придумал кроме как в два действия склеить всё
Сначала клеим имена и значения в одномерный массив:
$tempArray = [];
foreach ($_GET as $name => $value) {
$tempArray[] = $name . "=" . $value;
}
Там получилось чет типа:
["name1=value1", "name2=value2", "name3=value3"];
Далее ты берешь и имплодом склеиваешь массив в строку используя & как склеиватель:
$getPart = implode("&", $tempArray);
В $getPart теперь лежит строка: "name1=value1&name2=value2&name3=value3"
Далее всё что тебе нужно это склеить новую ссылку для редиректа:
берешь значит свой новый линк: http://link.com/ добавляешь у нему ? и нашу гет-часть
$newLink = "http://link.com/" . "?" . $getPart;
Теперь там лежит то что нужно:
http://link.com/?name1=value1&name2=value2&name3=value3
Го редиректить:
header("Location: $newLink");
Вот собственно и всё
Спасибо тебе тебе большое, анончик!
в старых версиях
http://php.net/manual/ru/function.mysql-insert-id.php
В пдо изи гуглится же
http://php.net/manual/ru/pdo.lastinsertid.php
Об этом в пасте про студентов написано.
заплюют ли меня остальные программисты ?
Даже зная, что ты нуб я все равно так возмущен тобою написанным, что хочу тебя говном с брандспойта окатить.
Тебе тело цикла для чего дали? Ну и вывод помесячный тогда запихни в третий параметр for'а, тогда тело цикла вообще пустым будет, красота. (Нет)
Повторим
-Нативный PHP (в ООП не могу);
-Поверхностно знаю JS (могу делать банальную хуйню на стороне клиента) (jQuery не учил);
-Могу верстать, но по обезьяньи (Просто знаю как работать с html/css)
-Могу в MVC структуру;
-Никогда не практиковал, но поковырявшись понял WordPress (как сделать магазин например);
-С базами данных могу работать;
-Начинал учить Yii2, что-то понял что-то нет, но отложил поняв, что еще рано браться за него.
Из опыта делал для себя разные хайп-пирамидки лел переделывал не готовые скрипты, а писал с нуля, пару скриптов продал какимто школьникам по 1000р. Делал чатик чисто для опыта.
Эту зиму хочу хочу посвятить изучению, чтобы уверенно шарить в этом всем, дабы к весне (лету?) можно было рассылать свои маня-резюме или искать работку во фрилансах и т.д.
Повторюсь: за что конкретно взяться? Можешь накидать список в порядке приоритета, что учить? Какие фреймворки там?
почему разница в ответах я не понимаю ведь код по сути один и тот же просто по другому оформлен
Идеально не бывает.
На learn.javascript.ru многие пишут, что прохождение учебника занимает 4-6 месяцев, если вникать в весь материал и решать все задачи.
Я не знаю как ты пишешь сюда из 2003 года, но на всякий случай пройди со своим вопросом в
>>825576 (OP) - тредж
Спасибо, а ты проходил его? Сколько времени заняло?
Как-то долго для этого учебника. Может это для тех, кто с начала программирование изучает?
если шаришь напиши пожалуйста почему разница в ответах с первым скрином
Нужно опрашивать кучу урлов (куча обращений к апи различных соцсетей)
Делаю это через мультикурл, и, в принципе, всё довольно быстро обрабатывается, но:
В массиве с ответами от сервера, во многих местах, вместо строки ответа висит объект curl, хотя я каждый результат запроса обрабатываю через curl_multi_getcontent().
Что с этим сделать можно? Уменьшит количество заспросов в одном мультикурле?
На пике пример ответа
>если шаришь напиши пожалуйста почему разница в ответах с первым скрином
Причина в том, что во втором скрипте ты прибавляешь к переменной $all значение переменной $creditBalance
SELECT tt., COUNT() dep_total FROM (SELECT dep.name as depname, dep.department_id, po.name as poname, COUNT(*) as post_total FROM department dep INNER JOIN post po ON dep.department_id=po.department_id INNER JOIN designations des ON des.post_id=po.post_id AND des.leave_date is NULL GROUP BY dep.department_id, po.post_id ) as tt GROUP BY tt.department_id;
Считает правильно, но из-за группировки по отделу, показывает только одну должность на отдел. Как быть?
даже если убрать ту разницу что написал ты все равно там будет 12 а там 13
Подробнее?
Выражения находящиеся внутри условий цикла выполняются после каждой итерации, перед проверкой условий цикла. $month++ в первом случае находится внутри условий, а во втором, в теле цикла.
access = age > 14 ? true : false;
и используем уродливую многоэтажную запись:
if (age > 14) {
access = true;
} else {
access = false;
}
?
Использую, когда считаю нужным. Достаточно часто, тот же свич я гораздо реже использую например. В чем суть проблемы то?
Зачем нужен композер, когда через PDO идёт подключение и работа со вмем и ко всему применяя 4 строчки кода? Я не понимат.
Чот посмотрел щас видео про композер и элокент, так ничего и непонял. Главное не понял ЗАЧЕМ? Зачем нужно вот это вот всё?
Композер это менеджер пакетов (библиотек). Как бонус, он также содержит автозагрузчик. Ты понимаешь, что значит "менеджер (т.е. тот кто управляет) пакетов (т.е. библиотек)"?
Если твой скрипт содердит 4 строчки с PDO то композер ему вряд ли нужен. Но вот реальные приложения содержат не 4, а 40 000 - 400 000 строк например.
Также, Eloquent это ORM работающий по модели ActiveRecord если я не путаю. У меня есть урок по теме https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Прочти его для начала.
Ну и не очень понятно зачем ты берешся смотреть урок, не изучив паттерны работы с базой данных (из моего урока), что такое пакеты и менеджер пакетов. У теюя непонимание из-за того что не изучил всю теорию которую нужно знать до просмотра урока.
Добавлю анона выше: композер может лочить версии зависимостей. Если у тебя в репозитории есть .lock-файл, то твой друг может спустя год стянуть твой репозиторий и командой composer install поставить те же самые версии библиотек, что использовал ты год назад и всё будет работать.
>>842130
Ну визитки-то и на программирование не шибко тянут.
>визитки-то и на программирование не шибко тянут.
Не оправдывает внедрения в них композера только ради того, чтобы элокент поставить, который будет в свою очередь только запоминать мэйлы клиентов.
Оправдывает. Если ты не начинающий то тебе проще написать быстренько composer.json и пользоваться высокоуровневой библиотекой, чем в сотый раз писать однотипные селекты. Ну и плюс это урок именно по этой библиотеке.
Конкретно у того анона, что задал вопрос, проблема в том что он не изучает последовательно, а проскакивает темы и берется сразу за сложное. Я бы советовал порешать нашу задачку про студентов для начала и на ней основы изучить. Можно с композером и элоквентом, можно с ПДО.
k? Порешаю вашу задачку.
Она?
https://github.com/codedokode/pasta/blob/master/student-list.md
В начале статьи они говорят что надо шаблоны отделять от логики отображения. А потом вот это...
>Если ты не начинающий то тебе проще написать быстренько composer.json и пользоваться высокоуровневой библиотекой,
И получить гору ошибок ковыряясь в куче кода неделю.
return $this->view->render($response, 'upload.html', $args);
а именно в render. Содержание шаблона не играет роли - висяки даже когда он пустой.
Вчера прошло само, сегодня снова то же самое
Криворуким вообще тяжело живётся.
Чем они отличаются, и для чего используют каждый из этих языков (именно в веб разработке)? В какой ситуации лучше использовать PHP (Symphony/Yii), а в какой RUBY (ROR) или PYTHON (Django)?
Ты делаешь файлообменник?
Ты уверен что зависает именно пхп? МОжет он отдает страницу на на ней подкючен скрипт или картинка с другого домена (например бутстрап из CDN ) который не загружается?
Открой отладчик в браузере (ctrl + shift + I) на вкладке Network и проверь.
Ну и не используй CDN.
>>842322
Python - cli скрипты, десктопные приложения (дропбокс например), веб-приложения
Руби - веб-приложения, cli приложения (vagrant например)
Считается что у них более аккуратный и лаконичный синтаксис, но есть и обратные стороны, например нет тайп хинтов, излишняя динамичность (в том же руби одна библиотека патчит другую и они это считают нормальным).
В общем-то ниша пересекается с пхп.
Если не ошибаюсь с выходом пхп7 и проведенными бенчмарками было показано, что новая версия пхп работает значительно производительнее и руби и питона, так? Разве это не плюс для бекенда?
На 5 и ниже не видел, видел бенчи с пхп7, порадовался за него.
Пыха и раньше была быстрее, в том числе и на хайлоад
Не знаю про руби но питон вообще славился тем, что это тормоз ещё лет эдак 7 назад.
лол ты смотрел там 1 скрипт всего
у питона много своих + некоторые вещи делаются элегантней , но не в вебе
Лол, я 7 лет назад писал на нём билинг для одной крупной компании, невероятно офигел когда обычная операция выборки из бд занимала от 10 секунд, и все считали нормальным. А отчётик в Excel сохранить - так вообще 10 минут, при том что сам отчёт формировался секунд 10, а всё остальное время - он данные обрабатывал.
> Соответственно, количество человек определяется классом Driver
Агрегация и Композиция, прочитай.
>Объект Car должен содержать следующие члены:
>1. Свойства Manufacturer, Model, MaxSeatNumber
>2. Методы:
>a. Array getPersons() – возвращает список людей в машине
>b. removePerson($person) – удаляет человека из машины
>c. addPerson($person) – добавить человека в машину
Так понятней?
>>842778
Класс Car содержит несколько объектов класса Driver, например в массиве, и в методе getDriver (если он считает лучше бы его назвать countDriver) просто возвращает кол-во элементов массива. А вот как получать объекты Driver и на каком этапе - решать тебе.
Интересно, оп в лофте работает? У них всё обучение 1 в 1 как у опа на сайте.
>занимала от 10 секунд
Пиздец. У нас когда в логах слоу квери от 10 секунд появилась весь день искали и решали проблему с локами таблицы одной, добились нормальной быстрой работы. Блядь, да даже когда за 0.5 выходит бубалех случается и начинаются поиски причины и оптимизации. Как можно работать, чтобы допускать запросы от 10 секунд?
>начинаются поиски причины и оптимизации
Когда я спрашивал такие вопросы, мне разводили руки и говорили "А что ты хотел, вот так вот он работает. Зато надёжно." А я сидел и тихо охреневал. Создаешь такой запрос по заказу начальства, запускаешь его, и уходишь курить на пол часа. Если повезёт - может ты вернёшься на место, а он результат выведет.
Алсо тогда я предпринял попытку перевести всё на PHP но у начальника был какой-то предрассудок и он пол дня орал про то, как он спит и видит как хакеры воруют его БД.
Нет, просто не понимаю, каким образом занести несколько объектов в массив Вот такой я тугой, да обычным while?
Поделись ссылкой, чё да как.
Если я правильно понял вопрос, то писать тесты не требуется, но если ты сделаешь то будет неплохо.
Мне он просто отвратителен без объективных причин. Но я и не навязываю.
Я тут ваши задания выполнять только начал и сейчас на задачке с айфоном в кредит, ну точнее на той главе, но на задачке про 10000 и как быстро в банке накапает лям.
нужно чтобы он прям числом количество лет вывел ответ или сойдет вариант при котором просто циклы будут выводиться до момента когда миллион будет и потом посчитать их?
простите за сумбурность
Сделай переменную $i в цикле for, с каждой итерацией эта переменная должна увеличиваться на единицу, в конце выведешь эту переменную в echo, получишь количество месяцев.
И это нужно, и сумма на момент остановки цикла, там же не ровно миллион будет.
Что касается клоунов у которых сайт в опере мини выглядит как черный квадрат - я думаю, это многое говорит об их квалификации и опыте.
Можно вывести только итог, можно вывести как ситуация меняется каждый год. Второе я думаю удобнее, так как наглядно видно как растет сумма.
>>843082
Редактор должен нравиться тебе или кому-то другому?
>>842793
$o1 = new Object;
$o2 = new Object;
$array = [$o1, $o2];
или
$array = [];
$array[] = $o1;
$array[] = $o2;
Если ты этого не знал, то советую перечитать главы про массивы и классы в моем или любом другом учебнике.
>>842791
Одно дело 10 секунд в скрипте генерирующем данные для веб-страницы, другое дело - оффлайновый скрипт который выполняется по крону и обрабатывает большой объем данных. Миллион строк все равно быстро не обработать.
>>842787
Нет и не знаю, что это. Все мои уроки и задачи придуманы самостоятельно без оглядки на другие курсы.
>>842774
> Существует два метода. Назовём их, например Car и Driver.
Не метода, а класса. Тебе бы надо теорию перечитать по классам.
> В классе Car есть метод в виде массива
Не в виде массива, а возвращающий массив может быть?
> количество человек определяется классом Driver.
Не классом Driver, а количеством объектов Driver хранящихся в объекте Car наверно
> Каким образом несколько раз подряд занести в метод класс Drive
каким образом добавить в массив несколько объектов класса Driver
Тебе стоит перечитать теорию, потому что есть ощущение что ты даже термины вроде "класс" и "метод" путаешь.
Можно вывести только итог, можно вывести как ситуация меняется каждый год. Второе я думаю удобнее, так как наглядно видно как растет сумма.
>>843082
Редактор должен нравиться тебе или кому-то другому?
>>842793
$o1 = new Object;
$o2 = new Object;
$array = [$o1, $o2];
или
$array = [];
$array[] = $o1;
$array[] = $o2;
Если ты этого не знал, то советую перечитать главы про массивы и классы в моем или любом другом учебнике.
>>842791
Одно дело 10 секунд в скрипте генерирующем данные для веб-страницы, другое дело - оффлайновый скрипт который выполняется по крону и обрабатывает большой объем данных. Миллион строк все равно быстро не обработать.
>>842787
Нет и не знаю, что это. Все мои уроки и задачи придуманы самостоятельно без оглядки на другие курсы.
>>842774
> Существует два метода. Назовём их, например Car и Driver.
Не метода, а класса. Тебе бы надо теорию перечитать по классам.
> В классе Car есть метод в виде массива
Не в виде массива, а возвращающий массив может быть?
> количество человек определяется классом Driver.
Не классом Driver, а количеством объектов Driver хранящихся в объекте Car наверно
> Каким образом несколько раз подряд занести в метод класс Drive
каким образом добавить в массив несколько объектов класса Driver
Тебе стоит перечитать теорию, потому что есть ощущение что ты даже термины вроде "класс" и "метод" путаешь.
https://github.com/codedokode/pasta/blob/master/db/databases.md
https://github.com/codedokode/pasta/blob/master/db/normalization.md
Такое ощущение, что, либо ты работаешь ночью, либо ты живешь во Владивостоке.
Ошибки не будет. в пхп нет объявлений переменных, которые анализируются на этапе компиляции, как в Си. В нем переменные создаются динамически по мере выполнения программы.
>>827649
В пхп нет "объявлений" переменных для компилятора. Команда вроде $x = 1; просто создает переменную, когда выполнение дойдет до этой строчки.
>>828182
Надо смотреть логи ошибок. В случае https, может не быть нужного расширения или сертификатов.
>>828460
Проще всего руками сделать скриншот и сохранить. Или как-то через phantomjs
>>829085
Оно вроде само генерируется, можно исключить эту папку из деплоя. Ну или как альтернатива, можно у себя сгенерировать и выгрузить, чтобы на сервере не тратить на это время.
>>828827
Для маленького сайта подойдет любой, и Lumen, и Slim, и Silex.
>>830421
Мне некогда смотреть, но я бы сделал следующее:
- нашел описание этих метатегов, открыл исходный HTML код страницы в браузере и проверил, все ли сделано верно
- проверил бы ссылку на картинку: открывается ли она если ее открыть в браузере
- создал бы новую страницу с этим метатегом, запостил ее в соцсети и проверил бы логи веб-сервера - были ли обращения от соцсети к странице, к картинке?
Насчет кеша - для надежной борьбы с ним можно просто поменять URL страницы и картинки.
Ошибки не будет. в пхп нет объявлений переменных, которые анализируются на этапе компиляции, как в Си. В нем переменные создаются динамически по мере выполнения программы.
>>827649
В пхп нет "объявлений" переменных для компилятора. Команда вроде $x = 1; просто создает переменную, когда выполнение дойдет до этой строчки.
>>828182
Надо смотреть логи ошибок. В случае https, может не быть нужного расширения или сертификатов.
>>828460
Проще всего руками сделать скриншот и сохранить. Или как-то через phantomjs
>>829085
Оно вроде само генерируется, можно исключить эту папку из деплоя. Ну или как альтернатива, можно у себя сгенерировать и выгрузить, чтобы на сервере не тратить на это время.
>>828827
Для маленького сайта подойдет любой, и Lumen, и Slim, и Silex.
>>830421
Мне некогда смотреть, но я бы сделал следующее:
- нашел описание этих метатегов, открыл исходный HTML код страницы в браузере и проверил, все ли сделано верно
- проверил бы ссылку на картинку: открывается ли она если ее открыть в браузере
- создал бы новую страницу с этим метатегом, запостил ее в соцсети и проверил бы логи веб-сервера - были ли обращения от соцсети к странице, к картинке?
Насчет кеша - для надежной борьбы с ним можно просто поменять URL страницы и картинки.
> МОжет он отдает страницу на на ней подкючен скрипт или картинка с другого домена
Я ж говорю, виснет даже если в шаблоне ничего нет, пустой файл. Бутстрапы еще не подключал
В network тоже никакой информации
Значит какая-то проблема с твоим хостингом, сетью, PHP, Апачом, браузером или еще чем-то. Ну например ты можешь взять другой браузер с чистой историей и без расширений и тестировать в нем. Если ты тестируешь локально то может ты что-то сломал в конфигах.
В любом случае мистики никакой быть не может, есть какая-то конкретная причина. Я бы для начала взял другой чистый браузер.
И кстати как ты определил, в какой строчке причина? Где ты запускаешь код, у себя или на удаленном хостинге? Как выглядит что "страница не прогружается".
Запускаю у себя. Как-как, комментированием. В итоге решил углубиться и обнаружил, что глючит на строчке 557: $this->lexer = new Twig_Lexer($this);
файл
D:\xampp\htdocs\filehosting\vendor\twig\twig\lib\Twig\Enviroment.php
Даже если __construct класса сделать пустым.
В другом браузере то же самое. Попробую поставить параллельно denwer сейчас xampp, посмотрю как на нём поедет
нет, к томуже со временем он оприменилось и говорюже на другом браузере сразу с изменениями открыло
Ничего браузер не делает. Браузерные расширения для блокировки рекламы или других целей могут добавлять свои скрипты на страницу.
Но скорее всего дело не в этом. ЧТо значит "не изменяется"? Открой отладчик (Ctrl + Shift + I) и посмотри текст скрипта, а на влкадке Network можно увидеть откуда он грузится, с сервера или из кеша, а по заголовкам понять почему.
Подскажи эту штуку, плз
>Браузерные расширения
пробую с Microsoft Edge - та же хуйня, хотя расширений там естесно нет
>ЧТо значит "не изменяется"?
то, что я могу удалить весь код из файла и сохранить его, а сайт продолжает работать, так какбудто код остался
>Ctrl + Shift + I
Ничего не происходит в FF и ME
Такая хуита уже не первый раз, соответственно я не путаю файл с которым работаю, к томуже со временем изменения таки применяются, как я написал выше
Вот сейчас почистил кеш и перезагрузил браузер и все норм применилось и изменяю файл и изменения сразу происходят
Все нашел, кому интересно это атрибут: contenteditable
Судя по тому, что не копируется папка с проектом, прооблемы с ФС
Пользуюсь Komodo IDE 8.5.
Файлы имеют расширение .html, но включают PHP код. Подсвечиваются либо PHP либо HTML теги.
Также не понятно как быть с отступами, в html вместо 4х пробелов стандарт - 2 пробела?
И как совмещать две иерархические структуры, т.е. у меня есть <DIV>, в него вложен <P> в котором <A>, и есть структура PHP блоков IF : ELSE : ENDIF. И теги обоих структур находятся шахматном порядке. Должна быть она общая иерархия? Есть единый гайд насчет этого?
Как вы совмещаете php/html? Как вы организовываете работу в IDE c View-файлами?
Попробуй расширения вроде phtml или php. Тогда может будет подсвечиваться и то и другое.
Отступы надо стараться соблюдать, где это возможно.
2 пробела используется при большой глубине вложенности тегов.
На последнем этапе валит ошибками (см. скрин 1). Это критично?
Судя по тому что на скрине 2, критично. Но чет я не уверен, что нужно перловские скрипты мне править. Я в тупике, в общем
время ставить линукс
Чтобы ОП был осведомлен что я не пропустил эту часть.
ОП подзабил кстати на проверку работ падаванов, я уже второй трендж ожидаю, например.
Видимо скрипт на перле немного устарел.
Ты можешь посмотреть исходники - если ты знаешь php то перл легко поймешь заглядывая иногда в гугл.
Принцип работы, как я понял, такой:
- скрипт configure.pl определяет разные настройки, нужные для компиляции и создает скрипт в котором они сохранены с именем вроде apr-1-config.pl. Он делает это не сам, а вызывая по очереди скрипты apr_win32.pl, apu_win32.pl
(согласен, что немного странно, но в линуксе такая система часто используется, при установке программы устанавливается баш скрипт который возвращает настройки с которыми она была скомпилирована).
- также он вызывает apxs_win32.pl, который создает apxs.bat
Как я понимаю, там должны быть переменные CC и LD указывающие на имя компилятора и линковщика, но он не смог определить какой у тебя установлен компилятор и потому команда вида
$CC /nologo ....
Превратилась в просто /nologo .... что и привело к выдаче ошибки.
Можно попробовать это проверить, там создается скрипт apr-1-config.pl или как-то так и он при вызове
perl apr-1-config.pl --cc
должен выдать имя компилятора.
Может дело в том, что у тебя не установлен компилятор? Судя по аргументам это должен быть Visual Studio C++ Express или другая версия Visual Studio. Опять же, есть вероятность что скрипт старый и не сможет правильно запустить новую версию компилятора.
В любом случае, скрипт кривой, так как при отстутсвии компилятора он должен об этом сообщать а не пытаться выполнить нерабочую команду. Также, там есть deprecated сообщения об устаревшем синтаксисе.
Также, ты используешь xampp, а не чистый Апач, возможно, это как-то влияет (хотя я сомневаюсь, мне кажется это скрипт кривой). Надо чтобы в Апаче была папка include с сишными заголовками.
Потому тут такие варианты:
- попробовать установить компилятор
- самому поправить скрипт и отправить разработчикам испарвления
- найти уже скопилированную кем-то библиотеку (вроде тут есть https://www.apachelounge.com/download/ но она может быть совместима только с их бинарником апача)
- использовать линукс
- не использовать xsendfile
Вообще, если есть время, я бы советовал помучаться и выбрать путь посложнее. Если ты будешь работать программистом, то у тебя тоже будут похожие ситуации и навыки поиска причины проблемы тебе бы пригодились.
Видимо скрипт на перле немного устарел.
Ты можешь посмотреть исходники - если ты знаешь php то перл легко поймешь заглядывая иногда в гугл.
Принцип работы, как я понял, такой:
- скрипт configure.pl определяет разные настройки, нужные для компиляции и создает скрипт в котором они сохранены с именем вроде apr-1-config.pl. Он делает это не сам, а вызывая по очереди скрипты apr_win32.pl, apu_win32.pl
(согласен, что немного странно, но в линуксе такая система часто используется, при установке программы устанавливается баш скрипт который возвращает настройки с которыми она была скомпилирована).
- также он вызывает apxs_win32.pl, который создает apxs.bat
Как я понимаю, там должны быть переменные CC и LD указывающие на имя компилятора и линковщика, но он не смог определить какой у тебя установлен компилятор и потому команда вида
$CC /nologo ....
Превратилась в просто /nologo .... что и привело к выдаче ошибки.
Можно попробовать это проверить, там создается скрипт apr-1-config.pl или как-то так и он при вызове
perl apr-1-config.pl --cc
должен выдать имя компилятора.
Может дело в том, что у тебя не установлен компилятор? Судя по аргументам это должен быть Visual Studio C++ Express или другая версия Visual Studio. Опять же, есть вероятность что скрипт старый и не сможет правильно запустить новую версию компилятора.
В любом случае, скрипт кривой, так как при отстутсвии компилятора он должен об этом сообщать а не пытаться выполнить нерабочую команду. Также, там есть deprecated сообщения об устаревшем синтаксисе.
Также, ты используешь xampp, а не чистый Апач, возможно, это как-то влияет (хотя я сомневаюсь, мне кажется это скрипт кривой). Надо чтобы в Апаче была папка include с сишными заголовками.
Потому тут такие варианты:
- попробовать установить компилятор
- самому поправить скрипт и отправить разработчикам испарвления
- найти уже скопилированную кем-то библиотеку (вроде тут есть https://www.apachelounge.com/download/ но она может быть совместима только с их бинарником апача)
- использовать линукс
- не использовать xsendfile
Вообще, если есть время, я бы советовал помучаться и выбрать путь посложнее. Если ты будешь работать программистом, то у тебя тоже будут похожие ситуации и навыки поиска причины проблемы тебе бы пригодились.
Можно скинуть те что посложнее так как робот проверяет только результат но не смотрит на качество кода.
>>844285
Погодите еще чуть-чуть. Я в этот треде уже почти до 150 поста дошел.
>>843211
Учитывая что ОП эти уроки сделал лет так 5 назад, а лофтблогу года два, скорее это они у него взяли. Хотя я думаю там всё стандартное. Преподаёшь массивы, и внезапно array тут сложно спорить. Ну и задачи немного таки различаются. Тот же список студентов - это список узеров сайта, да и прикручивать к нему надо капчу, мэйлер и пр.
Мельком глянул - у них по-другому. В ООП много замудреных слов, по их словам "ООП медленнее работает", сами примеры по ООП упрощенные, в обычном коде HTML вперемешку с кодом.
>в обычном коде HTML вперемешку с кодом.
Эм, ну такого бреда нет, или скинь про что ты говоришь. После MVC урока, такого не видел. А до этого формат требовал такие вставки делать, чтобы упростить донесения материала я думаю.
Ну ты же понимаешь что это ставится вовсе не для HTML?
Я djn как раз в эти гитхабы вхожу).
ОП, я напомню о себе как бы невзначай.
https://github.com/never3ver/students_list
И начал делать файлообменник, пока реализован базовый функционал + превьюшки + id3tag:
https://github.com/never3ver/fileshare
> найти уже скомпилированную кем-то библиотеку
Это помогло, спасибо.
> Вообще, если есть время, я бы советовал помучаться и выбрать путь посложнее. Если ты будешь работать программистом, то у тебя тоже будут похожие ситуации и навыки поиска причины проблемы тебе бы пригодились.
Вчера полдня с этим возился. А позавчера с другим. Ты, вообще, прав, но сейчас хочется именно попрограммировать, а не копаться
Значение $path="D:\xampp\htdocs\filehosting2\public\files\2016\09\18\tumblr_ny1r8pfaBf1ref7bto1_500.png"
В папке files в .htaccess прописал XSendFile On
Так же, не смотря на то, что я кодирую название файла и он выглядит так в оригинале "название файла.png":
<a href="/filehosting2/public/download/66/%D0%BD%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20%D1%84%D0%B0%D0%B9%D0%BB%D0%B0.png">Скачать</a>
при скачивании он обрезается до пробела (см. картинку)
Почему?
>Погодите еще чуть-чуть. Я в этот треде уже почти до 150 поста дошел.
Как можно тебе помочь?
https://github.com/never3ver/students_list
Ну ты уже не совсем новичок раз студентов делаешь. Новичков я насчитал тут 1-2, остальные либо мимо, либо уже делают серьезные задачи.
<?php
for ($i = 10000; $i <= 1000000; $i * 0.1) {
echo "($i) рублей у меня стало в этом году\n";
}
$i * 0.1
Эта команда делает умножение, но результат никуда не сохраняет и просто отбрасывает. чтобы сохраниь результат, надо писать $i = .... то есть исплоьзовать команду присваивания (=).
Спасибо,о Мудрейший
Не ОП. Не вижу другого способа, кроме как отвечать на вопросы анонов, если хватает компетенции. Бывает, даже благодарят. То есть ОПу уже отвечать/проверять меньше.
>Калькулятор: http://ideone.com/a3O2X9
>-2.43+3.62/2.1*9.02=5.1113333333333
Странно, гугл выдал 13.1187619048
Что-то у тебя всё не понятно с алгоритмом. Я бы сделал так:
Разбил строку на данные и операторы с помощью регулярных выражений
Определил приоритет выполнения действий
Выполнил действия
https://ideone.com/JibpcY
Возможны баги с отрицательным выражением без пробелов и отрицательным числом
Аноны, оцените моё решение тоже. Как вы думаете мне стоит тоже начинать помогать новичкам?
Я лично считаю, что у меня может быть не понятно почему я получаю новую строку и рекурсивно её считаю вместо того чтобы просто изменить имеющуюся и продолжать её вычислять. А дело в том, что в месте где мы считаем действия с 33 строки, при переназначении выражения на число, мы теряем ключи(номера позиции) данных и вместе с этим теряем возможность обращаться к предыдущему или следующему элементу от стоящего между ними элемента(оператора).
Я всегда замечал с собой то что я иногда переусложняю код, что говорит о моих пробелах в знаниях php. Я боюсь что мои подходы будут плохими примерами.
Спасибо за отзыв, но гугл выдает другой ответ, так как у него есть приоритет действий, а я делал без, как и были сказано в задании :)
http://devacademy.ru/posts/struktura-vieb-prilozhienii/
Ок, с приоритетом действий действительно было намного сложнее.
Паттерн Front Controller описанный Зандстра включает три класса, сам Front Controller, Command и Request. На самом деле там больше классов, но я немного упростил, чтобы легче было разобраться. Первый выполняет инициализацую - загрузку настроек и создает Request и передает его классу Command. Request - превращает данные из глобального массива $_SERVER в параметры запроса (разбирает строку запроса на параметры). Command отвечает за запуск нужного класса соответствующего параметрам класса Request.
В нашем случае запросы от клиента могут выглядеть так:
..\index.php
..\index.php?notify=registered
..\index.php?search=cat&sort=name&page=2&order=ASC
..\register.php
Что должен сделать класс Command в данном контексте? Подгрузить страницу index.php или register.php?
Шаблон довольно абстрактно описан, Command запускает код, а что это за код - контроллер, класс или представление уже решаю я самостоятельно?
Еще я видел такое объяснение на StackOverflow, где был единственный класс FC, который с помощью конструкции из IF запускал нужные страницы.
А можно отдельно использовать класс Request::getInstance() и потом его передавать в качестве аргумента в GateAway->getStudents( Request::getInstance() ) ?
Выгода от использование этого паттерна в нашем случае - можно вынести код загрузки init.php и аутенфикацию в FC. А также в будущем добавить страниц?
Еще вопрос, как будет выглядеть структура файлов?
Я могу в ..\public отставить файл index.php - это и будет Front Controller, а текущие контролеры index2.php и registered.php перенести в папку ..\controllers. index2.php можно будет переименовать в studentlist.php.
Или оставить все как есть, но добавить в ..\app FrontController.php и настроить .httaccess, чтобы все обращения к сайту вызывали этот файл? Тогда можно вообще отказаться от папки public и перенести все в ..\app?
Собсна по коду все понятно. Но вот он не работает. Входящий и исходящий массивы идентичные. В чем проблема?
Сделал. Так и думал что из-за двойного foreach запретит редактирование элементов.
> использую array_walk_recursive для mysqli_real_escape_string: в POST есть двумерный массив, а в array_walk в callback'е требуется echo
Значит надо не использовать там echo. Очевидно что этот echo добавляет данные в тело ответ и документ повреждается.
Более того, данные ты тоже экранируешь неграмотно: их надо экранировать в месте использования, в момент вставки в SQL запрос.
В общем, надо переделывать и менять подход с экранированием данных.
>>845041
$year это копия элемента массива, а не сам элемент. Вместо возни с указателями гораздо лучше создавать новый массив.
>>844928
В public хранится только то, что можно скачать или вызвать из браузера. Если у тебя за все отвечат Front Controller то в папке должен быть только index.php который его создает и запускает (если это класс).
> Или оставить все как есть, но добавить в ..\app FrontController.php и настроить .httaccess, чтобы все обращения к сайту вызывали этот файл?
Я сомневаюсь что это возможно так как htaccess только переписывает УРЛ (а не реальные пути к файлам) и потому не может никак указать на файл не в публичной папке.
>>844920
Вообще, паттерн Command применяют не прото так. Он пришел из десктопных приложений (вроде Ворд, Фотошоп), так как там действительно есть команды, которые может вызывать пользователь, и хочется иметь объект, представляющий эту команду (например для того чтобы можно было использовать наследование от базового класса или вести историю команд с возможностью отмены, чтобы можно было отслеживать возможность выполнения команды и делать кнопку на панели инструемнтов активной/неактивной - к примеру кнопка Вставить активируется только когда буфер обмена не пуст, кнопка Сохранить активируется если есть несохраненные изменения в файле).
Я не уверен что этот паттерн нужен в простых веб-приложениях. В сложных еще можно его как-то обосновать (сделать команду регистрации чтобы использовать ее в нескольких местах), но я не уверен что это нужно в простых. В веб-приложении, в отличие от десктопного, эти возможности не нужны.
То есть ты должен для начала определиться, почему ты хочешь использовать паттерн Команда. Должна быть какая-то прияина его использовать.
Ну и если посмотреть на схему взаимодействия классов, то в наши дни обычно делают так:
- фронт контроллер определяет какой контроллер и какой его метод будет обрабатывать запрос
- создает объект контроллера
- вызывает нужный метод
Как видишь, это очень напоминает архитектуру из Зандстры, только вместо объектов-Команд у нас команда - это просто метод в контроллере.
У меня есть подозрение что те, кто придумали эту схему, взяли ее из дестопных приложений.В них действительно в ответ на нажатия клавиш, клики мышью создаются объекты-Команды и отправляются на исполнение.
> использую array_walk_recursive для mysqli_real_escape_string: в POST есть двумерный массив, а в array_walk в callback'е требуется echo
Значит надо не использовать там echo. Очевидно что этот echo добавляет данные в тело ответ и документ повреждается.
Более того, данные ты тоже экранируешь неграмотно: их надо экранировать в месте использования, в момент вставки в SQL запрос.
В общем, надо переделывать и менять подход с экранированием данных.
>>845041
$year это копия элемента массива, а не сам элемент. Вместо возни с указателями гораздо лучше создавать новый массив.
>>844928
В public хранится только то, что можно скачать или вызвать из браузера. Если у тебя за все отвечат Front Controller то в папке должен быть только index.php который его создает и запускает (если это класс).
> Или оставить все как есть, но добавить в ..\app FrontController.php и настроить .httaccess, чтобы все обращения к сайту вызывали этот файл?
Я сомневаюсь что это возможно так как htaccess только переписывает УРЛ (а не реальные пути к файлам) и потому не может никак указать на файл не в публичной папке.
>>844920
Вообще, паттерн Command применяют не прото так. Он пришел из десктопных приложений (вроде Ворд, Фотошоп), так как там действительно есть команды, которые может вызывать пользователь, и хочется иметь объект, представляющий эту команду (например для того чтобы можно было использовать наследование от базового класса или вести историю команд с возможностью отмены, чтобы можно было отслеживать возможность выполнения команды и делать кнопку на панели инструемнтов активной/неактивной - к примеру кнопка Вставить активируется только когда буфер обмена не пуст, кнопка Сохранить активируется если есть несохраненные изменения в файле).
Я не уверен что этот паттерн нужен в простых веб-приложениях. В сложных еще можно его как-то обосновать (сделать команду регистрации чтобы использовать ее в нескольких местах), но я не уверен что это нужно в простых. В веб-приложении, в отличие от десктопного, эти возможности не нужны.
То есть ты должен для начала определиться, почему ты хочешь использовать паттерн Команда. Должна быть какая-то прияина его использовать.
Ну и если посмотреть на схему взаимодействия классов, то в наши дни обычно делают так:
- фронт контроллер определяет какой контроллер и какой его метод будет обрабатывать запрос
- создает объект контроллера
- вызывает нужный метод
Как видишь, это очень напоминает архитектуру из Зандстры, только вместо объектов-Команд у нас команда - это просто метод в контроллере.
У меня есть подозрение что те, кто придумали эту схему, взяли ее из дестопных приложений.В них действительно в ответ на нажатия клавиш, клики мышью создаются объекты-Команды и отправляются на исполнение.
> Что должен сделать класс Command в данном контексте? Подгрузить страницу index.php или register.php?
Если следовать Зандстре то там Команда отвечает за обработку запроса и вывод страницы. Соответственно у тебя были бы ShowStudentListCommand и ProfileCommand (регистрация + редактирование) и их задача обработать запрос и вернуть View с нужными данными, который затем выведет страницу.
То есть у Зандстры Command принимает Request и возвращает View.
> Request - превращает данные из глобального массива $_SERVER в параметры запроса (разбирает строку запроса на параметры).
Request это класс объект которого и представляет запрос. Это позволяет нам передавать, возвращать, модифицировать, копировать запрос и делать подобне вещи. Вообще, это скорее хорошая штука, а вот суперглобальные переменные в пхп - плохая, так как они доступны даже там, где это не требуется.
> А можно отдельно использовать класс Request::getInstance()
Синглтон это плохой в общем-то паттерн. Вот подумай, почему ты хочешь чтобы запрос был доступен глобально, а не чтобы он создавался в одном месте и передавался только куда нужно? Очевидно с точки зрения дизайна второе красивее, а синглтон - это возврат к суперглобальным переменным.
> Шаблон довольно абстрактно описан, Command запускает код, а что это за код - контроллер, класс или представление уже решаю я самостоятельно?
Ну попробуй сам его описать. В десктопных приложениях обычно Команда это класс, в котором есть метод execute() - выполнить команду (в MVC приложении обычно это сводится к изменению модели, ну например в воображаемом текстовом редакторе команда DeleteWord находит в модели документа текущий абзац и удаляет из него выделенное слово) и иногда undo() - он позволяет отменить выполнение команды.
Ну и по желанию могут быть дополнительные методы, например isAvailable() - доступна ли команда в данный момент?
Но это в десктопных приложениях, в вебе там по сути только метод execute и может быть.
> Выгода от использование этого паттерна в нашем случае - можно вынести код загрузки init.php и аутенфикацию в FC. А также в будущем добавить страниц?
Да, выгода в тм что все запросы проходят через один класс и можно что-то с ними делать со всеми.
> Что должен сделать класс Command в данном контексте? Подгрузить страницу index.php или register.php?
Если следовать Зандстре то там Команда отвечает за обработку запроса и вывод страницы. Соответственно у тебя были бы ShowStudentListCommand и ProfileCommand (регистрация + редактирование) и их задача обработать запрос и вернуть View с нужными данными, который затем выведет страницу.
То есть у Зандстры Command принимает Request и возвращает View.
> Request - превращает данные из глобального массива $_SERVER в параметры запроса (разбирает строку запроса на параметры).
Request это класс объект которого и представляет запрос. Это позволяет нам передавать, возвращать, модифицировать, копировать запрос и делать подобне вещи. Вообще, это скорее хорошая штука, а вот суперглобальные переменные в пхп - плохая, так как они доступны даже там, где это не требуется.
> А можно отдельно использовать класс Request::getInstance()
Синглтон это плохой в общем-то паттерн. Вот подумай, почему ты хочешь чтобы запрос был доступен глобально, а не чтобы он создавался в одном месте и передавался только куда нужно? Очевидно с точки зрения дизайна второе красивее, а синглтон - это возврат к суперглобальным переменным.
> Шаблон довольно абстрактно описан, Command запускает код, а что это за код - контроллер, класс или представление уже решаю я самостоятельно?
Ну попробуй сам его описать. В десктопных приложениях обычно Команда это класс, в котором есть метод execute() - выполнить команду (в MVC приложении обычно это сводится к изменению модели, ну например в воображаемом текстовом редакторе команда DeleteWord находит в модели документа текущий абзац и удаляет из него выделенное слово) и иногда undo() - он позволяет отменить выполнение команды.
Ну и по желанию могут быть дополнительные методы, например isAvailable() - доступна ли команда в данный момент?
Но это в десктопных приложениях, в вебе там по сути только метод execute и может быть.
> Выгода от использование этого паттерна в нашем случае - можно вынести код загрузки init.php и аутенфикацию в FC. А также в будущем добавить страниц?
Да, выгода в тм что все запросы проходят через один класс и можно что-то с ними делать со всеми.
Смотрел в сторону Middleware, но там нет массива $args. Пытался сделать роутер $app->any(''... подходящий под все url но он конфликтует с уже имеющимися
Вроде можно как-то сделать через $app->view->set(...) или как-то так в начале приложения. Хотя не очень понятно, зачем выносить путь к css файлу в переменную.
В контейнер в начале кода можно поместить путь и обращаться к нему через this. Но зачем усложнять себе жизнь и передавать один и тот же путь шаблон?
Делаем ставки господа.
Если тебя позвали на собеседование, значит уже шансы неплохие. Там я думаю многое будет зависеть от того как ты себя поведешь и сможешь ли ты стать частью коллектива. Если ты тому кто тебя собеседует ты не понравишься - он в любом случае сможет тебе задать вопрос на который ты не ответишь.
Ты ссылку на своих студентов в резюме прикреплял?
Ну значит тогда высок шанс того что эта шарага просто кого попало на собеседование зовет, а потом отбраковывает неподходящих.
Все равно удачи.
Удачи, анончик! Ты лучше выспись, чтобы не тупить на простых вопросах. А если не знаешь jQuery, то для нормальной компании не критично, так как учитcя он быстро.
Да, выспись лучше и хорошо позавтракай. Зарядочку утром сделай
>Буду ночь сидеть учить JQuery. Затем буду решать задачи из сборника "100 и 1 задача на собеседовании".
Плохая идея. На собеседовании потребуется умственная нагрузка, на которую тебе может не хватить сил после ночи зубрения.
Ты не с новосиба? Мб мы завтра с тобой на одно и тоже собеседование идем. Я сижу хуи пинаю и прост вспоминаю свой опыт и обосрамсы на прошлых собеседованиях. Надеюсь что не обосрусь хотя бы на тех вопросах, на которых УЖЕ обсирался :3
Чего только стоит например, система блокировки файлов (для защиты от одновременного изменения) с помощью бумажных карточек:
> Вопросы вызывал контроль версий — разработчиков теперь два. Соответствующего программного обеспечения под рукой не оказалось, поэтому в компании изобрели своё решение. Для этого на стену повесили блок для карточек, подобных тому, что используют для отслеживания приёма и ухода сотрудников на работу. Только здесь отслеживались файлы, а не люди. В каждое из отделений расположили карточку, которая соответствовала каждому из файлов исходника. Для редактирования файла нужно было взять карточку. По возвращении на ней должны была быть отмечена дата и внесённые изменения. Сам же файл уже был скопирован в сетевое хранилище.
Интересно, что тогда еще не везде были ОС с графическим интерфейсом (Windows еще не вышла) и программы должны были сами полностью рисовать свои окошки и меню. В то время, чтобы запрограммировать прямоугольник, в который можно вводить текст с форматированием и с автоматическим переносом слов, надо было приложить немало усилий. Сегодня достаточно написать несколько строчек:
<div id="text"></div>
...
div.contentEditable = true;
Ну и конечно сами программисты тогда были другие. Раньше разработчик должен был знать все - начиная от схемы работы микропроцессора и заканчивая сложными высокоуровневыми концепциями вроде Объекто-Ориентированного Программирования.
> Для этого на стену повесили блок для карточек
> карточку, которая соответствовала каждому из файлов исходника.
Ужасно, карточка на каждый файл. Насоздавал новых файлов - сиди вырезай карточки. А потом место на карточке могло закончится, так как там нельзя удалить строку, можно только зачеркнуть.
> система блокировки файлов (для защиты от одновременного изменения) с помощью бумажных карточек
Например, изкоробочный Vim создаёт swap-файл для каждого открытого в Vim'е файла. Если попытаться ещё одним Vim'ом в другом терминале открыть этот же файл, то редактор предупредит об этом и предложит воспользоваться, например, режимом read-only, либо edit anyway, либо вообще quit. А писать автора последнего исправления и время можно комментарием в сам файл, люди вон пихают в каждый файл лицензии на 100+ строк и ничего, всем норм.
Похоже на шутку, честно, не пойму, как до программистов (людей, пытающихся всё и всегда автоматизировать) не дошло, насколько такой подход неэффективен.
Карточка хранит не содержимое файла, а только его имя. Взял карточку = заблокировал файл.
Что касается вима, выглядит странно - по идее в ОС должны быть средства для нормальной блокировки файла без костылей, а это скорее выглядит как баг.
> А писать автора последнего исправления и время можно комментарием в сам файл, люди вон пихают в каждый файл лицензии на 100+ строк и ничего, всем норм.
А можно взять гит и не мучаться.
> Похоже на шутку, честно, не пойму, как до программистов (людей, пытающихся всё и всегда автоматизировать) не дошло, насколько такой подход неэффективен.
Возможно, вырезать карточки было быстрее чем спроектировать, разработать и написать систему контроля версий с нуля.
Значит вим костыль, а карточки - нет. Я упомянул о нём, так как его прародитель - Vi - создан примерно в 1960-х годах. То есть разрабы могли использовать уже готовый инструмент и он со всех сторон лучше чем карточки.
> а это скорее выглядит как баг.
Отключаемо. Ну и это скорее фича для запоминания несохранённых изменений, по типу того, как работает Ворд: виндоус завис, а ты не успел сохранить документ. Тут тебе ворд и предлагает выбор, так же как и вим.
>>846163
Да. Строки с одной кавычкой не понимают переменных. Допустим, у тебя есть переменная $a = 10;
echo 'Value: $a'; // "Value: $a"
echo "Value: $a"; // "Value: 10"
Хотя нет, Vim здесь здесь ни к месту, он решит проблему только если все работают за одной ОС. Но в любом случае странно, что программисты того времени, имея колоссальный багаж знаний и опыта, смогли додуматься только до доски с карточками. Представляю всё это движение в офисе из беспрерывно снующих туда-сюда программистов с карточками.
Есть PHP скрипт по созданию миниатюр. Принимает GET параметры имя файла и размер. Сохраняет миниатюру в нужном размере, а также выводит миниатюру в браузер (например передает имя сохраненного файла?). Умеет кешировать и отдавать из кеша.
Нужно сделать демонстрационную страницу.
На ней каким-то образом отображается миниатюры всех изображений из папки на сервере. html разметка с мини-галереей генерируется автоматически. Как это сделать?
Например, можно сделать еще один php скрипт, который проходит по папке с картинками и отправляет каждую из них в срипт создания миниатюры. Get запрос можно передать soccet-функцией, с помощью file() или fopen(), но мне кажется это костыли?
GET запрос лучше отправлять в PHP c помощью AJAX, правильно? Но тогда скрипт должен быть на JS, а сможет ли он прочитать имена файлов-изображений на сервере, для того чтобы отправлять их через AJAX?
Или нужно еще два скрипта, один на php, который соберет названия файлов на сервере. И JS скрипт, который отправить имена файлов в скрипт-минитюаризатор, получит адреса миниатюр и сгенерирует html разметку? Между собою они тоже будут общаться с помощью AJAX запроса?
> Представляю всё это движение в офисе из беспрерывно снующих туда-сюда программистов с карточками.
Целых 2 снующих туда-сюда программиста. Не забывай, что темпы разработки были другие - ты мог целый день отлаживать и допиливать единственный файл. Все было медленным, медленно компилировалось.
>>846536
Тебе надо разобраться как вообще работает веб-сервер и PHP.
> Есть PHP скрипт по созданию миниатюр. Принимает GET параметры имя файла и размер. Сохраняет миниатюру в нужном размере, а также выводит миниатюру в браузер (например передает имя сохраненного файла?). Умеет кешировать и отдавать из кеша.
Уже хорошо
> На ней каким-то образом отображается миниатюры всех изображений из папки на сервере. html разметка с мини-галереей генерируется автоматически. Как это сделать?
Поставить ссылку на скрипт выше.
> Например, можно сделать еще один php скрипт, который проходит по папке с картинками и отправляет каждую из них в срипт создания миниатюры. Get запрос можно передать soccet-функцией, с помощью file() или fopen(), но мне кажется это костыли?
Зачем отправлять запрос если можно сразу создать миниатюру?
> GET запрос лучше отправлять в PHP c помощью AJAX, правильно? Но тогда скрипт должен быть на JS, а сможет ли он прочитать имена файлов-изображений на сервере, для того чтобы отправлять их через AJAX?
Зачем так усложнять? Нет, конечно не может.
Вообще, это все уже много раз разжевано. Тут есть ровно 3 решения:
- скрипт который выводит страницу может заодно сгенерировать недостающие миниатюры (до отдачи страницы браузеру)
- можно сделать ссылки на картинки так, чтобы вызывался скрипт подготавливающий их
- можно по крону обходить папку и генерировать миниатюры, а в скрипте вывода страницы не выводить еще не сгенерированные
> Представляю всё это движение в офисе из беспрерывно снующих туда-сюда программистов с карточками.
Целых 2 снующих туда-сюда программиста. Не забывай, что темпы разработки были другие - ты мог целый день отлаживать и допиливать единственный файл. Все было медленным, медленно компилировалось.
>>846536
Тебе надо разобраться как вообще работает веб-сервер и PHP.
> Есть PHP скрипт по созданию миниатюр. Принимает GET параметры имя файла и размер. Сохраняет миниатюру в нужном размере, а также выводит миниатюру в браузер (например передает имя сохраненного файла?). Умеет кешировать и отдавать из кеша.
Уже хорошо
> На ней каким-то образом отображается миниатюры всех изображений из папки на сервере. html разметка с мини-галереей генерируется автоматически. Как это сделать?
Поставить ссылку на скрипт выше.
> Например, можно сделать еще один php скрипт, который проходит по папке с картинками и отправляет каждую из них в срипт создания миниатюры. Get запрос можно передать soccet-функцией, с помощью file() или fopen(), но мне кажется это костыли?
Зачем отправлять запрос если можно сразу создать миниатюру?
> GET запрос лучше отправлять в PHP c помощью AJAX, правильно? Но тогда скрипт должен быть на JS, а сможет ли он прочитать имена файлов-изображений на сервере, для того чтобы отправлять их через AJAX?
Зачем так усложнять? Нет, конечно не может.
Вообще, это все уже много раз разжевано. Тут есть ровно 3 решения:
- скрипт который выводит страницу может заодно сгенерировать недостающие миниатюры (до отдачи страницы браузеру)
- можно сделать ссылки на картинки так, чтобы вызывался скрипт подготавливающий их
- можно по крону обходить папку и генерировать миниатюры, а в скрипте вывода страницы не выводить еще не сгенерированные
Ну что, 3 недели спустя, думаю, можно и ответ сказать:
> как в третьем действии запилить изменения в переменную $текст
Вот у тебя есть такой код:
$text = '....';
$words = preg_split(...); // массив слов
$fixedWords = [];
foreach ($words as $word) {
$word = preg_replace(...); // исправляем ошибки в слове
$fixedWords[] = $word;
}
Логично что остается только написать
$fixedText = implode(' ', $fixedWords);
Хотя в этой задаче разбивать на слова не требуется - если правильно подобрать регулярки, то можно заменять слова сразу в исходном тексте:
$fixedText = preg_replace(..., $text);
>>831694
У preg_match есть 3-й аругмент куда можно передать массив и он заполнится частями исходного текста, попавшего в регулярку, смотри офиц. мануал.
>>832177
Не знаю, я в вордпрессе особо не разбираюсь.
>>833999
Не знаю, что посоветовать, попробуй просто var_dump или echo натыкать, чтобы увидеть, какие части кода выполняются, и что хранится в массивах или переменных.
Ну что, 3 недели спустя, думаю, можно и ответ сказать:
> как в третьем действии запилить изменения в переменную $текст
Вот у тебя есть такой код:
$text = '....';
$words = preg_split(...); // массив слов
$fixedWords = [];
foreach ($words as $word) {
$word = preg_replace(...); // исправляем ошибки в слове
$fixedWords[] = $word;
}
Логично что остается только написать
$fixedText = implode(' ', $fixedWords);
Хотя в этой задаче разбивать на слова не требуется - если правильно подобрать регулярки, то можно заменять слова сразу в исходном тексте:
$fixedText = preg_replace(..., $text);
>>831694
У preg_match есть 3-й аругмент куда можно передать массив и он заполнится частями исходного текста, попавшего в регулярку, смотри офиц. мануал.
>>832177
Не знаю, я в вордпрессе особо не разбираюсь.
>>833999
Не знаю, что посоветовать, попробуй просто var_dump или echo натыкать, чтобы увидеть, какие части кода выполняются, и что хранится в массивах или переменных.
> public function __construct(
> $status = false
> , $code = false
По PSR вроде принято в конце строки ставить запятую, запятая в начале как минимум странно смотрится.
В названии слово Object лишнее. Зачем писать что это объект, если это и так понятно?
У тебя слишком много аргументов у конструктора. Больше 4-5 аргументов делать не стоит. Более того, в твоем случае, все равно все поля не обязательные и можно сделать методы для их задания, вроде
$e = new Error(...);
$e->setLinks(...);
Странно тут то, что в объекте ErrorObject содержатся поля, которые вроде никак с ошибкой не связаны, ну например, source, meta, links и тд. И тут есть такие варианты:
- сделать несколько классов для разных видов ошибок с разными наборами полей
- не передавать поля вроде source, meta, links по отдельности, а передавать объект содержащий их
- убрать их вообще, так как они не имеют отношения к ошибке
>>834107
А какая у тебя задача? Если у тебя веб-приложение то каждый запрос обрабатывается отдельным процессом и если несколько пользователей пришлют запросы одновременно, то будет работать несколько процессов php на нескольких ядрах параллельно. То есть при высокой нагрузке будут задействованы все ядра.
Но в рамках одного процесса распараллелиться будет сложно. И скорее всего неэффективно, так как в php доступы к массивам или объектам могут быть защищены блокировками.
pthreads это очень низкоуровневая штука, от его подключения само собой ничего не распараллелится.
распараллеливаие обычно нужно на тяжелых вычислительных задачах. PHP не оптимизирован для таких задач и тебе лучше взять Си++ или C# где вычисления работают в разы быстрее. В последнем, кстати, есть встроенные средства для распараллеливания.
> public function __construct(
> $status = false
> , $code = false
По PSR вроде принято в конце строки ставить запятую, запятая в начале как минимум странно смотрится.
В названии слово Object лишнее. Зачем писать что это объект, если это и так понятно?
У тебя слишком много аргументов у конструктора. Больше 4-5 аргументов делать не стоит. Более того, в твоем случае, все равно все поля не обязательные и можно сделать методы для их задания, вроде
$e = new Error(...);
$e->setLinks(...);
Странно тут то, что в объекте ErrorObject содержатся поля, которые вроде никак с ошибкой не связаны, ну например, source, meta, links и тд. И тут есть такие варианты:
- сделать несколько классов для разных видов ошибок с разными наборами полей
- не передавать поля вроде source, meta, links по отдельности, а передавать объект содержащий их
- убрать их вообще, так как они не имеют отношения к ошибке
>>834107
А какая у тебя задача? Если у тебя веб-приложение то каждый запрос обрабатывается отдельным процессом и если несколько пользователей пришлют запросы одновременно, то будет работать несколько процессов php на нескольких ядрах параллельно. То есть при высокой нагрузке будут задействованы все ядра.
Но в рамках одного процесса распараллелиться будет сложно. И скорее всего неэффективно, так как в php доступы к массивам или объектам могут быть защищены блокировками.
pthreads это очень низкоуровневая штука, от его подключения само собой ничего не распараллелится.
распараллеливаие обычно нужно на тяжелых вычислительных задачах. PHP не оптимизирован для таких задач и тебе лучше взять Си++ или C# где вычисления работают в разы быстрее. В последнем, кстати, есть встроенные средства для распараллеливания.
>>И еще одно замечание. Текст в блоках с содержимым вкладки примыкает к бордеру. Это некрасиво, создается ощущение что тексту там тесно. Ведь во всех книгах и журналах у текста есть поля справа и слева. Так же должно быть и тут - у тела вкладки должны быть поля, хотя бы 5-10px и у заголовка вкладки тоже должны быть небольшие поля, чтобы буквы не прилипали к краю вплотную. Если присмотреться, поля вокруг текста есть везде - и на кнопках, и в полях ввода.
> Добавил поля: https://jsfiddle.net/tb7k9dw9/8/
Вот, другое дело. С полями сразу же стало аккуратнее.
Вообще, я вижу, ты HTML/CSS неплохо подучил, еще JS + jQuery выучишь и можешь уже версткой заниматься как минимум. Ну а в связке с PHP и фреймворками - вообще полноценные сайты делать.
> from {opacity: 0;}
> 50% {opacity: 0.5;}
> to {opacity: 1;}
А тут точка в 50% и не нужна наверно, браузер и без нее от 0 до 1 сможет проанимировать. Дополнительные промежучточные точки нужны если хочется сделать нелинейную анимацию, например с изменением скорости, с паузой, с поворотом в обратную сторону и тд.
> max-width: calc(100% + 2px);
Про это я вроде уже писал - тут calc не нужен и не стоит делать элемент шире своего родителя в данном случае.
>>Насчет удаления атрибутов с id, я придумал такой вариант:
>>Можно попробовать для связи табов и вкладок использовать их номера. CSS3 позволяет указать в стилях что-то вроде "N-й по счету элемент типа E" - и соответственно можно попробовать привязать первый инпут и первый label к первому телу вкладки, второй ко второму итд.:
Сделал: https://jsfiddle.net/1ax6k6hw/
Еще
В общем, по табам у нас были такие вопросы:
- max-width: calc - не нужен (это вроде обсудили)
- по возможности избавиться от id - решили обойтись номерами
- использовать префиксы в именах классов - с этим вроде разобрались
- адаптивность на маленьких экранах - вроде исправили
- отображение в древних CSS2.1 браузерах - это я сейчас проверю.
Я перезалил код на jsbin, так как jsfiddle не работает вообще в старых браузерах.
В Firefox 3.5 и 3.6 все прекрасно работает и переключается, разве что без анимации.
В ИЕ 9 все отображается
А вот в ИЕ8 - отображаются только заголовки вкладок: https://www.browserstack.com/screenshots/2c8f1a50213f7d05a2713db8cf2b15cfac961af6/win7_ie_8.0.png без содержимого.
(Остальные скриншоты https://developer.microsoft.com/en-us/microsoft-edge/tools/screenshots/?url=https://output.jsbin.com/supadozaza ).
Я помню, что мы пришли к выводу, что можно для браузеров, не поддерживающих CSS3, просто вывести содержимое вкладок вертикально. Но код, который использован для скрытия -
.content[class] {
display: none;
}
Это код из CSS2.1. Таким образом, для скрытия вкладок достаточно наличия CSS2.1, а вот для показа нужен CSS3. По моему, это правило я сам и посоветовал, не подумав. Нужен другой хак, который бы опирался на возможности CSS3. Чтобы без поддержки CSS3 тело вкладки не скрывалось бы.
А если делать идеально, то, так как показ вкладок сделан через селектор ~ и nth-of-type то и скрывать их хорошо бы через них (на случай если какой-то браузер поддерживает CSS3 селекторы частично). Тут конечно придется извернуться, что поделать. Я смог например сделать так (и конечно в таких случаях надо ставить комментарий):
/* Скрываем контент только в поддерживающих CSS3 браузерах,
в CSS2.1 браузерах содержимое всех вкладок выводится
вертикально как есть */
.tab-label ~ .wrapper > .content:nth-of-type(n) { display: none; }
Также, я упростил нагромождение правил для показа вкладок и сгруппировал их:
.tab:nth-of-type(1):checked ~ .wrapper > .content:nth-of-type(1),
.tab:nth-of-type(2):checked ~ .wrapper > .content:nth-of-type(2),
.tab:nth-of-type(3):checked ~ .wrapper > .content:nth-of-type(3),
.tab:nth-of-type(4):checked ~ .wrapper > .content:nth-of-type(4) {
display: block;
}
Вот итог: http://output.jsbin.com/lozefetinu/1 - в ИЕ8 все содержимое выводится вертикально: https://developer.microsoft.com/en-us/microsoft-edge/tools/screenshots/?url=http://output.jsbin.com/lozefetinu/1
На первый взгляд может конечно показаться, что это сложно, лишние усилия, но если представлять, какие селекторы к каким стандартам относятся, то задача выглядит не такой уж и сложной. В данном случае я сделал так, что для скрытия содержимого нужен тот же уровень поддержки CSS, что и для показа. По моему, это подход "progressive enhancement" - прогрессивное улучшение - браузеры с CSS2.1 получают базовую версию страницу без табов, а в CSS3 добавляется возможность переключать табы (для красоты можно еще скрыть заголовки табов в старых браузерах, чтобы пользователь не думал, что по ним можно кликать).
Я конечно не могу это гарантировать, но как правило, страницы, которые отображаются даже в древних браузерах, они и индексируются лучше (так как поисковый бот это по сути и есть древний браузер зачастую без поддержки JS), и например программы для чтения текста их лучше воспринимают.
И еще, например, почтовые клиенты часто поддерживают ограниченный набор HTML/CSS свойств. Часто письма верстают HTML-таблицами, так как многие свойства css могут вырезаться. Тут навыки верстки под старые браузеры и прогрессивного улучшения тоже пригодятся.
А, и еще добавлю про адаптивность. Сделать поддержку отображения на маленькой ширине для адаптивности недостаточно. Мобильные браузеры по умолчанию считают, что перед ними страница для десктопного браузера, и выделяют под нее область шириной 800 или 960px. И разумеется, отображают такую страницу в уменьшенном виде. Чтобы сказать, что страница адаптирована под маленькие экраны, надо добавлять тег meta viewport, про который нетрудно нагуглить информацию (например http://frontender.com.ua/mobile-web/wtf-viewport/).
Ну и разумеется, не надо использовать там свойства вроде user-scalable=no. Руки надо отрывать тем, кто запрещает изменение масштаба.
В общем, думаю, что с задачей на табы мы разобрались.
> Исправил: https://jsfiddle.net/0u3x5rsw/6/ https://jsfiddle.net/xx82knpf/2/
Ок, с агросервисом разобрались.
>>Задача про Короля Лир
> Исправил: https://jsfiddle.net/tsuh4gjw/9/
Теперь все верно, разве что это свойство
> p {
> max-width: 200px;
Не требуется так как ширина p вычисляется как ширина свободного места в родительском блоке.
>>Вот у этого метода конечно есть подвох - что если завтра кто-то поставит наш элемент на гигантскую страницу которая шире этих 10 000 px? Может лучше сделать родителя c overflow: hidden и тогда не придется так далеко загонять? Или все же не стоит? Я в принципе не настаиваю.
> Исправил: https://jsfiddle.net/fp8o0exq/6/
>>тут есть проблема: если двигать по кнопкам синюю рамку, то ширина кнопки при появлении рамки меняется и правая кнопка сдвигается на 1-2 пикслея вправо. Это некрасиво. Надо либо компенсировать лишний пиксель в бордере отрицательным маргином, либо сделать у всех кнопок border 1px и отрицательный маргин, чтобы они слегка наезжали друг на друга.
> Исправлено: https://jsfiddle.net/fp8o0exq/9/
>>Тень вроде выглядит похоже. Единственная проблема - если кнопка вжата, на ней есть тень, и на ней кликнуть, то тень пропадает, пока на кнопке фокус. Видимо потому, что тень переиспользуется для рамки. но у элемента может быть несколько теней - надо просто грамотно прописать правила.
> Я тоже увидел это, но не уделил этому нужного внимания. Исправил: https://jsfiddle.net/fp8o0exq/8/
Да, теперь с задачей про радиокнопки все в порядке.
> Еще я подумал, что можно было задать несколько теней с помощью box-sizing, но у них тогда бы был общий радиус теней https://jsfiddle.net/L69xmtwh/
Не нашел тут box-sizing, но нашел баг, что при клике по выделенной кнопке, она слегка меняет свой размер - возможно как раз потому, что box-sizing не указан.
>>Или же можно попробовать сделать рамку с помощью свойства outline: http://htmlbook.ru/css/outline
> Но ему же нельзя задать смещение по оси или размытие, или радиус углов если мы хотим использовать его за место бордера.
Да, нельзя, зато он поддерживается аж в CSS2.1 - может в какой-то ситуации это окажется важным.
В общем, как я понимаю, пока нерешенной остается только последняя задача про верстку.
>>И еще одно замечание. Текст в блоках с содержимым вкладки примыкает к бордеру. Это некрасиво, создается ощущение что тексту там тесно. Ведь во всех книгах и журналах у текста есть поля справа и слева. Так же должно быть и тут - у тела вкладки должны быть поля, хотя бы 5-10px и у заголовка вкладки тоже должны быть небольшие поля, чтобы буквы не прилипали к краю вплотную. Если присмотреться, поля вокруг текста есть везде - и на кнопках, и в полях ввода.
> Добавил поля: https://jsfiddle.net/tb7k9dw9/8/
Вот, другое дело. С полями сразу же стало аккуратнее.
Вообще, я вижу, ты HTML/CSS неплохо подучил, еще JS + jQuery выучишь и можешь уже версткой заниматься как минимум. Ну а в связке с PHP и фреймворками - вообще полноценные сайты делать.
> from {opacity: 0;}
> 50% {opacity: 0.5;}
> to {opacity: 1;}
А тут точка в 50% и не нужна наверно, браузер и без нее от 0 до 1 сможет проанимировать. Дополнительные промежучточные точки нужны если хочется сделать нелинейную анимацию, например с изменением скорости, с паузой, с поворотом в обратную сторону и тд.
> max-width: calc(100% + 2px);
Про это я вроде уже писал - тут calc не нужен и не стоит делать элемент шире своего родителя в данном случае.
>>Насчет удаления атрибутов с id, я придумал такой вариант:
>>Можно попробовать для связи табов и вкладок использовать их номера. CSS3 позволяет указать в стилях что-то вроде "N-й по счету элемент типа E" - и соответственно можно попробовать привязать первый инпут и первый label к первому телу вкладки, второй ко второму итд.:
Сделал: https://jsfiddle.net/1ax6k6hw/
Еще
В общем, по табам у нас были такие вопросы:
- max-width: calc - не нужен (это вроде обсудили)
- по возможности избавиться от id - решили обойтись номерами
- использовать префиксы в именах классов - с этим вроде разобрались
- адаптивность на маленьких экранах - вроде исправили
- отображение в древних CSS2.1 браузерах - это я сейчас проверю.
Я перезалил код на jsbin, так как jsfiddle не работает вообще в старых браузерах.
В Firefox 3.5 и 3.6 все прекрасно работает и переключается, разве что без анимации.
В ИЕ 9 все отображается
А вот в ИЕ8 - отображаются только заголовки вкладок: https://www.browserstack.com/screenshots/2c8f1a50213f7d05a2713db8cf2b15cfac961af6/win7_ie_8.0.png без содержимого.
(Остальные скриншоты https://developer.microsoft.com/en-us/microsoft-edge/tools/screenshots/?url=https://output.jsbin.com/supadozaza ).
Я помню, что мы пришли к выводу, что можно для браузеров, не поддерживающих CSS3, просто вывести содержимое вкладок вертикально. Но код, который использован для скрытия -
.content[class] {
display: none;
}
Это код из CSS2.1. Таким образом, для скрытия вкладок достаточно наличия CSS2.1, а вот для показа нужен CSS3. По моему, это правило я сам и посоветовал, не подумав. Нужен другой хак, который бы опирался на возможности CSS3. Чтобы без поддержки CSS3 тело вкладки не скрывалось бы.
А если делать идеально, то, так как показ вкладок сделан через селектор ~ и nth-of-type то и скрывать их хорошо бы через них (на случай если какой-то браузер поддерживает CSS3 селекторы частично). Тут конечно придется извернуться, что поделать. Я смог например сделать так (и конечно в таких случаях надо ставить комментарий):
/* Скрываем контент только в поддерживающих CSS3 браузерах,
в CSS2.1 браузерах содержимое всех вкладок выводится
вертикально как есть */
.tab-label ~ .wrapper > .content:nth-of-type(n) { display: none; }
Также, я упростил нагромождение правил для показа вкладок и сгруппировал их:
.tab:nth-of-type(1):checked ~ .wrapper > .content:nth-of-type(1),
.tab:nth-of-type(2):checked ~ .wrapper > .content:nth-of-type(2),
.tab:nth-of-type(3):checked ~ .wrapper > .content:nth-of-type(3),
.tab:nth-of-type(4):checked ~ .wrapper > .content:nth-of-type(4) {
display: block;
}
Вот итог: http://output.jsbin.com/lozefetinu/1 - в ИЕ8 все содержимое выводится вертикально: https://developer.microsoft.com/en-us/microsoft-edge/tools/screenshots/?url=http://output.jsbin.com/lozefetinu/1
На первый взгляд может конечно показаться, что это сложно, лишние усилия, но если представлять, какие селекторы к каким стандартам относятся, то задача выглядит не такой уж и сложной. В данном случае я сделал так, что для скрытия содержимого нужен тот же уровень поддержки CSS, что и для показа. По моему, это подход "progressive enhancement" - прогрессивное улучшение - браузеры с CSS2.1 получают базовую версию страницу без табов, а в CSS3 добавляется возможность переключать табы (для красоты можно еще скрыть заголовки табов в старых браузерах, чтобы пользователь не думал, что по ним можно кликать).
Я конечно не могу это гарантировать, но как правило, страницы, которые отображаются даже в древних браузерах, они и индексируются лучше (так как поисковый бот это по сути и есть древний браузер зачастую без поддержки JS), и например программы для чтения текста их лучше воспринимают.
И еще, например, почтовые клиенты часто поддерживают ограниченный набор HTML/CSS свойств. Часто письма верстают HTML-таблицами, так как многие свойства css могут вырезаться. Тут навыки верстки под старые браузеры и прогрессивного улучшения тоже пригодятся.
А, и еще добавлю про адаптивность. Сделать поддержку отображения на маленькой ширине для адаптивности недостаточно. Мобильные браузеры по умолчанию считают, что перед ними страница для десктопного браузера, и выделяют под нее область шириной 800 или 960px. И разумеется, отображают такую страницу в уменьшенном виде. Чтобы сказать, что страница адаптирована под маленькие экраны, надо добавлять тег meta viewport, про который нетрудно нагуглить информацию (например http://frontender.com.ua/mobile-web/wtf-viewport/).
Ну и разумеется, не надо использовать там свойства вроде user-scalable=no. Руки надо отрывать тем, кто запрещает изменение масштаба.
В общем, думаю, что с задачей на табы мы разобрались.
> Исправил: https://jsfiddle.net/0u3x5rsw/6/ https://jsfiddle.net/xx82knpf/2/
Ок, с агросервисом разобрались.
>>Задача про Короля Лир
> Исправил: https://jsfiddle.net/tsuh4gjw/9/
Теперь все верно, разве что это свойство
> p {
> max-width: 200px;
Не требуется так как ширина p вычисляется как ширина свободного места в родительском блоке.
>>Вот у этого метода конечно есть подвох - что если завтра кто-то поставит наш элемент на гигантскую страницу которая шире этих 10 000 px? Может лучше сделать родителя c overflow: hidden и тогда не придется так далеко загонять? Или все же не стоит? Я в принципе не настаиваю.
> Исправил: https://jsfiddle.net/fp8o0exq/6/
>>тут есть проблема: если двигать по кнопкам синюю рамку, то ширина кнопки при появлении рамки меняется и правая кнопка сдвигается на 1-2 пикслея вправо. Это некрасиво. Надо либо компенсировать лишний пиксель в бордере отрицательным маргином, либо сделать у всех кнопок border 1px и отрицательный маргин, чтобы они слегка наезжали друг на друга.
> Исправлено: https://jsfiddle.net/fp8o0exq/9/
>>Тень вроде выглядит похоже. Единственная проблема - если кнопка вжата, на ней есть тень, и на ней кликнуть, то тень пропадает, пока на кнопке фокус. Видимо потому, что тень переиспользуется для рамки. но у элемента может быть несколько теней - надо просто грамотно прописать правила.
> Я тоже увидел это, но не уделил этому нужного внимания. Исправил: https://jsfiddle.net/fp8o0exq/8/
Да, теперь с задачей про радиокнопки все в порядке.
> Еще я подумал, что можно было задать несколько теней с помощью box-sizing, но у них тогда бы был общий радиус теней https://jsfiddle.net/L69xmtwh/
Не нашел тут box-sizing, но нашел баг, что при клике по выделенной кнопке, она слегка меняет свой размер - возможно как раз потому, что box-sizing не указан.
>>Или же можно попробовать сделать рамку с помощью свойства outline: http://htmlbook.ru/css/outline
> Но ему же нельзя задать смещение по оси или размытие, или радиус углов если мы хотим использовать его за место бордера.
Да, нельзя, зато он поддерживается аж в CSS2.1 - может в какой-то ситуации это окажется важным.
В общем, как я понимаю, пока нерешенной остается только последняя задача про верстку.
>>821013 | http://arhivach.org/thread/193343/#821013 | Вопросы по студентам и инкапсуляции
>>Вообще, это относится не только к полям. Всегда лучше ограничивать область где доступно поле, функция, переменная, так как в этом случае проще анализировать код который с ней работает, так как меньше его объем.
> А что определяет какую функцию мы должны заинкапсулировать? Ведь можно прямо внутри функции ограничивать пришедшие аргументы.
А что значит "заинкапсулировать функцию"? Сделать ее приватной? Тогда выбор делается так: у нас у объекта есть набор действий, которые с ним можно сделать. Если функция реализует что-то из этого набора, то делаем ее публичной, если нет, и она нужна только внутри нашего класса, то приватной. И наконец, если она нужна в классе и хочется разрешить использовать ее наследникам, то делаем protected.
А что касается полей - тут есть разные мнения. Некоторые считают что публичных полей в принципе быть не должно и работать с объектом нужно только через методы. Таким образом, если нам понадобится поменять что-то в логике, то достаточно будет сделать это в одном методе. Некоторые считают, что публичные поля допустимы, особенно в примитивных объектах из нескольких полей.
Лучше наверно привести пример, чем обсуждать абстрактные вещи. Допустим, мы решили представить ломаную линию из нескольких отрезков (например, маршрут на карте) в виде объекта. Для начала, мы спроектируем, что можно будет делать с нашим объектом:
- создать, указав начальную точку (мы решили, что ломаная линия, не содержащая ни одной точки, невозможна, потому требуем передать начальные координаты в конструктор. Я сейчас не могу сказать как выгоднее - разрешить линии без точек вообще, требовать наличие минимум одной или требовать минимум 2 точки. Это надо сравнивать)
- добавить еще одну точку к линии
- найти длину линии (т.е сумму длин всех ее отрезков)
Теперь, имея этот список, мы можем написать класс PolyLine:
class PolyLine
{
// Массив со списком точек
private $points = [];
public function __construct($x, $y)
{
$this->points[] = [$x, $y];
}
public function addPoint($x, $y)
{
$this->points[] = [$x, $y];
}
/ Посчитать общую длину линии */
public function calculateLength()
{
$length = 0;
for ($i = 1; $i < count($this->points)) {
$length += $this->calculateSegmentLength(
$this->points[$i - 1][0],
$this->points[$i - 1][1],
$this->points[$i][0],
$this->points[$i][1]
);
}
return $length;
}
private function calculateSegmentlength($x1, $y1, $x2, $y2) { ... }
}
Вот в этом классе мы делаем публичными только те методы, которые запроектировали выше. А все остальное мы закрываем. Например, мы закрываем от доступа снаружи массив points. Это имеет такие преимущества:
- мы гарантируем что никто нам не положит в массив что-то не то, например, какую-то строку или что-то еще
- людям, которые изучают наш код, не надо смотреть на то, как класс реализован внутри, им достаточно увидеть заголовки и комментарии публичных методов и использовать их
- мы можем в любой момент поменять что-то во внутренней реализации класса, и не трогать остальной код, который его использует.
Ну например завтра мы решим что хранить координаты точки удобнее не в числовом массиве [$x, $y], а в таком ['x' => $x, 'y' => $y]. Или в виде объектов класса Point. И так как этот массив у нас приватный, мы можем смело менять его и нам не придется править код снаружи класса PolyLine так как изменилась только внутренняя реализация, а внешний интерфейс остался таким же.
Если проводить аналогии с автоматом по продаже кофе - мы можем заменить внутреннюю начинку в нем, а пользователи этого даже не заметят, если он будет так же работать. Хороший ООП код как раз и должен состоять из таких вот классов.
До ООП зачастую никакой инкапсуляции и не было, код состоял из функций и глобальных переменных, и можно было обращаться к любой. По мере роста объема программы разбираться в этом было сложнее. И разделение кода на классы нужно для того, чтобы справиться с этой сложностью, чтобы огромная программа могла бы оставаться относительно понятной, чтобы мы могли рассмтаривать класс отдельно от остального кода.
Ну конечно пример с линией простой: линия это такая вещь в себе, которая никак не взаимодействует с окружаюим миром. На практике конечно все бывает сложнее. А попробуем-ка написать класс для авторизации пользователя, который взаимодействует с браузером (ставит или проверяет куки). Начнем с проектирования:
- мы хотим иметь возможность залогиниться под определенным id, чтобы в дальнейшем мы могли видеть что запрос пришел от определенного пользователя
- мы хотим иметь возможность разлогиниться, то есть удалить с идентификатор пользователя
- мы хотим проверить, от какого пользователя пришел текущий запрос - или же он пришел от гостя
Я намеренно не закладываю в требования конкретный механизм авторизации. Попробуем теперь написать класс. Для залогинивания нам надо знать id пользователя и иметь возможность ставить куки. Мы передаем в метод authorize коллекцию кук ResponseCookies (которые мы позже передадим в браузер), и метод может добавлять туда нужные ему куки. Затем пишем метод разлогинивания logout. И наконец, метод проверки авторизации, которому мы передаем коллекцию пришедших от браузера кук.
class AuthHelper
{
public function authorize($userId, ResponseCookies $cookies) { ... }
public function logout(ResponseCookies $cookies) { ... }
public function getAuthUserId(RequestCookies $cookies) { ... }
}
Я специально не стал писать тут приватные поля и содержимое методов, так как это не важно. Если мы хотим залогиниться, нам не надо знать как устроен метод authorize, нам достаточно передать ему правильные аргументы, а для этого достаточно того кода, что я написал. Более того, тут даже проще понять, как пользоваться классом, чем если бы он был написан полностью.
Инкапсуляция в данном случае скрывает от нас лишнее, она не позволяет нам привязываться к внутреннему устройству класса (то есть посмотреть код мы конечно можем, но опираться на это знание нельзя, нужно использовать только то, что выставлено наружу публично, потому что внутренний код позже могут поменять).
Если мы будем работать с куками через массив $_COOKIE или функции вроде setcookie то конечно никакого красивого ООП не получится. Потому что если смотреть снаружи, то получится что мы ничего не передаем в функции, а они откуда-то берут нужные им данные. Потому во многих фреймворках есть классы для представления HTTP-запроса, ответа, и для кук из них.
Сам механизм авторизации мы скрыли. Теперь мы можем его менять, не трогая остальной код. Ну и даже если мы никогда не будем что-то менять, мы все равно упростили код, так как мы четко отделили код, ставящий и проверяющий куки от остального приложения.
Мы можем рассматривать код AuthHelper, не глядя как он используется, или наборот, мы можем рассматривать остальной код приложения, не заглядывая в класс авторизации.
Это конечно не всегда легко, придумать, как разбить код на классы. Можно только ухудшить код, если сделать много лишних классов или неудачно их спроектировать. Тут поможет практика и изучение чужого кода, например популярных фреймворков вроде Symfony или ее компонент.
Я могу еще дать аббревиатуру SOLID и ссылку на такую статью: https://habrahabr.ru/post/153225/
>>- допустим мы захотим поменять логику и не хранить значение в классе, а передавать куда-то еще, сохранять в файл или в базу или куда-то еще. Опять же, нам придется обойти весь код, найти там все обращения к полю $x и поменять их на вызов какого-то метода.
> Но ведь и в случае инкапсуляции нам придётся пройтись по всему коду и поменять функцию setX().
Нет, если мы меняем внутреннюю реализацию, не меняя интерфейс, то нам не надо проходиться по всему коду. Если мы меняем монитор у компьютера, нам ведь не надо лезть в системный блок и менять видеокарту заодно.
> Я бы хотел перенести, например, вот этот код https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L48-L51 в $AuthService->login(). Иначе у меня просто не будет Сервисов.
Если не будет, то пусть не будет. Не надо специально их создавать, чтобы были. Надо думать, какой код можно сделать повторно используемым, а какой выгоднее оставить в контроллере.
>>А ты его по памяти написать сможешь когда тебе будут нужны все студенты? лучше сделать условие что если $limit = 0 то конструкция LIMIT не вставляется. Правда, это не даст нам указывать OFFSET без LIMIT, но это почти никогда и не нужно.
> Можно принимаемые аргументы перенести в конец, и при вызове функции их не указывать.
Можно.
>>821013 | http://arhivach.org/thread/193343/#821013 | Вопросы по студентам и инкапсуляции
>>Вообще, это относится не только к полям. Всегда лучше ограничивать область где доступно поле, функция, переменная, так как в этом случае проще анализировать код который с ней работает, так как меньше его объем.
> А что определяет какую функцию мы должны заинкапсулировать? Ведь можно прямо внутри функции ограничивать пришедшие аргументы.
А что значит "заинкапсулировать функцию"? Сделать ее приватной? Тогда выбор делается так: у нас у объекта есть набор действий, которые с ним можно сделать. Если функция реализует что-то из этого набора, то делаем ее публичной, если нет, и она нужна только внутри нашего класса, то приватной. И наконец, если она нужна в классе и хочется разрешить использовать ее наследникам, то делаем protected.
А что касается полей - тут есть разные мнения. Некоторые считают что публичных полей в принципе быть не должно и работать с объектом нужно только через методы. Таким образом, если нам понадобится поменять что-то в логике, то достаточно будет сделать это в одном методе. Некоторые считают, что публичные поля допустимы, особенно в примитивных объектах из нескольких полей.
Лучше наверно привести пример, чем обсуждать абстрактные вещи. Допустим, мы решили представить ломаную линию из нескольких отрезков (например, маршрут на карте) в виде объекта. Для начала, мы спроектируем, что можно будет делать с нашим объектом:
- создать, указав начальную точку (мы решили, что ломаная линия, не содержащая ни одной точки, невозможна, потому требуем передать начальные координаты в конструктор. Я сейчас не могу сказать как выгоднее - разрешить линии без точек вообще, требовать наличие минимум одной или требовать минимум 2 точки. Это надо сравнивать)
- добавить еще одну точку к линии
- найти длину линии (т.е сумму длин всех ее отрезков)
Теперь, имея этот список, мы можем написать класс PolyLine:
class PolyLine
{
// Массив со списком точек
private $points = [];
public function __construct($x, $y)
{
$this->points[] = [$x, $y];
}
public function addPoint($x, $y)
{
$this->points[] = [$x, $y];
}
/ Посчитать общую длину линии */
public function calculateLength()
{
$length = 0;
for ($i = 1; $i < count($this->points)) {
$length += $this->calculateSegmentLength(
$this->points[$i - 1][0],
$this->points[$i - 1][1],
$this->points[$i][0],
$this->points[$i][1]
);
}
return $length;
}
private function calculateSegmentlength($x1, $y1, $x2, $y2) { ... }
}
Вот в этом классе мы делаем публичными только те методы, которые запроектировали выше. А все остальное мы закрываем. Например, мы закрываем от доступа снаружи массив points. Это имеет такие преимущества:
- мы гарантируем что никто нам не положит в массив что-то не то, например, какую-то строку или что-то еще
- людям, которые изучают наш код, не надо смотреть на то, как класс реализован внутри, им достаточно увидеть заголовки и комментарии публичных методов и использовать их
- мы можем в любой момент поменять что-то во внутренней реализации класса, и не трогать остальной код, который его использует.
Ну например завтра мы решим что хранить координаты точки удобнее не в числовом массиве [$x, $y], а в таком ['x' => $x, 'y' => $y]. Или в виде объектов класса Point. И так как этот массив у нас приватный, мы можем смело менять его и нам не придется править код снаружи класса PolyLine так как изменилась только внутренняя реализация, а внешний интерфейс остался таким же.
Если проводить аналогии с автоматом по продаже кофе - мы можем заменить внутреннюю начинку в нем, а пользователи этого даже не заметят, если он будет так же работать. Хороший ООП код как раз и должен состоять из таких вот классов.
До ООП зачастую никакой инкапсуляции и не было, код состоял из функций и глобальных переменных, и можно было обращаться к любой. По мере роста объема программы разбираться в этом было сложнее. И разделение кода на классы нужно для того, чтобы справиться с этой сложностью, чтобы огромная программа могла бы оставаться относительно понятной, чтобы мы могли рассмтаривать класс отдельно от остального кода.
Ну конечно пример с линией простой: линия это такая вещь в себе, которая никак не взаимодействует с окружаюим миром. На практике конечно все бывает сложнее. А попробуем-ка написать класс для авторизации пользователя, который взаимодействует с браузером (ставит или проверяет куки). Начнем с проектирования:
- мы хотим иметь возможность залогиниться под определенным id, чтобы в дальнейшем мы могли видеть что запрос пришел от определенного пользователя
- мы хотим иметь возможность разлогиниться, то есть удалить с идентификатор пользователя
- мы хотим проверить, от какого пользователя пришел текущий запрос - или же он пришел от гостя
Я намеренно не закладываю в требования конкретный механизм авторизации. Попробуем теперь написать класс. Для залогинивания нам надо знать id пользователя и иметь возможность ставить куки. Мы передаем в метод authorize коллекцию кук ResponseCookies (которые мы позже передадим в браузер), и метод может добавлять туда нужные ему куки. Затем пишем метод разлогинивания logout. И наконец, метод проверки авторизации, которому мы передаем коллекцию пришедших от браузера кук.
class AuthHelper
{
public function authorize($userId, ResponseCookies $cookies) { ... }
public function logout(ResponseCookies $cookies) { ... }
public function getAuthUserId(RequestCookies $cookies) { ... }
}
Я специально не стал писать тут приватные поля и содержимое методов, так как это не важно. Если мы хотим залогиниться, нам не надо знать как устроен метод authorize, нам достаточно передать ему правильные аргументы, а для этого достаточно того кода, что я написал. Более того, тут даже проще понять, как пользоваться классом, чем если бы он был написан полностью.
Инкапсуляция в данном случае скрывает от нас лишнее, она не позволяет нам привязываться к внутреннему устройству класса (то есть посмотреть код мы конечно можем, но опираться на это знание нельзя, нужно использовать только то, что выставлено наружу публично, потому что внутренний код позже могут поменять).
Если мы будем работать с куками через массив $_COOKIE или функции вроде setcookie то конечно никакого красивого ООП не получится. Потому что если смотреть снаружи, то получится что мы ничего не передаем в функции, а они откуда-то берут нужные им данные. Потому во многих фреймворках есть классы для представления HTTP-запроса, ответа, и для кук из них.
Сам механизм авторизации мы скрыли. Теперь мы можем его менять, не трогая остальной код. Ну и даже если мы никогда не будем что-то менять, мы все равно упростили код, так как мы четко отделили код, ставящий и проверяющий куки от остального приложения.
Мы можем рассматривать код AuthHelper, не глядя как он используется, или наборот, мы можем рассматривать остальной код приложения, не заглядывая в класс авторизации.
Это конечно не всегда легко, придумать, как разбить код на классы. Можно только ухудшить код, если сделать много лишних классов или неудачно их спроектировать. Тут поможет практика и изучение чужого кода, например популярных фреймворков вроде Symfony или ее компонент.
Я могу еще дать аббревиатуру SOLID и ссылку на такую статью: https://habrahabr.ru/post/153225/
>>- допустим мы захотим поменять логику и не хранить значение в классе, а передавать куда-то еще, сохранять в файл или в базу или куда-то еще. Опять же, нам придется обойти весь код, найти там все обращения к полю $x и поменять их на вызов какого-то метода.
> Но ведь и в случае инкапсуляции нам придётся пройтись по всему коду и поменять функцию setX().
Нет, если мы меняем внутреннюю реализацию, не меняя интерфейс, то нам не надо проходиться по всему коду. Если мы меняем монитор у компьютера, нам ведь не надо лезть в системный блок и менять видеокарту заодно.
> Я бы хотел перенести, например, вот этот код https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L48-L51 в $AuthService->login(). Иначе у меня просто не будет Сервисов.
Если не будет, то пусть не будет. Не надо специально их создавать, чтобы были. Надо думать, какой код можно сделать повторно используемым, а какой выгоднее оставить в контроллере.
>>А ты его по памяти написать сможешь когда тебе будут нужны все студенты? лучше сделать условие что если $limit = 0 то конструкция LIMIT не вставляется. Правда, это не даст нам указывать OFFSET без LIMIT, но это почти никогда и не нужно.
> Можно принимаемые аргументы перенести в конец, и при вызове функции их не указывать.
Можно.
>>821641 | http://arhivach.org/thread/193343/#821641
>>Также, у тебя в контроллере стоит 4 вызова render. Ты выносишь знание о структуре страницы в контроллер, но удобнее просто подключить один шаблон, а он уже пусть подключает что ему нужно.
> А как это реализовать? Сделать отдельный шаблон в котором будет вызываться render нужных элементов? Для этого нужно будет передавать Viewer который находиться в свойствах контроллера. Я такого не разу не делал, по моему это усложнение.
>
> Или сделать один шаблон который содержит в себе все элементы (т.е. они не вызываются с помощью render, а приписаны в ручную)? Если да, то не будет ли это лишней копипастой?
Я имел в виду, что не надо в контроллере вызывать отдельные куски шаблона. Отдельно вызвать вывод шапки, отдельно вызвать вывод таблицы. Нужно в контроллере вызвать шаблон, а он уже пусть подключает шапку, таблицу, меню и все, что ему нужно. Также удобно шаблоны-кусочки сложить в отдельную папку.
>>834374
> Что-то ничего не сохранилось. Должно быть так: https://jsfiddle.net/a6wk2qng/
Тут косяк - при нажатии на кнопку она становится на пиксель меньше и кнопки справа от нее двигаются.
http://ruhighload.com/post/Работа+с+индексами+в+MySQL
>>834510
>> $reg=preg_quote("/$find/ui");
> Судя по http://php.net/manual/ru/function.preg-quote.php как раз таки слеш / он не экранирует
Действительно. Я думал, экранирует. Но тогда твой код не заэкранирует слеши внутри $find, что вызовет ошибку.
> https://github.com/TheSidSpears/Students - что-то тут разметка кривая в README. Ну и вместо словесного описания, лучше просто привести пример как должен выглядеть конфиг Апача или положить файлик students.conf с виртуальным хостом (в линуксе можно для каждого сайта делать свой файл с конфигом в папке /etc/apache2/sites-available/).
README.md вносить в gitignore не стоит, он же нужен тем кто скачивает проект и пытается развернуть.
composer.phar класть в репозиторий не надо, он устаревает же.
> https://github.com/TheSidSpears/Students/blob/master/config.json
Тут ты смешал табы с пробелами и все разъехалось. Настрой свой редактор. Кстати, в линуксе есть команда expand, которую можно совместить с find и заменить табы во всем репозитории.
Комментариев конечно маловато. Например в начале файла https://github.com/TheSidSpears/Students/blob/master/app/container.php можно было бы написать, зачем он нужен.
> $container['action']=function ($c) {
> return $c['router']->getAction($_SERVER['REQUEST_URI']);;
> };
Это по моему неправильно. Контейнер лучше использовать для хранения объектов, не зависящих от текущего запроса, а не дял таких вещей. Вот если мы в командной строке, где REQUEST_URI вообще нету, твой код запустим, он не сломается? Контейнер - это хранилище объектов-сервисов, а не свалка всего подряд. И не его задача определять текущее действие.
Ну например если мы попробуем твоим приложением обработать несколько запросов подряд, без перезапуска, то у нас это не получится так как action там будет один и тот же.
> $container['routerFile']=function ($c) {
> return $c['json']->readJSON('router.json');
> };
я тут подумал, что этот файл нужен только в роутере и потому выставлять его наружу наверно смысла нет.
> https://github.com/TheSidSpears/Students/blob/master/app/controllers/Controller.php
> namespace StudentList\Controllers;
Неймспейс должен совпадать с именем папки с точностью до регистра букв, иначе в линуксе твой код не заработает. Придется повозиться с переименованиями, так как если просто так переименовать controllers в Controllers то гит под виндой может и не увидеть разницы (там по моему есть какая-то настройка для этого).
https://github.com/TheSidSpears/Students/blob/master/app/controllers/FrontController.php#L42
Вместо 4-этажного ифа лучше делать так:
if (...) {
inlcude ...;
return;
}
if (...) {
inlcude ...;
return;
}
Также, функцию показа страницы ошибки стоит вынести отдельно.
> https://github.com/TheSidSpears/Students/blob/master/app/controllers/FrontController.php#L25
> if ($this->c['router']->isUriValid) {
Вот это выглядит странно. Мы еще не передали в роутер УРЛ, а он уже знает, правильный ли он. Суперглобальные переменные? Лучше переделать на нормальную передачу аргументов.
Ну и если роутер "одноразовый", то есть оодин объект способен разроутить только один запрос, то надо не держать его в контейнере, контейнер скорее для повторно исплоьзуемых сервисов.
Еще почему-то мы не вызываем метод, а смотрим поле.
> $controllerName=$this->c['router']->getControllerName($this->c['action']);
Вот это тоже очень странный код. Вот попробуй его прочитать: мы просим роутер определить имя контроллера, и при этом передаем ему непонятно как определенное имя экшена. Но разве это не задача роутер - определять и контроллер, и экшен по УРЛ?
В моем понимании роутер может выглядеть как-то так:
$route = $router->route($request);
или так:
$route = $router->route($url);
или хотя бы так:
$router = new Router($url);
В моих примерах явно видно что куда передается. Даем что-то на вход и получаем что-то на выходе. В твоем случае это как-то запутанно и смысл, зачем это сделано, непонятен.
> $error=$controller->$actionFunc();
> if ($error==NULL) {
Тут нет проверки что страница с таким кодом вообще есть.
https://github.com/TheSidSpears/Students/blob/master/app/controllers/MainController.php#L13
> protected function prepareForView(){
В данном случае функцию лучше назвать showStidentList(). prepareForView ничего не значит так как каждый контроллер что-то подготавливает.
> $this->c['auth']->getUser()
Что-то мне кажется, $authHelper->getUser() смотрелось бы лучше. Вообще, не стоит сокращать container до c так как читаемость ухудашется.
> $viewer = new Helpers\TableUrlMaker(
Одинаковые вещи лучше называть одинаково, то есть $tableUrlMaker или $urlMaker
https://github.com/TheSidSpears/Students/blob/master/app/Router.php
А почему public $isUriValid; сделано полем? Чтобы его можно было менять снаружи? Что-то не очень понятно.
Что касается отрезания префикса из УРЛ, по моему конечно проще было бы либо:
- разбить УРЛ и префикс на массивы и удалить общее начало
- удалить префикс через preg_replace какой-нибудь
> for ($i=1; $i < count($explodePath)-1; $i++) {
Слеши с краев проще отрезать через trim.
> for ($i=1; $i < count($explodePath)-1; $i++) {
> $urlPath[]=$explodePath[$i];
Это можно заменить на array_slice
> https://github.com/TheSidSpears/Students - что-то тут разметка кривая в README. Ну и вместо словесного описания, лучше просто привести пример как должен выглядеть конфиг Апача или положить файлик students.conf с виртуальным хостом (в линуксе можно для каждого сайта делать свой файл с конфигом в папке /etc/apache2/sites-available/).
README.md вносить в gitignore не стоит, он же нужен тем кто скачивает проект и пытается развернуть.
composer.phar класть в репозиторий не надо, он устаревает же.
> https://github.com/TheSidSpears/Students/blob/master/config.json
Тут ты смешал табы с пробелами и все разъехалось. Настрой свой редактор. Кстати, в линуксе есть команда expand, которую можно совместить с find и заменить табы во всем репозитории.
Комментариев конечно маловато. Например в начале файла https://github.com/TheSidSpears/Students/blob/master/app/container.php можно было бы написать, зачем он нужен.
> $container['action']=function ($c) {
> return $c['router']->getAction($_SERVER['REQUEST_URI']);;
> };
Это по моему неправильно. Контейнер лучше использовать для хранения объектов, не зависящих от текущего запроса, а не дял таких вещей. Вот если мы в командной строке, где REQUEST_URI вообще нету, твой код запустим, он не сломается? Контейнер - это хранилище объектов-сервисов, а не свалка всего подряд. И не его задача определять текущее действие.
Ну например если мы попробуем твоим приложением обработать несколько запросов подряд, без перезапуска, то у нас это не получится так как action там будет один и тот же.
> $container['routerFile']=function ($c) {
> return $c['json']->readJSON('router.json');
> };
я тут подумал, что этот файл нужен только в роутере и потому выставлять его наружу наверно смысла нет.
> https://github.com/TheSidSpears/Students/blob/master/app/controllers/Controller.php
> namespace StudentList\Controllers;
Неймспейс должен совпадать с именем папки с точностью до регистра букв, иначе в линуксе твой код не заработает. Придется повозиться с переименованиями, так как если просто так переименовать controllers в Controllers то гит под виндой может и не увидеть разницы (там по моему есть какая-то настройка для этого).
https://github.com/TheSidSpears/Students/blob/master/app/controllers/FrontController.php#L42
Вместо 4-этажного ифа лучше делать так:
if (...) {
inlcude ...;
return;
}
if (...) {
inlcude ...;
return;
}
Также, функцию показа страницы ошибки стоит вынести отдельно.
> https://github.com/TheSidSpears/Students/blob/master/app/controllers/FrontController.php#L25
> if ($this->c['router']->isUriValid) {
Вот это выглядит странно. Мы еще не передали в роутер УРЛ, а он уже знает, правильный ли он. Суперглобальные переменные? Лучше переделать на нормальную передачу аргументов.
Ну и если роутер "одноразовый", то есть оодин объект способен разроутить только один запрос, то надо не держать его в контейнере, контейнер скорее для повторно исплоьзуемых сервисов.
Еще почему-то мы не вызываем метод, а смотрим поле.
> $controllerName=$this->c['router']->getControllerName($this->c['action']);
Вот это тоже очень странный код. Вот попробуй его прочитать: мы просим роутер определить имя контроллера, и при этом передаем ему непонятно как определенное имя экшена. Но разве это не задача роутер - определять и контроллер, и экшен по УРЛ?
В моем понимании роутер может выглядеть как-то так:
$route = $router->route($request);
или так:
$route = $router->route($url);
или хотя бы так:
$router = new Router($url);
В моих примерах явно видно что куда передается. Даем что-то на вход и получаем что-то на выходе. В твоем случае это как-то запутанно и смысл, зачем это сделано, непонятен.
> $error=$controller->$actionFunc();
> if ($error==NULL) {
Тут нет проверки что страница с таким кодом вообще есть.
https://github.com/TheSidSpears/Students/blob/master/app/controllers/MainController.php#L13
> protected function prepareForView(){
В данном случае функцию лучше назвать showStidentList(). prepareForView ничего не значит так как каждый контроллер что-то подготавливает.
> $this->c['auth']->getUser()
Что-то мне кажется, $authHelper->getUser() смотрелось бы лучше. Вообще, не стоит сокращать container до c так как читаемость ухудашется.
> $viewer = new Helpers\TableUrlMaker(
Одинаковые вещи лучше называть одинаково, то есть $tableUrlMaker или $urlMaker
https://github.com/TheSidSpears/Students/blob/master/app/Router.php
А почему public $isUriValid; сделано полем? Чтобы его можно было менять снаружи? Что-то не очень понятно.
Что касается отрезания префикса из УРЛ, по моему конечно проще было бы либо:
- разбить УРЛ и префикс на массивы и удалить общее начало
- удалить префикс через preg_replace какой-нибудь
> for ($i=1; $i < count($explodePath)-1; $i++) {
Слеши с краев проще отрезать через trim.
> for ($i=1; $i < count($explodePath)-1; $i++) {
> $urlPath[]=$explodePath[$i];
Это можно заменить на array_slice
> https://github.com/TheSidSpears/Students/blob/master/app/Authorization.php#L15
> function __construct($table){
Нужен тайп-хинт.
> setcookie('hash','');
Для удаления куки ей еще ставят время устаревания в прошлом
> $rows = $this->db->prepare("SELECT COUNT(*) FROM `students` WHERE CONCAT(`name`,' ',`sname`,' ',`group_num`,' ',`points`,' ',`gender`,' ',`email`,' ',`b_year`,' ',`is_resident`) LIKE '%:search%'");
По полю вроде gender искать ведь вряд ли получится. Тут стоит оставить только имя, фамилию, группу, число баллов.
И длинные запросы надо переносить и писать в несколько строчек.
> ERController
Вот каждый раз пытаюсь понять, что это значит? EditAndRegisterCOntroller? Тогда проще наверно было написать ProfileController.
https://github.com/TheSidSpears/Students/blob/master/app/controllers/ERController.php#L21
> protected function prepareForView(){
Лучше handleEditForm()
https://github.com/TheSidSpears/Students/blob/master/app/controllers/ERController.php#L51
> protected function setToken(){
Вот я бы вынес это из контроллера хотя бы в util, так как защита от CSRF может понадобиться и в другом месте.
> $this->ViewVars = compact('student','token','validErrors','router');
Я не очень понимаю, какая выгода от использования viewVars вместо явного вызова $this->render(..., compact(...));
https://github.com/TheSidSpears/Students/blob/master/app/controllers/ERController.php#L67
> if ($this->c['action']=='edit') {
> $student=$this->c['table']->getStudentByHash($_COOKIE['hash']);
А что если студент не найден в базе?
> if ($this->c['action']=='edit') {
> $student->hash=$_COOKIE['hash'];
Это наверно не требуется, записывать хеш в существующео студента.
https://github.com/TheSidSpears/Students/blob/master/app/validators/StudentValidator.php
Это конечно не требуется, но не красивее было бы переписать это в ООП-стиле? Ну например:
$fields = [];
$nameField = new StringField('name', 'Имя', 200);
$nameField->setMinLength(1);
$nameField->setCheckRegexp('/.../', '....');
$fields[] = $name;
$pointsField = new IntField('points', 'Кол-во баллов');
$pointsField->setRange(0, 300, ...);
$fields[] = $pointsField;
$this->validateFields($fields, $student);
Или например так:
$fields = [
(new StringField('name', 'имя', 200))->
setMinLength(1)->
setRegexp('/.../', '...'),
(new IntField(...))->
setRange(),
...
];
Так конечно можно далеко зайти, и написать чуть ли не свою библиотеку валидации, но будет выглядеть покрасивее чем сложная череда ифов и многомерных массивов. Твой код, он конечно, работает, но расширять его наверно будет не так удобно, как ООП код.
Ну переделывать конечно не обязательно, но я советую посмотреть, как это сделано в других фреймворках:
ZF2:
- https://framework.zend.com/manual/2.4/en/in-depth-guide/zend-form-zend-form-fieldset.html
- https://framework.zend.com/manual/2.4/en/modules/zend.validator.html
Symfony 2: http://symfony.com/doc/current/validation.html
Django: http://djbook.ru/rel1.7/topics/forms/
Yii2 (там валидация как и работа с базой, встроена в модели): https://yiiframework.com.ua/ru/doc/guide/2/input-validation/
Вообще, в вебе все уже давно придумано и стоит смотреть, как та или иная вещь реализована в других фреймворках.
По поводу views - я конечно понимаю стремление разбить все на части, но как-то немного запутанно на мой взгляд получилось.
Ну например вместо этого шаблона https://github.com/TheSidSpears/Students/blob/master/app/views/pages/edit.php логичнее было сразу /app/views/pages/erview.php и подключать.
Если хочешь попасть в этот замечательный список https://github.com/search?l=PHP&q=student+list&type=Repositories&utf8=✓ не забудь в описание репозитория (не в README) добавить слова student list.
При попытке развернуть твой проект на c9.io (то есть линукс) я получаю ошибку:
Fatal error: Class 'StudentList\Exceptions\ConfigException' not found in /home/ubuntu/workspace/app/Helpers/JSONLoader.php on line 12
Call Stack
Где-то ты файл потерял по дороге. И файл с роутами переименован почему-то.
Я разместил сайт в корневой папке, чтобы URL https://the-sid-spears-students-codedokode.c9users.io соответствовал папке public, и это ломает ссылки в твоем коде. Ну например в форме поиска указан УРЛ action='//search' и 2 слеша в начале значат что дальше идет имя домена (https://github.com/codedokode/pasta/blob/master/network/urls.md).
Тебе надо лучше протестировать свой код для случая когда он находится в корне веб-сервера.
Инструкции в README невнятные, толком не написано что надо загрузить дамп, вызвать композер, подправить конфиг итд.
При смене сортировки (клике по заголовку таблицы) надо сбрасывать номер страницы.
> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
Лучше бы не подключать скрипты со сторонних доменов, а загружать к себе. Да и не очень понятно зачем в IE8 добавлять поддержку media query.
https://the-sid-spears-students-codedokode.c9users.io/bootstrap/css/user.css
Свой CSS надо класть отдельно от бутстрапа, а то можно затереть при обновлении.
В методе highlight надо добавить игнорирование регистра букв.
При попытке открыть регистрацию я вижу
> Notice: Undefined offset: 36 in /home/ubuntu/workspace/app/Helpers/Util.php on line 11
Ты не учел что в массиве нет элемента с ключом 36.
Отправка формы регистрации не работает из-за кривых УРЛ.
> https://github.com/TheSidSpears/Students/blob/master/app/Authorization.php#L15
> function __construct($table){
Нужен тайп-хинт.
> setcookie('hash','');
Для удаления куки ей еще ставят время устаревания в прошлом
> $rows = $this->db->prepare("SELECT COUNT(*) FROM `students` WHERE CONCAT(`name`,' ',`sname`,' ',`group_num`,' ',`points`,' ',`gender`,' ',`email`,' ',`b_year`,' ',`is_resident`) LIKE '%:search%'");
По полю вроде gender искать ведь вряд ли получится. Тут стоит оставить только имя, фамилию, группу, число баллов.
И длинные запросы надо переносить и писать в несколько строчек.
> ERController
Вот каждый раз пытаюсь понять, что это значит? EditAndRegisterCOntroller? Тогда проще наверно было написать ProfileController.
https://github.com/TheSidSpears/Students/blob/master/app/controllers/ERController.php#L21
> protected function prepareForView(){
Лучше handleEditForm()
https://github.com/TheSidSpears/Students/blob/master/app/controllers/ERController.php#L51
> protected function setToken(){
Вот я бы вынес это из контроллера хотя бы в util, так как защита от CSRF может понадобиться и в другом месте.
> $this->ViewVars = compact('student','token','validErrors','router');
Я не очень понимаю, какая выгода от использования viewVars вместо явного вызова $this->render(..., compact(...));
https://github.com/TheSidSpears/Students/blob/master/app/controllers/ERController.php#L67
> if ($this->c['action']=='edit') {
> $student=$this->c['table']->getStudentByHash($_COOKIE['hash']);
А что если студент не найден в базе?
> if ($this->c['action']=='edit') {
> $student->hash=$_COOKIE['hash'];
Это наверно не требуется, записывать хеш в существующео студента.
https://github.com/TheSidSpears/Students/blob/master/app/validators/StudentValidator.php
Это конечно не требуется, но не красивее было бы переписать это в ООП-стиле? Ну например:
$fields = [];
$nameField = new StringField('name', 'Имя', 200);
$nameField->setMinLength(1);
$nameField->setCheckRegexp('/.../', '....');
$fields[] = $name;
$pointsField = new IntField('points', 'Кол-во баллов');
$pointsField->setRange(0, 300, ...);
$fields[] = $pointsField;
$this->validateFields($fields, $student);
Или например так:
$fields = [
(new StringField('name', 'имя', 200))->
setMinLength(1)->
setRegexp('/.../', '...'),
(new IntField(...))->
setRange(),
...
];
Так конечно можно далеко зайти, и написать чуть ли не свою библиотеку валидации, но будет выглядеть покрасивее чем сложная череда ифов и многомерных массивов. Твой код, он конечно, работает, но расширять его наверно будет не так удобно, как ООП код.
Ну переделывать конечно не обязательно, но я советую посмотреть, как это сделано в других фреймворках:
ZF2:
- https://framework.zend.com/manual/2.4/en/in-depth-guide/zend-form-zend-form-fieldset.html
- https://framework.zend.com/manual/2.4/en/modules/zend.validator.html
Symfony 2: http://symfony.com/doc/current/validation.html
Django: http://djbook.ru/rel1.7/topics/forms/
Yii2 (там валидация как и работа с базой, встроена в модели): https://yiiframework.com.ua/ru/doc/guide/2/input-validation/
Вообще, в вебе все уже давно придумано и стоит смотреть, как та или иная вещь реализована в других фреймворках.
По поводу views - я конечно понимаю стремление разбить все на части, но как-то немного запутанно на мой взгляд получилось.
Ну например вместо этого шаблона https://github.com/TheSidSpears/Students/blob/master/app/views/pages/edit.php логичнее было сразу /app/views/pages/erview.php и подключать.
Если хочешь попасть в этот замечательный список https://github.com/search?l=PHP&q=student+list&type=Repositories&utf8=✓ не забудь в описание репозитория (не в README) добавить слова student list.
При попытке развернуть твой проект на c9.io (то есть линукс) я получаю ошибку:
Fatal error: Class 'StudentList\Exceptions\ConfigException' not found in /home/ubuntu/workspace/app/Helpers/JSONLoader.php on line 12
Call Stack
Где-то ты файл потерял по дороге. И файл с роутами переименован почему-то.
Я разместил сайт в корневой папке, чтобы URL https://the-sid-spears-students-codedokode.c9users.io соответствовал папке public, и это ломает ссылки в твоем коде. Ну например в форме поиска указан УРЛ action='//search' и 2 слеша в начале значат что дальше идет имя домена (https://github.com/codedokode/pasta/blob/master/network/urls.md).
Тебе надо лучше протестировать свой код для случая когда он находится в корне веб-сервера.
Инструкции в README невнятные, толком не написано что надо загрузить дамп, вызвать композер, подправить конфиг итд.
При смене сортировки (клике по заголовку таблицы) надо сбрасывать номер страницы.
> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
Лучше бы не подключать скрипты со сторонних доменов, а загружать к себе. Да и не очень понятно зачем в IE8 добавлять поддержку media query.
https://the-sid-spears-students-codedokode.c9users.io/bootstrap/css/user.css
Свой CSS надо класть отдельно от бутстрапа, а то можно затереть при обновлении.
В методе highlight надо добавить игнорирование регистра букв.
При попытке открыть регистрацию я вижу
> Notice: Undefined offset: 36 in /home/ubuntu/workspace/app/Helpers/Util.php on line 11
Ты не учел что в массиве нет элемента с ключом 36.
Отправка формы регистрации не работает из-за кривых УРЛ.
Плохая идея хранить полные пути, лучше относительно корня проекта или папки с файлами. Иначе на хостинг не перенести твой сайт.
В files не нужны уникальные индексы?
https://github.com/TheSidSpears/FileHosting/blob/master/README.md
Подучи маркдаун (это я не ругаюсь, а название такое)
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L8
Не очень понятно зачем 2 URL для главной страницы.
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L36
После успешной обработки формы надо редиректтть а то по F5 файл опять закачается. Перечитай про обработку форм.
> //Передаём пользователю id добавленного файла, чтобы он редактировал его в add_info
> $_SESSION['fileId']=$fileId;
А зачем тут сессия? Можно ведь выдвавать пользователю токен в куках и проверять его при редактировании файла. Так можно будет редактировать не один последний файл.
Вместо сессии можно использовать куки для хранения id последнего файла или указывать его в УРЛ при редиректе.
Да и для работы с сессией по моему есть что-то в Слиме. И ты сессию выдаешь не только тем, кто загружал файл, а вообще всем - стоило бы сделать "ленивые" сессии на основе слимовских, чтобы они создавались только при наличии данных.
Вот я вижу у них есть такая штука: http://docs.slimframework.com/sessions/cookies/ - она хранит данные в зашифрованных куках. Ты мог бы ее допилить или сделать свою похожую на основе пхп-сессий.
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L52
> $app->post('/add_info', function ($request, $response, $args) {
А почему тут у тебя обработка POST идет отдельно от GET? И что-то я не вижу проверки на ошибки. И более того, я не виджу проверки что пользователь имеет право добавлять опсиание.
> $this->filesGW->addDescription($_SESSION['fileId'],$_POST['description']);
Оба этих поля могут отстутствовать.
> https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L81
> $fileModel=$this->filesGW->getFile($args['id']);
> if ($fileModel!=NULL) {
А если она null то что? Надо показывать 404 по стандарту.
Что касается X-Sendfile - хорошо бы сделать в качестве запасной меры отдачу через php. там есть объект Response, в доках Слима написано что он соответствует PSR-7. В этой спецификации есть такое место:
http://www.php-fig.org/psr/psr-7/#1-3-streams
> The body of an HTTP message can be very small or extremely large. Attempting to represent the body of a message as a string can easily consume more memory than intended because the body must be stored completely in memory.
> StreamInterface is used in order to hide the implementation details when a stream of data is read from or written to.
Порывшись в коде ResponseInterface, затем MessageInterface, мы видим:
> public function withBody(StreamInterface $body);
Оно позволяет указать в качестве тела ответа "поток" (открытый файл например), не читая его целиком в память. Осталось только найти объект который бы реализовал StreamInterface для файла.
В коде Слима мы такой видим: https://github.com/slimphp/Slim/blob/3.x/Slim/Http/Stream.php
Надо создать этот объект и в конструктор ему передать дескриптом открытого на чтение файла. И проверить, заработает или нет.
если что, мануал пхп про потоки: http://php.net/manual/ru/book.stream.php - это что-то вроде абстракции над всем, что можно читать или писать.
Я также советую порыться в коде PSR-7 (и реализующих его классов в Слиме) и разобраться.
Чем и хорош ООП, что по коду можно понять что и куда надо передавать.
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L136
> $response = $response->withHeader('Content-Disposition','attachment; filename=' . $args['name']);
Это не работает, так как там можно использовать только латиницу. Я писал про это в комментариях к задаче.
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L154
> $app->post('/add_comment', function ($request, $response, $args){
Тут нет валидации.
https://github.com/TheSidSpears/FileHosting/blob/master/src/classes/FilesFileManager.php#L11
> $uploaddir=$_SERVER['DOCUMENT_ROOT'].$uploadUri.$file->path;
не полагайся на DOCUMENT_ROOT, он например не работает в консоли. Вообще, всю работу с путями стоит вынести в какой-то отдельный метод.
Не вижу SQL дампа для комментариев.
> Models/CommentModel
Что-то тут слов Model многовато.
https://github.com/TheSidSpears/FileHosting/blob/master/src/classes/Models/FileModel.php#L15
> $this->path=date("Y").'/'.date("m").'/'.date("d").'/';
> $this->name=Helper::execDisable(Helper::transliterate($name));
Мне кажется путь и имя удобнее задавать снаружи, в класс отвечающем за сохранение файлов.
https://github.com/TheSidSpears/FileHosting/blob/master/src/classes/Models/CommentsSorter.php
есть методы хранения комментариев в БД уже отсортированными: https://github.com/codedokode/pasta/blob/master/db/trees.md
https://github.com/TheSidSpears/FileHosting/blob/master/src/classes/Helpers/Helper.php
Форматирование ужасное
> if (preg_match("/^((\w)|(\d)|[ -_])*(.)(php|phtml|html|js)$/iu", $fileName)){
Ты используешь "черные списки" (разрешено все, что не запрещено), а надо испоьзовать белые. Твой фильтр заблокирует .htaccess? Опять же, я подробно написал комментарии к задаче по этому поводу.
Метаданные о файле наверно стоит хранить в БД, чтобы не дергать файл при каждом просмотре страницы. Особенно если у нас файлы хранятся на отдельном сервере.
> <script src="{{ projectFolder }}/vendor/components/jquery/jquery.js"></script>
Обычно папка vendor за пределами publiс и потому недоступна снаружи.
https://github.com/TheSidSpears/FileHosting/blob/master/public/js/helper.js#L5
Вместо этого удобнее просто перемещать единственную форму, чем сощздавать каждый раз новую. Шаблоны должны быть в HTML коде, а не в JS.
https://github.com/TheSidSpears/FileHosting/blob/master/public/css/bootstrap/css/user.css
Твои файлы не должны быть внутри сторонних библиотек. Лучше всего помещать библиотеку в отдельную папку в исходном виде.
Плохая идея хранить полные пути, лучше относительно корня проекта или папки с файлами. Иначе на хостинг не перенести твой сайт.
В files не нужны уникальные индексы?
https://github.com/TheSidSpears/FileHosting/blob/master/README.md
Подучи маркдаун (это я не ругаюсь, а название такое)
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L8
Не очень понятно зачем 2 URL для главной страницы.
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L36
После успешной обработки формы надо редиректтть а то по F5 файл опять закачается. Перечитай про обработку форм.
> //Передаём пользователю id добавленного файла, чтобы он редактировал его в add_info
> $_SESSION['fileId']=$fileId;
А зачем тут сессия? Можно ведь выдвавать пользователю токен в куках и проверять его при редактировании файла. Так можно будет редактировать не один последний файл.
Вместо сессии можно использовать куки для хранения id последнего файла или указывать его в УРЛ при редиректе.
Да и для работы с сессией по моему есть что-то в Слиме. И ты сессию выдаешь не только тем, кто загружал файл, а вообще всем - стоило бы сделать "ленивые" сессии на основе слимовских, чтобы они создавались только при наличии данных.
Вот я вижу у них есть такая штука: http://docs.slimframework.com/sessions/cookies/ - она хранит данные в зашифрованных куках. Ты мог бы ее допилить или сделать свою похожую на основе пхп-сессий.
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L52
> $app->post('/add_info', function ($request, $response, $args) {
А почему тут у тебя обработка POST идет отдельно от GET? И что-то я не вижу проверки на ошибки. И более того, я не виджу проверки что пользователь имеет право добавлять опсиание.
> $this->filesGW->addDescription($_SESSION['fileId'],$_POST['description']);
Оба этих поля могут отстутствовать.
> https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L81
> $fileModel=$this->filesGW->getFile($args['id']);
> if ($fileModel!=NULL) {
А если она null то что? Надо показывать 404 по стандарту.
Что касается X-Sendfile - хорошо бы сделать в качестве запасной меры отдачу через php. там есть объект Response, в доках Слима написано что он соответствует PSR-7. В этой спецификации есть такое место:
http://www.php-fig.org/psr/psr-7/#1-3-streams
> The body of an HTTP message can be very small or extremely large. Attempting to represent the body of a message as a string can easily consume more memory than intended because the body must be stored completely in memory.
> StreamInterface is used in order to hide the implementation details when a stream of data is read from or written to.
Порывшись в коде ResponseInterface, затем MessageInterface, мы видим:
> public function withBody(StreamInterface $body);
Оно позволяет указать в качестве тела ответа "поток" (открытый файл например), не читая его целиком в память. Осталось только найти объект который бы реализовал StreamInterface для файла.
В коде Слима мы такой видим: https://github.com/slimphp/Slim/blob/3.x/Slim/Http/Stream.php
Надо создать этот объект и в конструктор ему передать дескриптом открытого на чтение файла. И проверить, заработает или нет.
если что, мануал пхп про потоки: http://php.net/manual/ru/book.stream.php - это что-то вроде абстракции над всем, что можно читать или писать.
Я также советую порыться в коде PSR-7 (и реализующих его классов в Слиме) и разобраться.
Чем и хорош ООП, что по коду можно понять что и куда надо передавать.
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L136
> $response = $response->withHeader('Content-Disposition','attachment; filename=' . $args['name']);
Это не работает, так как там можно использовать только латиницу. Я писал про это в комментариях к задаче.
https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L154
> $app->post('/add_comment', function ($request, $response, $args){
Тут нет валидации.
https://github.com/TheSidSpears/FileHosting/blob/master/src/classes/FilesFileManager.php#L11
> $uploaddir=$_SERVER['DOCUMENT_ROOT'].$uploadUri.$file->path;
не полагайся на DOCUMENT_ROOT, он например не работает в консоли. Вообще, всю работу с путями стоит вынести в какой-то отдельный метод.
Не вижу SQL дампа для комментариев.
> Models/CommentModel
Что-то тут слов Model многовато.
https://github.com/TheSidSpears/FileHosting/blob/master/src/classes/Models/FileModel.php#L15
> $this->path=date("Y").'/'.date("m").'/'.date("d").'/';
> $this->name=Helper::execDisable(Helper::transliterate($name));
Мне кажется путь и имя удобнее задавать снаружи, в класс отвечающем за сохранение файлов.
https://github.com/TheSidSpears/FileHosting/blob/master/src/classes/Models/CommentsSorter.php
есть методы хранения комментариев в БД уже отсортированными: https://github.com/codedokode/pasta/blob/master/db/trees.md
https://github.com/TheSidSpears/FileHosting/blob/master/src/classes/Helpers/Helper.php
Форматирование ужасное
> if (preg_match("/^((\w)|(\d)|[ -_])*(.)(php|phtml|html|js)$/iu", $fileName)){
Ты используешь "черные списки" (разрешено все, что не запрещено), а надо испоьзовать белые. Твой фильтр заблокирует .htaccess? Опять же, я подробно написал комментарии к задаче по этому поводу.
Метаданные о файле наверно стоит хранить в БД, чтобы не дергать файл при каждом просмотре страницы. Особенно если у нас файлы хранятся на отдельном сервере.
> <script src="{{ projectFolder }}/vendor/components/jquery/jquery.js"></script>
Обычно папка vendor за пределами publiс и потому недоступна снаружи.
https://github.com/TheSidSpears/FileHosting/blob/master/public/js/helper.js#L5
Вместо этого удобнее просто перемещать единственную форму, чем сощздавать каждый раз новую. Шаблоны должны быть в HTML коде, а не в JS.
https://github.com/TheSidSpears/FileHosting/blob/master/public/css/bootstrap/css/user.css
Твои файлы не должны быть внутри сторонних библиотек. Лучше всего помещать библиотеку в отдельную папку в исходном виде.
ОП пару дней не заходил в тред, аноны одичали.
>>834935
> Поменял значение у width: https://jsfiddle.net/tb7k9dw9/9/
Теперь ок
>>834983
Не знаю что ты там исправлял, все сломано снова. Если ты пишешь new Some\Something то этот путь отсчитывается относительно текущего неймспейса, надо писать new \Some\Something или использовать use.
>>835026
Имена методов с подчеркиваниями соответстуют PSR? Что-то я забыл. И в именах переменных слишком много сокращений, не надо так.
> $filtr_req = filter_var( $this->get_url_requested(), FILTER_SANITIZE_URL);
Не советую, этот filter_var не любит русские буквы в доменах например.
> $req_arr = explode( '/', $filtr_req);
Надо разбивать на части не весь URL а только PATH который можно получить из parse_url. А то если у тебя идет знак вопроса с параметрами, он тоже попадет в массив.
> if($i == 0 && array_key_exists('0', $req_arr)){
Почему проверка не через count()? Первый элемент может отсутствовать?
Ну и вот такой подход, как у тебя. когда УРЛ имеет вид:
/ctrl/action/x/1/y/2/
Имеет недостатки, что там нет строгих проверок и один УРЛ можно записать кучей способов. Поисковики это не любят, да и с точки зрения HTTP лучше чтобы УРЛ страницы был жестко определен.
>>835072
> - Валидировать реквест - не задача роутера.
Почему, можно это делать. Чтобы вернуть 404.
>>835127
> насчет ошибок я тоже задумывался, но подумал что не знаю как лучше реализовать в случае ошибки писать exit('Код ошибки') ?
Возвращать код ошибки. Не завершать скрипт.
Про PSR-7 - хороший совет, почитай хотя бы что это.
>>835305
Аяксом но смысла особого не вижу, больше проблем чем пользы.
ОП пару дней не заходил в тред, аноны одичали.
>>834935
> Поменял значение у width: https://jsfiddle.net/tb7k9dw9/9/
Теперь ок
>>834983
Не знаю что ты там исправлял, все сломано снова. Если ты пишешь new Some\Something то этот путь отсчитывается относительно текущего неймспейса, надо писать new \Some\Something или использовать use.
>>835026
Имена методов с подчеркиваниями соответстуют PSR? Что-то я забыл. И в именах переменных слишком много сокращений, не надо так.
> $filtr_req = filter_var( $this->get_url_requested(), FILTER_SANITIZE_URL);
Не советую, этот filter_var не любит русские буквы в доменах например.
> $req_arr = explode( '/', $filtr_req);
Надо разбивать на части не весь URL а только PATH который можно получить из parse_url. А то если у тебя идет знак вопроса с параметрами, он тоже попадет в массив.
> if($i == 0 && array_key_exists('0', $req_arr)){
Почему проверка не через count()? Первый элемент может отсутствовать?
Ну и вот такой подход, как у тебя. когда УРЛ имеет вид:
/ctrl/action/x/1/y/2/
Имеет недостатки, что там нет строгих проверок и один УРЛ можно записать кучей способов. Поисковики это не любят, да и с точки зрения HTTP лучше чтобы УРЛ страницы был жестко определен.
>>835072
> - Валидировать реквест - не задача роутера.
Почему, можно это делать. Чтобы вернуть 404.
>>835127
> насчет ошибок я тоже задумывался, но подумал что не знаю как лучше реализовать в случае ошибки писать exit('Код ошибки') ?
Возвращать код ошибки. Не завершать скрипт.
Про PSR-7 - хороший совет, почитай хотя бы что это.
>>835305
Аяксом но смысла особого не вижу, больше проблем чем пользы.
Скоро перекатимся в новый тред.
Напомню себе глянуть https://github.com/Phrlog/StudentList из #835694
>Поставить ссылку на скрипт выше.
<img src="script.php?name=2.jpg&size=max">
Я правильно понял?
В идеале без фрейворков.
А то пытаюсь что то свое сделать, вроде начинается неплохо, но чем дальше, тем больше говонокода.
>>Поставить ссылку на скрипт выше.
Я опять что-то пропустил в этой жизни) Вот этот вариант не работает <img src="script.php?name=2.jpg&size=max">
Наверное ты имел ввиду что-то другое?
Я пробовал через require, но он не пропускает параметры.
Я гуглил, но в основном ссылки делают <a href="script.php?name=2.jpg&size=max"> - вот так открывается миниатюра нужного размера. Но как это вывести на странице?
>>Зачем отправлять запрос если можно сразу создать миниатюру?
Т.е. сделать скрипт который и проверяет папку на наличие новых картинок и уменьшает их, а затем генерирует страницу целиком? Это не подходит к условиям задачи.
>>скрипт который выводит страницу может заодно сгенерировать недостающие миниатюры (до отдачи страницы браузеру)
Каким образом, если за генерацию миниатюр отвечает скрипт-аутист, который общается с миром посредством получения параметров из GET запроса (это условие задачи)?
>>Зачем так усложнять?
>>Вообще, это все уже много раз разжевано.
Что почитать на эту тему?
У меня 2 вопроса - почему мануал для создания php кода написан на убогом юкозе? И ещё посоветуйте достойный аналог Notepad++ для Linux.
Он не написан на юкозе, а хостится там (после закрытия народа сайты перенесли на юкоз, а времени купить и настроить отдельный домен у ОПа не было). Написан он на php.
w2 http://ideone.com/myLCaF
w3 http://ideone.com/FU3kOw
w4_1 http://ideone.com/HUBDWh
Начал час назад.
Вроде получается кошерно - по поводу оформления будут предъявы?
Вместо exit лучше использовать elseif
>Вообще, я вижу, ты HTML/CSS неплохо подучил, еще JS + jQuery выучишь и можешь уже версткой заниматься как минимум. Ну а в связке с PHP и фреймворками - вообще полноценные сайты делать.
В этом и цель. Думаю, после js и jquery уже смогу устроиться верстальщиком куда-нибудь, а может и сам соберу веб-конторку, кто знает...
>> max-width: calc(100% + 2px);
>Про это я вроде уже писал - тут calc не нужен и не стоит делать элемент шире своего родителя в данном случае.
Это уже, как вы заметили, я исправил >>834935
>>846546
>- отображение в древних CSS2.1 браузерах
>А вот в ИЕ8 - отображаются только заголовки вкладок: https://www.browserstack.com/screenshots/2c8f1a50213f7d05a2713db8cf2b15cfac961af6/win7_ie_8.0.png без содержимого.
>А если делать идеально, то, так как показ вкладок сделан через селектор ~ и nth-of-type то и скрывать их хорошо бы через них (на случай если какой-то браузер поддерживает CSS3 селекторы частично). Тут конечно придется извернуться, что поделать.
>На первый взгляд может конечно показаться, что это сложно, лишние усилия, но если представлять, какие селекторы к каким стандартам относятся, то задача выглядит не такой уж и сложной. В данном случае я сделал так, что для скрытия содержимого нужен тот же уровень поддержки CSS, что и для показа. По моему, это подход "progressive enhancement" - прогрессивное улучшение - браузеры с CSS2.1 получают базовую версию страницу без табов, а в CSS3 добавляется возможность переключать табы (для красоты можно еще скрыть заголовки табов в старых браузерах, чтобы пользователь не думал, что по ним можно кликать).
Меня больше беспокоит как такой подход реализовать для динамических страниц. Например, администратор из админки хочет удалить и\или добавить новые вкладки, и nth-of-type\-child уже не поможет, потому что заранее неизвестно сколько таких элементов.
>>846546
>В общем, как я понимаю, пока нерешенной остается только последняя задача про верстку.
Да, скоро сделаю и её.
>>846548
Оу, а вы, похоже, уже отвечали на эти вопросы http://arhivach.org/thread/193343/#835052
Но ничего, развернутый ответ никогда не бывает лишним. Для меня лично именно прояснились многие вещи по поводу инкапсуляции.
Кстати, по этим вопросам у меня уже всё исправлено >>836646
Скоро ещё js будет.
>>846550
>>>821641 | http://arhivach.org/thread/193343/#821641
>
>>>Также, у тебя в контроллере стоит 4 вызова render. Ты выносишь знание о структуре страницы в контроллер, но удобнее просто подключить один шаблон, а он уже пусть подключает что ему нужно.
>> А как это реализовать? Сделать отдельный шаблон в котором будет вызываться render нужных элементов? Для этого нужно будет передавать Viewer который находиться в свойствах контроллера. Я такого не разу не делал, по моему это усложнение.
>>
>> Или сделать один шаблон который содержит в себе все элементы (т.е. они не вызываются с помощью render, а приписаны в ручную)? Если да, то не будет ли это лишней копипастой?
>
>Я имел в виду, что не надо в контроллере вызывать отдельные куски шаблона. Отдельно вызвать вывод шапки, отдельно вызвать вывод таблицы. Нужно в контроллере вызвать шаблон, а он уже пусть подключает шапку, таблицу, меню и все, что ему нужно. Также удобно шаблоны-кусочки сложить в отдельную папку.
А такой подход подойдет?
https://github.com/someApprentice/Students/blob/master/app/Controller/IndexAction.php#L54
https://github.com/someApprentice/Students/blob/master/public/index.php
Могу переделать чтобы сам шаблон вызывал нужные ему элементы
class IndexAction
{
...
$this->viewer->render('templates/index.phtml', compact('pager'));
}
index.phtml
$container['Viewer']->render('templates/head.phtml');
$container['Viewer']->render('templates/list.phtml', compact('pager'));
$container['Viewer']->render('templates/foot.phtml');
>>846550
>> Что-то ничего не сохранилось. Должно быть так: https://jsfiddle.net/a6wk2qng/
>Тут косяк - при нажатии на кнопку она становится на пиксель меньше и кнопки справа от нее двигаются.
Ах да, точно. Просто я решил тот подвох раньше чем решил проблему с лишним пикселем. Исправил: https://jsfiddle.net/a6wk2qng/1/
>Вообще, я вижу, ты HTML/CSS неплохо подучил, еще JS + jQuery выучишь и можешь уже версткой заниматься как минимум. Ну а в связке с PHP и фреймворками - вообще полноценные сайты делать.
В этом и цель. Думаю, после js и jquery уже смогу устроиться верстальщиком куда-нибудь, а может и сам соберу веб-конторку, кто знает...
>> max-width: calc(100% + 2px);
>Про это я вроде уже писал - тут calc не нужен и не стоит делать элемент шире своего родителя в данном случае.
Это уже, как вы заметили, я исправил >>834935
>>846546
>- отображение в древних CSS2.1 браузерах
>А вот в ИЕ8 - отображаются только заголовки вкладок: https://www.browserstack.com/screenshots/2c8f1a50213f7d05a2713db8cf2b15cfac961af6/win7_ie_8.0.png без содержимого.
>А если делать идеально, то, так как показ вкладок сделан через селектор ~ и nth-of-type то и скрывать их хорошо бы через них (на случай если какой-то браузер поддерживает CSS3 селекторы частично). Тут конечно придется извернуться, что поделать.
>На первый взгляд может конечно показаться, что это сложно, лишние усилия, но если представлять, какие селекторы к каким стандартам относятся, то задача выглядит не такой уж и сложной. В данном случае я сделал так, что для скрытия содержимого нужен тот же уровень поддержки CSS, что и для показа. По моему, это подход "progressive enhancement" - прогрессивное улучшение - браузеры с CSS2.1 получают базовую версию страницу без табов, а в CSS3 добавляется возможность переключать табы (для красоты можно еще скрыть заголовки табов в старых браузерах, чтобы пользователь не думал, что по ним можно кликать).
Меня больше беспокоит как такой подход реализовать для динамических страниц. Например, администратор из админки хочет удалить и\или добавить новые вкладки, и nth-of-type\-child уже не поможет, потому что заранее неизвестно сколько таких элементов.
>>846546
>В общем, как я понимаю, пока нерешенной остается только последняя задача про верстку.
Да, скоро сделаю и её.
>>846548
Оу, а вы, похоже, уже отвечали на эти вопросы http://arhivach.org/thread/193343/#835052
Но ничего, развернутый ответ никогда не бывает лишним. Для меня лично именно прояснились многие вещи по поводу инкапсуляции.
Кстати, по этим вопросам у меня уже всё исправлено >>836646
Скоро ещё js будет.
>>846550
>>>821641 | http://arhivach.org/thread/193343/#821641
>
>>>Также, у тебя в контроллере стоит 4 вызова render. Ты выносишь знание о структуре страницы в контроллер, но удобнее просто подключить один шаблон, а он уже пусть подключает что ему нужно.
>> А как это реализовать? Сделать отдельный шаблон в котором будет вызываться render нужных элементов? Для этого нужно будет передавать Viewer который находиться в свойствах контроллера. Я такого не разу не делал, по моему это усложнение.
>>
>> Или сделать один шаблон который содержит в себе все элементы (т.е. они не вызываются с помощью render, а приписаны в ручную)? Если да, то не будет ли это лишней копипастой?
>
>Я имел в виду, что не надо в контроллере вызывать отдельные куски шаблона. Отдельно вызвать вывод шапки, отдельно вызвать вывод таблицы. Нужно в контроллере вызвать шаблон, а он уже пусть подключает шапку, таблицу, меню и все, что ему нужно. Также удобно шаблоны-кусочки сложить в отдельную папку.
А такой подход подойдет?
https://github.com/someApprentice/Students/blob/master/app/Controller/IndexAction.php#L54
https://github.com/someApprentice/Students/blob/master/public/index.php
Могу переделать чтобы сам шаблон вызывал нужные ему элементы
class IndexAction
{
...
$this->viewer->render('templates/index.phtml', compact('pager'));
}
index.phtml
$container['Viewer']->render('templates/head.phtml');
$container['Viewer']->render('templates/list.phtml', compact('pager'));
$container['Viewer']->render('templates/foot.phtml');
>>846550
>> Что-то ничего не сохранилось. Должно быть так: https://jsfiddle.net/a6wk2qng/
>Тут косяк - при нажатии на кнопку она становится на пиксель меньше и кнопки справа от нее двигаются.
Ах да, точно. Просто я решил тот подвох раньше чем решил проблему с лишним пикселем. Исправил: https://jsfiddle.net/a6wk2qng/1/
> setcookie('hash','');
> Для удаления куки ей еще ставят время устаревания в прошлом
Это для справки или ж\это обязательное условие?
В твоем случае ставится кука с пустым содержимым (содержит пустую строку). Проблем от этого нет, но удалить бы ее было аккуратнее.
Подскажите, почему в выводится (1,2) и (1,2) вместо (5,8) и (1,2)? Вроде все корректно работает до этого момента.
Объясни нубу, разве можно использовать метод одного класса в другом?
У тебя метод showPoint() назначен в одном классе, а используется в другом.
Вот я и проверил. Не очень понятно, почему работает именно так.
попробуй Atom
> $p1=$p2=new Point;
Нужно поменять на
$p2=new Point;
$p1=new Point;
В PHP (как и во многих других языках) объект передаётся по ссылке при копировании (простые типы - по значению). То есть у тебя $p1 и $p2 указывают в памяти на один и тот же объект, поэтому изменяя значения у $p2 ты тем самым меняешь значения у $p1 тоже.
Можно попробовать почитать здесь: http://php.net/manual/ru/language.oop5.references.php
Оказывается и у ОПа об этом написано: http://archive-ipq-co.narod.ru/l1/pasta.html
>>847057
Конечно, так например работает Dependency Injection.
А когда объект содержит в себе другие объекты (Line содержит Point) - это называется агрегация. Тоже описано здесь: http://archive-ipq-co.narod.ru/l1/pasta.html
Можно ли (и нужно ли) использовать PHP DOM extension для парсинга HTML страниц? В HTML документе есть отличия от XML, например в последнем, все теги должны иметь закрывающий тег. Также разметка HTML страницы может быть не валидна.
Как избежать проблем с этим? Если пропустить HTML через beautify, а потом парсить с помощью Xpath. Есть ли в стандартной PHP DOM библиотеке подобная функция вроде HTML-beautify? Существует метод DOMDocument::normalize, но судя по спецификации он не совсем для этого?
Существуют сторонние библиотеки: Simple DOM Parser, PHPQuery, ZEND.DOM. Но я новичок, что лучше, начать с изучения основ: спецификации DOM и Xpath, если это подходящие инструменты? Или перестать волноваться и полюбить Simple DOM Parser, заблудиться с PHPQuery или постичь ZEND.DOM?
Купи впску с 256 озу за 100 рублей в месяц
Вот например одно из заданий (http://ideone.com/V0sgIx) мне мозг выедало час, в результате чего я получил результат всё равно не тот, что нужен. Реквест - в каком месте не допилен код, и вообще стоит ли мне этим заниматься, если я уже на таком простом моменте туплю и сижу часами?
потому что я понимаю что это ОООЧЕНЬ легко, но как делать всё равно не могу выкупить
В третьем условии цикла for лучше не ставить выражение, пока твёрдо не уяснишь себе его работу.
Вот прямо сейчас попробуй поставить туда $year ++, а это выражение из третьего условия перенеси в тело цикла.
Сразу всё начинает работать правильно.
Дело в том, что когда у тебя в теле цикла значение доходит до 970172, то оно выводится в последний раз, а далее ещё раз идёт расчёт внутри тела и ещё одна итерация не идёт, потому что уже не соответствует условию в цикле (что у нас в скобках на втором месте).
Схватишь такие моменты - потом легко всё пойдёт, это ты ещё до задачи про Айфон не дошёл, hahaha.
В гугле подсовывают что-то не то.
$p1 и $p2 указывают на один и тот же объект, а не на 2 разных.
>>847057
Метод можно вызывать где угодно при условии, что у нас есть сам объект и что метод публичный.
>>846583
> Вот этот вариант не работает <img src="script.php?name=2.jpg&size=max">
Значит надо изучить, почему, а не говорить "почему-то не работает". Может это принципиально невозможно, а может просто где-то у тебя баг.
> Я пробовал через require, но он не пропускает параметры.
Потому что в require указывается имя файла, а не URL который будет далее обработан веб-сервером и разобран. Тебе надо разобраться, как работает веб-сервер, как он обрабатывает HTTP-запросы и как в его составе работает PHP (где почитать? ну не знаю, давай свой урок посоветую где чуть-чуть это упомянуто: https://github.com/codedokode/pasta/blob/master/soft/web-server.md ). А require просто выполняет код из указанного файла.
> Каким образом, если за генерацию миниатюр отвечает скрипт-аутист, который общается с миром посредством получения параметров из GET запроса (это условие задачи)?
Вообще, есть способ вызвать этот скрипт, и передать ему параметры, и даже очистить то, что он выведет в виде заголовков и тела ответа, но это будет очень грязный хак конечно.
>>>Зачем так усложнять?
>>>Вообще, это все уже много раз разжевано.
> Что почитать на эту тему?
Не знаю. Мне просто ответ на вопрос показался очевидным, и я сам ведь раньше делал скрипты для генерации превьюшек.
>>846829
w2 http://ideone.com/myLCaF
Верно.
w3 http://ideone.com/FU3kOw
Верно.
w4_1 http://ideone.com/HUBDWh
Все правильно.
>>846869
http://ideone.com/kwxOpb
Тут не обязательно ставить несколько раз exit, лучше поставить блок if ... elseif .. else
$p1 и $p2 указывают на один и тот же объект, а не на 2 разных.
>>847057
Метод можно вызывать где угодно при условии, что у нас есть сам объект и что метод публичный.
>>846583
> Вот этот вариант не работает <img src="script.php?name=2.jpg&size=max">
Значит надо изучить, почему, а не говорить "почему-то не работает". Может это принципиально невозможно, а может просто где-то у тебя баг.
> Я пробовал через require, но он не пропускает параметры.
Потому что в require указывается имя файла, а не URL который будет далее обработан веб-сервером и разобран. Тебе надо разобраться, как работает веб-сервер, как он обрабатывает HTTP-запросы и как в его составе работает PHP (где почитать? ну не знаю, давай свой урок посоветую где чуть-чуть это упомянуто: https://github.com/codedokode/pasta/blob/master/soft/web-server.md ). А require просто выполняет код из указанного файла.
> Каким образом, если за генерацию миниатюр отвечает скрипт-аутист, который общается с миром посредством получения параметров из GET запроса (это условие задачи)?
Вообще, есть способ вызвать этот скрипт, и передать ему параметры, и даже очистить то, что он выведет в виде заголовков и тела ответа, но это будет очень грязный хак конечно.
>>>Зачем так усложнять?
>>>Вообще, это все уже много раз разжевано.
> Что почитать на эту тему?
Не знаю. Мне просто ответ на вопрос показался очевидным, и я сам ведь раньше делал скрипты для генерации превьюшек.
>>846829
w2 http://ideone.com/myLCaF
Верно.
w3 http://ideone.com/FU3kOw
Верно.
w4_1 http://ideone.com/HUBDWh
Все правильно.
>>846869
http://ideone.com/kwxOpb
Тут не обязательно ставить несколько раз exit, лучше поставить блок if ... elseif .. else
> Меня больше беспокоит как такой подход реализовать для динамических страниц. Например, администратор из админки хочет удалить и\или добавить новые вкладки, и nth-of-type\-child уже не поможет, потому что заранее неизвестно сколько таких элементов.
Сделать правило на 10-15 элементов - вряд ли где-то будет столько вкладок. Для гарантии можно в динамически генерируемых вкладках поставить ограничение на количество. А так, не всегда получается найти идеальное решение - иногда чем-то надо жертвовать.
> Могу переделать чтобы сам шаблон вызывал нужные ему элементы
Да, только передавать контейнер в шаблон - лишнее. Ведь в контейнере много сервисов, и большинство из них из шаблона не должны вызываться. Лучше сделать так:
- можно подключать шаблон внутри объекта, тогда в шаблоне можно будет использовать $this->render(...). Так было сделано в ZF1 - там переданные переменные были доступны как $this->var, и можно было вызвать вспомогательные методы
- можно сделать отдельную функцию или статический метод вроде ViewUtil::render(...) или $view->render(...)
> Ах да, точно. Просто я решил тот подвох раньше чем решил проблему с лишним пикселем. Исправил: https://jsfiddle.net/a6wk2qng/1/
Ок, думаю, мы с этой задачей закончили. Я не помню, делали ли мы еще версию для старых браузеров (чтобы там показывались сами радиокнопки), но если понятно, как это сделать, то можно не делать.
>>846621
Под линукс есть gedit, но не знаю, насколько он достойный.
>>847176
Либо DOM либо специализированная библиотека вроде phpQuery.
> В HTML документе есть отличия от XML, например в последнем, все теги должны иметь закрывающий тег. Также разметка HTML страницы может быть не валидна.
Потому у ДОМ есть метод loadHtml и способы отключения вывода ошибок.
Там еще и с кодировками проблема может быть.
> Существует метод DOMDocument::normalize, но судя по спецификации он не совсем для этого?
Он для склеивания вместе текстовых нод.
> Но я новичок, что лучше, начать с изучения основ: спецификации DOM и Xpath, если это подходящие инструменты?
Лучше сначала DOM + Xpath, потом библиотеки.
>>847183
Иногда бывает пробный бесплатный период у хостинга - можно воспользоваться.
>>847305
У тебя есть цикл и условие его выполнения:
$i <= 1000000;
Если $i больше миллиона то тело не выполняется. И потому вероятность что сработает это условие:
> if ($i >= 1000000) {
очень маленькая - оно сработает только если на счету ровно миллион (если больше - тело цикла не выполняется).
Вместо $i и $b лучше было использовать более говорящее название.
>>847532
Не знаю.
> Меня больше беспокоит как такой подход реализовать для динамических страниц. Например, администратор из админки хочет удалить и\или добавить новые вкладки, и nth-of-type\-child уже не поможет, потому что заранее неизвестно сколько таких элементов.
Сделать правило на 10-15 элементов - вряд ли где-то будет столько вкладок. Для гарантии можно в динамически генерируемых вкладках поставить ограничение на количество. А так, не всегда получается найти идеальное решение - иногда чем-то надо жертвовать.
> Могу переделать чтобы сам шаблон вызывал нужные ему элементы
Да, только передавать контейнер в шаблон - лишнее. Ведь в контейнере много сервисов, и большинство из них из шаблона не должны вызываться. Лучше сделать так:
- можно подключать шаблон внутри объекта, тогда в шаблоне можно будет использовать $this->render(...). Так было сделано в ZF1 - там переданные переменные были доступны как $this->var, и можно было вызвать вспомогательные методы
- можно сделать отдельную функцию или статический метод вроде ViewUtil::render(...) или $view->render(...)
> Ах да, точно. Просто я решил тот подвох раньше чем решил проблему с лишним пикселем. Исправил: https://jsfiddle.net/a6wk2qng/1/
Ок, думаю, мы с этой задачей закончили. Я не помню, делали ли мы еще версию для старых браузеров (чтобы там показывались сами радиокнопки), но если понятно, как это сделать, то можно не делать.
>>846621
Под линукс есть gedit, но не знаю, насколько он достойный.
>>847176
Либо DOM либо специализированная библиотека вроде phpQuery.
> В HTML документе есть отличия от XML, например в последнем, все теги должны иметь закрывающий тег. Также разметка HTML страницы может быть не валидна.
Потому у ДОМ есть метод loadHtml и способы отключения вывода ошибок.
Там еще и с кодировками проблема может быть.
> Существует метод DOMDocument::normalize, но судя по спецификации он не совсем для этого?
Он для склеивания вместе текстовых нод.
> Но я новичок, что лучше, начать с изучения основ: спецификации DOM и Xpath, если это подходящие инструменты?
Лучше сначала DOM + Xpath, потом библиотеки.
>>847183
Иногда бывает пробный бесплатный период у хостинга - можно воспользоваться.
>>847305
У тебя есть цикл и условие его выполнения:
$i <= 1000000;
Если $i больше миллиона то тело не выполняется. И потому вероятность что сработает это условие:
> if ($i >= 1000000) {
очень маленькая - оно сработает только если на счету ровно миллион (если больше - тело цикла не выполняется).
Вместо $i и $b лучше было использовать более говорящее название.
>>847532
Не знаю.
У меня получилось вставить вот так: плагин IncludeMe и [includeme file="first.html"].
При этом файл first.html лежит в корневой директории сайта.
Для подключения файла из папки вот так сделал: [includeme src="http://site.com/ads/first.html"].
При этом у объявления становится размер изображений на странице, дальше надо по вкусу добавить ширину и высоту у вставленного файла (поставил: width=100% height="auto").
>Значит надо изучить, почему, а не говорить "почему-то не работает". Может это принципиально невозможно, а может просто где-то у тебя баг.
Ты меня запутал. Один из вариантов, который я предложил, сделать это с помощью AJAX. Ты сказал что это переусложнение, отправил в направлении "поставить ссылку". И что это значит поставить ссылку? Я пол дня сидел, проверял все самые безумные варианты, которые только пришли мне в голову и листал stack-overflow. Скажи пожалуйста, что конкретно ты имел ввиду?
>Тебе надо разобраться, как работает веб-сервер, как он обрабатывает HTTP-запросы и как в его составе работает PHP
Я прочитал про сервер, запросы и заголовки все твои уроки, по три раза наверное. Но из практики отправки запросов-заголовков у меня только урок про студентов и валидации формы. Я могу конечно еще раз это прочитать, но не думаю что будет толк. Сложно понять то, что не можешь представить. Тем-более запомнить. Возможно позже я вернусь к этому, после парочки простых скриптов.
>Вообще, есть способ вызвать этот скрипт, и передать ему параметры, и даже очистить то, что он выведет в виде заголовков и тела ответа, но это будет очень грязный хак конечно.
Значит тот первый вариант с сылкой на скрипт, который ты имел ввиду, не подходит для данного условия?
Вот смотри, в чем переусложнение. Что происходит когда ты вводишь в браузер http://example.local/index.php ? Очевидно, браузер отправляет HTTP-запрос, веб-сервер видит что в URL указан php-скрипт и запускает его с помощью интерпретатора. Тот выполняет скрипт, скрипт выводит какой-то HTML код и он отправляется назад в браузер.
А теперь вопрос, что будет если наш php скрипт выведет что-то такое:
<img src="http://example.local/image.php?...">
браузер, увидев тег img, попытается загрузить картинку, для чего отправит еще один запрос по указанному адресу, и будет ждать что ему придет ответ с картиночным content-type и данными картинки в теле ответа.
Соответственно, ты предлагаешь зачем-то сделать аякс-запросы. Но я не понимаю, зачем они нужны если браузер сам умеет при обнаружении картинки отправлять HTTP запрос (судя по твоим словам, это "не работает", но почему не работает, ты не разобрался, а надо разобраться и исправить). Я уже писал, у тебя целых 2 альтернативы:
- можно в момент вывода страницы вызвать скрипт генерации превьюшек, возможно придется использовать хаки если тот скрипт что-то выводит или меняет заголовки ответа
- можно вызвать скрипт генерации с помощью тега img
На мой взгляд, вариант 1 должен работать, надо просто найти причину ошибки. Ну или найти причину почему он невозможен в принципе.
По моим ощущениям ты пока плохо понимаешь как работает протокол HTTP, как браузер и веб-сервер обмениваются данными, а тестовое задание которое тебе дали, как раз и пытается это проверить. Соответственно, единственный вариант - лучше разобраться в этом протоколе.
stack overflow тебе тут не поможет. Если ты начинающий, то ты должен больше читать статьи или учебники, разбираться как все устроено, а не искать хаки на SO. SO для тех, кто на работе, и чей начальник не разрешает тратить время на изучение ситуации.
Вот что поможет:
- урок про веб-сервер который, может я уже давал, (увы, я пока его целиком не успел сделать): https://github.com/codedokode/pasta/blob/master/soft/web-server.md
- также, если недописанный урок по HTTP: https://github.com/codedokode/pasta/blob/master/network/http.md
- может быть, поискать статьи про протокол http, если не найдешь, то есть спецификация, она конечно большая по объему но зато подробная:
- англ: https://www.w3.org/Protocols/rfc2616/rfc2616.txt
- рус: http://lib.ru/WEBMASTER/rfc2068/
- википедия https://ru.wikipedia.org/wiki/HTTP
Я понимаю, что в первый раз ты скорее всего просто запутаешься от кучи непонятных слов. Но если ты будешь параллельно например смотреть в отладчике браузера запросы и одновременно искать упомянутые там заголовки в спецификации, может что-то и станет понятнее.
- также, открой отладчик в браузере (Ctrl + Shift + I или F12) на вкладке Network и посмотри какие запросы идут на сервер и какие ответы приходят.
Также, предлагаю сделать такие вещи:
1) сделай статическую страницу на HTML, без PHP, с несколькими картинками. Надо, чтобы она открывалась через локальный сервер (апач, денвер и тд). Открой ее в браузере и в этот момент внимательно изучи в отладчике какие запросы отправляются и какие ответы приходят.
1b) Попробуй с помощью putty или telnet или другой программы для ручной отправки HTTP запросов отправь вручную запрос на получение картинки и посмотри какой ответ придет. Как это сделать - описано в моем уроке про веб-сервер. Если у тебя нет или не запускается telnet, скачай putty и используй там raw режим или найди любую другую программу для ручной отправки HTTP запросов.
Например, под линукс и виндоус, если ты не боишься командной строки, можно использовать курл. Я обычно его и использую:
curl -v 'http://example.com/image.png'
(гайд по командной строке есть в ОП посте)
Также, есть такое расширение для Хрома: https://www.getpostman.com/ - но мне кажется что тебе было бы полезнее помучаться с командной строкой конечно а не жать на цветные прямоугольники.
2) сделай теперь то же самое, но на PHP и с использованием скрипта генерации превьюшек в теге img. Опять же посмотри какие запросы отправляются и сравни их с теми что были на статической странице. Может, ты увидишь чем они отличаются и почему не работают
2b) Попробуй вручную отправить запрос по ссылке из тега img (которая указывает на скрипт генерации превьюшек) и посмотреть, что придет в ответ.
Если и после этого у тебя что-то не заработает - запости сюда HTTP-запросы которые отправляются при загрузке страницы и кпревьюшек и какие ответы на них прихоядт. Имея эти данные, думаю, найти причину будет несложно.
А пока я в любом случае тебе помочь не могу так как не вижу какие запросы отправляются и что приходит в ответ.
Вот смотри, в чем переусложнение. Что происходит когда ты вводишь в браузер http://example.local/index.php ? Очевидно, браузер отправляет HTTP-запрос, веб-сервер видит что в URL указан php-скрипт и запускает его с помощью интерпретатора. Тот выполняет скрипт, скрипт выводит какой-то HTML код и он отправляется назад в браузер.
А теперь вопрос, что будет если наш php скрипт выведет что-то такое:
<img src="http://example.local/image.php?...">
браузер, увидев тег img, попытается загрузить картинку, для чего отправит еще один запрос по указанному адресу, и будет ждать что ему придет ответ с картиночным content-type и данными картинки в теле ответа.
Соответственно, ты предлагаешь зачем-то сделать аякс-запросы. Но я не понимаю, зачем они нужны если браузер сам умеет при обнаружении картинки отправлять HTTP запрос (судя по твоим словам, это "не работает", но почему не работает, ты не разобрался, а надо разобраться и исправить). Я уже писал, у тебя целых 2 альтернативы:
- можно в момент вывода страницы вызвать скрипт генерации превьюшек, возможно придется использовать хаки если тот скрипт что-то выводит или меняет заголовки ответа
- можно вызвать скрипт генерации с помощью тега img
На мой взгляд, вариант 1 должен работать, надо просто найти причину ошибки. Ну или найти причину почему он невозможен в принципе.
По моим ощущениям ты пока плохо понимаешь как работает протокол HTTP, как браузер и веб-сервер обмениваются данными, а тестовое задание которое тебе дали, как раз и пытается это проверить. Соответственно, единственный вариант - лучше разобраться в этом протоколе.
stack overflow тебе тут не поможет. Если ты начинающий, то ты должен больше читать статьи или учебники, разбираться как все устроено, а не искать хаки на SO. SO для тех, кто на работе, и чей начальник не разрешает тратить время на изучение ситуации.
Вот что поможет:
- урок про веб-сервер который, может я уже давал, (увы, я пока его целиком не успел сделать): https://github.com/codedokode/pasta/blob/master/soft/web-server.md
- также, если недописанный урок по HTTP: https://github.com/codedokode/pasta/blob/master/network/http.md
- может быть, поискать статьи про протокол http, если не найдешь, то есть спецификация, она конечно большая по объему но зато подробная:
- англ: https://www.w3.org/Protocols/rfc2616/rfc2616.txt
- рус: http://lib.ru/WEBMASTER/rfc2068/
- википедия https://ru.wikipedia.org/wiki/HTTP
Я понимаю, что в первый раз ты скорее всего просто запутаешься от кучи непонятных слов. Но если ты будешь параллельно например смотреть в отладчике браузера запросы и одновременно искать упомянутые там заголовки в спецификации, может что-то и станет понятнее.
- также, открой отладчик в браузере (Ctrl + Shift + I или F12) на вкладке Network и посмотри какие запросы идут на сервер и какие ответы приходят.
Также, предлагаю сделать такие вещи:
1) сделай статическую страницу на HTML, без PHP, с несколькими картинками. Надо, чтобы она открывалась через локальный сервер (апач, денвер и тд). Открой ее в браузере и в этот момент внимательно изучи в отладчике какие запросы отправляются и какие ответы приходят.
1b) Попробуй с помощью putty или telnet или другой программы для ручной отправки HTTP запросов отправь вручную запрос на получение картинки и посмотри какой ответ придет. Как это сделать - описано в моем уроке про веб-сервер. Если у тебя нет или не запускается telnet, скачай putty и используй там raw режим или найди любую другую программу для ручной отправки HTTP запросов.
Например, под линукс и виндоус, если ты не боишься командной строки, можно использовать курл. Я обычно его и использую:
curl -v 'http://example.com/image.png'
(гайд по командной строке есть в ОП посте)
Также, есть такое расширение для Хрома: https://www.getpostman.com/ - но мне кажется что тебе было бы полезнее помучаться с командной строкой конечно а не жать на цветные прямоугольники.
2) сделай теперь то же самое, но на PHP и с использованием скрипта генерации превьюшек в теге img. Опять же посмотри какие запросы отправляются и сравни их с теми что были на статической странице. Может, ты увидишь чем они отличаются и почему не работают
2b) Попробуй вручную отправить запрос по ссылке из тега img (которая указывает на скрипт генерации превьюшек) и посмотреть, что придет в ответ.
Если и после этого у тебя что-то не заработает - запости сюда HTTP-запросы которые отправляются при загрузке страницы и кпревьюшек и какие ответы на них прихоядт. Имея эти данные, думаю, найти причину будет несложно.
А пока я в любом случае тебе помочь не могу так как не вижу какие запросы отправляются и что приходит в ответ.
https://github.com/Phrlog/StudentList/
> Создайте файл Database.php в каталоге app, снабдив его следующей информацией:
Было бы хорошо создать заготовку этого файла, ну например Database.php или Database.php.dist (если это вызывает проблемы с гитом), чтобы оставалось только подправить пару значений.
Вот этот автосгенерированный файл мне не нравится, так как в нем трудно найти то, что относится именно к твоему проекту: https://github.com/Phrlog/StudentList/blob/master/.gitignore
https://github.com/Phrlog/StudentList/blob/master/public/index.php#L8
Этот способ автозагрузки устарел много лет назад, почитай урок https://github.com/codedokode/pasta/blob/master/php/autoload.md
> https://github.com/Phrlog/StudentList/blob/master/public/index.php#L8
> $app = new FrontController($development = true);
А зачем писать $development = ? Если можно просто true?
https://github.com/Phrlog/StudentList/blob/master/public/css/main.css
Код бутстрапа не должен быть перемешан с твоим кодом, а должен быть в отдельной папке, иначе его тяжело будет обновлять и трудно понять, где именно твой код.
https://github.com/Phrlog/StudentList/blob/master/app/FrontController.php#L22
Я не уверен, что это хорошая идея закладывать выбор режима отладки в коде. По идее, это должно задаваться в конфиге, чтобы пользователь не должен был бы ковыряться в твоем коде и искать где что поменять. Более того, саму эту настройку гораздо удобнее задать в php.ini или конфиге апача (которые для этого и предназначены!). А ты по сути игнорируешь заданную администратором сервера в php.ini настройку.
> return $url = explode('/', $_SERVER['REQUEST_URI']);
Тут есть проблема что URL може содержать query string, то есть параметры после знака вопроса. От них лучше избавиться с помощью parse_url.
> https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L17
> private $model;
Вот это не очень удачно. Ты думаешь что в MVC контроллер всегда работает ровно с одной моделью, но это не так. Их может быть ноль, может быть несколько. И еще есть путаница, что моделью иногда назвают часть приложения (то есть много классов вместе), а иногда класс представляющий сущность, например, модель студента.
В твоем случае правильнее назвать поле $studentDataGateway или $studentDG
https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L31
> private $form = array(
А нет ли тут дублирования данных с валидатором? Хотя конечно ситуация сложная, непонятно где хранить эти данные, можно и так оставить наверно.
https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L66
> @$search
Это ужасно. ЧТо тут делает знак @? Ты сам получается не знаешь, есть у тебя такая переменная или нет?
> $pager = new Pager($this->model, $studentsPerPage);
Вот это мне тоже не нравится. Зачем пагинатору класс работы с БД? Ему ведь нужно только число студентов - ну так проще только его и передать. Надо стараться разделять зоны ответственности между классами.
> $student->password = $student->generatePassword(40);
Если метод generatePassword не использует $this то его лучше сделать статическим чтобы показать что он не привязан к конкретному экземпляру студента.
> $validate->validate($student, 'register');
> if ($validate->getErrors() != false) {
Вот это на мой взгляд не очень хороший подход - делать так, что одна функция валидирует, а другая возвращает результат. Почему бы сразу не возвращать результат? Либо сделать одноразовый валидатор, которому студент передается в конструктор (но мне это меньше нравится).
> $result = "Неудача! Исправьте ошибки";
Надо бы с пользователем повежливее разговаривать...
> if ($this->model->insert($student)) {
Я думаю, что если использовать исключения в PDO то insert ошибку вернуть не может и потому if тут будет не нужен.
> public function register()
> public function edit()
Эти функции слишком похожи, их надо объдинить в одну, а не клонировать код.
https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L158
> } else {
> header('Location: /index');
Неправильно. При ошибке в URL надо показывать страницу 404, а не молча редиректить, ничего не объясняя.
https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L168
А где выдача HTTP-код 404? Почитай в википедии или где-то еще про коды состояния протокола HTTP. Немного про сам HTTP можно почитать в моем уроке https://github.com/codedokode/pasta/blob/master/soft/web-server.md или погуглить.
> https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L25
> $this->pdo = new PDO(
Объект PDO правильнее передавать снаружи, а не жестко прописывать в классе его создание. Почитай про DI https://github.com/codedokode/pasta/blob/master/arch/di.md
Также, перечитай урок про исключения: https://github.com/codedokode/pasta/blob/master/php/exceptions.md Вот это вот место неправильное:
> } catch (PDOException $e) {
> echo "Connection error.";
> }
Тут не надо перехватывать исключение. Если ты хочешь его перехатывать, то надо делать это глобально и показыать правильную страницу ошибки с кодом 500, а не невнятную надпись, отдаваемую с кодом 200.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L79
Длинные строки надо переносить.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L105
> $student = $sth->fetchAll(PDO::FETCH_CLASS, "StudentModel")[0];
Есть функция fetch для получения одной записи.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L129
> = $sth->fetch()[0];
есть fetchColumn()
https://github.com/Phrlog/StudentList/
> Создайте файл Database.php в каталоге app, снабдив его следующей информацией:
Было бы хорошо создать заготовку этого файла, ну например Database.php или Database.php.dist (если это вызывает проблемы с гитом), чтобы оставалось только подправить пару значений.
Вот этот автосгенерированный файл мне не нравится, так как в нем трудно найти то, что относится именно к твоему проекту: https://github.com/Phrlog/StudentList/blob/master/.gitignore
https://github.com/Phrlog/StudentList/blob/master/public/index.php#L8
Этот способ автозагрузки устарел много лет назад, почитай урок https://github.com/codedokode/pasta/blob/master/php/autoload.md
> https://github.com/Phrlog/StudentList/blob/master/public/index.php#L8
> $app = new FrontController($development = true);
А зачем писать $development = ? Если можно просто true?
https://github.com/Phrlog/StudentList/blob/master/public/css/main.css
Код бутстрапа не должен быть перемешан с твоим кодом, а должен быть в отдельной папке, иначе его тяжело будет обновлять и трудно понять, где именно твой код.
https://github.com/Phrlog/StudentList/blob/master/app/FrontController.php#L22
Я не уверен, что это хорошая идея закладывать выбор режима отладки в коде. По идее, это должно задаваться в конфиге, чтобы пользователь не должен был бы ковыряться в твоем коде и искать где что поменять. Более того, саму эту настройку гораздо удобнее задать в php.ini или конфиге апача (которые для этого и предназначены!). А ты по сути игнорируешь заданную администратором сервера в php.ini настройку.
> return $url = explode('/', $_SERVER['REQUEST_URI']);
Тут есть проблема что URL може содержать query string, то есть параметры после знака вопроса. От них лучше избавиться с помощью parse_url.
> https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L17
> private $model;
Вот это не очень удачно. Ты думаешь что в MVC контроллер всегда работает ровно с одной моделью, но это не так. Их может быть ноль, может быть несколько. И еще есть путаница, что моделью иногда назвают часть приложения (то есть много классов вместе), а иногда класс представляющий сущность, например, модель студента.
В твоем случае правильнее назвать поле $studentDataGateway или $studentDG
https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L31
> private $form = array(
А нет ли тут дублирования данных с валидатором? Хотя конечно ситуация сложная, непонятно где хранить эти данные, можно и так оставить наверно.
https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L66
> @$search
Это ужасно. ЧТо тут делает знак @? Ты сам получается не знаешь, есть у тебя такая переменная или нет?
> $pager = new Pager($this->model, $studentsPerPage);
Вот это мне тоже не нравится. Зачем пагинатору класс работы с БД? Ему ведь нужно только число студентов - ну так проще только его и передать. Надо стараться разделять зоны ответственности между классами.
> $student->password = $student->generatePassword(40);
Если метод generatePassword не использует $this то его лучше сделать статическим чтобы показать что он не привязан к конкретному экземпляру студента.
> $validate->validate($student, 'register');
> if ($validate->getErrors() != false) {
Вот это на мой взгляд не очень хороший подход - делать так, что одна функция валидирует, а другая возвращает результат. Почему бы сразу не возвращать результат? Либо сделать одноразовый валидатор, которому студент передается в конструктор (но мне это меньше нравится).
> $result = "Неудача! Исправьте ошибки";
Надо бы с пользователем повежливее разговаривать...
> if ($this->model->insert($student)) {
Я думаю, что если использовать исключения в PDO то insert ошибку вернуть не может и потому if тут будет не нужен.
> public function register()
> public function edit()
Эти функции слишком похожи, их надо объдинить в одну, а не клонировать код.
https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L158
> } else {
> header('Location: /index');
Неправильно. При ошибке в URL надо показывать страницу 404, а не молча редиректить, ничего не объясняя.
https://github.com/Phrlog/StudentList/blob/master/app/Controller.php#L168
А где выдача HTTP-код 404? Почитай в википедии или где-то еще про коды состояния протокола HTTP. Немного про сам HTTP можно почитать в моем уроке https://github.com/codedokode/pasta/blob/master/soft/web-server.md или погуглить.
> https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L25
> $this->pdo = new PDO(
Объект PDO правильнее передавать снаружи, а не жестко прописывать в классе его создание. Почитай про DI https://github.com/codedokode/pasta/blob/master/arch/di.md
Также, перечитай урок про исключения: https://github.com/codedokode/pasta/blob/master/php/exceptions.md Вот это вот место неправильное:
> } catch (PDOException $e) {
> echo "Connection error.";
> }
Тут не надо перехватывать исключение. Если ты хочешь его перехатывать, то надо делать это глобально и показыать правильную страницу ошибки с кодом 500, а не невнятную надпись, отдаваемую с кодом 200.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L79
Длинные строки надо переносить.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L105
> $student = $sth->fetchAll(PDO::FETCH_CLASS, "StudentModel")[0];
Есть функция fetch для получения одной записи.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L129
> = $sth->fetch()[0];
есть fetchColumn()
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L145
> return !(boolean)$result;
Проще написать return $result == '0';
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L175
> if (strcasecmp($student->$s, $search) == 0) {
Эта функция не работает нормально с русскими буквами, не используй ее.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L176
> $student->$s = "<b>" . h($student->$s) . "</b>";
Тут аж 3 ошибки. Во-первых, в модели не должно быть HTML кода, за вывод данных у нас отвечает Представление, во-вторых, неправильно заменять данные в модели студента, так как эта модель представляет копию данных в базе и неудобно работать с объектом, когда в нем подменяют информацию. Ну представь например что мы захотим посчитать длину имени, и посчитаем ее вместе с HTML кодом. Или мы решим обновить что-нибудь у студента и занесем этот HTML код в базу.
наконец, третья ошибка тут:
> foreach ($students as &$student) {
для объектов не надо указывать & так как $student это и есть по сути указатель на объект.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L269
> WHERE id LIKE ?
Почему LIKE?
https://github.com/Phrlog/StudentList/blob/master/app/StudentModel.php#L37
> $this->$name = h($attribute);
Тут ошибка. Неправильно применять тут экранирование, в базе надо хранить исходные данные. Экранировать надо в шаблоне в момент вывода. Тогда сразу видно, экранируются данные или нет. А при твоем подходе, когда данные экранируются перед вставкой в базу и не экранируются при выводе есть шансы, что:
- где-то забудут что-то проэкранировать и понять это невозможно так как экранирование делается не в том месте где идет вывод. Вот объясни, как в твоем коде доказать что абсолютно все выводимые данные экранированы? Отслеживать каждую переменную?
- есть шансы что данные буду проэкранированы дважды и пользователь увидит & quot вместо кавычек. По моему в форме редактирования именно это и происходит.
перечитай мой урок по XSS.
https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L55
> public function setErrorsInForm($form)
> public function setValuesInForm($form, $student)
Вот это неудачные методы. Они получают массив данных неизвестной структуры, что-то с ним делают и возвращает. Более того, непонятно что они вообще делает в классе валидации, так как по сути это просто выдача данных (которая уже есть в методе getErrors()), только каким-то запутанным способом. А второй метод вообще данные из класса никак не использует.
https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L97
HTML-код должен быть в шаблоне.
https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L209
А почему этот метод публичный? Перечитай-ка мой пост про инкапсуляцию: >>846548
> filter_var($email, FILTER_VALIDATE_EMAIL)
Хочу предупредить что функция не поддерживает емайлы вида n3zMameANUSприме^V-рPUNCTUM9R`рф
> } elseif (!$this->dataGateway->isUniqueEmail($email)) {
А при редактировании емайл менять нельзя? Надо сделать чтобы было можно.
> https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L141
> @$this->errors["groupNumber"] .= $this->applyTags("Недопустимый формат номера группы, разрешается использовать только кирилицу и цифры");
Ошибки надо исправлять, а не прятать. В этой задаче (как и во всех других наших задачах) нельзя использовать оператор @ вообще.
https://github.com/Phrlog/StudentList/blob/master/app/View.php#L11
> const VIEWS_BASEDIR = "../templates/";
Не очень хорошо использовать относительный путь, он зависит от текущего каталога, лучше получать полный путь через __DIR__
https://github.com/Phrlog/StudentList/blob/master/app/View.php#L19
> ob_start
Если делать правильно,то надо при исключении отменять буферизацию и пробрасывать исключение дальше.
https://github.com/Phrlog/StudentList/blob/master/app/students.sql#L32
> `groupNumber` tinytext,
Зачем тут tinytext? Там всего 5 символов.
> `birthDate` date NOT NULL,
Нужен только год в условии задачи по моему.
> `mark` int(255) NOT NULL,
255 это не макс. значение, а рекомендуемая ширина поля при выводе.
> ADD UNIQUE KEY `unique_id` (`id`);
Это нужно объявить как первичный ключ. Прочитай, что это такое: https://github.com/codedokode/pasta/blob/master/db/normalization.md
> `password` varchar(255) DEFAULT NULL
Название не очень удачное так как это не такой пароль, который можно ввести в форму. Лучше назвать token.
https://github.com/Phrlog/StudentList/blob/master/templates/edit.php#L5
Не хватает HTML5 валидации, ну например атрибута required хотя бы.
> @$student->sex == 'male'
Опять @
> <br />
Слеш в HTML не ставится
https://github.com/Phrlog/StudentList/blob/master/templates/students.php#L1
> print "<div class='alert al
Неудобно писать HTML в кавычках, тут нужен иф.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L145
> return !(boolean)$result;
Проще написать return $result == '0';
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L175
> if (strcasecmp($student->$s, $search) == 0) {
Эта функция не работает нормально с русскими буквами, не используй ее.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L176
> $student->$s = "<b>" . h($student->$s) . "</b>";
Тут аж 3 ошибки. Во-первых, в модели не должно быть HTML кода, за вывод данных у нас отвечает Представление, во-вторых, неправильно заменять данные в модели студента, так как эта модель представляет копию данных в базе и неудобно работать с объектом, когда в нем подменяют информацию. Ну представь например что мы захотим посчитать длину имени, и посчитаем ее вместе с HTML кодом. Или мы решим обновить что-нибудь у студента и занесем этот HTML код в базу.
наконец, третья ошибка тут:
> foreach ($students as &$student) {
для объектов не надо указывать & так как $student это и есть по сути указатель на объект.
https://github.com/Phrlog/StudentList/blob/master/app/StudentDataGateway.php#L269
> WHERE id LIKE ?
Почему LIKE?
https://github.com/Phrlog/StudentList/blob/master/app/StudentModel.php#L37
> $this->$name = h($attribute);
Тут ошибка. Неправильно применять тут экранирование, в базе надо хранить исходные данные. Экранировать надо в шаблоне в момент вывода. Тогда сразу видно, экранируются данные или нет. А при твоем подходе, когда данные экранируются перед вставкой в базу и не экранируются при выводе есть шансы, что:
- где-то забудут что-то проэкранировать и понять это невозможно так как экранирование делается не в том месте где идет вывод. Вот объясни, как в твоем коде доказать что абсолютно все выводимые данные экранированы? Отслеживать каждую переменную?
- есть шансы что данные буду проэкранированы дважды и пользователь увидит & quot вместо кавычек. По моему в форме редактирования именно это и происходит.
перечитай мой урок по XSS.
https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L55
> public function setErrorsInForm($form)
> public function setValuesInForm($form, $student)
Вот это неудачные методы. Они получают массив данных неизвестной структуры, что-то с ним делают и возвращает. Более того, непонятно что они вообще делает в классе валидации, так как по сути это просто выдача данных (которая уже есть в методе getErrors()), только каким-то запутанным способом. А второй метод вообще данные из класса никак не использует.
https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L97
HTML-код должен быть в шаблоне.
https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L209
А почему этот метод публичный? Перечитай-ка мой пост про инкапсуляцию: >>846548
> filter_var($email, FILTER_VALIDATE_EMAIL)
Хочу предупредить что функция не поддерживает емайлы вида n3zMameANUSприме^V-рPUNCTUM9R`рф
> } elseif (!$this->dataGateway->isUniqueEmail($email)) {
А при редактировании емайл менять нельзя? Надо сделать чтобы было можно.
> https://github.com/Phrlog/StudentList/blob/master/app/Validation.php#L141
> @$this->errors["groupNumber"] .= $this->applyTags("Недопустимый формат номера группы, разрешается использовать только кирилицу и цифры");
Ошибки надо исправлять, а не прятать. В этой задаче (как и во всех других наших задачах) нельзя использовать оператор @ вообще.
https://github.com/Phrlog/StudentList/blob/master/app/View.php#L11
> const VIEWS_BASEDIR = "../templates/";
Не очень хорошо использовать относительный путь, он зависит от текущего каталога, лучше получать полный путь через __DIR__
https://github.com/Phrlog/StudentList/blob/master/app/View.php#L19
> ob_start
Если делать правильно,то надо при исключении отменять буферизацию и пробрасывать исключение дальше.
https://github.com/Phrlog/StudentList/blob/master/app/students.sql#L32
> `groupNumber` tinytext,
Зачем тут tinytext? Там всего 5 символов.
> `birthDate` date NOT NULL,
Нужен только год в условии задачи по моему.
> `mark` int(255) NOT NULL,
255 это не макс. значение, а рекомендуемая ширина поля при выводе.
> ADD UNIQUE KEY `unique_id` (`id`);
Это нужно объявить как первичный ключ. Прочитай, что это такое: https://github.com/codedokode/pasta/blob/master/db/normalization.md
> `password` varchar(255) DEFAULT NULL
Название не очень удачное так как это не такой пароль, который можно ввести в форму. Лучше назвать token.
https://github.com/Phrlog/StudentList/blob/master/templates/edit.php#L5
Не хватает HTML5 валидации, ну например атрибута required хотя бы.
> @$student->sex == 'male'
Опять @
> <br />
Слеш в HTML не ставится
https://github.com/Phrlog/StudentList/blob/master/templates/students.php#L1
> print "<div class='alert al
Неудобно писать HTML в кавычках, тут нужен иф.
Нету, но в твоем случае $id должен вставляться через плейсхолдер.
>>836122
Можно завернуть Pimple в класс с методами вроде getSomeService либо унаследоваться от него. Но вообще, Pimple это для простых проектов где сервисов разумное количество.
Вопрос на SO на англ: http://stackoverflow.com/questions/6395737/how-do-i-make-my-php-ide-understand-dependency-injection-containers
>>836777
Вообще, по идее композер предназначен для подключения пхп библиотек, а не CSS или JS. Для них есть пакетный менеджер bower. Также, есть какое-то расширениек композеру, чтобы ставить пакеты из bower.
Но вот перемещать их в публичную папку ("публиковать") он не умеет. Это уже не его задача.
Решают проблему тем, что делают или настраивают систему публикации, например:
- пишут скрипт на bash или на php, копирующий файлы или делающий симлинки
- пишут скрипт для gulp (gulp - это средство для склеивания, обработки статических файлов, написанное на Node.JS)
В твоем случае можно написать например php или bash скрипт и прописать его вызов в composer.json, чтобы он атоматически вызывался после установки или обновления пакетов. Мы по моему пару тредов назад это обсуждали уже. Скопирую старые посты:
----------
№815520
Переписал код для композер скрипта: http://ideone.com/j6DyCf . Оп посмотри позяз. Вроде работает правильно по пикрелейтеду. Алсо вопрос, как-то в composerjson можно будет передать в статический метод аргументы? Или придется без аргументов.
-----------
Аноним 08/08/16 Пнд 05:01:01 №815913
>>815520
Это описано в документации: https://getcomposer.org/doc/articles/scripts.md
Аргументы передавать нельзя. Но из функции-обработчика можно получить объект Composer и из него конфиг. Таким образом, нужные параметры можно закодировать в самом composer.json в ключе extra. Так поступают плагины к композеру, расширяющие его возможности.
Но я бы конечно еще подумал, стоит ли так делать. Надо делать как можно более простое решение, а не сложную навороченную систему.
Также, под линуксом вместо копирования файлов может быть выгоднее ставить симлинки (под новой виндой тоже есть симлинки, но с ними все сложно). Изучи-ка эту тему.
Вообще, проблема, которую ты затронул (установка сторонних клиентских библиотек) довольно важная. Я пока не знаю какого-то единого решения, знаю только что каждый решает ее по своему.
Более того, в теории тут можно получить определенную выгоду. Например, бутстрап или jquery UI состоят из отдельных компонентов и можно собрать маленькую версию библиотеки, включающую только нужные тебе в проекте вещи. И задавать, какие компоненты нам нужны, в конфиге, так что легко было бы добавлять или убирать их.
Если бы мы могли как-то это описать в конфиге, чтобы оно само скачивалось и компилировалось, было бы неплохо. Но это конечно усложнит установку, так как потребует как минимум Node.JS ставить и пакеты к ней.
Тут еще конечно возникает вопрос, а должен ли композер этим заниматься? По идее для установки клиентских библиотек есть bower, есть системы сборки и минификации вроде gulp, также есть assetic из Симфони, предназначенный примерно для того же.
Вот ссылка на Assetic: https://github.com/kriswallsmith/assetic Он немного сложный, но умеет делать то, что нам требуется.
Более того, композер позволяет писать к нему плагины. Которые могут запускаться во время установки библиотек и что-то делать, например, копировать файлы в публичную папку. Можно попробовать поискать такой плагин или написать самому. Если это распространенная задача, и такой плагин подошел бы ко многим проектам, то выгоднее сделать именно плагин к композеру, который можно повторно использовать, а не одноразовый класс, который подходит только к одному твоему проекту и который надо будет вручную добавлять и подправлять под каждый новый проект.
Но если решать это через композер, то возникает вопрос, где мы должны остановиться? Вот мы скачали библиотеку композером. далее, надо ее перенести в публичную папку - должен композер это делать? хорошо, а если мы хотим далее склеить несколько css файлов в один - должен композер это делать? А переписывать в файлах URL к картинкам кто должен? А минифицировать?
Так мы можем прийти к тому, что перенесем в композер половину bower и gulp (или половину assetic). правильно ли это?
Я тут погуглил и для начала, нашел такую штуку: https://github.com/fxpio/composer-asset-plugin
Это по сути аналог bower, который умеет читать данные из репозиториев bower и устанавливать его пакеты (если ты не в курсе - то bower это пакетный менеджер для фронтенда, т есть аналог композера для js и css библиотек).
У него есть свои проблемы:
- пока плагин может поставить только пользователь глобально, и нельзя прописать в composer.json для одного проекта
- он только скачивает, но не публикует библиотеки
В общем, я бы предложил рассмотреть такие варианты:
- посмотреть, может ли нам тут помочь Assetic
- написать или поискать плагин к композеру для публикации библиотек, в том числе с поддержкой симлинков
- если эти варианты не годятся, то пользоваться своим скриптом, вроде того что ты написал
---------------
Нету, но в твоем случае $id должен вставляться через плейсхолдер.
>>836122
Можно завернуть Pimple в класс с методами вроде getSomeService либо унаследоваться от него. Но вообще, Pimple это для простых проектов где сервисов разумное количество.
Вопрос на SO на англ: http://stackoverflow.com/questions/6395737/how-do-i-make-my-php-ide-understand-dependency-injection-containers
>>836777
Вообще, по идее композер предназначен для подключения пхп библиотек, а не CSS или JS. Для них есть пакетный менеджер bower. Также, есть какое-то расширениек композеру, чтобы ставить пакеты из bower.
Но вот перемещать их в публичную папку ("публиковать") он не умеет. Это уже не его задача.
Решают проблему тем, что делают или настраивают систему публикации, например:
- пишут скрипт на bash или на php, копирующий файлы или делающий симлинки
- пишут скрипт для gulp (gulp - это средство для склеивания, обработки статических файлов, написанное на Node.JS)
В твоем случае можно написать например php или bash скрипт и прописать его вызов в composer.json, чтобы он атоматически вызывался после установки или обновления пакетов. Мы по моему пару тредов назад это обсуждали уже. Скопирую старые посты:
----------
№815520
Переписал код для композер скрипта: http://ideone.com/j6DyCf . Оп посмотри позяз. Вроде работает правильно по пикрелейтеду. Алсо вопрос, как-то в composerjson можно будет передать в статический метод аргументы? Или придется без аргументов.
-----------
Аноним 08/08/16 Пнд 05:01:01 №815913
>>815520
Это описано в документации: https://getcomposer.org/doc/articles/scripts.md
Аргументы передавать нельзя. Но из функции-обработчика можно получить объект Composer и из него конфиг. Таким образом, нужные параметры можно закодировать в самом composer.json в ключе extra. Так поступают плагины к композеру, расширяющие его возможности.
Но я бы конечно еще подумал, стоит ли так делать. Надо делать как можно более простое решение, а не сложную навороченную систему.
Также, под линуксом вместо копирования файлов может быть выгоднее ставить симлинки (под новой виндой тоже есть симлинки, но с ними все сложно). Изучи-ка эту тему.
Вообще, проблема, которую ты затронул (установка сторонних клиентских библиотек) довольно важная. Я пока не знаю какого-то единого решения, знаю только что каждый решает ее по своему.
Более того, в теории тут можно получить определенную выгоду. Например, бутстрап или jquery UI состоят из отдельных компонентов и можно собрать маленькую версию библиотеки, включающую только нужные тебе в проекте вещи. И задавать, какие компоненты нам нужны, в конфиге, так что легко было бы добавлять или убирать их.
Если бы мы могли как-то это описать в конфиге, чтобы оно само скачивалось и компилировалось, было бы неплохо. Но это конечно усложнит установку, так как потребует как минимум Node.JS ставить и пакеты к ней.
Тут еще конечно возникает вопрос, а должен ли композер этим заниматься? По идее для установки клиентских библиотек есть bower, есть системы сборки и минификации вроде gulp, также есть assetic из Симфони, предназначенный примерно для того же.
Вот ссылка на Assetic: https://github.com/kriswallsmith/assetic Он немного сложный, но умеет делать то, что нам требуется.
Более того, композер позволяет писать к нему плагины. Которые могут запускаться во время установки библиотек и что-то делать, например, копировать файлы в публичную папку. Можно попробовать поискать такой плагин или написать самому. Если это распространенная задача, и такой плагин подошел бы ко многим проектам, то выгоднее сделать именно плагин к композеру, который можно повторно использовать, а не одноразовый класс, который подходит только к одному твоему проекту и который надо будет вручную добавлять и подправлять под каждый новый проект.
Но если решать это через композер, то возникает вопрос, где мы должны остановиться? Вот мы скачали библиотеку композером. далее, надо ее перенести в публичную папку - должен композер это делать? хорошо, а если мы хотим далее склеить несколько css файлов в один - должен композер это делать? А переписывать в файлах URL к картинкам кто должен? А минифицировать?
Так мы можем прийти к тому, что перенесем в композер половину bower и gulp (или половину assetic). правильно ли это?
Я тут погуглил и для начала, нашел такую штуку: https://github.com/fxpio/composer-asset-plugin
Это по сути аналог bower, который умеет читать данные из репозиториев bower и устанавливать его пакеты (если ты не в курсе - то bower это пакетный менеджер для фронтенда, т есть аналог композера для js и css библиотек).
У него есть свои проблемы:
- пока плагин может поставить только пользователь глобально, и нельзя прописать в composer.json для одного проекта
- он только скачивает, но не публикует библиотеки
В общем, я бы предложил рассмотреть такие варианты:
- посмотреть, может ли нам тут помочь Assetic
- написать или поискать плагин к композеру для публикации библиотек, в том числе с поддержкой симлинков
- если эти варианты не годятся, то пользоваться своим скриптом, вроде того что ты написал
---------------
https://github.com/anotherCodeMunkey/studentsList/
https://github.com/anotherCodeMunkey/studentsList/blob/master/localhost.sql#L22
> CREATE DATABASE `students`
Не надо в дампе писать создание БД так как твой дамп нельзя теперь загрузить в БД с другим названием.
> `birthYear` date NOT NULL,
Для года есть отдельный тип
> `hash` varchar(200) NOT NULL,
Тут хорошо бы добавить пояснение с помощью слова COMMENT
> ENGINE=MyISAM
Изучи-ка отличия MyISAM от InnoDB:
https://rtfm.co.ua/mysql-otlichiya-mezhdu-myisam-i-innodb/
http://itif.ru/otlichiya-myisam-innodb/
MyISAM это старый движок без внешних ключей и транзакций.
Также, я вижу что у тебя нет отдельной публичной папки, а весь код фактически лежит в ней. Это плохая идея, достаточно забыть какой-то файл там и он может оказаться доступным для скачивания хакерами.
Вот тут написано подробнее: https://github.com/codedokode/pasta/blob/master/student-list.md#Выносим-код-за-корень-сервера
> https://github.com/anotherCodeMunkey/studentsList/blob/master/autoloader.php
Функция устарела, читай урок про автозагрузку https://github.com/codedokode/pasta/blob/master/php/autoload.md
https://github.com/anotherCodeMunkey/studentsList/blob/master/settings.php
Хранить настройки в константах не очень хорошая идея, так как они будут доступны везде, а лучше когда мы можем ограничивать доступ к ним. Лучше переменные.
> https://github.com/anotherCodeMunkey/studentsList/blob/master/index.php
Тут надо бы как-то упростить код, ну например, в простейшем варианте можно передавать параметр, обозначающий тип действия: index.php?act=register. Можно использовать интересную возможность добавлять путь после имени скрипта (PATH_INFO): /index.php/register. Можно сделать для разных действий разные входные скрипты: /register.php
Дописал пункт в задачу: https://github.com/codedokode/pasta/blob/master/student-list.md#Структура-url-и-роутинг
https://github.com/anotherCodeMunkey/studentsList/tree/master/app/cls
Что значит cls? Что за сокращения? Student кстати это модель студента (или сущность, или модель предметной области, или доменная модель).
> https://github.com/anotherCodeMunkey/studentsList/blob/master/app/cls/Student.php#L26
> public function get($info)
Это в общем плохо, так как у нас получается что-то похожее на массив, а не класс. Лучше или сделать функции-геттеры, или публичные свойства.
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/controller/Ctrl.php
Слишком большой контроллер. Как минимум регистрацию стоит вынести в отдельный класс.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/CleanInput.php
Это класс с черной магией. Ты можешь объяснить, почему фильтруются именно такие конструкции? Это все возможные опасные конструкции или не все? Если не все, то почему?
> if (get_magic_quotes_gpc()) {
Предлагаю упростить себе жизнь и отказываться работать, если включена эта настройка. Или просто не проверять ее, а написать об этом в README. Она уж много лет как отключена по умолчанию.
https://github.com/anotherCodeMunkey/studentsList/tree/master/app/model/lib
Тоже неудачное название. Что значит lib? И почему model, остальные классы это не часть модели?
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/model/lib/Db.php
Это вредный паттерн, получение зависимостей через статическую функцию. Почитай про DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
Я думаю что класс Db не нужен.
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/model/lib/TableDataGateway.php#L5
> class TableDataGateway extends Db
Наследовать надо сущности одного типа. Классы TableDataGateway и Db - разные, и выполняют разные задачи. Ну например ты можешь унаследовать Танк от СредствоПередвижения, но не можешь наследовать Танк от Самолета или от Пехотинца. Потому что это разные, а не однотипные сущности.
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/model/lib/TableDataGateway.php#L24
> WHERE name = :name OR lastName = :lastName
такой поиск не позволяет искать по части имени или фамилии. Погугли про LIKE. И кстати у нас есть задачи по базам данных: https://github.com/codedokode/pasta/blob/master/db/databases.md
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/model/lib/TableDataGateway.php#L87
> ':name' => $student->get(name),
Почему у name нет кавычек? У тебя наверно отображение ошибок (display_errors) выключено и ты их даже не замечаешь.
https://github.com/anotherCodeMunkey/studentsList/tree/master/helper
А почему helper не внутри app?
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php
Тут надо использовать dependency injection.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php#L17
> public function verify(array $loginInfo)
Тут плохо, что ты передаешь массив так как непонятно что в нем должно быть. В твоем случае наверно проще передать данные отдельными аргументами.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php#L21
> foreach($this->students as $student)
Неэффективно выбирать всех студентов из базы, чтобы потом найти одного. Надо выбирать только одного нужного.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php#L26
> }else{
> continue;
Это не требуется.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php#L19
> md5($loginInfo['eMail'].$loginInfo['password']);
Алгоритм генерации хеша должен быть в одном экземпляре, а не в нескольких местах кода.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/Pager.php
Я думаю, тут можно обойтись без базы данных, сделав чтобы класс исключительно занимался расчетом пагинации.
Что делает этот цикл https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/Pager.php#L23 - я что-то не понял. Группирует студентов по страницам? Но нам нужны только те, что на текщей странице, а остальных незачем даже выбирать из базы.
> $_SESSION['table'] = $students;
Это тоже что-то странное. Ты знаешь, что такое сессия? Это файл, который хранится на диске (загружается при выполнении session_start() и сохраняется после завершения скрипта). Сессия индивидуальна для каждого пользователя, но общая для всех вкладок в браузере. Уничтожается после 20-30 минут неактивности. Зачем в эти файлы сессий каждому пользователю копировать все содержимое базы данных?
И кстати, как я написал, так как куки для всех вкладок браузера общие, то и сессии в скриптах, которые из них вызываются, тоже общие. Ты это учел? Номеру страницы определенно в сессии делать нечего так как пользователь может открыть несколько вкладок с разными номерами страниц.
Я тебе советую внимательно изучить какие есть особенности у сессий и подумать, где их стоит использовать.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/RegisterHelper.php
Смысл этого класса мне не очень понятен. Если ты хотел вынести код регистрации нового студента, то это можно просто сделать методом в классе-сервисе. ну и конечно не стоит тут передавать данные в виде массива так как понять, что именно должно быть в этом массиве, глядя на класс, невозможно.
И вообще, этот RegisterHelper выглядит как кусок контроллера. Он и в базу что-то сохраняет, и куки ставит.
> https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/Validator.php#L13
> [@][\\S\\-\\+]+
\\S это наверно лишнее, в имени домена могут быть только буквы (включая русские), цифры и минус.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/Validator.php#L19
> $_SESSION['error'] = 'eMail'
Вот это явно неправильно. Зачем использовать сессию, когда данные можно просто передавать обычным образом в переменной?
Советую также почитать мой урок про работу с формами: https://github.com/codedokode/pasta/blob/master/forms.md
Также, стоит добавить файл README и в нем кратко написать, что это за проект и как его развернуть.
https://github.com/anotherCodeMunkey/studentsList/
https://github.com/anotherCodeMunkey/studentsList/blob/master/localhost.sql#L22
> CREATE DATABASE `students`
Не надо в дампе писать создание БД так как твой дамп нельзя теперь загрузить в БД с другим названием.
> `birthYear` date NOT NULL,
Для года есть отдельный тип
> `hash` varchar(200) NOT NULL,
Тут хорошо бы добавить пояснение с помощью слова COMMENT
> ENGINE=MyISAM
Изучи-ка отличия MyISAM от InnoDB:
https://rtfm.co.ua/mysql-otlichiya-mezhdu-myisam-i-innodb/
http://itif.ru/otlichiya-myisam-innodb/
MyISAM это старый движок без внешних ключей и транзакций.
Также, я вижу что у тебя нет отдельной публичной папки, а весь код фактически лежит в ней. Это плохая идея, достаточно забыть какой-то файл там и он может оказаться доступным для скачивания хакерами.
Вот тут написано подробнее: https://github.com/codedokode/pasta/blob/master/student-list.md#Выносим-код-за-корень-сервера
> https://github.com/anotherCodeMunkey/studentsList/blob/master/autoloader.php
Функция устарела, читай урок про автозагрузку https://github.com/codedokode/pasta/blob/master/php/autoload.md
https://github.com/anotherCodeMunkey/studentsList/blob/master/settings.php
Хранить настройки в константах не очень хорошая идея, так как они будут доступны везде, а лучше когда мы можем ограничивать доступ к ним. Лучше переменные.
> https://github.com/anotherCodeMunkey/studentsList/blob/master/index.php
Тут надо бы как-то упростить код, ну например, в простейшем варианте можно передавать параметр, обозначающий тип действия: index.php?act=register. Можно использовать интересную возможность добавлять путь после имени скрипта (PATH_INFO): /index.php/register. Можно сделать для разных действий разные входные скрипты: /register.php
Дописал пункт в задачу: https://github.com/codedokode/pasta/blob/master/student-list.md#Структура-url-и-роутинг
https://github.com/anotherCodeMunkey/studentsList/tree/master/app/cls
Что значит cls? Что за сокращения? Student кстати это модель студента (или сущность, или модель предметной области, или доменная модель).
> https://github.com/anotherCodeMunkey/studentsList/blob/master/app/cls/Student.php#L26
> public function get($info)
Это в общем плохо, так как у нас получается что-то похожее на массив, а не класс. Лучше или сделать функции-геттеры, или публичные свойства.
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/controller/Ctrl.php
Слишком большой контроллер. Как минимум регистрацию стоит вынести в отдельный класс.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/CleanInput.php
Это класс с черной магией. Ты можешь объяснить, почему фильтруются именно такие конструкции? Это все возможные опасные конструкции или не все? Если не все, то почему?
> if (get_magic_quotes_gpc()) {
Предлагаю упростить себе жизнь и отказываться работать, если включена эта настройка. Или просто не проверять ее, а написать об этом в README. Она уж много лет как отключена по умолчанию.
https://github.com/anotherCodeMunkey/studentsList/tree/master/app/model/lib
Тоже неудачное название. Что значит lib? И почему model, остальные классы это не часть модели?
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/model/lib/Db.php
Это вредный паттерн, получение зависимостей через статическую функцию. Почитай про DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
Я думаю что класс Db не нужен.
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/model/lib/TableDataGateway.php#L5
> class TableDataGateway extends Db
Наследовать надо сущности одного типа. Классы TableDataGateway и Db - разные, и выполняют разные задачи. Ну например ты можешь унаследовать Танк от СредствоПередвижения, но не можешь наследовать Танк от Самолета или от Пехотинца. Потому что это разные, а не однотипные сущности.
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/model/lib/TableDataGateway.php#L24
> WHERE name = :name OR lastName = :lastName
такой поиск не позволяет искать по части имени или фамилии. Погугли про LIKE. И кстати у нас есть задачи по базам данных: https://github.com/codedokode/pasta/blob/master/db/databases.md
https://github.com/anotherCodeMunkey/studentsList/blob/master/app/model/lib/TableDataGateway.php#L87
> ':name' => $student->get(name),
Почему у name нет кавычек? У тебя наверно отображение ошибок (display_errors) выключено и ты их даже не замечаешь.
https://github.com/anotherCodeMunkey/studentsList/tree/master/helper
А почему helper не внутри app?
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php
Тут надо использовать dependency injection.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php#L17
> public function verify(array $loginInfo)
Тут плохо, что ты передаешь массив так как непонятно что в нем должно быть. В твоем случае наверно проще передать данные отдельными аргументами.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php#L21
> foreach($this->students as $student)
Неэффективно выбирать всех студентов из базы, чтобы потом найти одного. Надо выбирать только одного нужного.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php#L26
> }else{
> continue;
Это не требуется.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/LoginHelper.php#L19
> md5($loginInfo['eMail'].$loginInfo['password']);
Алгоритм генерации хеша должен быть в одном экземпляре, а не в нескольких местах кода.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/Pager.php
Я думаю, тут можно обойтись без базы данных, сделав чтобы класс исключительно занимался расчетом пагинации.
Что делает этот цикл https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/Pager.php#L23 - я что-то не понял. Группирует студентов по страницам? Но нам нужны только те, что на текщей странице, а остальных незачем даже выбирать из базы.
> $_SESSION['table'] = $students;
Это тоже что-то странное. Ты знаешь, что такое сессия? Это файл, который хранится на диске (загружается при выполнении session_start() и сохраняется после завершения скрипта). Сессия индивидуальна для каждого пользователя, но общая для всех вкладок в браузере. Уничтожается после 20-30 минут неактивности. Зачем в эти файлы сессий каждому пользователю копировать все содержимое базы данных?
И кстати, как я написал, так как куки для всех вкладок браузера общие, то и сессии в скриптах, которые из них вызываются, тоже общие. Ты это учел? Номеру страницы определенно в сессии делать нечего так как пользователь может открыть несколько вкладок с разными номерами страниц.
Я тебе советую внимательно изучить какие есть особенности у сессий и подумать, где их стоит использовать.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/RegisterHelper.php
Смысл этого класса мне не очень понятен. Если ты хотел вынести код регистрации нового студента, то это можно просто сделать методом в классе-сервисе. ну и конечно не стоит тут передавать данные в виде массива так как понять, что именно должно быть в этом массиве, глядя на класс, невозможно.
И вообще, этот RegisterHelper выглядит как кусок контроллера. Он и в базу что-то сохраняет, и куки ставит.
> https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/Validator.php#L13
> [@][\\S\\-\\+]+
\\S это наверно лишнее, в имени домена могут быть только буквы (включая русские), цифры и минус.
https://github.com/anotherCodeMunkey/studentsList/blob/master/helper/Validator.php#L19
> $_SESSION['error'] = 'eMail'
Вот это явно неправильно. Зачем использовать сессию, когда данные можно просто передавать обычным образом в переменной?
Советую также почитать мой урок про работу с формами: https://github.com/codedokode/pasta/blob/master/forms.md
Также, стоит добавить файл README и в нем кратко написать, что это за проект и как его развернуть.
передавать режим валидации (register/edit) можно, но обозначения лучше сделать константами.
>>837499
> А что если я выберу пол, но руками в разметке поменяю значение инпута на любую рандомную строку? Я правильно понимаю что у меня получится отправить форму без ошибок?
В таком случае проще всего приводить любые значения к одному из 2 вариантов: male или female, что бы не пришло на вход. Хотя выдавать ошибку может и лучше, а то если мы сломаем форму (например переименуем инпут выбора пола) то без ошибки в базу просто запишется дефолтный пол и пройдет какое-то время прежде чем ошибка будет обнаружена.
>>837526
> Верстку уберу и просто сообщение об ошибке оставлю, но не думаю что от этого мое решение будет более элегантным, как лучше сделать не знаю пока.
Валидатор может выдавать имя поля и текст ошибки, а представление - оформлять его как угодно и заворачивать в теги.
Делаю SQL-запрос SELECT FROM `product` where id="7";
Всё выводит правильно. Пытаюсь сделать то же самое в php - нихрена не делает.
$userstable = "product";
$query = "SELECT FROM $userstable WHERE id = '7'";
$res = mysql_query($query);
$ress = array (0 => "0", 1 => "1", 2 => "2", 3 => "3", 4 => "4", 5 => "5", 6 => "6", 7 => "7", 8 => "8");
$i = 0;
while(list ($key, $val) = each ($res))
{
$ress[$i] = $val;
$i = $i + 1;
}
Я запутался.
Всё оказалось проще. Проблему решил.
w5_2 http://ideone.com/GrLzRd
w5_1 http://ideone.com/4k77jp
w5_3 http://ideone.com/D1a5Y7
w5_4 http://ideone.com/tjw5rr
w5_5 http://ideone.com/4M4dIM
w5_6 http://ideone.com/GedxvM
Ну, как видишь, у тебя не берут. Во всех обычных мухосранях берут. Как раз таки с PHP и фронтенд разрабов реже всех спрашивают вышку, не знаю, что у тебя за город такой.
Оп лучей добра тебе за терпение, которые ты проявляешь.
Разобрался с панелью network в инструментах разработчика в браузере. Установил cURL. За одно прописал PATH для php и mysql. Еще раз прочитал про заголовки и сервер и немного w3c документацию.
> https://github.com/TheSidSpears/FileHosting/blob/master/src/routes.php#L92
> $fileModel=$this->filesGW->getFile($args['id']);
> if ($fileModel!=NULL) {
> А если она null то что? Надо показывать 404 по стандарту.
А как? Тут https://juriansluiman.nl/article/156/app-notfound-for-slim-v3 сказано, что надо всего лишь прописать return $response->withStatus(404); и будет показываться стандартное представление, но у меня body пустой
Так же, пытаюсь с phpDocumentor разобраться, пока не очень получается. ОП, что думаешь об этой технологии, ипользуешь, советуешь использовать?
> А зачем тут сессия?...
Как тебе такой алгоритм?
Добавление файла
Если у пользователя есть кук с токеном, он заносится в таблицу files в строку user_token
Если нет - создаётся новый и отдаётся пользователю (а так-же заносится в таблицу)
Теперь можно реализовать правку и удаление пользователем своих файлов
> А почему тут у тебя обработка POST идет отдельно от GET?
А почему должно быть иначе? В get скрипт отправляет когда пользователь добавил файл на главной, а в post, когда пользователь нажал "Добавить описание" в $app->get('/add_info'...
> Это не работает, так как там можно использовать только латиницу
Спрашивал об этом здесь: >>844390 >>844408 >>844411
Почему с латиницей не работает тоже?
В слиме можно бросать NotFoundException если хочешь чтобы показывалась страница 404 фреймворка.
https://github.com/slimphp/Slim/blob/3.x/Slim/Exception/NotFoundException.php
$response->withStatus(404) просто отдаст пустую страницу с статусом 404.
>Так же, пытаюсь с phpDocumentor разобраться, пока не очень получается
Вторая версия кривая и багованая, она зависит от старых пакетов и поэтому её рекомендуют устанавливать только глобально (через PEAR), так же она не работает с PHP 7. Разработчики пишут что третья версия будет работать нормально и поддерживать современный стандарт, но судя по их гитхабу они что-то не очень спешат её разрабатывать.
> w5_2 http://ideone.com/GrLzRd
Шапка цикла стала слишком большой - лучше было вынести часть команд в тело цикла. Решено верно.
> w5_1 http://ideone.com/4k77jp
Верно.
> w5_3 http://ideone.com/D1a5Y7
Правильно.
> w5_4 http://ideone.com/tjw5rr
Все верно.
> w5_5 http://ideone.com/4M4dIM
Правильно.
> w5_6 http://ideone.com/GedxvM
Тоже все правильно.
>>847941
Так а саму задачу-то свою решил? Понял, почему не срабатывает скрипт генерации превью, если ссылку на него ставить в img?
>>848292
> $response->withStatus(404);
Эта команда ставит код ответа 404, но тело-то страницы пустое остается и в браузере это будет выглядеть как белая страница. В Слиме должен быть стандартный способ показать 404 страницу.
Тут вот http://www.slimframework.com/docs/handlers/not-found.html есть кое-что. Видно что в Слиме в контейнере Pimple (надеюсь ты прочитал про него) есть сервис notFoundHandler и этот сервис является функцией с 2 аргументами. Раз так, мы можем получить ее из контейнера и вызвать и может быть она сгенерирует нужную страницу.
Также, анон ниже пишет про исключение, если это работает, то лучше использовать его.
> пытаюсь с phpDocumentor разобраться, пока не очень получается. ОП, что думаешь об этой технологии, ипользуешь, советуешь использовать?
А что именно не работает? Док-комменты - вещь нужная, даже если ты не будешь из них генерировать документацию.
>>848304
> Если у пользователя есть кук с токеном, он заносится в таблицу files в строку user_token
> Если нет - создаётся новый и отдаётся пользователю (а так-же заносится в таблицу)
Можно, но тут стоит еще рассмотреть вариант сделать таблицу users с токенами и ссылку на нее из таблицы файлов. Читал про нормализацию БД?
> А почему должно быть иначе?
Потому что ошибки неудобно показывать если обработчики отдельные. У тебя валидация просто не сделана, и ты этого не видишь. И еще наверно про алгоритм работы с формами не читал, раз не знал про это.
> Почему с латиницей не работает тоже?
Я не могу сказать так, покажи какой HTTP-ответ приходит, какие заголовки, какое тело ответа. Посмотреть можно либо в отладчике браузера, либо отправив запрос вручную программой для командной строки вроде wget или curl, либо расширением вроде postman, или на худой конец телнетом.
>>848341
Пакеты PEAR вроде бы можно ставить и через композер, чтобы не мучаться с PEAR (это такой древний, но официальный пакетный менеджер).
> w5_2 http://ideone.com/GrLzRd
Шапка цикла стала слишком большой - лучше было вынести часть команд в тело цикла. Решено верно.
> w5_1 http://ideone.com/4k77jp
Верно.
> w5_3 http://ideone.com/D1a5Y7
Правильно.
> w5_4 http://ideone.com/tjw5rr
Все верно.
> w5_5 http://ideone.com/4M4dIM
Правильно.
> w5_6 http://ideone.com/GedxvM
Тоже все правильно.
>>847941
Так а саму задачу-то свою решил? Понял, почему не срабатывает скрипт генерации превью, если ссылку на него ставить в img?
>>848292
> $response->withStatus(404);
Эта команда ставит код ответа 404, но тело-то страницы пустое остается и в браузере это будет выглядеть как белая страница. В Слиме должен быть стандартный способ показать 404 страницу.
Тут вот http://www.slimframework.com/docs/handlers/not-found.html есть кое-что. Видно что в Слиме в контейнере Pimple (надеюсь ты прочитал про него) есть сервис notFoundHandler и этот сервис является функцией с 2 аргументами. Раз так, мы можем получить ее из контейнера и вызвать и может быть она сгенерирует нужную страницу.
Также, анон ниже пишет про исключение, если это работает, то лучше использовать его.
> пытаюсь с phpDocumentor разобраться, пока не очень получается. ОП, что думаешь об этой технологии, ипользуешь, советуешь использовать?
А что именно не работает? Док-комменты - вещь нужная, даже если ты не будешь из них генерировать документацию.
>>848304
> Если у пользователя есть кук с токеном, он заносится в таблицу files в строку user_token
> Если нет - создаётся новый и отдаётся пользователю (а так-же заносится в таблицу)
Можно, но тут стоит еще рассмотреть вариант сделать таблицу users с токенами и ссылку на нее из таблицы файлов. Читал про нормализацию БД?
> А почему должно быть иначе?
Потому что ошибки неудобно показывать если обработчики отдельные. У тебя валидация просто не сделана, и ты этого не видишь. И еще наверно про алгоритм работы с формами не читал, раз не знал про это.
> Почему с латиницей не работает тоже?
Я не могу сказать так, покажи какой HTTP-ответ приходит, какие заголовки, какое тело ответа. Посмотреть можно либо в отладчике браузера, либо отправив запрос вручную программой для командной строки вроде wget или curl, либо расширением вроде postman, или на худой конец телнетом.
>>848341
Пакеты PEAR вроде бы можно ставить и через композер, чтобы не мучаться с PEAR (это такой древний, но официальный пакетный менеджер).
>>812156
>>826200
>>816231
Что-то у тебя история изменений пустая - https://github.com/never3ver/students_list/commits/master - лучше бы приучаться писать комментарии к коммитам.
https://github.com/never3ver/students_list/blob/master/README.md
В разметке список склеился в один абзац. Подучи маркдаун.
https://github.com/never3ver/students_list/blob/master/public/regedit.php#L34
После того как ты выдал HTTP код 302 и заголовок Location, твой скрипт продолжает работать и выводит форму. Зачем? для ответа 302 тело не требуется, оно все равно не покажется в браузере.
https://github.com/never3ver/students_list/blob/master/app/Authorization.php
По поводу этого класса:
- я вижу проблему с инкапсуляцией (ее нет) в том, что значение пришедшей куки передается снаружи, а выставляется кука внутри класса. Мне кажется, лучше было бы засунуть чтение $_COOKIE в сам класс, так, чтобы только этот класс работал бы с авторизационной кукой, а другие про нее даже не знали.
- так как твой класс "одноразовый" (кука передается в конструктор), можно бы кешировать состояние авторизации в полях класса и при втором обращении возвращать закешированное значение
- метод logIn() генерирует куку и проставляет ее в модель студента. Что, если мы возьмем студента из базы и вызовем logIn? Он поменяет ему куку? Как-то неаккуратно получается.
- сохранение состояния авторизации сделано неидеально. Рассмотрим такой сценарий:
1) приходит запрос без куки
2) isAuthorized() возвращает false
3) вызываем logIn()
4) isAuthorised() по прежнему возвращает false
Понятно, что в приложении такой ситуации нет, но все же на мой взгляд, это не очень аккуратно. Либо надо переименовать как-то метод, чтобы дать понять что он возвращает не текущее состояние авторизации, а состояние на момент запуска скрипта.
https://github.com/never3ver/students_list/blob/master/app/Helper.php#L9
> $word = preg_quote($word);
> $word = htmlspecialchars($word, ENT_QUOTES);
Мне кажется, это должно делаться в обратном порядке так как мы подставляем переменную регулярку и потому последней должна быть вызвана preg_quote. Более того, в том месте, где мы делаем замену, надо подставлять не пропущенное через preg_quote значение.
То есть надо представлять для чего делается каждый вид экранирования. В HTML код незачем вставлять обработанное preg_quote значение.
Также, при вызове preg_quote надо указывать символ-ограничитель (слеш), чтобы и его экранировало тоже.
> public static function getSymbol($order) {
Название функции неудачное и не говорит, что это за символ.
https://github.com/never3ver/students_list/blob/master/app/StudentValidator.php#L23
> if (!filter_var($student->email, FILTER_VALIDATE_EMAIL)) {
Эта функция имеет недостаток: не пропускает емайлы вроде i(%evanANUSпр/;!имерPUNCTUMрzDeф
Тут http://php.net/manual/ru/filter.filters.validate.php написано
> Обратите внимание, что функция работает только с ASCII-ссылками, таким образом, интернациональные доменные имена (содержащие не-ASCII символы) не пройдут проверку.
> !preg_match("/[0-9]{4}/", $student->birthYear)
Можно было бы добавить проверку на то что начинается с 19 или 20
https://github.com/never3ver/students_list/blob/master/templates/index.html
Я вижу, ты решил разбить верстку страницы на компоненты. Вообще, это правильный подход, так как позволяет рассматривать части страницы отдельно. Но вот с реализацией тут есть проблема, в том, что непонятно какие переменные какой частью используются. Если использвать шаблонизатор twig и макросы, то проблемы нет: там при вызове явно передаются значения, вроде такого:
{{ pagination(pager) }}
{{ studentTable(students) }}
Но, увы, при использовании шаблонов php, получается не так красиво (переменные явно не передаются, имена файлов длинные). Надеюсь, в следующих проектах ты познакомишься с твигом.
https://github.com/never3ver/students_list/blob/master/templates/parts/okEdited.html
Советую вместо диалоговых окон с единственной кнопкой просто выводить плашку вверху страницы. В бутстрапе есть стиль для этого. Диалоговые окна блокируют страницу, вынуждают нажимать кнопку и раздражают. И не работают без яваскрипта.
https://github.com/never3ver/students_list/blob/master/templates/editForm.html#L9
> required="required"
Можно писать просто required без значения - это то же самое.
> <span class = "redtext">
Плохое название класса. Надо указывать не оформление (для этого есть CSS), а назначение класса.
> https://github.com/never3ver/students_list/blob/master/templates/editForm.html#L31
> value='M' required checked='checked'
А тут условие if не нужно в атрибуте checked?
> $student->sex=="F"
Вместо F и M надо сделать константы вроде Student::GENDER_MALE, то же для местных/иногородних.
Дописал пункт в "правила хорошего кода": https://github.com/codedokode/pasta/blob/master/good-code.md#Используй-константы-когда-есть-выбор-из-нескольких-вариантов
https://github.com/never3ver/students_list/blob/master/templates/parts/pagination.html#L7
> <li class="disabled"><span><<</span></li>
Знак "меньше" надо писать с помощью html мнемоник.
Фамилия "д'Артаньян" не принимается. Кстати, у тебя разрешена кавычка в имени, но еще есть символ апофтрофа ` (а также разные юникодные закорючки похожие на него)
Если ввести имя "Антон", а потом сделать поиск по "ант", то выведется имя с маленькой буквы: "антон". Надо не заменять текст на искомое значение, а лишь окружать найденное значение тегами mark.
Так, в общем, сделано хорошо. Надеюсь, что создавать простые приложения ты научился, посмотрим, что будет дальше.
Когда будешь сдавать на проверку, напомни мне, что там почти все готово и оставалось только исправить несколько мелких багов.
>>812156
>>826200
>>816231
Что-то у тебя история изменений пустая - https://github.com/never3ver/students_list/commits/master - лучше бы приучаться писать комментарии к коммитам.
https://github.com/never3ver/students_list/blob/master/README.md
В разметке список склеился в один абзац. Подучи маркдаун.
https://github.com/never3ver/students_list/blob/master/public/regedit.php#L34
После того как ты выдал HTTP код 302 и заголовок Location, твой скрипт продолжает работать и выводит форму. Зачем? для ответа 302 тело не требуется, оно все равно не покажется в браузере.
https://github.com/never3ver/students_list/blob/master/app/Authorization.php
По поводу этого класса:
- я вижу проблему с инкапсуляцией (ее нет) в том, что значение пришедшей куки передается снаружи, а выставляется кука внутри класса. Мне кажется, лучше было бы засунуть чтение $_COOKIE в сам класс, так, чтобы только этот класс работал бы с авторизационной кукой, а другие про нее даже не знали.
- так как твой класс "одноразовый" (кука передается в конструктор), можно бы кешировать состояние авторизации в полях класса и при втором обращении возвращать закешированное значение
- метод logIn() генерирует куку и проставляет ее в модель студента. Что, если мы возьмем студента из базы и вызовем logIn? Он поменяет ему куку? Как-то неаккуратно получается.
- сохранение состояния авторизации сделано неидеально. Рассмотрим такой сценарий:
1) приходит запрос без куки
2) isAuthorized() возвращает false
3) вызываем logIn()
4) isAuthorised() по прежнему возвращает false
Понятно, что в приложении такой ситуации нет, но все же на мой взгляд, это не очень аккуратно. Либо надо переименовать как-то метод, чтобы дать понять что он возвращает не текущее состояние авторизации, а состояние на момент запуска скрипта.
https://github.com/never3ver/students_list/blob/master/app/Helper.php#L9
> $word = preg_quote($word);
> $word = htmlspecialchars($word, ENT_QUOTES);
Мне кажется, это должно делаться в обратном порядке так как мы подставляем переменную регулярку и потому последней должна быть вызвана preg_quote. Более того, в том месте, где мы делаем замену, надо подставлять не пропущенное через preg_quote значение.
То есть надо представлять для чего делается каждый вид экранирования. В HTML код незачем вставлять обработанное preg_quote значение.
Также, при вызове preg_quote надо указывать символ-ограничитель (слеш), чтобы и его экранировало тоже.
> public static function getSymbol($order) {
Название функции неудачное и не говорит, что это за символ.
https://github.com/never3ver/students_list/blob/master/app/StudentValidator.php#L23
> if (!filter_var($student->email, FILTER_VALIDATE_EMAIL)) {
Эта функция имеет недостаток: не пропускает емайлы вроде i(%evanANUSпр/;!имерPUNCTUMрzDeф
Тут http://php.net/manual/ru/filter.filters.validate.php написано
> Обратите внимание, что функция работает только с ASCII-ссылками, таким образом, интернациональные доменные имена (содержащие не-ASCII символы) не пройдут проверку.
> !preg_match("/[0-9]{4}/", $student->birthYear)
Можно было бы добавить проверку на то что начинается с 19 или 20
https://github.com/never3ver/students_list/blob/master/templates/index.html
Я вижу, ты решил разбить верстку страницы на компоненты. Вообще, это правильный подход, так как позволяет рассматривать части страницы отдельно. Но вот с реализацией тут есть проблема, в том, что непонятно какие переменные какой частью используются. Если использвать шаблонизатор twig и макросы, то проблемы нет: там при вызове явно передаются значения, вроде такого:
{{ pagination(pager) }}
{{ studentTable(students) }}
Но, увы, при использовании шаблонов php, получается не так красиво (переменные явно не передаются, имена файлов длинные). Надеюсь, в следующих проектах ты познакомишься с твигом.
https://github.com/never3ver/students_list/blob/master/templates/parts/okEdited.html
Советую вместо диалоговых окон с единственной кнопкой просто выводить плашку вверху страницы. В бутстрапе есть стиль для этого. Диалоговые окна блокируют страницу, вынуждают нажимать кнопку и раздражают. И не работают без яваскрипта.
https://github.com/never3ver/students_list/blob/master/templates/editForm.html#L9
> required="required"
Можно писать просто required без значения - это то же самое.
> <span class = "redtext">
Плохое название класса. Надо указывать не оформление (для этого есть CSS), а назначение класса.
> https://github.com/never3ver/students_list/blob/master/templates/editForm.html#L31
> value='M' required checked='checked'
А тут условие if не нужно в атрибуте checked?
> $student->sex=="F"
Вместо F и M надо сделать константы вроде Student::GENDER_MALE, то же для местных/иногородних.
Дописал пункт в "правила хорошего кода": https://github.com/codedokode/pasta/blob/master/good-code.md#Используй-константы-когда-есть-выбор-из-нескольких-вариантов
https://github.com/never3ver/students_list/blob/master/templates/parts/pagination.html#L7
> <li class="disabled"><span><<</span></li>
Знак "меньше" надо писать с помощью html мнемоник.
Фамилия "д'Артаньян" не принимается. Кстати, у тебя разрешена кавычка в имени, но еще есть символ апофтрофа ` (а также разные юникодные закорючки похожие на него)
Если ввести имя "Антон", а потом сделать поиск по "ант", то выведется имя с маленькой буквы: "антон". Надо не заменять текст на искомое значение, а лишь окружать найденное значение тегами mark.
Так, в общем, сделано хорошо. Надеюсь, что создавать простые приложения ты научился, посмотрим, что будет дальше.
Когда будешь сдавать на проверку, напомни мне, что там почти все готово и оставалось только исправить несколько мелких багов.
>>844366
>>846602
Неплохо бы добавить README.md с описанием, что это, и инструкцией по установке.
> https://github.com/never3ver/fileshare/blob/master/composer.json#L16
> "mkdir -p public/bootstrap",
Вот тут, я не уверен, что это всегда будет корректно работать: а что с правами на папку? что с удалением старых файлов перед копированием новых? что с windows где нет команды cp?
Ну то есть в твоем случае это может и годится, но в более сложном случае я бы на php написал боле надежный и кроссплатформенный скрипт.
https://github.com/never3ver/fileshare/blob/master/test_fileshare.sql#L29
> `size` varchar(45) DEFAULT NULL,
Почему size это varchar? почему разрешен NULL?
> `time` timestamp
Лучше более понятное название, вроде uploadTime.
> use \Psr\Http\Message\ServerRequestInterface as Request;
Я не уверен что стоит убирать из названия интерфейса слово "Interface".
> $config['displayErrorDetails'] = TRUE;
Сюда лучше писать значение из ini_get('display_errors'), чтобы на продакшене оно автоматически отключалось.
Конфиг наверно стоит вынести отдельно, чтобы не надо было ковыряться в коде.
> https://github.com/never3ver/fileshare/blob/master/public/index.php#L49
> do {
> $tmpName = Helper::generateTmpName();
> } while ($dataGateway->isTmpNameExisting($tmpName));
Из-за бага тут может получиться бесконечный цикл. Надо ограничить число попыток, а также вынести генерацию имени в отдельный метод.
> $dataGateway->addFile($file);
> move_uploaded_file($_FILES['uploadfile']['tmp_name'], "files/$tmpName");
Неправильно сначала добавлять запись в БД, а только потом сохранять файл, так как при ошибке можно получить запись, для которой нет файла.
https://github.com/never3ver/fileshare/blob/master/public/index.php#L56
После успешной обработки POST запроса надо делать редирект. Забыл алгоритм работы с формами?
И кстати, у тебя нет проверки на ошибки при загрузке - перечитай мануал http://php.net/manual/ru/features.file-upload.post-method.php
Я думаю, что удобнее анализировать файл 1 раз при загрузке, а не при каждом открытии страницы. Но хотя... если это недолго, то наверно можно и так.
Но неудобно работать с многомерным массивом, в котором могут отсутствовать любые поля и который возвращает функция analyze(). Надо бы сделать ему объектную обертку, то есть класс вроде MediaInfo или FileProperties, что-то такое. И подумать, а не стоит ли его тоже сохранять в БД.
GetID3 надо подключить через композер. Поищи ее в packagist.
Для отдачи файла, кроме отдачи через PHP, надо бы прикрутить отдачу через X-SendFile, если такое расширение есть. В комментариях к задаче это вроде описано.
readfile использовать неправильно, так как оно работает в обход Слима. Надо использовать возможность указать в качестве тела ответа ($reponse->setBodyStream() по моему) произвольный поток, в том числе, поток, соответствующий открытому на чтение файлу.
Вот в этом посте разбирали: >>846554
Также, давай сделаем написание кода тебе чуть интереснее. Сделай опциональную поддержку отдачи файла, сжатого с помощью gzip. PHP содержит функции для потокового сжатия данных (то есть кусочками, не закачивая файл в память целиком):
- https://habrahabr.ru/post/221849/
- http://php.net/manual/ru/wrappers.compression.php
- http://php.net/manual/ru/filters.compression.php
- http://webcache.googleusercontent.com/search?q=cache:https://webo.in/articles/habrahabr/30-gzip-versus-broadband/&ie=UTF-8&gws_rd=cr&ei=r1jtV8g-wtvpBOqBlmA
А протокол HTTP позволяет передавать данные в сжатом виде при условии поддержки этого клиентом и сервером. За это отвечают HTTP заголовки Accept-Encoding и Content-Encoding:
- https://en.wikipedia.org/wiki/HTTP_compression
- http://rfc2.ru/2068.rfc
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation#The_Accept-Encoding.3a_header
Думаю, стоит сделать так:
- если клиент поддерживает сжатие
- и если файл не относится к уже сжатым (по MIME типу или расширению)
- и если мы не используем X-SendFile
- то отдавать его в сжатом виде
Некоторые типы файлов уже сжаты (PNG, JPEG, GIF, MP3, видео) - для них можно завести "черный" список расширений или MIME типов. Сжимать их бесполезно.
> $response = $response->withHeader('Content-Disposition', 'attachment; filename=' . $file->getName());
Это работает только с латинскими именами, так как в заголоках можно использовать только ASCII.
https://github.com/never3ver/fileshare/blob/master/public/index.php#L76
> if (file_exists($path)) {
А что будет, если файла не существует?
Также, давай добавим тебе еще дополнительную задачку. Сделай скрипт командной строки для администратора с такими возможностями:
1) загрузить файлы с диска командой вроде php cli.php upload /tmp/myfile.txt /tmp/otherfile.txt
2) удалить файл командой вроде php cli.php delete 100
3) просмотреть список последних загруженных файлов командой php cli.php last
Скрипт должен быть недоступен веб-серверу и пользователям.
Также, если у тебя есть время, я бы советовал сделать к приложению тесты. Оно относительно несложное и на нем хорошо поучиться делать юнит- и браузерные тесты. Ну и как бонус, легко будет находить ошибки в твоем приложении при правках.
Думаю, тебе вполне хватит PhpUnit и примитивного HTML браузера (например, можно взять компонент для тестов из Симфони ( http://symfony.com/doc/current/testing.html , http://symfony.com/doc/current/components/dom_crawler.html )), можно codeception. Подробный урок по тестированию: https://gist.github.com/codedokode/a455bde7d0748c0a351a
> ORDER BY time DESC
> FROM fileshare WHERE tmpName = :tmpName
Чтобы это не тормозило на больших объекмах, нужны индексы по time и tmpName. У меня есть ссылка про индексы в задачах на mysql: http://ruhighload.com/post/Работа+с+индексами+в+MySQL
https://github.com/never3ver/fileshare/blob/master/app/Helper.php#L11
> $source[mt_rand(0, count($source) - 1)]
удобнее наверно использовать array_rand, чтобы не забыть вычесть единичку.
https://github.com/never3ver/fileshare/blob/master/templates/file.html.twig#L7
> {{ file.getName()|e }}
В твиге есть автоэкранирование.
> src="../files/{{ file.getTmpName() }}"
генерировать имена и пути файлов надо централизованно, а не склеивая куски путей. Также, надо бы для картинок делать уменьшенную превьюшку, а не отдавать гигантскую картинку.
>>844366
>>846602
Неплохо бы добавить README.md с описанием, что это, и инструкцией по установке.
> https://github.com/never3ver/fileshare/blob/master/composer.json#L16
> "mkdir -p public/bootstrap",
Вот тут, я не уверен, что это всегда будет корректно работать: а что с правами на папку? что с удалением старых файлов перед копированием новых? что с windows где нет команды cp?
Ну то есть в твоем случае это может и годится, но в более сложном случае я бы на php написал боле надежный и кроссплатформенный скрипт.
https://github.com/never3ver/fileshare/blob/master/test_fileshare.sql#L29
> `size` varchar(45) DEFAULT NULL,
Почему size это varchar? почему разрешен NULL?
> `time` timestamp
Лучше более понятное название, вроде uploadTime.
> use \Psr\Http\Message\ServerRequestInterface as Request;
Я не уверен что стоит убирать из названия интерфейса слово "Interface".
> $config['displayErrorDetails'] = TRUE;
Сюда лучше писать значение из ini_get('display_errors'), чтобы на продакшене оно автоматически отключалось.
Конфиг наверно стоит вынести отдельно, чтобы не надо было ковыряться в коде.
> https://github.com/never3ver/fileshare/blob/master/public/index.php#L49
> do {
> $tmpName = Helper::generateTmpName();
> } while ($dataGateway->isTmpNameExisting($tmpName));
Из-за бага тут может получиться бесконечный цикл. Надо ограничить число попыток, а также вынести генерацию имени в отдельный метод.
> $dataGateway->addFile($file);
> move_uploaded_file($_FILES['uploadfile']['tmp_name'], "files/$tmpName");
Неправильно сначала добавлять запись в БД, а только потом сохранять файл, так как при ошибке можно получить запись, для которой нет файла.
https://github.com/never3ver/fileshare/blob/master/public/index.php#L56
После успешной обработки POST запроса надо делать редирект. Забыл алгоритм работы с формами?
И кстати, у тебя нет проверки на ошибки при загрузке - перечитай мануал http://php.net/manual/ru/features.file-upload.post-method.php
Я думаю, что удобнее анализировать файл 1 раз при загрузке, а не при каждом открытии страницы. Но хотя... если это недолго, то наверно можно и так.
Но неудобно работать с многомерным массивом, в котором могут отсутствовать любые поля и который возвращает функция analyze(). Надо бы сделать ему объектную обертку, то есть класс вроде MediaInfo или FileProperties, что-то такое. И подумать, а не стоит ли его тоже сохранять в БД.
GetID3 надо подключить через композер. Поищи ее в packagist.
Для отдачи файла, кроме отдачи через PHP, надо бы прикрутить отдачу через X-SendFile, если такое расширение есть. В комментариях к задаче это вроде описано.
readfile использовать неправильно, так как оно работает в обход Слима. Надо использовать возможность указать в качестве тела ответа ($reponse->setBodyStream() по моему) произвольный поток, в том числе, поток, соответствующий открытому на чтение файлу.
Вот в этом посте разбирали: >>846554
Также, давай сделаем написание кода тебе чуть интереснее. Сделай опциональную поддержку отдачи файла, сжатого с помощью gzip. PHP содержит функции для потокового сжатия данных (то есть кусочками, не закачивая файл в память целиком):
- https://habrahabr.ru/post/221849/
- http://php.net/manual/ru/wrappers.compression.php
- http://php.net/manual/ru/filters.compression.php
- http://webcache.googleusercontent.com/search?q=cache:https://webo.in/articles/habrahabr/30-gzip-versus-broadband/&ie=UTF-8&gws_rd=cr&ei=r1jtV8g-wtvpBOqBlmA
А протокол HTTP позволяет передавать данные в сжатом виде при условии поддержки этого клиентом и сервером. За это отвечают HTTP заголовки Accept-Encoding и Content-Encoding:
- https://en.wikipedia.org/wiki/HTTP_compression
- http://rfc2.ru/2068.rfc
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation#The_Accept-Encoding.3a_header
Думаю, стоит сделать так:
- если клиент поддерживает сжатие
- и если файл не относится к уже сжатым (по MIME типу или расширению)
- и если мы не используем X-SendFile
- то отдавать его в сжатом виде
Некоторые типы файлов уже сжаты (PNG, JPEG, GIF, MP3, видео) - для них можно завести "черный" список расширений или MIME типов. Сжимать их бесполезно.
> $response = $response->withHeader('Content-Disposition', 'attachment; filename=' . $file->getName());
Это работает только с латинскими именами, так как в заголоках можно использовать только ASCII.
https://github.com/never3ver/fileshare/blob/master/public/index.php#L76
> if (file_exists($path)) {
А что будет, если файла не существует?
Также, давай добавим тебе еще дополнительную задачку. Сделай скрипт командной строки для администратора с такими возможностями:
1) загрузить файлы с диска командой вроде php cli.php upload /tmp/myfile.txt /tmp/otherfile.txt
2) удалить файл командой вроде php cli.php delete 100
3) просмотреть список последних загруженных файлов командой php cli.php last
Скрипт должен быть недоступен веб-серверу и пользователям.
Также, если у тебя есть время, я бы советовал сделать к приложению тесты. Оно относительно несложное и на нем хорошо поучиться делать юнит- и браузерные тесты. Ну и как бонус, легко будет находить ошибки в твоем приложении при правках.
Думаю, тебе вполне хватит PhpUnit и примитивного HTML браузера (например, можно взять компонент для тестов из Симфони ( http://symfony.com/doc/current/testing.html , http://symfony.com/doc/current/components/dom_crawler.html )), можно codeception. Подробный урок по тестированию: https://gist.github.com/codedokode/a455bde7d0748c0a351a
> ORDER BY time DESC
> FROM fileshare WHERE tmpName = :tmpName
Чтобы это не тормозило на больших объекмах, нужны индексы по time и tmpName. У меня есть ссылка про индексы в задачах на mysql: http://ruhighload.com/post/Работа+с+индексами+в+MySQL
https://github.com/never3ver/fileshare/blob/master/app/Helper.php#L11
> $source[mt_rand(0, count($source) - 1)]
удобнее наверно использовать array_rand, чтобы не забыть вычесть единичку.
https://github.com/never3ver/fileshare/blob/master/templates/file.html.twig#L7
> {{ file.getName()|e }}
В твиге есть автоэкранирование.
> src="../files/{{ file.getTmpName() }}"
генерировать имена и пути файлов надо централизованно, а не склеивая куски путей. Также, надо бы для картинок делать уменьшенную превьюшку, а не отдавать гигантскую картинку.
>> if (!filter_var($student->email, FILTER_VALIDATE_EMAIL)) {
>Эта функция имеет недостаток: не пропускает емайлы вроде i3Z-vanANUSпр(jXимерPUNCTUMEH-рф
А в седьмой версии пропускает нормально. На ideone PHP версии 5.6, там возвращает false.
>Так а саму задачу-то свою решил? Понял, почему не срабатывает скрипт генерации превью, если ссылку на него ставить в img?
Вместе с cURL я настроил xdebug и прошелся с ним по скрипту буквально построчно. Это оказался баг самого скрипта генератора тумб. По невнимательности, после тестирования основного функционала, я добавил новое условие, не уменьшать изображение, если его размер БОЛЬШЕ чем размер тумбы. Поэтому он отдавал изображение в оригинальном размере. Но лишь для нового изображения, которое я тестировал по ссылке через src, для которого не было тумб корректных размеров в папке с кешем. Поэтому я не смог сразу понять, что именно не работает.
Калькулятор
Плохо, что switch с операциями скопирован 2 раза. От копипасты надо избавиться.
А так, верно сделано.
Навигатор (поиск кратчайшего пути):
> Как сделать с рекурсией не догнал, поэтому решил алгоритмом Дейскры
Рекурсией получилось бы наверно менее эффективно. Но суть примерно такой, что мы пишем функцию примерно такого вида:
найтиПутьКЦели(текущаяТочка, цель, пройденныеТочки, списокВсехУзлов) -> путь
То есть она получает на вход информацию о том, где мы находимся, куда мы идем, что мы уже прошли, и должна вернуть путь к цели. "Путь" в данном случае может быть просто массивом с названиями точек, которые ведут от текущей точки до цели.
На первый взгляд непонятно, как написать такую функцию, потому сначала рассмотрим частный случай. Предположим, что текущаяТочка находится в одном шаге от цели. Тогда решение очевидно - путь к цели будет состоять из единственной точки - самой цели:
если (от текущей точки до цели один шаг) {
вернуть путь из одной точки: [цель];
}
А если цель не в одном шаге от текущей точки, а в нескольких? Допустим, мы в 2 шагах от цели. Допустим, мы в точке "Горьковская", а цель у нас "Чкаловская". Соседние с нами точки - это "Крепость", "Гостиный Двор" и "Петроградская". Так как мы в 2 шагах от цели, то одна из этих точек в одном шаге от нее (это "Петроградская", хотя мы об этом пока не знаем). Мы можем просто попробовать проложить маршрут к цели через каждую из соседних точек, и выбрать ту, путь от которой до цели короче.
Таким образом, сначала мы определили, как добраться до цели, если мы в 1 шаг от нее, потом с помощью рекурсивного вызова мы смогли добраться до цели, находясь в 2 шагах от нее. Продолжая рассуждения, мы можем так же добраться до цели, находясь на любом расстоянии.
Если мы не рядом с целью, мы берем все соседние с текущей точки, в которых мы еще не были (это важно, иначе мы будем ходить кругами). И для каждой соседней точки по очереди вызываем эту же функцию, то есть будем пытаться проложить маршрут к цели через все соседние с текущей точки. Мы получим список маршрутов. Остается только выбрать из них кратчайший и вернуть его:
если (до цели больше одного шага) {
для каждой соседней точки, в которой мы еще не были: {
проложить путь через эту соседнюю точку и сохранить в массив маршрутов;
}
выбрать из массива маршрутов кратчайший путь;
вернуть его;
}
Допустим что от текущей точки до цели N шагов. Если мы пробуем проложить путь через каждую из соседних точек, то какая-то из них ведет к цели и находится от нее в N-1 шаге. В свою очередь при попытке проложить путь через эту точку, функция будет пербирать ее соседей и найдет точку которая в N-2 шагах от цели. И так далее, пока мы не окажемся рядом с целью.
В общем, с помощью рекурсии мы просто перебираем все возможные пути, и выбираем из них кратчайший.
Попробуй написать решение с рекурсией. Тут важно строго определить типы передаваемых аргументов и возвращаемое значение. когда ты рассматриваешь рекурсивную функцию, не пытайся представить себе всю цепочку вызовов. Рассматривай только один шаг - что функция получает на вход, и что она вернет. Если функция за один вызов приближается к цели хотя бы на 1 шаг, то рано или поздно она до нее доберется.
Также, дам дополнительную задачку на рекурсию, чтобы лучше в ней разобраться:
- дана маска и имя файла. Надо написать функцию, проверяющую, соответствует ли имя файла маске. Маска может содержать любые символы, и 2 из них имеют специальное значение:
- знак вопроса соответствует ровно 1 любому символу в имени файла
- звездочка соответствует любому числу любых символов, в том числе их отсутствию
- остальные символы в маске обозначают сами себя
Тесты:
Маска, имя файла, результат
# Обычные символы
test, test, true
test, test.txt, false
test, t, false
# Знак вопроса соотв. ровно 1 символу
te?t, test, true
te?t, tesst, false
te?t, tet, false
te?t, te?t, true
t???, test, true
t???, tes, false
# Звездочка соотв. любому числу символов
t*, test, true
t*, t, true
t*, rest, false
*t, rest, true
# Звездочек может быть много
*t***t*, test, true
*t***t*, rest, false
# Проверяем наличие бектрекинга
t*st, teskest, true
*t*st, teskest, true
# Проверяем пустые строки
"", "", true
"", t, false
t, "", false
*, "", true
"" здесь обозначает пустую строку. Звездочки я заменил на специальные, чтобы разметка не сломалась.
> $paths[$startPoint]['label'] = 0;
> $pointValue['visited'] = false;
Надо не добавлять элементы в и без того сложный массив, а сделать отдельные массивы. А так у тебя все вперемешку получилось: тут же и соседние станции, тут же и label которая не является соседней станцией. Как потом разобраться?
В алгоритме не хватает комментариев, понять что к чему довольно сложно.
Вызов searchPath(...); надо убрать из функции doAlgorithmDeijkstra, чтобы она занималась только расчетом минимальных расстояний.
Подозрительно что простановка минимального времени делается 2 раза:
> if ($pointValue['label'] < $minLabel && (!$pointValue['visited'])) {
> if ($paths[$point]['label'] > $newLabel) {
В алгоритме Дейкстры по моему это только 1 раз делается. Я глянул https://ru.wikipedia.org/wiki/Алгоритм_Дейкстры#.D0.90.D0.BB.D0.B3.D0.BE.D1.80.D0.B8.D1.82.D0.BC - действительно, 1 раз.
Числа прописью
> 1 => array(' тысяча ', ' тысячи ', ' тысяч '),
чтобы не мучаться с пробелами, проще было наверно складывать слова в массив, а потом склеивать с помощью implode().
В общем, верно решено.
Компания "Вектор" ооп
> public function addDepartmentToCompany($newDepartment)
Тут нужен тайп-хинт.
Тайп хинты позволяют указать, что аргумент функции должен быть определенного типа (например быть объектом определенного класса или его наследника). Тайп хинт делает код понятнее (так как видно какого типа переменная) и надежнее (так как PHP не позволит передать что-то неразрешенное и ты сразу увидишь ошибку). Используй их везде.
Мануал: http://php.net/manual/ru/language.oop5.typehinting.php
Обрати внимание, что php7 усовершенствовал систему тайп-хинтов - теперь можно в их качестве указывать примитивные типы вроде int/string, а в php7.1 стало можно указывать тайп-хинт для возвращаемого функцией значения: https://habrahabr.ru/post/267799/ (увы, возможность вернуть null пока не реализовали, так что ждем)
> //Распечатать информацию о компании (стоит ли вынести это в отдельный класс (например, CompanyReport??))
Однозначно. Это код для решения одной частной задачи, а класс Компания лучше делать более универсальным. Ну то есть если твоя программа дальше не будет развиваться, то допустимо наверно вывод сделать прямо в классе, хотя это и корявенько. Но если она будет дальше развиваться, то класс так быстро превратится в свалку разных методов, потому лучше сделать так, чтобы класс Компания отвечал только за хранение данных о компании, а не вывод.
Есть "принцип единой ответственности", который подразумевает, что каждый класс решает ровно 1 задачу.
> public function addEmployeeToDepartment($newEmployee)
Нужен тайп-хинт.
> //Имя сотрудника
Вообще есть стандарт для комментирования - docComment - советую его изучить. Тогда твои коментарии будут видеть, например, IDE и делать подсказки на их основании.
> construct($name = 'Username', $rank = 0, $isDirector = false)
Вообще, $rank не может быть равен нулю. Не надо подставлять по умолчанию заведомо неправильные значения. Также, я думаю, что не надо делать имена по умолчанию, так как создается ощущение, что их можно не указвать (или ты так и хотел?). То есть если ты хочешь сделать какое-то значение обязательным для указания, то не надо делать там значение по умолчанию.
> class Manager extends Employee
> {
> protected $salary = 500;
Тут есть проблема: тот, кто наследует твой класс должен как-то догадаться что надо переопределить поля. А если он забудет переопределить, то это не будет обнаружено. Лучше использовать абстрактные методы, которые специально для таких ситуаций и придуманы.
> $rank = (int) mb_substr($people[$i], mb_strlen($people[$i]) - 1, 1);
Тут удобнее анализировать строку регуляркой. Она умеет захватывать части строки.
> new Manager("Имя{$j}"
Если тебе имя не нужно, может и не надо было его делать? А то так мы еще можем год рождения, номер паспорта, рост и вес добавить например.
И таблица у тебя что-то разъехалась.
Ждем еще антикризисные меры.
Калькулятор
Плохо, что switch с операциями скопирован 2 раза. От копипасты надо избавиться.
А так, верно сделано.
Навигатор (поиск кратчайшего пути):
> Как сделать с рекурсией не догнал, поэтому решил алгоритмом Дейскры
Рекурсией получилось бы наверно менее эффективно. Но суть примерно такой, что мы пишем функцию примерно такого вида:
найтиПутьКЦели(текущаяТочка, цель, пройденныеТочки, списокВсехУзлов) -> путь
То есть она получает на вход информацию о том, где мы находимся, куда мы идем, что мы уже прошли, и должна вернуть путь к цели. "Путь" в данном случае может быть просто массивом с названиями точек, которые ведут от текущей точки до цели.
На первый взгляд непонятно, как написать такую функцию, потому сначала рассмотрим частный случай. Предположим, что текущаяТочка находится в одном шаге от цели. Тогда решение очевидно - путь к цели будет состоять из единственной точки - самой цели:
если (от текущей точки до цели один шаг) {
вернуть путь из одной точки: [цель];
}
А если цель не в одном шаге от текущей точки, а в нескольких? Допустим, мы в 2 шагах от цели. Допустим, мы в точке "Горьковская", а цель у нас "Чкаловская". Соседние с нами точки - это "Крепость", "Гостиный Двор" и "Петроградская". Так как мы в 2 шагах от цели, то одна из этих точек в одном шаге от нее (это "Петроградская", хотя мы об этом пока не знаем). Мы можем просто попробовать проложить маршрут к цели через каждую из соседних точек, и выбрать ту, путь от которой до цели короче.
Таким образом, сначала мы определили, как добраться до цели, если мы в 1 шаг от нее, потом с помощью рекурсивного вызова мы смогли добраться до цели, находясь в 2 шагах от нее. Продолжая рассуждения, мы можем так же добраться до цели, находясь на любом расстоянии.
Если мы не рядом с целью, мы берем все соседние с текущей точки, в которых мы еще не были (это важно, иначе мы будем ходить кругами). И для каждой соседней точки по очереди вызываем эту же функцию, то есть будем пытаться проложить маршрут к цели через все соседние с текущей точки. Мы получим список маршрутов. Остается только выбрать из них кратчайший и вернуть его:
если (до цели больше одного шага) {
для каждой соседней точки, в которой мы еще не были: {
проложить путь через эту соседнюю точку и сохранить в массив маршрутов;
}
выбрать из массива маршрутов кратчайший путь;
вернуть его;
}
Допустим что от текущей точки до цели N шагов. Если мы пробуем проложить путь через каждую из соседних точек, то какая-то из них ведет к цели и находится от нее в N-1 шаге. В свою очередь при попытке проложить путь через эту точку, функция будет пербирать ее соседей и найдет точку которая в N-2 шагах от цели. И так далее, пока мы не окажемся рядом с целью.
В общем, с помощью рекурсии мы просто перебираем все возможные пути, и выбираем из них кратчайший.
Попробуй написать решение с рекурсией. Тут важно строго определить типы передаваемых аргументов и возвращаемое значение. когда ты рассматриваешь рекурсивную функцию, не пытайся представить себе всю цепочку вызовов. Рассматривай только один шаг - что функция получает на вход, и что она вернет. Если функция за один вызов приближается к цели хотя бы на 1 шаг, то рано или поздно она до нее доберется.
Также, дам дополнительную задачку на рекурсию, чтобы лучше в ней разобраться:
- дана маска и имя файла. Надо написать функцию, проверяющую, соответствует ли имя файла маске. Маска может содержать любые символы, и 2 из них имеют специальное значение:
- знак вопроса соответствует ровно 1 любому символу в имени файла
- звездочка соответствует любому числу любых символов, в том числе их отсутствию
- остальные символы в маске обозначают сами себя
Тесты:
Маска, имя файла, результат
# Обычные символы
test, test, true
test, test.txt, false
test, t, false
# Знак вопроса соотв. ровно 1 символу
te?t, test, true
te?t, tesst, false
te?t, tet, false
te?t, te?t, true
t???, test, true
t???, tes, false
# Звездочка соотв. любому числу символов
t*, test, true
t*, t, true
t*, rest, false
*t, rest, true
# Звездочек может быть много
*t***t*, test, true
*t***t*, rest, false
# Проверяем наличие бектрекинга
t*st, teskest, true
*t*st, teskest, true
# Проверяем пустые строки
"", "", true
"", t, false
t, "", false
*, "", true
"" здесь обозначает пустую строку. Звездочки я заменил на специальные, чтобы разметка не сломалась.
> $paths[$startPoint]['label'] = 0;
> $pointValue['visited'] = false;
Надо не добавлять элементы в и без того сложный массив, а сделать отдельные массивы. А так у тебя все вперемешку получилось: тут же и соседние станции, тут же и label которая не является соседней станцией. Как потом разобраться?
В алгоритме не хватает комментариев, понять что к чему довольно сложно.
Вызов searchPath(...); надо убрать из функции doAlgorithmDeijkstra, чтобы она занималась только расчетом минимальных расстояний.
Подозрительно что простановка минимального времени делается 2 раза:
> if ($pointValue['label'] < $minLabel && (!$pointValue['visited'])) {
> if ($paths[$point]['label'] > $newLabel) {
В алгоритме Дейкстры по моему это только 1 раз делается. Я глянул https://ru.wikipedia.org/wiki/Алгоритм_Дейкстры#.D0.90.D0.BB.D0.B3.D0.BE.D1.80.D0.B8.D1.82.D0.BC - действительно, 1 раз.
Числа прописью
> 1 => array(' тысяча ', ' тысячи ', ' тысяч '),
чтобы не мучаться с пробелами, проще было наверно складывать слова в массив, а потом склеивать с помощью implode().
В общем, верно решено.
Компания "Вектор" ооп
> public function addDepartmentToCompany($newDepartment)
Тут нужен тайп-хинт.
Тайп хинты позволяют указать, что аргумент функции должен быть определенного типа (например быть объектом определенного класса или его наследника). Тайп хинт делает код понятнее (так как видно какого типа переменная) и надежнее (так как PHP не позволит передать что-то неразрешенное и ты сразу увидишь ошибку). Используй их везде.
Мануал: http://php.net/manual/ru/language.oop5.typehinting.php
Обрати внимание, что php7 усовершенствовал систему тайп-хинтов - теперь можно в их качестве указывать примитивные типы вроде int/string, а в php7.1 стало можно указывать тайп-хинт для возвращаемого функцией значения: https://habrahabr.ru/post/267799/ (увы, возможность вернуть null пока не реализовали, так что ждем)
> //Распечатать информацию о компании (стоит ли вынести это в отдельный класс (например, CompanyReport??))
Однозначно. Это код для решения одной частной задачи, а класс Компания лучше делать более универсальным. Ну то есть если твоя программа дальше не будет развиваться, то допустимо наверно вывод сделать прямо в классе, хотя это и корявенько. Но если она будет дальше развиваться, то класс так быстро превратится в свалку разных методов, потому лучше сделать так, чтобы класс Компания отвечал только за хранение данных о компании, а не вывод.
Есть "принцип единой ответственности", который подразумевает, что каждый класс решает ровно 1 задачу.
> public function addEmployeeToDepartment($newEmployee)
Нужен тайп-хинт.
> //Имя сотрудника
Вообще есть стандарт для комментирования - docComment - советую его изучить. Тогда твои коментарии будут видеть, например, IDE и делать подсказки на их основании.
> construct($name = 'Username', $rank = 0, $isDirector = false)
Вообще, $rank не может быть равен нулю. Не надо подставлять по умолчанию заведомо неправильные значения. Также, я думаю, что не надо делать имена по умолчанию, так как создается ощущение, что их можно не указвать (или ты так и хотел?). То есть если ты хочешь сделать какое-то значение обязательным для указания, то не надо делать там значение по умолчанию.
> class Manager extends Employee
> {
> protected $salary = 500;
Тут есть проблема: тот, кто наследует твой класс должен как-то догадаться что надо переопределить поля. А если он забудет переопределить, то это не будет обнаружено. Лучше использовать абстрактные методы, которые специально для таких ситуаций и придуманы.
> $rank = (int) mb_substr($people[$i], mb_strlen($people[$i]) - 1, 1);
Тут удобнее анализировать строку регуляркой. Она умеет захватывать части строки.
> new Manager("Имя{$j}"
Если тебе имя не нужно, может и не надо было его делать? А то так мы еще можем год рождения, номер паспорта, рост и вес добавить например.
И таблица у тебя что-то разъехалась.
Ждем еще антикризисные меры.
Не знаю, я не читал
>>840510
Как я понимаю, он на производительность заточен так как классы на Си написаны. Если тебя интересует производительность, то ты наверно захочешь избавиться от затрат времени на инициализаию приложения при каждом запросе. для этого тебе надо самому в приложении открывать порт и принимать HTTP-запросы от браузера (либо FastCGI запросы от веб-сервера). Например, используя reactPHP.
Тогда ты сможешь один раз инициализировать приложение и обработать много запросов. Но любая ошибка в коде приведет к утечкам памяти или падению приложения. Можешь поэкспериментировать с этим.
>>840636
> //Дать приказ на повышение менеджеров
> public function raiseManager()
Вот это уже нарушение принципа единой ответственности - класс Департамент не должен этим заниматься, этим должен заниматься Антикризисный Комитет. Ты в общий класс, представляющий компанию, суешь методы для решения одной частной задачи. Так он быстро превратится в свалку, а ведь суть ООП заключается в разделении кода на отдельные классы.
> ->checkProffession("Инженер")
Я бы проверял не человеческое название (которое может поменяться), а имя класса. Его можно получить как Manager::class, это такая встроенная константа.
> public function reduceEngineerInDep($percent)
Она удаляет всех инженеров, не разбирая ранги, а там по моему требуется сначала увольнять с низшим рангом. Или я путаю? Также, нельзя увольнять босса-инженера.
Вообще, увольнение лучше сделать по другому:
- отобрать подходящих инженеров
- отсортировать
- взять 40% через array_slice
- уволить
> public function findDirector()
Если директора нет, будет ошибка обращения к несуществующей переменной. И увидеть эту ошибку очень легко: переменная $director создается внутри тела цикла, а используется снаружи. А должно быть наоборот - создается снаружи, используется внутри.
> class AnalystImproved extends Analyst
думаю что неправильно создавать класс только ради смены зарплаты. Плюс, пересоздавать объекты вообще плохая идея, так как у них может быть куча свойств и надо как-то всех их сохранить. Более того, эти свойства могут дописать позже. Лучше всего никогда не пересоздавать объект работника.
> $vectorWithoutEngineer = createCompany(
> $vectorRaiseAnalytics = createCompany(
Тут копипаста. Лучше настроить возможность клонирования компании и департаментов.
Не знаю, я не читал
>>840510
Как я понимаю, он на производительность заточен так как классы на Си написаны. Если тебя интересует производительность, то ты наверно захочешь избавиться от затрат времени на инициализаию приложения при каждом запросе. для этого тебе надо самому в приложении открывать порт и принимать HTTP-запросы от браузера (либо FastCGI запросы от веб-сервера). Например, используя reactPHP.
Тогда ты сможешь один раз инициализировать приложение и обработать много запросов. Но любая ошибка в коде приведет к утечкам памяти или падению приложения. Можешь поэкспериментировать с этим.
>>840636
> //Дать приказ на повышение менеджеров
> public function raiseManager()
Вот это уже нарушение принципа единой ответственности - класс Департамент не должен этим заниматься, этим должен заниматься Антикризисный Комитет. Ты в общий класс, представляющий компанию, суешь методы для решения одной частной задачи. Так он быстро превратится в свалку, а ведь суть ООП заключается в разделении кода на отдельные классы.
> ->checkProffession("Инженер")
Я бы проверял не человеческое название (которое может поменяться), а имя класса. Его можно получить как Manager::class, это такая встроенная константа.
> public function reduceEngineerInDep($percent)
Она удаляет всех инженеров, не разбирая ранги, а там по моему требуется сначала увольнять с низшим рангом. Или я путаю? Также, нельзя увольнять босса-инженера.
Вообще, увольнение лучше сделать по другому:
- отобрать подходящих инженеров
- отсортировать
- взять 40% через array_slice
- уволить
> public function findDirector()
Если директора нет, будет ошибка обращения к несуществующей переменной. И увидеть эту ошибку очень легко: переменная $director создается внутри тела цикла, а используется снаружи. А должно быть наоборот - создается снаружи, используется внутри.
> class AnalystImproved extends Analyst
думаю что неправильно создавать класс только ради смены зарплаты. Плюс, пересоздавать объекты вообще плохая идея, так как у них может быть куча свойств и надо как-то всех их сохранить. Более того, эти свойства могут дописать позже. Лучше всего никогда не пересоздавать объект работника.
> $vectorWithoutEngineer = createCompany(
> $vectorRaiseAnalytics = createCompany(
Тут копипаста. Лучше настроить возможность клонирования компании и департаментов.
https://dkab.github.io/jasmine-tests/?spec=6
>6. Наша функция partial позволяет фиксировать только первые аргументы. Усовершенствуй ее, чтобы зафиксировать можно было любые аргументы, пропущенные аргументы обозначаются с помощью undefined. Обрати внимание, что теперь мы переименовали ее в partialAny, чтобы не путать с предыдущей:
А можно подсказку как решать эту задачу?
Я сделал в три разных решения, и в последнем, самом удачном, выдаются такие ошибки:
должна подставлять лишние аргументы в конец списка: partialAny(fn, 1)(2, 3) -> [1, 2, 3] //(2, 3) - что это?
может вызываться несколько раз и результаты во второй раз не зависят от первого
https://jsfiddle.net/udj4L7qy/
https://jsfiddle.net/udj4L7qy/2/
https://jsfiddle.net/udj4L7qy/3/
Остальные задачи вроде легко решил. Если что могу скинуть в тред.
>11. дан список вида «страна, город, население»...
https://jsfiddle.net/50cncv5L/
>12. Некая сеть фастфудов предлагает несколько видов гамбургеров...
https://jsfiddle.net/y28h2o2b/
Не много не понял что должны возвращать исключения. Нужно написать своё собственное исключение HamburgerException?
>13. В одном городе есть электрическая сеть. К ней могут быть подключены...
Количество ватт генерируемое всей сетью может быть отрицательным и положительным значением. В зависимости от значения считается цена купли\продажи
https://jsfiddle.net/6591a2sL/
>14. напиши функцию, определяющую тип переменной. Результат должен быть одной из строк: 'undefined', 'boolean' (для true/false), 'null', 'number', 'string', 'function', 'array', 'array-like', 'object'...
https://jsfiddle.net/5q3r473h/
>15. Напиши функцию неглубокого копирования объектов и массивов...
https://jsfiddle.net/uyey3at1/
>16. Напиши функцию глубокого копирования объектов и массивов...
https://jsfiddle.net/j8pydqsg/
https://dkab.github.io/jasmine-tests/?spec=6
>6. Наша функция partial позволяет фиксировать только первые аргументы. Усовершенствуй ее, чтобы зафиксировать можно было любые аргументы, пропущенные аргументы обозначаются с помощью undefined. Обрати внимание, что теперь мы переименовали ее в partialAny, чтобы не путать с предыдущей:
А можно подсказку как решать эту задачу?
Я сделал в три разных решения, и в последнем, самом удачном, выдаются такие ошибки:
должна подставлять лишние аргументы в конец списка: partialAny(fn, 1)(2, 3) -> [1, 2, 3] //(2, 3) - что это?
может вызываться несколько раз и результаты во второй раз не зависят от первого
https://jsfiddle.net/udj4L7qy/
https://jsfiddle.net/udj4L7qy/2/
https://jsfiddle.net/udj4L7qy/3/
Остальные задачи вроде легко решил. Если что могу скинуть в тред.
>11. дан список вида «страна, город, население»...
https://jsfiddle.net/50cncv5L/
>12. Некая сеть фастфудов предлагает несколько видов гамбургеров...
https://jsfiddle.net/y28h2o2b/
Не много не понял что должны возвращать исключения. Нужно написать своё собственное исключение HamburgerException?
>13. В одном городе есть электрическая сеть. К ней могут быть подключены...
Количество ватт генерируемое всей сетью может быть отрицательным и положительным значением. В зависимости от значения считается цена купли\продажи
https://jsfiddle.net/6591a2sL/
>14. напиши функцию, определяющую тип переменной. Результат должен быть одной из строк: 'undefined', 'boolean' (для true/false), 'null', 'number', 'string', 'function', 'array', 'array-like', 'object'...
https://jsfiddle.net/5q3r473h/
>15. Напиши функцию неглубокого копирования объектов и массивов...
https://jsfiddle.net/uyey3at1/
>16. Напиши функцию глубокого копирования объектов и массивов...
https://jsfiddle.net/j8pydqsg/
Мапер работает с конкретной сущностью, для этого он сожет выбирать данные из разных таблиц чтобы заполнить эту сущность. А tdg работает с одной таблицей, и только с ней. Чтобы заполнить сущность с джвух таблиц тебе нужно будет создавать gateway для каждой, когда мапер может это делать один.
Разница в том, что в TDG собраны просто любые методы для работы с таблицей, а в DM - методы, предназначеннные для маппинга сущностей на строки таблицы и обратно.
Условно говоря с TDG мы можем использовать
$tdg->updateRating($postId, $newrating);
А с DM обычно используют что-то вроде такого:
$post = $dm->getPostById($postId);
$post->setRating($newRating);
$dm->flush(); // сохранить изменения
А вообще, лучше всего открыть Фаулера (и лучше бы не сайт, а книгу с подробными объяснениями) и сравнить самостоятельно:
http://design-pattern.ru/patterns/table-data-gateway.html
http://design-pattern.ru/patterns/data-mapper.html
На схемах видно, что DM заточен именно на загрузку/сохранение моделей в таблицу, а TDG может работать с таблицей как хочет.
Также, если понимаешь английский , есть вопрос на SO (я его не читал) http://stackoverflow.com/questions/804751/what-is-the-difference-between-the-data-mapper-table-data-gateway-gateway-da
В том то и дело, что они не обязательны, но могут существовать (http://jsonapi.org/format/#errors), и я не придумал ничего лучше как засунуть их в конец аргументов. Идея в том что все ошибки и дата по ходу сценария упаковываются в один - синглтон, да, да, объект JsonAPI, который является единственной точкой вывода (самовалидируется, отправляет заголовки, кодирует сам себя в json-строку, и является вместилищем единственной print), ибо это есть суть API в моем понимании. Один запрос в определенном формате -> один ответ.
З.Ы. Вообще, очень интересно твое мнение по поводу некоторых вещей, отвечаешь ли ты на почту кодедокоде?
З.З.Ы. По поводу запятых: привык так писать, плюс 2 пробела вместо четырех, сейчас экстренно перепривыкаю на PSR)
> упаковываются в один - синглтон, да, да
Серьезно, синглтон? Это в твоем случае признак плохого дизайна. Хороший MVC дизайн выглядел бы как-то так:
$result = $controller->handle($request);
А не опирался бы на глобально доступный объект. Я вообще не вижу никаких преимуществ у синглтона тут: код становится запутаннее, в чем проблема создать объект и вернуть - непонятно.
> Идея в том что все ошибки и дата по ходу сценария
Ну вот это и плохо. Это значит что ты не можешь разбить код на отдельные независимые части, а сделал их все запутанными друг с другом. По идее должно быть как-то так:
// Получаем одни данные
$data = $someService->getSomeData();
$response->setSomeData($data);
// сохраняем ошибку
$reponse->addError('some error');
То есть с API и JSON работает только контроллер, а сервисы, которые он вызывает, ничего про JSON и API вообще не знают. Это же MVC.
Возвращаясь к твоему объекту, вряд ли у любой ошибки будет ровно 7 параметров. Тут можно:
1) сделать статические конструкторы, такого вида:
$error = ErrorObject::createLoadDataError($x, $y);
$error = ErrorObject::createTimeoutError($x, $y);
2) сделать разные классы для разных ошибок
3) убрать необязтаельные параметры из конструктора и добавлять их через сеттеры
В общем, ООП дает много вариантов.
> объект JsonAPI, который является единственной точкой вывода (самовалидируется, отправляет заголовки, кодирует сам себя в json-строку, и является вместилищем единственной print)
Это приведет к тому , что класс превратится в God Object в которому будет половина логики твоего приложения. А что ты будешь делать, когда тебе надо будет добавить второй, третий, четвертый и тд метод АПИ, возвращающий данные другого формата?
На почту можно, но в треде писать лучше в том плане что может кто-то еще что-то полезное для себя найдет в ответе.
> упаковываются в один - синглтон, да, да
Серьезно, синглтон? Это в твоем случае признак плохого дизайна. Хороший MVC дизайн выглядел бы как-то так:
$result = $controller->handle($request);
А не опирался бы на глобально доступный объект. Я вообще не вижу никаких преимуществ у синглтона тут: код становится запутаннее, в чем проблема создать объект и вернуть - непонятно.
> Идея в том что все ошибки и дата по ходу сценария
Ну вот это и плохо. Это значит что ты не можешь разбить код на отдельные независимые части, а сделал их все запутанными друг с другом. По идее должно быть как-то так:
// Получаем одни данные
$data = $someService->getSomeData();
$response->setSomeData($data);
// сохраняем ошибку
$reponse->addError('some error');
То есть с API и JSON работает только контроллер, а сервисы, которые он вызывает, ничего про JSON и API вообще не знают. Это же MVC.
Возвращаясь к твоему объекту, вряд ли у любой ошибки будет ровно 7 параметров. Тут можно:
1) сделать статические конструкторы, такого вида:
$error = ErrorObject::createLoadDataError($x, $y);
$error = ErrorObject::createTimeoutError($x, $y);
2) сделать разные классы для разных ошибок
3) убрать необязтаельные параметры из конструктора и добавлять их через сеттеры
В общем, ООП дает много вариантов.
> объект JsonAPI, который является единственной точкой вывода (самовалидируется, отправляет заголовки, кодирует сам себя в json-строку, и является вместилищем единственной print)
Это приведет к тому , что класс превратится в God Object в которому будет половина логики твоего приложения. А что ты будешь делать, когда тебе надо будет добавить второй, третий, четвертый и тд метод АПИ, возвращающий данные другого формата?
На почту можно, но в треде писать лучше в том плане что может кто-то еще что-то полезное для себя найдет в ответе.
>рекуррентных нейронных сетей
Где ты прочитал об этом? Это просто красивое название для маркетинга. Постигание программирования даётся гораздо проще, но дай угадаю, ты ничего не знаешь о программировании, не ходил ни на какие курсы по программированию, не сдавал программирование в институте, ты просто хочешь научиться такой крутой штуке как программирование, тем более, ты, наверно, слышал что это самостоятельно легко учиться в короткие сроки? Неважно, начинай учить PHP в шапке есть все ссылки.
>Постигание программирования даётся гораздо проще, но дай угадаю, ты ничего не знаешь о программировании
Ты вообще поехавший? Где я сказал, что мне нужна помощь в изучении PHP? С чего ты вообще взял, что я не умею программировать? Просьба была скинуть название книги, лекций о нейронных сетях. В этот тред написал потому, что реализовывать задуманное планирую на PHP.
По соседству есть пару веток про machine learning и нейронные сети, думаю там тебе скорее ответят, чем тут.
Спасибо за наводку!
11 задача: https://ideone.com/ntoBFC
Ты на мой взгляд все переусложнил. Если требуется сорировать только по 1-2 критериям, то проще просто написать вручную 2 функции. Если надо сделать сортировку по любому полю, то можно сделать 2 функции - одну для сравнения строк и другую для сравнения чисел. Порядок сортировки меняется сменой знака:
var compareFn;
if (поле числовое) {
compareFn = функция сравнения чисел;
} else {
compareFn = функция сравнения строк;
}
var direction = 1 или -1;
cities.sort(function (a, b) {
return direction * compareFn(a[field], b[field]);
});
Попробуй упростить код.
12 задача: https://ideone.com/U2fJ2w
> Hamburger.SIZE_SMALL = 'SMALL';
> function Hamburger(size, stuffing) {
Лушче сначала создать функцию, а потом проставлять ей свойства. Ты полагаешься на тонкие особенности в яваскрипте, которые могут в каких-то случаях работать не так.
В свойстве вроде this._size лучше хранить константу, обозначающую размер. В твоем случае, как например сделать метод getSize() или проверить, что размер большой? Удобнее хранить исходные данные, а не производные от них.
По моему ты все как-то переусложняешь. Вот есть гамбургер, у него есть свойство размер, зачем там вместо размера хранить стоимость и число калорий? Я не очень понимаю, в чем тут выгода. Ты исходишь из того, что размер будет использоваться только для расчета калорий и цены, но завтра может быть появятся какие-то дополнительные условия.
> А этот статический метод будет при каждом вызове новый массив создавать или возвращать уже существующий? Если второе, то я тогда не совсем понимаю, зачем приватный геттер, если можно в статических свойствах массивы хранить. (сейчас так сделал).
Ну плюс тут в том, что получается что массив нельзя изменить, если используется функция-геттер. Можно и в свойстве хранить конечно.
Так, работает верно, но мне кажется что лучше в свойствах хранить исходные данные.
11 задача: https://ideone.com/ntoBFC
Ты на мой взгляд все переусложнил. Если требуется сорировать только по 1-2 критериям, то проще просто написать вручную 2 функции. Если надо сделать сортировку по любому полю, то можно сделать 2 функции - одну для сравнения строк и другую для сравнения чисел. Порядок сортировки меняется сменой знака:
var compareFn;
if (поле числовое) {
compareFn = функция сравнения чисел;
} else {
compareFn = функция сравнения строк;
}
var direction = 1 или -1;
cities.sort(function (a, b) {
return direction * compareFn(a[field], b[field]);
});
Попробуй упростить код.
12 задача: https://ideone.com/U2fJ2w
> Hamburger.SIZE_SMALL = 'SMALL';
> function Hamburger(size, stuffing) {
Лушче сначала создать функцию, а потом проставлять ей свойства. Ты полагаешься на тонкие особенности в яваскрипте, которые могут в каких-то случаях работать не так.
В свойстве вроде this._size лучше хранить константу, обозначающую размер. В твоем случае, как например сделать метод getSize() или проверить, что размер большой? Удобнее хранить исходные данные, а не производные от них.
По моему ты все как-то переусложняешь. Вот есть гамбургер, у него есть свойство размер, зачем там вместо размера хранить стоимость и число калорий? Я не очень понимаю, в чем тут выгода. Ты исходишь из того, что размер будет использоваться только для расчета калорий и цены, но завтра может быть появятся какие-то дополнительные условия.
> А этот статический метод будет при каждом вызове новый массив создавать или возвращать уже существующий? Если второе, то я тогда не совсем понимаю, зачем приватный геттер, если можно в статических свойствах массивы хранить. (сейчас так сделал).
Ну плюс тут в том, что получается что массив нельзя изменить, если используется функция-геттер. Можно и в свойстве хранить конечно.
Так, работает верно, но мне кажется что лучше в свойствах хранить исходные данные.
Тут длинноват заголовок цикла, так что часть операций лучше бы перенести в тело на отдельные строчки.
Ну и требуется посчитать общую сумму выплаченного - должно получиться около 61270.
>>840853
Когда ты обновляешь страницу, браузер отправляет новый HTTP запрос и запускается скрипт. Чтобы этого не было, надо после успешной обработки POST данных делать редирект, например на страницу с сообщением об успешном выполнении операции. Тогда обновляться будет только эта страница.
>>840929
Тебе надо изучить работу с масссивами и объектами, циклы.
https://github.com/enotocode/Studentslist
> Requirements:
> SLQ 5.6.17 or above
Что-то ты опечатался. И еще, у тебя разметка кривая получилась в README, почитай про маркдаун-разметку.
https://github.com/enotocode/Studentslist/blob/master/abiturients.sql
> `egePoints` varchar(3)
Число баллов на ЕГЭ - это все-таки число, а не строка. Вот телефон или номер паспорта - это строка, а число баллов - это число. Потому что их можно считать, складывать, и тд.
https://github.com/enotocode/Studentslist/blob/master/public/index.php#L2
> require_once '/../app/init.php';
Вот тут путь странный, это путь от корня диска или относительно текущего файла? Лучше использовать абсолютные пути, например на основе __DIR__, так как они гарантированно укажут на нужный файл. А относительный путь может например зависеть от того, какой текущий каталог, чему равен include_path и тд.
> if (empty($_GET)) {
> // Подгружаем страницу изменения данных
> header('Location: register.php');
Скрипт после редиректа не завершается, а продолжает работать у тебя.
Сам редирект тоже немного странный, а как же зайти на страницу с таблицей студентов? Мне кажется, это неудобно, если она не открывается напрямую.
> // Подгружаем страницу сообщения
> require("../templates/message.html");
Вот это неправильно. Ты тут фактически делаешь вывод части страницы, а это не задача контроллера - выводить страницу по частям. Это должно делать представление, то есть основной шаблон страницы подключает что ему нужно.
https://github.com/enotocode/Studentslist/blob/master/public/autoloader.php
Это наверно должно быть не в публичной папке.
https://github.com/enotocode/Studentslist/blob/master/public/autoloader.php#L11
> echo 'Class "'. $fullFileName .'" does not exist.';
Автозагрузчик не должен выводить ошибок. Если класса нет, он его просто не загружает. перечитай урок про автозагрузку.
Имя файла в автозагрузчике лучше формировать абсолютное, а не начинающееся с '..'
https://github.com/enotocode/Studentslist/blob/master/app/init.php
Константы, отнсящиеся к полу и месту проживания логично поместить в класс Abiturient. Константы с порядком сортировки - в класс работы с БД.
> throw new exception
Имена классов пишут с боьшой буквы.
https://github.com/enotocode/Studentslist/blob/master/app/init.php#L20
> $dns = $settings['database']['driver'] .
Тут может можно sprintf использовать, чтобы не так страшно выглядело?
https://github.com/enotocode/Studentslist/blob/master/app/Abiturient.php#L27
Магические методы надо всегда использовать с осторожностью. Так как они затрудняют понимаие кода, требуют в нем разбираться. Для свойств студента наверно удобнее было использовать магические метод __get, __isset и __set (хотя тогда возникает вопрос, не проще ли сделать публичные свойства).
В любом случае, при обращении к несуществующему свойству надо выбрасывать исключение, а не прятать ошибку. У тебя можно написать $abiturient->asdasdada() и никакой ошибки не будет.
> $args = preg_split('/(?<=\w)(?=[A-Z])/', $methodName);
Для разделения на части удобнее использовать либо preg_match либо банальный mb_substr.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientDataGateway.php#L19
> public function __construct($pdo)
Тут нужен тайп-хинт.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientDataGateway.php#L53
> $sql = "SELECT * FROM abiturients WHERE id = " . $id;
id надо подставлять через плейсхолдер, а у тебя тут может быть sql инъекция так как содержаться там может что угодно.
> $student = $stm->fetchAll(\PDO::FETCH_ASSOC);
> return $student[0];
Есть метод для выбора 1 строчки.
> $values = $this->getValuesAsArray($abiturient);
> array_pop($values); // delete userPassword
Это ненадежно, ты полагаешься на что пароль будет идти последним, но где гарантия этого? Очень легко поменять порядок полей в массиве и непраивльно что код из-за этого сломается где-то в другом месте.
> foreach ($values as $valueName => $value) {
> $sql = "UPDATE abiturients SET " . $valueName . " = '" . $value . "' WHERE id = " . $id;
Неэффективно обновлять по 1 полю. Надо сформировать большой запрос и обновить все разом. Можно например собирать SQL код в один массив, а значения плейсхолдеров - в другой. Такой метод даже можно вынести отдельно, так как часто есть задача обновить сущность по id.
Примерно так:
$sqlParts[] = ' name = :name ';
$placeholders[':name'] = $name;
Только с подстановкой имен полей из переменной.
Вообще, для сборки сложных запросов из кусков есть паттерн Query Builder, но тут наверно можно и без него обойтись.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientDataGateway.php#L75
> [^a-zA-ZА-Яа-я0-9\\s\-\`]
Буква ё находится не между а и я, а идет отдельно в юникоде. Ее надо указать отдельно.
Возвращать логично не массивы, а объекты Abiturient - у тебя ведь весь код заточен на работу с ними.
> $searchKey = preg_replace (
> array("![^a-zA-ZА-Яа-я0-9\s]!ui", "! +!ui"),
Алгоритм упрощения выражения дублируется и его надо вынести в отдельный метод.
> $result = $stm->fetch(\PDO::FETCH_NUM);
> return $result[0];
есть метод для получения одного значения.
> $email = filter_var ($value, FILTER_VALIDATE_EMAIL);
Имей в виду, что емайлы вроде ivavn}nANUSTy ивановPUNCTUMBVOрф не проходят через эту функцию, по крайней мере в PHP5. Там по моему даже в мануале это упомянуто для проверки URL.
У функций валидации не очень удачно выбран формат возвращаемых значений. Логично возвращать true/false, а не null/не-null, так как null легко спутать с '' при нестрогом (==) сравнении.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientValidator.php#L34
> if ($value>$max | $value<$min) {
| это битовая операция (читай мануал и википедию: https://ru.wikipedia.org/wiki/Битовые_операции ). Ты наверно имел в виду логическую операцию ||.
> $this->errors["name"][0] = $this->findForbiddenSymbols('/[^a-zа-яё]/ui', $name);
Тут наверно логичнее в качестве ключей исопльзовать строки, то есть $errors['name']['forbidden'], так как испоьзуются они потом в другом месте кода и надо помнить какой номер чему соответствует. Более того, возможно тут стоит упростить код, сделав удобный класс для представления ошибки или списка ошибок. Чтобы было вроде
$error = new ValidationError(...);
или
$errors->add(['x' => .., 'y' => ...]);
Сама идея хранить отдельно параметры ошибки от шаблона сообщения хорошая (это позволяет например делать перевод сообщения на другой язык), но вот реализация немного запутанная.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientValidator.php#L73
> date_default_timezone_set('UTC');
Вот это нехорошо, твоя функция как побочный эффект меняет глобально часовой пояс. Это может вызвать ошибки в другом месте кода. Посмотри, нельзя ли указать часовой пояс локальнро для одной функции, может передать в date, может использовать класс DateTime который вроде их поддерживает.
> return $this->errors;
Не очень понятно, зачем сохранять список в свойстве.
https://github.com/enotocode/Studentslist
> Requirements:
> SLQ 5.6.17 or above
Что-то ты опечатался. И еще, у тебя разметка кривая получилась в README, почитай про маркдаун-разметку.
https://github.com/enotocode/Studentslist/blob/master/abiturients.sql
> `egePoints` varchar(3)
Число баллов на ЕГЭ - это все-таки число, а не строка. Вот телефон или номер паспорта - это строка, а число баллов - это число. Потому что их можно считать, складывать, и тд.
https://github.com/enotocode/Studentslist/blob/master/public/index.php#L2
> require_once '/../app/init.php';
Вот тут путь странный, это путь от корня диска или относительно текущего файла? Лучше использовать абсолютные пути, например на основе __DIR__, так как они гарантированно укажут на нужный файл. А относительный путь может например зависеть от того, какой текущий каталог, чему равен include_path и тд.
> if (empty($_GET)) {
> // Подгружаем страницу изменения данных
> header('Location: register.php');
Скрипт после редиректа не завершается, а продолжает работать у тебя.
Сам редирект тоже немного странный, а как же зайти на страницу с таблицей студентов? Мне кажется, это неудобно, если она не открывается напрямую.
> // Подгружаем страницу сообщения
> require("../templates/message.html");
Вот это неправильно. Ты тут фактически делаешь вывод части страницы, а это не задача контроллера - выводить страницу по частям. Это должно делать представление, то есть основной шаблон страницы подключает что ему нужно.
https://github.com/enotocode/Studentslist/blob/master/public/autoloader.php
Это наверно должно быть не в публичной папке.
https://github.com/enotocode/Studentslist/blob/master/public/autoloader.php#L11
> echo 'Class "'. $fullFileName .'" does not exist.';
Автозагрузчик не должен выводить ошибок. Если класса нет, он его просто не загружает. перечитай урок про автозагрузку.
Имя файла в автозагрузчике лучше формировать абсолютное, а не начинающееся с '..'
https://github.com/enotocode/Studentslist/blob/master/app/init.php
Константы, отнсящиеся к полу и месту проживания логично поместить в класс Abiturient. Константы с порядком сортировки - в класс работы с БД.
> throw new exception
Имена классов пишут с боьшой буквы.
https://github.com/enotocode/Studentslist/blob/master/app/init.php#L20
> $dns = $settings['database']['driver'] .
Тут может можно sprintf использовать, чтобы не так страшно выглядело?
https://github.com/enotocode/Studentslist/blob/master/app/Abiturient.php#L27
Магические методы надо всегда использовать с осторожностью. Так как они затрудняют понимаие кода, требуют в нем разбираться. Для свойств студента наверно удобнее было использовать магические метод __get, __isset и __set (хотя тогда возникает вопрос, не проще ли сделать публичные свойства).
В любом случае, при обращении к несуществующему свойству надо выбрасывать исключение, а не прятать ошибку. У тебя можно написать $abiturient->asdasdada() и никакой ошибки не будет.
> $args = preg_split('/(?<=\w)(?=[A-Z])/', $methodName);
Для разделения на части удобнее использовать либо preg_match либо банальный mb_substr.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientDataGateway.php#L19
> public function __construct($pdo)
Тут нужен тайп-хинт.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientDataGateway.php#L53
> $sql = "SELECT * FROM abiturients WHERE id = " . $id;
id надо подставлять через плейсхолдер, а у тебя тут может быть sql инъекция так как содержаться там может что угодно.
> $student = $stm->fetchAll(\PDO::FETCH_ASSOC);
> return $student[0];
Есть метод для выбора 1 строчки.
> $values = $this->getValuesAsArray($abiturient);
> array_pop($values); // delete userPassword
Это ненадежно, ты полагаешься на что пароль будет идти последним, но где гарантия этого? Очень легко поменять порядок полей в массиве и непраивльно что код из-за этого сломается где-то в другом месте.
> foreach ($values as $valueName => $value) {
> $sql = "UPDATE abiturients SET " . $valueName . " = '" . $value . "' WHERE id = " . $id;
Неэффективно обновлять по 1 полю. Надо сформировать большой запрос и обновить все разом. Можно например собирать SQL код в один массив, а значения плейсхолдеров - в другой. Такой метод даже можно вынести отдельно, так как часто есть задача обновить сущность по id.
Примерно так:
$sqlParts[] = ' name = :name ';
$placeholders[':name'] = $name;
Только с подстановкой имен полей из переменной.
Вообще, для сборки сложных запросов из кусков есть паттерн Query Builder, но тут наверно можно и без него обойтись.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientDataGateway.php#L75
> [^a-zA-ZА-Яа-я0-9\\s\-\`]
Буква ё находится не между а и я, а идет отдельно в юникоде. Ее надо указать отдельно.
Возвращать логично не массивы, а объекты Abiturient - у тебя ведь весь код заточен на работу с ними.
> $searchKey = preg_replace (
> array("![^a-zA-ZА-Яа-я0-9\s]!ui", "! +!ui"),
Алгоритм упрощения выражения дублируется и его надо вынести в отдельный метод.
> $result = $stm->fetch(\PDO::FETCH_NUM);
> return $result[0];
есть метод для получения одного значения.
> $email = filter_var ($value, FILTER_VALIDATE_EMAIL);
Имей в виду, что емайлы вроде ivavn}nANUSTy ивановPUNCTUMBVOрф не проходят через эту функцию, по крайней мере в PHP5. Там по моему даже в мануале это упомянуто для проверки URL.
У функций валидации не очень удачно выбран формат возвращаемых значений. Логично возвращать true/false, а не null/не-null, так как null легко спутать с '' при нестрогом (==) сравнении.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientValidator.php#L34
> if ($value>$max | $value<$min) {
| это битовая операция (читай мануал и википедию: https://ru.wikipedia.org/wiki/Битовые_операции ). Ты наверно имел в виду логическую операцию ||.
> $this->errors["name"][0] = $this->findForbiddenSymbols('/[^a-zа-яё]/ui', $name);
Тут наверно логичнее в качестве ключей исопльзовать строки, то есть $errors['name']['forbidden'], так как испоьзуются они потом в другом месте кода и надо помнить какой номер чему соответствует. Более того, возможно тут стоит упростить код, сделав удобный класс для представления ошибки или списка ошибок. Чтобы было вроде
$error = new ValidationError(...);
или
$errors->add(['x' => .., 'y' => ...]);
Сама идея хранить отдельно параметры ошибки от шаблона сообщения хорошая (это позволяет например делать перевод сообщения на другой язык), но вот реализация немного запутанная.
https://github.com/enotocode/Studentslist/blob/master/app/AbiturientValidator.php#L73
> date_default_timezone_set('UTC');
Вот это нехорошо, твоя функция как побочный эффект меняет глобально часовой пояс. Это может вызвать ошибки в другом месте кода. Посмотри, нельзя ли указать часовой пояс локальнро для одной функции, может передать в date, может использовать класс DateTime который вроде их поддерживает.
> return $this->errors;
Не очень понятно, зачем сохранять список в свойстве.
https://github.com/enotocode/Studentslist
https://github.com/enotocode/Studentslist/blob/master/app/Autorization.php#L14
> $pass = md5(uniqid(rand(),true));
Непонятна стойкость пароля. ну напимер md5 никак ее не увеличивает, так как ее результат полностью соответствует входным данным. Чтобы пароль был случайным, каждый символ должен выбираться случайно. Приведу пример:
md5(mt_rand(1, 10))
На первый взгляд пароль надежный, но в реальности там всего 10 варианто значений.
Алсо вот тут еще про это написано https://github.com/codedokode/pasta/blob/master/good-code.md#Не-злоупотребляй-хешированием
https://github.com/enotocode/Studentslist/blob/master/app/ViewHelper.php#L5
> $htmlTag = "<strong><\strong>"
Бекслеш не в ту сторону. Также, можно было просто передавать 2 тега отедльными аргументами либо использовать плейсхолдер вроде <mark>%s</mark>. Также, для выделения текста есть специальный HTML5 тег mark.
https://github.com/enotocode/Studentslist/blob/master/app/ViewHelper.php#L12
> $mask = "/" . $mask . "/iu";
При подстановке в регулярку, если там могут быть специсмволы, можно использовтаь функцию preg_quote которая их заэкранирует бекслешем.
https://github.com/enotocode/Studentslist/blob/master/app/config.ini
В конфиг лучше класть только то, что может менять пользователь. Поменяь драйвер или кодировку, не сломав код, не получится, потому мне кажется, нет смысла выносить это в конфиг.
> <input/>
В HTML (в отличие от XML) слеш в конце не ставится. HTML4 его не доспускает, HTML5 допускает но игнорирует и потому <div/> воспринимается как <div>. В HTML в отличие от XML у input просто нет закрывающего тега.
> <input type="radio" name="gender" value="GENDER_MALE" id="GENDER_MALE"
> <?php if (isset($values['gender']) && ($values['gender']==GENDER_MALE))
Ты в одном месте используешь строку GENDER_MALE,а в другом константу. По идее твой код не должен ломаться если мы поменяем значение константы GENDER_MALE например на 1.
> <?php if (isset($values['gender']) && ($values['gender']==GENDER_FEMALE)): echo "checked"; endif
Можно писать <?= условие ? 'checked' : '' ?>
В форму наверно удобнее данные передавать не в виде массива, а в виде объекта Abiturient. И обрабатывать так же.
> placeholder="Иван"
Лучше наверно "Например, Иван"
> value=
> "<?php if (isset($values['name'])): echo $values['name']; endif; ?>"
Нет экранирования данных. Нет ли тут XSS? Урок https://github.com/codedokode/pasta/blob/master/security/xss.md
> <input required type="number" class="form-control" name="dateOfBirth"
Тут можно задать min/max
> <input type="radio" name="registry"
> <label for="REGISTRY_LOCAL">Местная</label>
Можно засунуть input внутрь label и тогда id/for не нужны.
Вообще, для id стоит добавлять префикс, например form_name вместо name. Иначе в реальном большом приложении есть шанс что эти id совпадут с каким-то другим id на странице.
https://github.com/enotocode/Studentslist/blob/master/templates/list.html#L11
> <input type="text"
Вообще для поиска есть специальный тип инпута search - с крестиком, историей поиска и может быть еще с какими-то особенностями, погугли на эту тему.
https://css-tricks.com/webkit-html5-search-inputs/ (англ)
http://stackoverflow.com/questions/11589770/input-type-text-vs-input-type-search-in-html5 (англ)
https://github.com/enotocode/Studentslist/blob/master/templates/list.html#L13
> <button type="submit" class="btn btn-default"/>Найти</button>
Слишком много слешей. Сделай-ка вот что: загрузи страницы своегосайта в браузер, открой исходный код и отправь его в валидатор:
- https://validator.w3.org/
- https://html5.validator.nu/
И посмотри что покажет. Есть также расширения для браузеров, облегчающие отправку на проверку но я как параноик не доверяю расширениям.
> найденные по запросу «<?= $curl['search']; ?>».</p>
XSS ведь тут?
И еще, ты подключашешь шаблон через include. Но это имеет недостаток: не видно, какие переменные передаются в него. Возможно лучше передавать их явно как-то так:
$view->render('index.phtml', [
'x' => $x,
'y' => $y
]);
> if (isset($_COOKIE['userPassword']) && ($autorization->getUserId($_COOKIE['userPassword']))) > {
> $userId = $autorization->getUserId($_COOKIE['userPassword']);
> }
У тебя класс авторизации по сути просто ищет юзера по куке. А рассмотри еще такой вариант, чтобы класс авторизации отвечал и за простановку/ проверку/очистку авторизационных кук. Может так будет лушче?
https://github.com/enotocode/Studentslist/blob/master/public/register.php#L40
> foreach($errors as $es) {
> if (is_array($es)) {
> foreach($es as $e) {
> if (isset($e)) {
Что-то как-то сложно. Во-первых, мне не нравится is_array - ты получается сам не знаешь какого у тебя типа данные? Во-вторых для проверки на null есть функция is_null и сравнение $x === null, а isset проверяет существование переменной.
Обновление студента лучше делать так: загрузить студента из БД, затем обновить в нем данные из POST. Иначе если у тебя в модели есть поле, которого нет в форме, оно потеряется. Ты по сути пересоздаешь студента заново.
> require_once '\..\app\init.php';
Обратные слеши работают только на Windows, а прямые - везде.
https://github.com/enotocode/Studentslist
https://github.com/enotocode/Studentslist/blob/master/app/Autorization.php#L14
> $pass = md5(uniqid(rand(),true));
Непонятна стойкость пароля. ну напимер md5 никак ее не увеличивает, так как ее результат полностью соответствует входным данным. Чтобы пароль был случайным, каждый символ должен выбираться случайно. Приведу пример:
md5(mt_rand(1, 10))
На первый взгляд пароль надежный, но в реальности там всего 10 варианто значений.
Алсо вот тут еще про это написано https://github.com/codedokode/pasta/blob/master/good-code.md#Не-злоупотребляй-хешированием
https://github.com/enotocode/Studentslist/blob/master/app/ViewHelper.php#L5
> $htmlTag = "<strong><\strong>"
Бекслеш не в ту сторону. Также, можно было просто передавать 2 тега отедльными аргументами либо использовать плейсхолдер вроде <mark>%s</mark>. Также, для выделения текста есть специальный HTML5 тег mark.
https://github.com/enotocode/Studentslist/blob/master/app/ViewHelper.php#L12
> $mask = "/" . $mask . "/iu";
При подстановке в регулярку, если там могут быть специсмволы, можно использовтаь функцию preg_quote которая их заэкранирует бекслешем.
https://github.com/enotocode/Studentslist/blob/master/app/config.ini
В конфиг лучше класть только то, что может менять пользователь. Поменяь драйвер или кодировку, не сломав код, не получится, потому мне кажется, нет смысла выносить это в конфиг.
> <input/>
В HTML (в отличие от XML) слеш в конце не ставится. HTML4 его не доспускает, HTML5 допускает но игнорирует и потому <div/> воспринимается как <div>. В HTML в отличие от XML у input просто нет закрывающего тега.
> <input type="radio" name="gender" value="GENDER_MALE" id="GENDER_MALE"
> <?php if (isset($values['gender']) && ($values['gender']==GENDER_MALE))
Ты в одном месте используешь строку GENDER_MALE,а в другом константу. По идее твой код не должен ломаться если мы поменяем значение константы GENDER_MALE например на 1.
> <?php if (isset($values['gender']) && ($values['gender']==GENDER_FEMALE)): echo "checked"; endif
Можно писать <?= условие ? 'checked' : '' ?>
В форму наверно удобнее данные передавать не в виде массива, а в виде объекта Abiturient. И обрабатывать так же.
> placeholder="Иван"
Лучше наверно "Например, Иван"
> value=
> "<?php if (isset($values['name'])): echo $values['name']; endif; ?>"
Нет экранирования данных. Нет ли тут XSS? Урок https://github.com/codedokode/pasta/blob/master/security/xss.md
> <input required type="number" class="form-control" name="dateOfBirth"
Тут можно задать min/max
> <input type="radio" name="registry"
> <label for="REGISTRY_LOCAL">Местная</label>
Можно засунуть input внутрь label и тогда id/for не нужны.
Вообще, для id стоит добавлять префикс, например form_name вместо name. Иначе в реальном большом приложении есть шанс что эти id совпадут с каким-то другим id на странице.
https://github.com/enotocode/Studentslist/blob/master/templates/list.html#L11
> <input type="text"
Вообще для поиска есть специальный тип инпута search - с крестиком, историей поиска и может быть еще с какими-то особенностями, погугли на эту тему.
https://css-tricks.com/webkit-html5-search-inputs/ (англ)
http://stackoverflow.com/questions/11589770/input-type-text-vs-input-type-search-in-html5 (англ)
https://github.com/enotocode/Studentslist/blob/master/templates/list.html#L13
> <button type="submit" class="btn btn-default"/>Найти</button>
Слишком много слешей. Сделай-ка вот что: загрузи страницы своегосайта в браузер, открой исходный код и отправь его в валидатор:
- https://validator.w3.org/
- https://html5.validator.nu/
И посмотри что покажет. Есть также расширения для браузеров, облегчающие отправку на проверку но я как параноик не доверяю расширениям.
> найденные по запросу «<?= $curl['search']; ?>».</p>
XSS ведь тут?
И еще, ты подключашешь шаблон через include. Но это имеет недостаток: не видно, какие переменные передаются в него. Возможно лучше передавать их явно как-то так:
$view->render('index.phtml', [
'x' => $x,
'y' => $y
]);
> if (isset($_COOKIE['userPassword']) && ($autorization->getUserId($_COOKIE['userPassword']))) > {
> $userId = $autorization->getUserId($_COOKIE['userPassword']);
> }
У тебя класс авторизации по сути просто ищет юзера по куке. А рассмотри еще такой вариант, чтобы класс авторизации отвечал и за простановку/ проверку/очистку авторизационных кук. Может так будет лушче?
https://github.com/enotocode/Studentslist/blob/master/public/register.php#L40
> foreach($errors as $es) {
> if (is_array($es)) {
> foreach($es as $e) {
> if (isset($e)) {
Что-то как-то сложно. Во-первых, мне не нравится is_array - ты получается сам не знаешь какого у тебя типа данные? Во-вторых для проверки на null есть функция is_null и сравнение $x === null, а isset проверяет существование переменной.
Обновление студента лучше делать так: загрузить студента из БД, затем обновить в нем данные из POST. Иначе если у тебя в модели есть поле, которого нет в форме, оно потеряется. Ты по сути пересоздаешь студента заново.
> require_once '\..\app\init.php';
Обратные слеши работают только на Windows, а прямые - везде.
Задачка на проверку номера телефона
https://github.com/vanus7/regexp/tree/master
Правильно ссылку дал(лол)?
Вроде правильная регулярка, а везде ошибку выдает
помогите укажите где проебался
Мы уже в новом треде >>848550 (OP)
Ссылку наверно лучше давать как Raw https://raw.githubusercontent.com/vanus7/regexp/master/README.md так как в файлах md еть разметка и она может съесть звездочки.
Ошибка тут:
+7
+ это спецсимвол, задающий число повторений. Надо его заэкранировать.
спасибо!
Это наверно запоздавший ответ, но может тебе лучше поискать готовую утилиту для отправки файла по почте? Ну и надо все равно помнить что в почтовых сервисах часто есть ограничение порядка 10-20 Мб на файлы.
>>841710
Программа должна выводить, сколько в итоге заплачено по кредиту за все время. Должно получиться около 61270.
>>841546
Если ты не знаешь язык, ты не сможешь написать нужную тебе программу. Возможно, для твоих целей больше подойдет node.js
>>841773
Если тебе нужен порядок, в каком учить, то начинай с PHP и SQL. Тебе нужно знать ООП прежде чем браться за фреймворки. Из фреймворков - Slim, Yii2, Symfony 2/3.
У нас в ОП посте есть задачки по PHP/SQL/JS/HTML
>>841785
У тебя шапка цикла очень длинная и плохо читается. Расчет баланса лучше перенести в тело цикла.
Также, хорошо бы прикладывать ссылку на ideone.
Число 5000 повторяется 2 раза, надо вынести его в переменную.
>>841789
Значит, ты что-то не учел, как выполняется цикл. Перечитай про порядок выполнения действий в цикле, если хочешь разобраться.
>>841827
Посмотри внимательно, в каком порядке выполняются действия в цикле. Скорее всего. там есть разница в 1-м и 2-м вариантах.
Это наверно запоздавший ответ, но может тебе лучше поискать готовую утилиту для отправки файла по почте? Ну и надо все равно помнить что в почтовых сервисах часто есть ограничение порядка 10-20 Мб на файлы.
>>841710
Программа должна выводить, сколько в итоге заплачено по кредиту за все время. Должно получиться около 61270.
>>841546
Если ты не знаешь язык, ты не сможешь написать нужную тебе программу. Возможно, для твоих целей больше подойдет node.js
>>841773
Если тебе нужен порядок, в каком учить, то начинай с PHP и SQL. Тебе нужно знать ООП прежде чем браться за фреймворки. Из фреймворков - Slim, Yii2, Symfony 2/3.
У нас в ОП посте есть задачки по PHP/SQL/JS/HTML
>>841785
У тебя шапка цикла очень длинная и плохо читается. Расчет баланса лучше перенести в тело цикла.
Также, хорошо бы прикладывать ссылку на ideone.
Число 5000 повторяется 2 раза, надо вынести его в переменную.
>>841789
Значит, ты что-то не учел, как выполняется цикл. Перечитай про порядок выполнения действий в цикле, если хочешь разобраться.
>>841827
Посмотри внимательно, в каком порядке выполняются действия в цикле. Скорее всего. там есть разница в 1-м и 2-м вариантах.
Надо выяснить сначала, почему так происходит.
>>841862
Если записи в последних 2 колонках имеют разный вид группировки (по должности и по отделу соответственно), то для последней колонки стоит сделать отдельный подзапрос и приджойнить его к основному.
А у тебя подзапрос по моему вообще лишний. Зачем он там нужен? Первые 4 колонки получаются обычной группировкой.
Также, у тебя избыточное условие группировки: GROUP BY dep.department_id, po.post_id - тут хватит одной колонки.
Если что, у нас в ОП посте есть задачки на SQL, чтобы лучше его понимать.
>>842122
Композер - это менеджер пакетов
PDO - это расширение, которое позволяет работать с базой данных
Они не являются заменой друг друга. Могу также посоветовать кроме видеокурсов, где показывают какую кнопку нажимать, читать статьи, где объясняется как что работает.
>>842130
на практике большинство проектов большие. Проекты где "4 строчки на PDO" бывают разве что в учебных курсах.
Композер надо знать, если ты хочешь делать проекты на php. Как и PDO.
>>842130
> шаблоны отделять от логики отображения.
Непраивльно. Шаблоны с логикой отображения внутри надо отделять от логики обработки данных. В примере в верхней половине простая логика работы с данными, а в нижней - шаблон. В реальном приложении это будут 2 отдельных файла.
>>843323
new Twig_Lexer еще может вызвать срабатывание автозагрузчика и обращение к диску, но я что-то сомневаюсь что в этом дело.
>>843362
Сайт, который ты просматриваешь, у тебя на локальном компьютере или на хостинге? хостинги могут добавлять к ответам заголовки вроде "закешировать этот файл на N часов". Решается либо очисткой кеша в браузере, либо отключением, либо разработкой у себя локально.
Надо выяснить сначала, почему так происходит.
>>841862
Если записи в последних 2 колонках имеют разный вид группировки (по должности и по отделу соответственно), то для последней колонки стоит сделать отдельный подзапрос и приджойнить его к основному.
А у тебя подзапрос по моему вообще лишний. Зачем он там нужен? Первые 4 колонки получаются обычной группировкой.
Также, у тебя избыточное условие группировки: GROUP BY dep.department_id, po.post_id - тут хватит одной колонки.
Если что, у нас в ОП посте есть задачки на SQL, чтобы лучше его понимать.
>>842122
Композер - это менеджер пакетов
PDO - это расширение, которое позволяет работать с базой данных
Они не являются заменой друг друга. Могу также посоветовать кроме видеокурсов, где показывают какую кнопку нажимать, читать статьи, где объясняется как что работает.
>>842130
на практике большинство проектов большие. Проекты где "4 строчки на PDO" бывают разве что в учебных курсах.
Композер надо знать, если ты хочешь делать проекты на php. Как и PDO.
>>842130
> шаблоны отделять от логики отображения.
Непраивльно. Шаблоны с логикой отображения внутри надо отделять от логики обработки данных. В примере в верхней половине простая логика работы с данными, а в нижней - шаблон. В реальном приложении это будут 2 отдельных файла.
>>843323
new Twig_Lexer еще может вызвать срабатывание автозагрузчика и обращение к диску, но я что-то сомневаюсь что в этом дело.
>>843362
Сайт, который ты просматриваешь, у тебя на локальном компьютере или на хостинге? хостинги могут добавлять к ответам заголовки вроде "закешировать этот файл на N часов". Решается либо очисткой кеша в браузере, либо отключением, либо разработкой у себя локально.
.htaccess наверно нужен.
>>844390
Надо смотреть заголовки HTTP ответа в отладчике браузера.
>>844408
Я не могу на 100% быть уверен, но возможно обрезается из-за того, что расширение в URL стоит PNG, а Content-Type в HTTP ответе стоит text/html. Возможно из-за противроречия фаерфокс обрезщает имя файла. Или же из-за того, что ты использовал
attachment; filename=' . basename($path))
Это неправильный код, так как в заголовках нельзя использовать кирилицу (и возможно пробелы в имени файла).
И кстати в комментариях к задаче вроде упомянуто, что использовать filename= не надо.
>>844746
Ты можешь помочь, отвечая на какие-то вопросы, которые ты точно знаешь, например про задачи которые ты уже решал, или платиновые вопросы про то, сколько времени надо изучать. Только отвечай без фанатизма, будь доброжелателен и отвечай если ты уверен в ответе.
Там не требуется в задаче соблюдать приоритет. Что касается твоего решения, оно конечно оригинальное, но не очень эффективное, так как выражение многократно перезаписывается. Алгоритмы для разбора математических выражений давно уже придуманы.
Для начала, мы "парсим" (делаем лексический анализ, если говорить умным языком) выражение, преобразуя строку в массив токенов (лексем): "2 + 2 / 2" => ['2', '+', '2', '/', '2'].
Затем, используя алогритм сортировочной станции, мы преобразуем массив токенов в выражение в обратной польской нотации - RPN (это когда знак операции пишут не между операндами, а после): ['2', '+', '2', '/', '2'] => ['2', '2', '2', '/', '+'].
RPN выглядит странно для человека, но с ней алгоритм вычисления получается очень простой, с использованием стека. В отличие от обычного выражения, где надо учитывать приоритеты действий, в RPN мы просто идем слева направо и вычисляем выражение. Есть даже язык программирования Форт, основанный на похожем принципе.
Нам остается только вычилить значение в обратной польской нотации.
Ссылки:
- https://habrahabr.ru/post/100869/
- https://ru.wikipedia.org/wiki/Алгоритм_сортировочной_станции
- https://ru.wikipedia.org/wiki/Обратная_польская_запись
Также, есть еще другой подход. Он применяется, если мы хотим например не вычислить выражение, а как-то его преобразовать (упростить, и тд).
- сначала выражение разбивается на токены
- затем мы проходим по массиву токенов и, учитывая приоритет операций, делаем синтаксический анализ. На выходе мы получим AST = дерево разбора (абстрактное синтаксическое дерево). Это такая конструкция, состоящая из узлов, которые соответствуют числам и операциям:
- http://aliev.me/runestone/Trees/ParseTree.html
- https://ru.wikipedia.org/wiki/Синтаксический_анализ
Простейший способ получить дерево разбора - это алгоитм рекурсивного спуска. Есть и более сложные алгоритмы.
Имея дерево, мы можем вычислять значение выражения или делать над ним любые преобразования.
Компиляторы и интерпретаторы языков программирования (включая php) разбирают текст программы примерно так же - сначала выделяют токены, а потом строят дерево (в случае php это будет большое дерево вида файл -> класc -> определения констант, полей и функций -> список команд -> составляющие команды, части выражений).
Эти алгоритмы давно уже все изучены. Более того, есть генераторы парсеров, где ты описываешь грамматику и получаешь автоматически сгенерированный парсер для нее. То есть если ты захочешь сделать свой язык программирования, парсер текста программы для него можно сгенерировать.
Если тебе захочется больше почитать про алгоритмы, то могу посоветовать такие ссылки:
- http://aliev.me/runestone/
- SICP, перевод на русский: http://newstar.rinet.ru/~goga/sicp/
- гуглить "лексический и синтаксический анализ"
- книга "Ахо А., Сети Р., Ульман Д. Компиляторы. Принципы, технологии, инструменты." (довольно сложная)
.htaccess наверно нужен.
>>844390
Надо смотреть заголовки HTTP ответа в отладчике браузера.
>>844408
Я не могу на 100% быть уверен, но возможно обрезается из-за того, что расширение в URL стоит PNG, а Content-Type в HTTP ответе стоит text/html. Возможно из-за противроречия фаерфокс обрезщает имя файла. Или же из-за того, что ты использовал
attachment; filename=' . basename($path))
Это неправильный код, так как в заголовках нельзя использовать кирилицу (и возможно пробелы в имени файла).
И кстати в комментариях к задаче вроде упомянуто, что использовать filename= не надо.
>>844746
Ты можешь помочь, отвечая на какие-то вопросы, которые ты точно знаешь, например про задачи которые ты уже решал, или платиновые вопросы про то, сколько времени надо изучать. Только отвечай без фанатизма, будь доброжелателен и отвечай если ты уверен в ответе.
Там не требуется в задаче соблюдать приоритет. Что касается твоего решения, оно конечно оригинальное, но не очень эффективное, так как выражение многократно перезаписывается. Алгоритмы для разбора математических выражений давно уже придуманы.
Для начала, мы "парсим" (делаем лексический анализ, если говорить умным языком) выражение, преобразуя строку в массив токенов (лексем): "2 + 2 / 2" => ['2', '+', '2', '/', '2'].
Затем, используя алогритм сортировочной станции, мы преобразуем массив токенов в выражение в обратной польской нотации - RPN (это когда знак операции пишут не между операндами, а после): ['2', '+', '2', '/', '2'] => ['2', '2', '2', '/', '+'].
RPN выглядит странно для человека, но с ней алгоритм вычисления получается очень простой, с использованием стека. В отличие от обычного выражения, где надо учитывать приоритеты действий, в RPN мы просто идем слева направо и вычисляем выражение. Есть даже язык программирования Форт, основанный на похожем принципе.
Нам остается только вычилить значение в обратной польской нотации.
Ссылки:
- https://habrahabr.ru/post/100869/
- https://ru.wikipedia.org/wiki/Алгоритм_сортировочной_станции
- https://ru.wikipedia.org/wiki/Обратная_польская_запись
Также, есть еще другой подход. Он применяется, если мы хотим например не вычислить выражение, а как-то его преобразовать (упростить, и тд).
- сначала выражение разбивается на токены
- затем мы проходим по массиву токенов и, учитывая приоритет операций, делаем синтаксический анализ. На выходе мы получим AST = дерево разбора (абстрактное синтаксическое дерево). Это такая конструкция, состоящая из узлов, которые соответствуют числам и операциям:
- http://aliev.me/runestone/Trees/ParseTree.html
- https://ru.wikipedia.org/wiki/Синтаксический_анализ
Простейший способ получить дерево разбора - это алгоитм рекурсивного спуска. Есть и более сложные алгоритмы.
Имея дерево, мы можем вычислять значение выражения или делать над ним любые преобразования.
Компиляторы и интерпретаторы языков программирования (включая php) разбирают текст программы примерно так же - сначала выделяют токены, а потом строят дерево (в случае php это будет большое дерево вида файл -> класc -> определения констант, полей и функций -> список команд -> составляющие команды, части выражений).
Эти алгоритмы давно уже все изучены. Более того, есть генераторы парсеров, где ты описываешь грамматику и получаешь автоматически сгенерированный парсер для нее. То есть если ты захочешь сделать свой язык программирования, парсер текста программы для него можно сгенерировать.
Если тебе захочется больше почитать про алгоритмы, то могу посоветовать такие ссылки:
- http://aliev.me/runestone/
- SICP, перевод на русский: http://newstar.rinet.ru/~goga/sicp/
- гуглить "лексический и синтаксический анализ"
- книга "Ахо А., Сети Р., Ульман Д. Компиляторы. Принципы, технологии, инструменты." (довольно сложная)
> if ($billsRequired <= $billsInATM) {
> return $billsRequired;
> } else {
Тут можно просто использовать min/max
Решено верно.
> "#\R#u
По моему это неудачный выбор. В windows конец строки обозначается 2 символами \r\n, в линуксе - только \n. Таким образом, лучше разбивать по \n.
Удалить оставшийся \r можно через trim().
> $lengths = array_map(function ($match) {
> return mb_strlen($match);
> }, $matches);
array_map('mb_strlen', $matches)
Название функции является чем-то вроде указателя на нее:
- http://php.net/manual/ru/language.types.callable.php
- https://habrahabr.ru/post/259991/
Решено верно.
> (count($order) > $skip - 1)
можно написать еще как count(..) >= $skip
> $temp_array = array_merge(array_slice($order, $skip - 1), array_slice($order, 0, $skip - 1));
Чтобы вырезать элемент из середины, есть array_splice. Чтобы перенумеровать индексы по порядку, есть array_values.
Считает верно.
>>844883
Загугли кусок кода из статьи: https://www.google.ru/search?q=class+Asset+implements+ContainsRecordedMessages&newwindow=1&gbv=1&sei=UWP0V6GdB8nIsQGK6aLgDA
http://programmingarehard.com/2015/03/04/structing-my-application.html/
>>846581
Можно посмотреть проекты из раздела trending:
https://github.com/explore
https://github.com/trending/php
Но не факт что там будут простые проекты. Без фреймворков смысла делать сайт не вижу, разве что в целях изучения основ написания приложений.
Или вот например - создание блога на Симфони: https://habrahabr.ru/post/301760/
Но вообще, тебе надо бы начинать с основ, изучения ООП и MVC, у нас есть задачки на обе этих темы в ОП посте. По ООП есть глава в моем учебнике.
> if ($billsRequired <= $billsInATM) {
> return $billsRequired;
> } else {
Тут можно просто использовать min/max
Решено верно.
> "#\R#u
По моему это неудачный выбор. В windows конец строки обозначается 2 символами \r\n, в линуксе - только \n. Таким образом, лучше разбивать по \n.
Удалить оставшийся \r можно через trim().
> $lengths = array_map(function ($match) {
> return mb_strlen($match);
> }, $matches);
array_map('mb_strlen', $matches)
Название функции является чем-то вроде указателя на нее:
- http://php.net/manual/ru/language.types.callable.php
- https://habrahabr.ru/post/259991/
Решено верно.
> (count($order) > $skip - 1)
можно написать еще как count(..) >= $skip
> $temp_array = array_merge(array_slice($order, $skip - 1), array_slice($order, 0, $skip - 1));
Чтобы вырезать элемент из середины, есть array_splice. Чтобы перенумеровать индексы по порядку, есть array_values.
Считает верно.
>>844883
Загугли кусок кода из статьи: https://www.google.ru/search?q=class+Asset+implements+ContainsRecordedMessages&newwindow=1&gbv=1&sei=UWP0V6GdB8nIsQGK6aLgDA
http://programmingarehard.com/2015/03/04/structing-my-application.html/
>>846581
Можно посмотреть проекты из раздела trending:
https://github.com/explore
https://github.com/trending/php
Но не факт что там будут простые проекты. Без фреймворков смысла делать сайт не вижу, разве что в целях изучения основ написания приложений.
Или вот например - создание блога на Симфони: https://habrahabr.ru/post/301760/
Но вообще, тебе надо бы начинать с основ, изучения ООП и MVC, у нас есть задачки на обе этих темы в ОП посте. По ООП есть глава в моем учебнике.
Это копия, сохраненная 6 октября 2016 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.