Добро пожаловать в наш уютный тред. Тут мы изучаем язык PHP (а также JS/CSS/HTML/SQL), решаем задачки и даже делаем простые сайты! Зачем? Кто-то хочет научиться программировать, кто-то - делать сайты, кто-то - просто размять мозги и заняться чем-то полезным.
Это не чат! Также, перед Новым годом ОП довольно занят, а на праздниках будет появляться нечасто.
Это тред для начинающих. Не написал за свою жизнь ни одной программы и имеешь тройку по математике? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Netbeans PHP или PhpStorm (с ним будет удобнее).
Что самое главное для программиста? Умение аккуратно оформлять код (читай второй пост, прежде чем писать код).
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП заходит редко, где-то раз в 2-3 дня, у него мало времени, не жди его, решай задачки дальше. ОП отвечает на все вопросы по его задачкам и учебнику, а вот насчет каких-то других вещей - только если останется время. Но в треде немало анонимных экспертов разного уровня, так что вряд ли вопрос останется без ответа.
У нас есть уроки по основам PHP, они собраны и выложены по адресу http://archive-ipq-co.narod.ru/ Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь, то надо начать с него. Он простой и понятный (по крайней мере в начале). Там есть задачи, их надо решать обязательно (чтобы стать программистом, надо писать код — иначе никак). Пости ссылки на решения в тред, мы их проверим, напишем замечания и дадим советы по улучшению.
Если не знаешь как решать, запости код, напиши в каком месте остановился и попроси подсказку.
Ты прошел весь учебник? Молодец, но это были лишь основы языка PHP, этого недостаточно. Вот что в идеале надо изучить еще: ООП, как работает веб-сервер, HTML/CSS, SQL, PDO, работа с таблицами в БД, работа с формами, MVC, git, composer, JS, фреймворки, автоматизированное тестирование.
Надо переходить к более серьезным задачкам, которые научат тебя всему этому.
Решения задач лучше показать мне, особенно на ООП,так как сам ты вряд ли увидишь все ошибки. Пости свой код на гитхаб и вкидывай ссылку в тред по мере решения. Я прокомментирую и укажу на ошибки.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания JS/HTML/CSS/SQL. Решай их параллельно с задачами выше.
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
У ОПа нет аккаунтов и групп вконтакте, в фейсбуке, в твиттере, все "пхп-треды" там поддельные.
Платиновые вопросы
- Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко. - Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета) - Что надо знать чтобы найти работу - разработчику: PHP, SQL, HTML/CSS, JS, ООП, Git, композер, MVC, фреймворк. Верстальщику - HTML/CSS, JS, jQuery - Можно подробнее про поиск работы, собеседования - нет, ОП писать не будет, но может кто из анонов захочет рассказать. Поищите тред перезвонивших, а также раздел /wrk/. - Сколько времени надо изучать все это? - все зависит от тебя, но не меньше 6-8 месяцев - Посоветуйте редактор кода - Sublime Text 3, Notepad++, PhpStorm - Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию. - Что самое главное для программиста? Умение аккуратно оформлять код. - ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет. - Подскажи сайты для поиска работы, я не умею гуглить? — hh.ru, geekjob.ru, moikrug.ru (склеен с brainstorage.me), fl.ru, upwork.com (бывший одеск). Имей в виду, что кроме фриланса есть еще постоянная удаленная работа (remote job) когда тебе не надо тратить время на поиск заказов и переговоры с неадекватными заказчиками.
Код нужно писать не как попало, а аккуратно и по правилам. Почему? Потому, что на неакуратно написанный код не хочется даже смотреть.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Вообще, в 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 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?» Не годится: «В гугле забанили?» Не годится: «Твой код плохой» Хорошо: «Вот, как можно улучшить этот код: ...» Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского или русского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде» Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)» Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
>Изучив теорию, советую ответить на вопрос: чему будет равен (или на какой объект будет указывать) this внутри функции в следующих случаях: >1) >function x() { }; x(); this будет в контексте объекта-функции x() >2) >var z = {}; >z.test = function () {}; >z.test(); this будет в контексте объекта z >3) >var z = {}; >z.test = function() {}; >var fn = z.test; >fn(); this будет в контексте объекта fn, т.е. функции-объекта z.test
Ещё можно добавить четвертый пример: 4) var z = {}; z.test = function() {}; var fn = z.test.call(z); fn(); В котором this будет в контексте объекта z
>Лучше не ужасаться, а написать, что именно непонятно, и попросить совет. В нашем треде обычно всегда что-нибудь посоветуют. Нужно стараться решать самому
>>897643 >По поводу скуки. Эти задачи - это основы, которые надо пройти, чтобы перейти к более интересным вещам вроде добавления интерактивных элементов на страницу. Если не понимать, как работают замыкания, как определяется this, то сложно будет разбраться в коде, где это используется. >Если тебе не очень нравятся абстрактные задачи, ты ведь знаешь HTML, ты можешь попробовать начинать параллельно делать что-нибудь посложнее, можно игру в сапера, можно игру "арканоид" ( https://gist.github.com/codedokode/9933897 ) или, например, какое-нибудь простое приложение, если тебе не нравятся игры (например, написать простую электронную таблицу вроде экселя). Главное, чтобы это было на какую-то интересную тебе тему. Мне интересно как делается динамическая подгрузка контента. Думаю, я мог бы сделать страницу с простым чатиком в задаче с Студентами, но я не знаю с чего начать. Это же ajax или уже есть что-то получше?
Управление элементами html, я думаю, это что на уровне css если нет, то я разберусь и с этим, так что пока я не буду на этом заострять внимание, но до этого тоже дойдет.
>>897643 >Что касается задачи: > >> var args = [].slice.call(arguments); >> args.shift(); >Было бы хорошо, если бы ты понял, как сделать это без shift(). call тут по сути вызывает функцию [].slice, подсовывая в this ей вместо массива переменную arguments. Надо подумать, как тут передать в эту функцию еще и аргумент, чтобы она сделала копию, начиная с 1-го, а не с 0-го элемента. > >> for(var i = 0; i < args.length; i++) { >> oldArgs = args; >Скопировать массив можно методом slice: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/slice Забыл исправить замечания... https://jsfiddle.net/92nc4fj2/1/
>>function x() { }; x(); > this будет в контексте объекта-функции x() Что значит "будет в контексте"? Не очень понятная фраза. Давай попробуем проверить экспериментально. Открой отладчик браузера и введи туда
function x() { console.log(this); } x();
И посмотри, что выведется в консоль.
Аналогично и с другими примерами.
Это все-таки основы языка, без них трудно будет что-то писать.
> Нужно стараться решать самому Да, верно, нужно самому гуглить, искать документацию, так как например на работе никто не будет за тобой по пятам ходить и решать проблемы за тебя. Но если ты не смог сам найти решение, то наверно смысла дальше ждать нет. Можно попросить подсказку или спросить, в каком направлении дальше гуглить. Мы тут все-таки не злодеи, поможем, тем более если человек сам старается найти решение.
>>899034 > console.log(target); > var s = getComputedStyle(target); Этих строк у меня не должно быть в конечном варианте. Не хочет jsfiddle сохранить мой отредактированный код.
В пыхе ведь нет хуитки типа $prop { get { return $this->prop; } set { $this->prop *=2; } } Т.е. можно всё хуярить через конструктор, ну или на крайняк магию, но мне это кажется не шибко элегантным способом. Идея в том, что бы свойство получало значение только если к нему обращаются.
>>899466 Не совсем. Короче делаю я допустим свойство length, которое равно strlength($this->str). Но я хочу, что бы значение свойства расчитывалось только если к нему обращаются. Такое можно сделать?
>>899481 >Лучше сделай православную функцию-геттер. GetLength(), и всё. А данные спрячь в private. Да я понимаю. Просто я хочу немного потренироваться в ООП, до этого немного читал c#. Просто там намного удобней получать некоторые данные, типа сделал строку да и получай себе ее длинну через точку, там свойство само просчитывается. Хочу типа такой класс сделать, который по факту строка, а все методы для работы со строками были бы включены в него, лол. Алсо, почему функция strlen определяет длинну одного кирилического символа = 2, а символ латиницы равно 1?
>>899488 >Алсо, почему функция strlen определяет длинну одного кирилического символа = 2, а символ латиницы равно 1? strlen возвращает длинну в байтах. Кириличный символ кодируется 2 байтами (в utf-8). Для мультибайтовых кодировок используют mb_ функции. mb_strlen() в твоем случае. В шапке, в учебнике ОПа, в статье про строки есть шпаргалка.
>>899503 Сишная функция strlen тоже возвращает количество байт, для тебя и Си - "хуита", байтоёб? Юникод это больное место скриптовых языков, у руби например до 2007-го года не было его поддержки. Алсо всё самое уродливое в PHP (проблемы с юникодом, функции возвращающие false вместо выбрасывания исключений) унаследованы от Си. Для поддержки юникода в PHP давно используются mb_* функции, для исключений - ООП обёртки.
Не надо паттерн из других языков тащить в PHP. Вот сделают у нас такие свойства - тогда и будешь использовать. А пока поддержки на уровне языка нет, это только усложняет код без надобности.
> хочу немного потренироваться в ООП Это к ООП мало отношения имеет. В ООП принято для вычисляемых величин использовать методы, свойства из C# это по сути просто синтаксический сахар для вызова метода.
> Хочу типа такой класс сделать, который по факту строка, а все методы для работы со строками были бы включены в него, лол Хорошая идея, сделай, я прокомментирую. Я уже на старте вижу несколько принципиальных вопросов:
- будет ли объект иммутабельным или нет? - будет ли для строки использоваться какая-то кодровка или это будет набор байт?
Тут однозначного ответа нет, в каждом случае есть свои преимущества и недостатки.
Поэтому будет полезно, если ты поломаешь себе голову и попробуешь спроектировать класс для строк (хотя на практике я не вижу особого смысла его использовать конечно, только потери в производительности).
> Алсо, почему функция strlen определяет длинну одного кирилического символа = 2, а символ латиницы равно 1? Потому что она считает длину в байтах, а не в символах. В PHP строка это набор байт, а не символов. Подробнее в моем уроке: https://github.com/codedokode/pasta/blob/master/cs/strings.md
Ну так в PHP тоже есть mb_strlen. Ты мануал или мой урок хотя бы открой и прочитай прежде чем критиковать. В Си strlen считает байты, почему она в PHP должна что-то другое считать?
Кстати, Майкрософт героически внедрила utf-16 в винду аж в 1995 году по моему. Им пришлось сделать по 2 экземляра каждой системной функции.
Ну например CreateFileA создает файл и принимает имя файла в 8-битной системной кодировке (win-1251 для российской версии винды), а CreateFileW - в utf-16
Но позже в Юникод начали добавлять новые символы и 16 бит уже стало не хватать.
В линуксе никто не захотел заморачиваться и там используется другой подход, такой же как в PHP: любые строки рассматриваются как последовательности байт. Системный вызов вроде fopen никак не интерпретирует имя файла, а просто передает его файловой системе. Что, если записать файл в одной системе и подключить жесткий диск к системе с другой кодировкой?
>>899566 >будет ли объект иммутабельным или нет? Иммутабельным - это как? >хотя на практике я не вижу особого смысла его использовать конечно, только потери в производительности Это да. Просто сам язык было бы проще учить, если все хотя бы самые необходимые функции были бы как-то инкапсулированы в один класс, или например если строка это объект, то легче к примеру вычленять из него отдельные символы и т.д. Но да, такое точно будет потреблять больше ресурсов.
>>899572 Ты из тех самых поехавших, у которых векторы - это связанные списки? От того, что ты не используешь оверлоад, у тебя ucfirst для utf-8 не прибавится.
Иммутабельные объекты нельзя изменять, при любых операциях создается новый объект. Мутальные - можно.
У иммутабельных объектов есть преимущества и недостатки:
- иммутабельные объекты создаются при любом изменении, больше расход ресурсов. Особенно неэффективно, например, циклом менять строку по 1 символу - каждый раз создается новый объект - с другой стороны, иммутабельность позволяет для 2 одинаковых строк не создавать 2 объекта, а использовать один и тот же: $a = $b = new String(..). В случае с мутабельными объектами так делать нельзя, так как при изменении строки в одной переиенной она поменяется и в другой. - с другой стороны, ты знаешь, что если ты передал куда-то такой объект, никто его не поменяет. Это позволяет так же иногда делать какие-то оптимизации. Ну например если ты взял из объекта длину строки, ты можешь сохранить ее и не бояться что длина поменяется. - в многопоточных программах параллельный доступ из нескольких потоков к таким объектам не требует блокировки (в случае мутабельных объектов в один момент времени с ним должен работать только один поток и нужна блокировка) - очень легко проверить, что строка изменилась с помощью $s1 !== $s2: если строка изменилась, то там будет другой экземпляр объекта. В случае мутабельных объектов, нам надо сохранять копию объекта и позже сравнивать каждое свойство. Вот пример кода:
$s1 = new String(...); $s2 = someFunction($s1); if ($s2=== $s1) { echo "Строка не изменилась"; } else { echo "Возможно, это другая строка"; }
- ну и иногда логика работы программы подразумевает, что объект неизменяемый. Например, банковские транзакции или записи в реестре собственности на имущество - их должно быть нельзя поменять. - в некоторых паттернах программирования, в асинхронном программировании иногда наличие иммутабельных объектов делает программу более надежной, так как мы знаем, что никто случайно их не поменяет в промежутке между асинхронными операциями.
Ну вот пример где пригодилсиь бы иммутабьельные объекты. Допустим, у нас есть приложение на JS и в нем объект-модель, и мы передаем эту модель в функцию работы с API, чтобы она сохранила ее на сервер. Функция разумеется асинхронная, то есть она добавляет модель в очередь на сохранение и возвразщается, а когда именно она будет отправлена на сервер - неизвестно. И есть такой код:
var user = new UserModel(...); saveToServer(user); ...
Вот здесь, если user мутабельный, то есть риск что код ниже поменяет его и на сервер отправятся не те данные, которые мы передали в saveToServer. Потому мы должны либо делать копию, либо использовать иммутабельную модель, которую мы разумеется назовем не UserModel, а UserState.
>>899581 В общем-то кажись понял. Ну, мой тогда наверное будет мутабельным - как бы строки часто надо конкатенировать. Но тут я даже не знаю как сделать - самый простой сделать метод, типа concat($str) { $this->string += $str } но тут падает юзабельность, т.к. не выйдет просто две строчки склеивать. А в идеале я хочу, что бы объект можно было бы использовать просто как обычную переменную. Вот распечатывать ее легко, сделал _toString и все.
Если ты сам будешь делать класс-строку, то да, будет тратиться больше ресурсов. Если бы она была сделана на уровне самого языка, как в JS, то ее можно было бы сделать оптимизированной.
>>899588 Мне вот собственно интересно, почему такого не сделали? Вот есть же PDO например. Я думаю стильность-модность-молодежность языка сразу же повысилась бы, плюс не обязательно же заставлять всех это использовать, просто добавить как фичу.
>>899582 Я взглянул на этот текст, там какой-то поехавший обращается ко мне на ты и расписывает налево-направо, что хорошо, а что плохо, как будто что-то знает. А сам в соседнем треде не может отличить связанный список от вектора.
Только если для ознакомления. Нейронные сети - это все-таки задачи на обработку больших объемов данных и лучше всего реализуются на языках вроде Си. Насколько я знаю, есть готовые библиотеки, но не знаю, можно ли их подключить из PHP. Обычно там используют сишную библиотеку и управляющий скрипт на скриптовом языке вроде Питона.
Да, только тут есть один момент. У DOM элемента внутреннее содержимое можно получить 2 способами:
- node.innerHTML (долгое время был нестандартным и работал только в ИЕ, потом вроде стандартизовали) - node.textContent
Надо сравнить, как работают эти 2 свойства в разных браузерах при условии что в шаблоне содержатся теги, переносы строк и различные спецсимволы вроде & gt ;
Также важно помнить, что теги script и style имеют немного другое правило обработки содержимого: весь текст в них воспринимается как есть, html-мнемоники и теги не интерпретируются, в отличие от содержимого обычных элементов вроде div.
http://ideone.com/UoEV5H Таки сделал небольшой набросок класса для строк. Только не смогу решить проблемку с выводом неправильной кодировки в методе charAt, ОПчик, расскажи позяз как это делается.
> $chars = preg_split("//u", $this->string, null, PREG_SPLIT_NO_EMPTY); > return $chars[$c]; Дороговато каждый раз создавать огромный массив символов, чтобы взять из него один. Лучше бы mb_substr использовать.
И еще, //u работает только со строками в utf-8. У тебя нигде не написано, в какой кодировке строку надо передавать в конструктор.
Ты должен определиться, как ты будешь воспринимать строки: как набор байт, как строка в жестко заданной кодировке, как строка, где кодировка указывается отдельно. Иначе нельзя гарантировать, что код работает корректно, если он не знает, в какой кодировке строка.
У аргумента в конструкторе надо бы сделать пустое знаечние по умолчанию.
Также, я бы тебе советовал изучить, как реализованы объекты строк в других языках, например:
>>899682 >как строка в жестко заданной кодировке Хорошо, буду делат в utf-8 по умолчанию с возможностью сменить кодировку. >У аргумента в конструкторе надо бы сделать пустое знаечние по умолчанию. А это зачем?
Конструктор такая же функция и у нее могут быть значения аргументов по умолчанию. И давай-ка вместо маленьких постов каждые 5 минут писать большие со списком вопросов.
>>899705 http://ideone.com/UoEV5H Ну сегодня пока родил вот это вот чудо. Тогда вопросы, лол. Iconv громоздкая функция, зачем определять изначальную кодировку строки?
Ты используешь mb_detect_encoding. Как по твоему она работает?
Если ты читал мой урок, то знаешь что строка в памяти хранится как последовательность байт. Вот у тебя есть строка из 2 байт - 194 185 - как определить, в какой она кодировке? В одной кодировке это будут одни символы, в другой - другие. Как понять, какая была использована при кодировании строки?
> public function __construct() Почему не __construct($string = '') ?
> changeEncod Не надо так сокращать. И в чем смысл этой функции? Вот я создам строку с utf-8, сконвертирую ее в windows-1251 и что дальше? Твой класс будет работать корректно? charAt() например?
По моему тебе надо разрбраться, что такое кодировки и как строка кодируется в памяти.
> $this->string += $param; += это числовое сложение, а не строк
Пишу свой фреймворк, храню данные о текущем используемом контроллере и экшене в свойствах главного контроллера, в виде MainController и IndexAction, то есть уже с приставками, которые добавляются в самом начале при парсинге адресной строки. Дошёл до загрузки файла вида, передают в класс главного вида фреймворка данные из контроллера для того, чтобы создать путь к подгружаемому файлу с видом, который находится по адрессу:
application/views/[контроллера без приставки]/[action без приставки]
В итоге у меня такая проблема, например, для пути нужен main, а у меня в свойствах содержится MainController, будет правильно обрезать приставку Controller специальной функцией? Или же лучше создать специальные свойства у главного контроллера где хранить имя контроллера и экшена без приставов? Спрашиваю потому, что если воспользоваться первым вариантом, то получается что вначале получаешь имя контроллера из строки, потом дописываешь Controller, используешь его, а потом заново режешь, как-то не логично, лишнее действие. Если использовать второй вариант, то как-то тоже не логично, зачем плодить свойства у главного контроллера. Единственное что более менее логично выглядит это изначально хранить исходные данные в массиве без приставок, но так придётся плодить в контроллере много лишних переменных для временного значения.
>>899893 Сделай класс Router, который будет парсить строку и методами выдавать имена с приставками и без приставок. И в контроллере храни объект этого класса. Например.
Продолжаю писать фрейворк, и возник вопрос как правильно использовать модель? Я в главной моделе фреймворка делаю два параметра $pdo (объект подключения к базе данных) и $table(текущая таблица БД), в её конструкторе заполняют pdo через синглетон, который находится в специальном отдельном классе Db. Далее в основной моделе описывают методы для работы с БД, типо findAll, findOne и т.д. В итоге, модель на стороне разработчика наследует главную модель и получает таким образом методы для работы с БД, а также само собой соединение с БД. Также через эту модель можно переопределить таблицу в базе данных. Казалось, всё бы нормально, но возникает проблема, при реальном использовании этой конструкции, в контроллере создаётся объекта класса модели, чтобы получить возможность использовать методы для работы в базе данных, в итоге и основной код, вроде манипуляции с различными данными из базы данных будет осуществляться в контроллере а не в моделе. Как сделать иначе? Создать в модели дополнительные свойства, в которых хранить определённые результаты манипуляция и получить к ним доступ из контроллера? Но это кажется как-то неправильно, да и откуда знать, сколько будет получено результатов и сколько свойств создавать в моделе? В итоге в моём фреймворке, на текущий момент работа с контроллером будет выглядеть так:
class Main extends Application {
public function indexAction() {
$model = new Model();
$posts = $model->findAll();
//делаем что-нибудь с переменной posts
//передаём переменные в вид $this->set("posts", "title_post");
}
}
Меня в общем смущает, что в контроллере будет много кода, а в моделе его практически не будет вообще
>>899904 Модель вообще не должна иметь публичных методов для доступа к базе данных. Вместо этого можно определить в ней пару конкретных методов типа getPosts(), возвращающие результат, удобный для отображения. Потом вызывать их из контроллера, а еще лучше прямо из представления. А в контроллере обрабатывать ввод.
там в следующей задаче написано про многопоточность, я что-то не нашел ничего внятного про многопоточность на пхп, только открытие нескольких процессов вручную (зачем?).
>>899904 Не понял чего ты хочешь от своей модели добиться. Модель это не какой-то конкретный класс, который умеет делать все, это набор классов для работы с данными. Если ты уж хочешь сделать некую модель, в которой будут базовые методы для работы с таблицами типа findAll(), insert() и т.д., короче CRUD, то сделай абстрактный класс с этой с этими методами, а уже от него наследуй нужные тебе вещи. Например, у тебя есть абстрактный класс Model с методами CRUD, и тебе надо сделать модель для работы со статьями, тогда просто делай класс Articles и наследуй его от Model. Правда я сам ньюфаг, но делал бы так, может ОП поправит меня.
Продолжаются вопросы по поводу фреймворка. Дошёл до подключения стилей и скриптов. Как лучше это реализовать? Пока что имею такое предположение. Создать например специальный метод в главном View фреймворка (или вообще создать отдельный класс), который будет принимать 3 параметра ИМЯ_ШАБЛОНА(layout), ВИД_ПОДКЛЮЧЕНИЯ (CSS, JS и т.д.), АДРЕС_ПОДКЛЮЧЕНИЯ. В итоге можно создать вообще отдельный файл например в папке конфиге, в котором можно будет заносить данные для подключений. Сам же метод надо будет вызывать в каждом layout, достаточно просто вызывать его не передавая никаких параметров, он уже сам будет решать куда что подключить, при помощи перебора свойств, если например КСС то вверх, если JS то в футер. В правильном направлении мыслю?
>>900367 Какая-то сложная конструкция. View обычно получает имя темплейта, плюс путь к css и js файлам, view рендерит темплейт, передает ему путь к css и скриптам, те грузятся из темплейта. Пути к css, js и темплейтам можно в ini хранить, да.
>>900508 Алсо места где какой скрипт и css грузить, обычно намертво в темплейте забиты, подставляются только пути к директориям на серваке, полученные от view.
Знаю, что тред посвящен php, но как для новичка, можете подсказать, кто-то использовал для изучения html и css сайтик htmlacademy, есть ли смысл в этом курсе? Либо же лучше учить тэги на htmlbook и практиковаться попутно?
>>900525 Htmlacademy хорошо зайдет для изучение базы и основ, дерзай. Для более серьезного изучения конечно же придется обмазываться изучениям доп.литературы. Алсо не научишься ты тупо читая теги.
Давно не использовать регулярные выражения, поэтому торможу, задача предельно проста. Имеется строка, которая должна содержать только буквы латинского алфавита и разделена неизвестное количество раз одним символом, например "&", то есть подходят строки такого вида:
И прочие подобные им. Количество символов может быть любым, наличие & не обязательно, как например в третьем варианте. Как проверить соответствие такой строки через регулярные выражения?
Как работать с PostgreSQL из под PHP? Какие нужны предварительные установки? Сижу сейчас читаю на php.net инструкцию по настройке связи с PostgreSQL, и уже на первом мануале обосрался: Для того, чтобы включить поддержку PostgreSQL необходимо скомпилировать PHP с директивой --with-pgsql[=DIR] . Параметр DIR определяет путь к установочной директории PostgreSQL, скомпилировать с директивой - это как? установочная директория - папка, где все файлы, связанные с postgresql лежат, или путь к папке с .exe, который как бе и есть сам сервер?
Алсо, сейчас хостую демонстрационный сайт через XAMPP. Хватит ли мне его, чтобы взаимодействовать с PostgreSQL, или нужно ставить специально сконфигурированный под него апач?
Продолжаются вопросы по написанию фреймворка. Я смотрю на его структуру и уже написанные и работающие классы и методы и замечаю такую проблему. Я не делал класса Router, решил сделать всё через метод route в главном контроллере. В итоге, главный контроллер фреймворка в моём случае выполняет следующие функции: - В конструкторе парсит строку, заносит в свойства класса значение контроллера, экшена и параметров - В методе route вызывает запрашиваемый контроллер и его метод. - После вызова текущего контролера и его метода, вызывает специальный метод главного контроллера getView, в котором создаётся объект класса главного View и уже в нём вызывает функция render с передаваемыми параметрами. В итоге, большая часть всех функций у меня возложена на главный контроллер, это правильно вообще? Думал создать класс Router и переложить в него часть функций, но не вижу смысла передавать туда сюда одни и те же параметры постоянно, мне кажется что так как сейчас удобнее.
> `author` varchar(255) NOT NULL DEFAULT 'anonymous' Лучше по умолчанию записывать null, а при выводе заменять. А то не отличить тех, кто не ввел имя, от тех, кто ввел anonymous.
Файлы настроек
У структуры кода, которая используется (с вынесенем файлов settings.php, dependencies.php), я вижу такой недостаток:
- нет bootstrap-файла. Если ты например захочешь добавить скрипт командной строки, и в нем исплоьзовать контейнер и настройки, то придется копипастить код создания $app из index.php.
Мне бы больше понравилась такая структура: файл bootstrap.php, который инициализирует приложение, задавая настройки и сервисы в контейнере. А роуты можно прописать либо в отдельном файле либо в самом index.php.
Как я понимаю, эту схемы ты не сам придумал, а взял из шаблона приложения на Slim (Slim-Skeleton). Я даже писал авторам этого шаблона, высказав те же мысли, но они написали, что их такая схема устраивает.
Также, в settings.php очень много настроек. Возможно (пользователям) было бы удобнее, если бы настройки, которые пользователь может менять, были бы вынесены в один конфиг, а которые не может - в другой. В более сложном приложении получится очень большой конфиг, в котором будет трудно разобраться. В Симфони например эта проблема решается тем, что есть файл конфига config.yml, и есть файл с изменяемыми параметрами parameters.yml.
В твоем случае изменяемые настройки - это например имя базы данных. А неизменяемые - это например драйвер базы данных или template_path.
Соответственно, неизменяемые параметры конфига вроде settings.resize, или templates_path, если они используются только 1 раз, можно даже прописать прямо в контейнере. А можно конечно и в конфиге.
Не очень хорошо, что экземпляр request используется в контейнере.
Сервисы - это классы, которые обычно существуют в одном экземпляре, и не представляют какую-то сущность, а содержат полезные методы. И обычно сервисы не должны иметь в зависимостях HTTP запрос, так как в теории они могут служить для обработки нескольких запросов. Или их можно вызывать из скриптов командной строки, где никакого запроса нет. У тебя twig нельзя использовать в скрипте для командной строки, так как там нет объекта request. А необходимость может быть, например, чтобы сгенерировать письмо по twig-шаблону.
Кстати, такие проблемы много где есть. В Симфони роутер (и генератор URL) в качестве зависимости получает HTTP-запрос. Если его вызвать из скрипта командной строки, он может генерировать URL вида http://localhost/... (так как он привык брать имя домена из заголовка запроса HTTP_HOST, а тут его нету). Это указывает на ошибку в проектировании (или в настройке) генератора URL в данном случае.
> $capsule->setAsGlobal(); Вот тут ты делаешь экземпляр объекта доступным через статические методы (Capsule::table(..)). Но тут есть проблема, что мы не знаем точный момент, когда будет выполнена эта строчка (setAsGlobal), и, соответственно, не можем сказать, с какого момента можно использовать статические методы. Она ведь не сработает, пока кто-то не запросит экземпляр $container->db. Эту строку надо было помещать не в контейнер, а после него, чтобы объект был гарантированно создан в начале программы:
$container->db->setAsGlobal();
Либо же мы бы могли сделать функцию $container->initDatabase() и вызывать ее перед созданием сервисов, которым нужна база данных.
А сейчас у тебя все работает с расчетом на то, что кто-то создаст сервис db до того, как попытается воспользоваться какими-то методами Eloquent. Это ненадежно, надо явно создать нужный объект.
Вот этот код очень нелогичный:
> $container['filesModel'] = $container->factory(function ($c) { > $c->db; > return new App\Models\Files; > }); Какой смысл в строчке $c->db? Никакого. Ты видимо полагаешься на побочные эффекты (вызов setAsGlobal) от создания сервиса db. Побочные эффекты это плохо, и они неочевидны при чтении кода. Не надо так, это как раз то, с чем борется dependency injection. Если бы сервис принрмал $db как аргумент, было бы так:
return new App\Model\FilesTable($c->db);
Но Eloquent вроде не позволяет передать объект БД в модели. Их схема с статическими методами не очень хорошо сочетается с DI контейнером. Это в общем-то проблема паттерна Active Record, потому что там возможен такой код:
$user = new User; $user->save();
Или такой: $users = User::all();
И тут непонятно, где User берет соединение с БД. Эту проблему теоретически можно решить, если явно передавать объект БД в конструктор (new User($db)) или если создавать объекты не самому ($user = $db->createUser()), но обычно Active Record получает объект БД через статические методы со всеми вытекающими последствиями. Как я понимаю, причина в том, что laravel скопирован с ruby on rails, а там хотят минимум настроек и конфигурации и потому они хотят использовать статические методы вместо DI.
И еще, как я понимаю, в Eloquent объект App\Model\Files одновременно представляет и одну запись из базы, и объект, позволяющий делать операции над таблицей. И вдобавок еще можно использовать статические методы вроде Files::take(10). Потому код выглядит не очень логично: где-то ты получаешь Files из контейнера, а где-то создаешь через new.
Насчет контроллеров, я вижу, там много зависимостей, иногда для упрощения в конструктор контроллера просто передают сам контейнер. Но можно и сервисы по отдельности, конечно.
> foreach ($files as &$file) { > $file->downloadUrl = $this->router->pathFor('downloadFile', array( Это в общем плохая идея, добавлять свойства в объекты. Объекты тем и хороши, что они относятся к какому-то классу, и можно посмотреть, какие у них есть свойства. И ты знаешь, что если у тебя объект класа X, то у него есть свойства a, b, с. А если добавлять свойства динамически, то появляется путаница, где-то эти свойства доступны, где-то нет.
Мне кстати и в Eloquent не нравится, что в модели не объявлены свойства. Понятно, что это делается, чтобы экономить время на их написании, но в более-менее сложном приложении очень неудобно, когда не знаешь какие у объекта свойства. А если их добавляют динамически, то путаницы только больше становится.
Получать viewUrl/downloadUrl удобнее наверно в самом шаблоне.
Я бы тебе советовал для начала все же учиться писать обычный ООП код с минимумом магии и побочных эффектов, а когда освоишь, можешь пробовать другие подходы. Eloquent, раз ты уж его взял, пусть остается, но постарайся его интегрировать с минимумом побочных эффектов и без странных вызовов вроде $c->db;
> $files = $this->filesModel->take($this->appSettings['listFilesCount'])->orderBy('id', 'desc')->get(); Вот тут я вижу, что ты плохо понимаешь MVC. Ты пишешь много кода в контроллере, вместо того, чтобы разбивать код на контроллер (который разбирает HTTP-запрос) и модель (которая отвечает за логику обработки данных). В данном случае должно быть примерно так:
А у тебя получаются "толстые контроллеры", то есть ты весь код обработки запроса пишешь длинной стеной и кладешь его в контроллер, а надо учиться разбивать его на отдельные действия. Код держать в контроллере невыгодно, так как там он не повторно используемый.
Контроллер обычно только управляет процессом обработки запроса. Ему не надо знать как сортируются записи в базе и по каким условиям их надо выбирать.
То же самое касается обработчика запроса на загрузку файла. У тебя сейчас все в контроллере. А ведь сохранение нового файла - это часть модели. Чтобы ты лучше понял, как сделать код повторно используемым, сделай такую вещь:
Напиши скрипт командной строки, который принимает на вход имя 1 или более файлов, загружает их и выводит ссылки. Используется примерно так:
Соответственно, тебе надо сделать методы загрузки (и может быть валидации) в модели. И придумать, как сделать код загрузки, не завязанный на массив $_FILES, которого не будет в консольном скрипте. Идея MVC как раз в том, что методы модели могут использоваться несколькими контроллерами и данные из нее выводиться несколькими представлениями.
Также, вот этот код непраильный:
> $newFile = $this->filesModel;
Это ведь не создает новый объект, а берет существующий из контейнера. Если второй раз выполнить эту строчку, то вернется тот же самый объект. Тут очевидно должно быть написано $x = new Files.
> $newFile->save(); > if($newFile->id) { Вот это непонятно, а как тут может не быть id? В каком случае?
Далее, неправильно коммитить транзакцию до сохранения файла на диск так как в этом случае возможна ситуация, когда запись в базе уже есть, а файла на диске еще нет.
> return $response->withJson([ > 'text' => $file->getError(), > 'status' => 'ERROR' > ], 400); Я не уверен что стоит использовать тут HTTP код 400, так как клиентские библиотеки вроде jquery с методом $.ajax не дадут тебе JSON-объект, если код ответа не равен 200. Хотя я видел случаи, когда HTTP-коды ошибок используются для сигнализирования об ошибках валидации, но это не всегда удобно.
var commentContainer = document.querySelector('.addition-area')
На мой взгляд, так будет проще и понятнее.
Также, мне не нравится паттерн, когда JS-файл содержит код вне функций, выполняющийся при подключении файла. Мне больше нравится идея, когда файл содержит только функции (или объекты), но ничего не делает сам при подключении. Так получается нагляднее,когда какая-то функция вызвыается явно. Хотя на практике часто пишут в внешнем файле $.ready(function () { .. }), мне это не нравится.
Если ты подключаешь JS файл в конце HTML-файла, то есть недостаток, что до момента загрузки этого файла кнопки и интерактивные элементы не работают. А вот если подключить файл в начале и использовать onclick="doSomething()" то такой проблемы нет. Хотя часто делают так, как ты сделал, но мне это не нравится.
> (function(f, w) { По моему не очень удачные названия переменных.
Насчет перетаскивания и вставки файлов через Ctrl + V - а ты тестировал перетаскивание и копирование из разных источников? (папка, браузеры, офисные программы)? Если нет, советую протестировать, так как они часто передают информацю о файле в разных форматах.
> xhr.onreadystatechange = function() { > if (xhr.readyState == 4) { > if(xhr.status == 200) { > var data = JSON.parse(xhr.responseText) Вот тут тоже у тебя стена кода без разбиения на функции. Не надо напрямую использовать XMLHttpRequest, он неуклюжий, лучше написать отдельную функцию отправки запроса.
Могу кое-что добавить в дополнение к написанному аноном выше. Прочитай примеры использования mysqli в мануале и обрати внимание на то, что после почти каждого вызова mysqli-функции стоит if с проверкой на ошибку. Дело в том, что mysqli сама не пишет причины ошибок, и надо ставить дополнительные if (или использовать PDO, который умеет выбрасывать исключения).
Вообще, эта проблема (скрытие информации об ошибках) есть не только в PHP. В C#, если я не путаю, если создать новый тред и в нем выбросить непойманное исключение, то тред молча завершается, а исключение теряется, если специально не проверять его наличие. Это конечно неправильно, всегда должно быть так что по умолчанию ошибка фиксруется в логи и завершает программу, а далее уже разработчик при желании может ее перехватывать.
Можно, но с use наверно код будет красивее. А в чем проблема? Ты руками что ли use пишешь? В моем Sublime например есть плагин PHP Companion, который вставляет use при нажатии F5 на имени класса. И в любой нормальной IDE есть такая функция.
Также, неймспейсы по PSR-4 должны начинаться с большой буквы.
Если ты пишешь что "нахватался по верхам", то это плохо. Ведь надо уверенно знать основы, чтобы изучать какие-то более сложные, высокоуровневые вещи. Прежде чем изучать фреймворк, надо знать ООП и MVC.
И соответственно, если ты захочешь сделать какой-то свой проект, ну например, чат какой-нибудь, то без базовых знаний будешь делать там элементарные ошибки.
По HTML/CSS я бы предложил решить наши задачи из ОП поста. Если ты хорошо знаешь HTML, ты их решишь за 1-2 дня все. По PHP - решить задачи на ООП (Вектор и кошки-мышки) из учебника из ОП поста, а затем например задачу про список студентов. Я вот не знаю, какой уровень в курсах, которые ты смотрел, и насколько ты их усвоил, а вот в своих задачах я уверен.
Если бы ты успешно решишь эти задачи, значит базовые знания ты освоил и можно браться за что-то сложнее - изучать фреймворки или писать какое-то приложение, которое ты хотел бы сделать. И я тогда бы мог его проверить и например дать какие-то советы.
Насчет "сайта на CMS", CMS обычно проектируются так, чтобы делать на них сайты без программирования, за счет настройки конфигурации в админке, в лучшем случае с правкой HTML шаблонов. Это очень низкий уровень и я не советую как-то на него ориентироваться. Если ты наши более сложные задачи пройдешь, в любой CMS ты разберешься без проблем.
>>901543 >mysqli_query Перво наперво - забудь это и юзай PDO, люк. Если тебя пугают оопэшные штуки - учи ООП. Второе уже поясняли - прочитай эрор меседж ты с вероятностью 90% сам пофиксишь проблему.
> - После вызова текущего контролера и его метода, вызывает специальный метод главного контроллера getView, в котором создаётся объект класса главного View и уже в нём вызывает функция render с передаваемыми параметрами. А ты можешь сформулровать, что должны делать контроллеры? Что они получают на вход, что выдают на выходе? В ООП ты должен для каждого класса знать, какая у него задача, что он получает и что может вернуть.
Как я понимаю из твоего описания, у тебя контроллер должен вернуть массив данных для шаблонизатора. Но это выглядит как ограничение. А что, если ты хочешь вернуть страницу с HTTP кодом 404 или 403? Что если ты хочешь вернуть не-HTML ответ (plain text или картинку или JSON)? Удобно ли это сделать в твоей архитектуре?
Сейчас я все чаще вижу такую реализацию контроллера: контроллер получает на вход объект HTTP запроса и отдает на выходе объект HTTP-ответа. Выглядит это так:
$response = $controller->indexAction($request); или $controller->indexAction($request, $response);
В микрофреймворках контроллер может быть функцией: function (Request $req, Resonse $res).
Иногда Front Controller анализирует названия переменных в аргументах метода. Если есть роут /news/{date} и у метода контроллера есть аргумент с именем $date (newsAction($request, $date)) то в него передается значение date из URL. Для определения имени переменной используется Reflection.
Я советую, если ты решишь делать эти объекты, использовать именно интерфейс PSR-7 и не изобретать очередной велосипед. Есть готовые реализации: https://packagist.org/providers/psr/http-message-implementation . Если решишь не делать эти объекты, то тогда можно брать данные напрямую из GET/POST/SERVER и самостоятельно выводить ответ.
Я бы тебе советовал посмотреть микрофреймворки вроде Slim, Silex, может что-то подчерпнешь.
> Думал создать класс Router и переложить в него часть функций Задача роутера только проанализировать URL и выбрать контролер и метод.
Дело в том, что PHP, как и многие открытые проекты, распространяется в виде архива кода на Си. Процессор, разумеется, выполнять исходный код не способен, потому ты должен "скомпилировать" из исходного кода исполняемый файл (в случае Windows - php.exe), который можно будет запустить. Также, PHP использует сторонние библиотеки и имеет много настроек компиляции (какие возможности включать, какой каталог использовать для определенных целей итд), потому в общем процесс сборки выглядит так:
- скачиваем исходынй код - устанавливаем исходный код нужных сторонних библиотек - устанавливаем компилятор Си и утилиты для сборки вроде make - запускаем скрипт конфигурации, задаем нужные нам настройки, а остальные скрипт выставит по умолчанию - запускаем скрипт сборки, ждем пока компилируются все нужные файлы - запускаем скрипт установки, который установит скопилированные файлы из временной папки в нужную
То есть задача проекта PHP - дать исходный код, а его сборка - задача пользователя. Этот подход имеет то преимущество, что пользователь может максимально гибко настраивать параметры сборки под свои нужды (например не включать ненужные ему модули) и даже при желании внести какие-то правки в код.
Это довольно долго и неудобно, потому под популярные ОС добрые люди собирают PHP и позволяют скачать уже скомпилированную версию. Под Windows можно скачать PHP с сайта, под Дебианом - установить через apt-get. Под маком есть программа homebrew, которая сильно упрощает процесс сборки и сама скачивает нужные библиотеки и настраивает конфигураци. А сборку вручную делают те, кто хочет каких-то нестандартных настроек.
Обрати внимание, что бинарные файлы привязаны к конкретной ОС и архитектуре процессора (32 или 64 бита). Нельзя взять бинарник от линукса и запустить на Windows.
Расширения распространяются аналогично - в виде исходных кодов, причем для их сборки нужны еще и часть исходников самого PHP нужной версии. И популярные расширения тоже можно скачать уже скопилированные (для windows это dll файлы, которые кладутся в папку к PHP и прописываются в конфиг php.ini). Более того, под Windows часть расширений идет в комплекте с PHP, надо только включить их загрузку в php.ini.
А если расширение малоизветсное, то придется собирать вручную, то есть идти тяжелым путем.
Теперь объясню фразу
> Для того, чтобы включить поддержку PostgreSQL необходимо скомпилировать PHP с директивой --with-pgsql[=DIR]
Исходный код расширения postgres включен в состав PHP и его не надо скачивать отдельно, достаточно при конфигурации сборки включить одну опцию. Если ты скачал скопилированный PHP, то скорее всего это расширение входит в его состав, и следующее предложение говорит об этом: "Если доступен динамический модуль, то он может быть включен директивой extension в php.ini, либо функцией dl()."
Тебе надо проверить что есть нужная dll (php_pgsql.dll) и прописать ее в php.ini.
Под Дебианом популярные расширения ставятся через apt-get, непопулярные компилируются утилитой pecl (вообще, там установка и сборка намного проще чем под виндой).
> установочная директория - папка, где все файлы, связанные с postgresql лежат, или путь к папке с .exe, который как бе и есть сам сервер? Имеется в виду директория, где лежат заголовки postgres (заголовки - headers - это специальные исходные коды, которые позволяют исходному коду PHP узнать, как правильно вызывать функции из кода postgres. Если ты хочешь чтобы твой код мог вызывать методы сторонней библиотеки, тебе нужен либо полный исходный код этой библиотеки, либо скомпилированная библиотека + заголовки, описывающие имеющиеся в ней функции).
> Алсо, сейчас хостую демонстрационный сайт через XAMPP. Хватит ли мне его, чтобы взаимодействовать с PostgreSQL, или нужно ставить специально сконфигурированный под него апач? Да, это вообще не важно, ты можешь даже использовать для разработки встроенный в PHP веб-сервер. Конфигурировать надо не веб-сервер, а PHP.
- просто прописать пути в шаблонах - сделать простой класс ResourceManager, в который в контроллере мы добавляем пути и позже в шаблоне генерируем теги link/script - пойти хардкорным путем, определять нужные скрипты/стили в шаблоне и как-то их сканировать/анализировать чтобы знать, кому что нужно. Если используется шаблонизатор, можно ввести свои теги вроде {{ require-script 'news.js' }}. Ведь этот скрипт нужен для элементов в шаблоне и логично там и прописывать зависимость от него, где он нужен. - твой вариант с конфигом
Знание, где какие файлы нужны полезно если ты захочешь сделать пайплайн для склеивания/минификации файлов.
Надо учесть такие вещи:
- иногда надо подключить файл с стороннего домена (вроде google analytics) - сторонние библиотеки обычно устанавливают копированием папки, и в ней есть свои подпапки вроде js/css
> ИМЯ_ШАБЛОНА(layout), ВИД_ПОДКЛЮЧЕНИЯ (CSS, JS и т.д.), насчет варианта с отдельным конфигом - надо взвесить все за и против. С одной стороны, информация собрана в одном месте, с другой - как-то все сложно получается и информация отделена от контроллера и шаблона.
> Сам же метод надо будет вызывать в каждом layout, достаточно просто вызывать его не передавая никаких параметров, он уже сам будет решать куда что подключить, при помощи перебора свойств, если например КСС то вверх, если JS то в футер. Подключение JS в футере имеет недостатки:
- нельзя вызывать JS функции в теле страницы из тега script - пока футер не загрузится, все JS-кнопки не работают, так как не определена прописанная в их обработчике функция (или даже обработчик еще не повешен)
Ты наверно где-то увидел, что подключают JS в футере, и бездумно решил это скопировать. Зря. Надо проанализировать как преимущества, так и недостатки такого подхода. А ведь 99% просто копируют то, что делают популярные разработчики, а например мое мнение, которое им противоречит, почему-то никого не интересует. Такая вот у нас среда, вредные идеи хорошо распространяются среди малограмотных масс.
Мне например нравится когда JS файлы содержат только функции или классы и ничего не делают сами, пока мы не вызовем какую-то функцию. Но фронтендщики все как один пишут в них код вроде
$(document).ready(function() { инициализация })
А в чем проблема? А в том, что по мере развития сайта никто не удаляет старый код инициализации, и он только растет, более того, он запускается на каждой странице, даже если нужен только на одной. Я такое видел много раз. Этот подход с document.ready годится для сайта из нескольких страниц, а не для блоьшого развивающегося портала, но глупые верстальщитки везде как один копируют этот подход.
По моему это все пошло с моды на "unobtrusive javascript", описанный много лет назад в какой-то статье вроде такой:
Сами-то идеи хорошие, но верстальщики уяснили из них ровно 2 вещи:
- активные элементы надо помечать классами - в JS коде надо написать $(document).ready, ищущий элементы с этими классами и вешающий на них события.
А то, что на большом сайте этих ready будут десятки и они будут выполняться на каждой загрузке страницы, никого не беспокоит. Или например то, что оригинальная идея предполагала, что активные элементы могут работать и без яваскрипта, а яваскрипт лишь улучшает их.
Хуже того, иногда эти скрипты по document.ready меняют что-то в верстке, и верстка прыгает по этому событию. Это вообще ужасно.
То есть я хочу сказать, что надо:
- учитывать что на больших сайтах требуются какие-то принципы для организации кода - надо думать, что будет происходить до инициализации JS кода и с какого момента нам доступны те или иные функции
ОПчик, а ты будешь делать гайды и задачки по паттернам? Все эти фабрикаторы, декораторы, хотелось бы от тебя почитать что по чем, а то я вообще понять не могу что оно и как его готовить. И тоже самое по алгоритмам - есть какие-то мастхевные алгоритмы для вкатывальщиков? Ну хотя бы что бы не собеседовании не обосраться.
Опять спам-листАноним27/12/16 Втр 02:07:48#104№901673
Они тебе с вероянтностью 99% и не нужны. Паттерны же используют не просто так, а дял какой-то цели.
Например фабрику для таких целей:
- если есть сторонняя библиоека, которую мы не можем менять, она создает какие-то объекты и мы бы хотели повлиять на процесс их создания - автор выносит его в фабрику, и теперь мы можем написать свою фабрику и передать в эту библиотеку
- если нам надо создавать однотипные объекты на основе какого-нибудь конфига. Ну например у нас есть XML-файл с описанием формы и полей в ней, мы пропускаем его через фабрику и получаем соответствующие элементам формы объекты
Ну то есть не очень часто встречающиеся задачи.
У меня пока много других тем, которые надо осветить, и эта не самая важная.
У меня есть уроки по DI и по паттернам работы с БД:
Могу пока посоветовать изучить Симфони и ее компоненты (например, Формы, Доктрину), там многие паттерны использованы, и можно попробовать подумать, почему сделано именно так, а не иначе, как бы эту задачу решал ты.
Если ты не понимаешь Симфони, то за паттерны браться рановато. Нужен определенный опыт.
Если тебе это нужно для собеседования (что довольно глупо спрашивать, на мой взгляд, если в компании не используют фреймворк уровня Симфони), в интернете есть полно статей, выучи какой-нибудь синглтон, если собеседующий сам не знает паттерны (скорее всего так и есть), то он не сможет тебя завалить. Я бы конечно, если бы мне начали рассказывать про паттерны, спросил где их используют, какие преимущества, а в конце спросил бы, а не проще ли это сделать без паттерна, по-простому? И какой тогда смысл его использовать?
По алгоритмам гугли "структуры данных и алогритмы". Есть книги, видеолекции,вот например: http://aliev.me/runestone/
В алгоритмах самое важное это уметь оценивать их сложность по времени и по памяти, (я тут немного написал: http://arhivach.org/thread/216627/#898481 ) . При изучении структур данных желательно иметь представление о том как устроена память компьютера (это набор пронумерованных 1-байтовых ячеек) и исходя из этого ты может быть например поймешь сильные и слабые стороны вектора и связанного списка. Если не поймешь - спрашивай.
Самая большая (гигантская) книга по алгоритмам - это Кнут, "Искусство программирования". Ее тебе изучать лет 10 наверно придется. Там только про одни генераторы случайных чисел огромная глава.
У тебя есть проблема в том, что ошибка не передается из socketWrite выше. Ты пишешь в консоль, закрываешь сокет, но вызывающий код об этом не знает и пытается дальше использовать этот закрытый сокет. Неправильно.
> $buf = trim($buf); > ... > if (preg_match('/\n/', $buf)) { trim удаляет \n с краев и preg_match сработает только если он был в середине пакета данных. Странная логика, не находишь?
Я просил сделать следующее: функцию-генератор, которая при каждой иерации возвращает 1 строку из пришедших данных. Так как данные приходят пакетами незвестного размера, то может быть 1 пакет с несколькими строками, может быть несколько пакетов с 1 строкой. И при таком подходе нам нужен внутренний буфер для накопления данных. И удобнее всего это делать генератором. Вот алгоритм:
буфер = ''; вечный цикл { - читаем пакет данных; - проверяем ошибки; - проверяем, не закрыл ли отправитель сокет, если да, то выплевываем остаток буфера, если там что-то есть и выходим; - кладем пакет в буфер - пока буфер содержит символ \n, цикл { - вырезаем первую строку из буфера вместе с символом \n - выплевываем ее } }
Вот примерно такая логика. Как видишь, без генератора обычной функцией реализовать такую логику сложно (разве что использовать глобальную переменную-буфер, но код будет гораздо запутаннее - можешь проверить и попробовать написать для сравнения, и из-за глобальной переменной нельзя работать более чем с 1 сокетом одновременно).
Генератор хорош тем, что позволяет как бы выполнять 2 цикла (цикл приема данных из сокета внутри функции и цикл генерации ответа на строку), переключаясь между ними с помощью yield.
Твой генератор работает как-то криво и через раз.
> там в следующей задаче написано про многопоточность, я что-то не нашел ничего внятного про многопоточность на пхп, только открытие нескольких процессов вручную (зачем?). Многопоточность здесь значит возможность обрабатывать несколько потоков данных одновременно. Твой echo сервер например в один момент времени работает только с одним потоком: либо ждет пока пакет отправится, либо пишет в консоль, либо ждет пока пакет придет, либо ждет нового соединения. Это сильно нас ограничивает. Для чата даже из 2 человек нужна многопоточность, так сервер должен ждать входящее сообщение на 2 сокетах сразу. И отправлять 2 людям, не зависая если кто-то из них не принимает пакеты.
В твоем случае я предлагаю сделать асинхронную однопоточную архитектуру, но пока без системы событий, вручную. Идея такая:
- делаем операции на сокетах неблокирующими - используем socket_select, чтобы ожидать событий на нескольких сокетах сразу - как только произойдет какое-то событие (пришли новые данные, сокет готов к передаче новой порции данных) - обрабатываем это событие и снова вызваем socket_select в ожидании следюущего.
То есть освой работу с неблокирующими сокетами и socket_select.
Имей в виду что socket_select неэффективен при большом числе сокетов (десятки, сотни). Для них нужно что-то вроде libevent. Но ты пока об этом не беспокойся, у тебя их так много не будет.
В клиенте-загрузчике тут while (false !== ($out = socket_read($socket, 2048))) нет проверки, почему вернулся false - это ошибка или просто соединение закрыто сервером.
> $response = preg_split('/\r\n\r\n/', $response, 2)[1]; Тут ты предполагаешь что в ответе сервера есть эта последовательнсоть. Но что если он выдал ошибочный ответ? Также, советую сделать \r необязательным, чтобы работала и просто \n\n - так надежнее.
> Далее в основной моделе описывают методы для работы с БД, типо findAll, findOne Это просто класс для работы с базой данных. На практике тебе не нужны методы вроде findAll, а нужны getLatestNews, getNewsByYear и так далее.
> Казалось, всё бы нормально, но возникает проблема, при реальном использовании этой конструкции, в контроллере создаётся объекта класса модели, чтобы получить возможность использовать методы для работы в базе данных, в итоге и основной код, вроде манипуляции с различными данными из базы данных будет осуществляться в контроллере а не в моделе. Либо сделать в "модели" дополнительные методы с бизнем-логикой, либо сделать слой классов-сервисов с бизнес-логикой, которые для работы с БД используют класс работы с БД.
Вообще, ты плохо понял MVC если думаешь:
- что модель это один класс или набор однотипных классов - что назначение модели лишь работать с БД
Вот рассмотрим регистрацию пользователя. Что тут задача контроллера, а что модели? Задача контроллера это:
- принять данные из запроса - попросить модель проверить их - попросить модель зарегистрировать нового пользователя - залогинить нового плоьзователя
Задача модели:
- проверка данных нового пользователя - создание нужных записей в БД, их может быть больше одной если например данные хранятся в нескольких таблицах - дополнительные задачи, вроде отправки письма подтверждения email
Потому модель может состоять из разных классов-сервисов.
Проверить, насколько верно у тебя реализовано разделение логики можно, попробовав написать альтернативный контроллер. Например, скрипт регистрации пользователя, который запускается из командной строки и должен либо вывести id нового пользователя либо сообщение об ошибке в данных. Или можно попробовать заменить хранение данных в базе на хранение в файле. Придется ли что-то менять, кроме модели?
Вообще, ты решал нашу задачу про студентов? Там многие такие вопросы разбираются.
> Создать в модели дополнительные свойства, в которых хранить определённые результаты манипуляция и получить к ним доступ из контроллера? Не надо. Функция должна возвращать результат через return, а не обходным способом.
> class Main extends Application { Странные названия. Main - это контроллер? А что такое Application?
> //передаём переменные в вид > $this->set("posts", "title_post"); Странно, ты передаешь данные в вид, но метод вызываешь на контроллере. Зачем тут посредник и почему нельзя передать данные напрямую?
> Меня в общем смущает, что в контроллере будет много кода, а в моделе его практически не будет вообще Это назывеатся "толстый контроллер" и это плохо, так как код в контроллере нельзя повторно использовать из других мест. Логика работы приложения должна быть в модели.
> В итоге у меня такая проблема, например, для пути нужен main, а у меня в свойствах содержится MainController, будет правильно обрезать приставку Controller специальной функцией Проще хранить без приставки. Но вообще, в современных фреймворках я вижу тенденцию отказа от атоматического выбора имени в пользу явного вызова рендеринга, вроде такого:
>>901674 Окей, посмотрю Фаулера. Алсо вспомнил про синглтон - я когда читал учил ООП понял как его делать, но не очень понял зачем он вообще нужен. Часто пишут, мол для соединения БД, типа ссылку на один и тот же объект возвращать, но немного обосрался когда это делал, в плане того что с ним получается несколько запутаней, да и опять же прочитал, что сейчас уже драйвер БД сам закрывает ненужные соединения, ну или вроде того. И вообще везде срачи на форумах в контексте использования синглтона. >Изучи векторы, связные списки, очереди, стеки, деревья, графы, хеш-таблицы (словари) для начала. Вот этого я пиздецки боюсь, я же гуманитарий по образованию и матана дальше квадратного корня не знаю. Это сильно надо вкатывальщику?
Матан там не нужен, но хорошо бы понять как устроена память и спросить себя, а как бы ты например реализовал хранение массивов, добавление в них элементов, как бы ты хранил деревья и тд. Я могу подсказать, если что-то будет непонятно.
>>901665 Я как раз сейчас про ООП смотрю. Лучше чем на js но -> немного раздражает с оператором . Окей как закончу курсы начну решать про список студентов. А курсы от бауманки Специалист с бабочки скачал
А вообще меня маман уже взашей гонит работать так что хочется упор на цмски сделать, а тут диплом на носу и тему можно выбрать веб-сайт. Так что хочется пока что на хлеб заработать себе на мамке кредиты за учебу ещё , а не в элиту кодинга идти со сложными веб приложениями.
Как во фреймворке реализовать получение данных из формы? Так как я пишу пока только пробный фреймворк, чтобы лучше понять как они вообще работают, то реализовать как в Yii2 со методом rules() у меня пока наверное не получится. Нормально будет, если под форму на стороне разработчика будет создаваться модель со свойствами равными полям формы. После чего, в контроллере на стороне разработчика будет создаваться экземпляр класса модели формы, а затем заполнятся все её свойства из ПОСТ или ГЕТ массива. После чего можно спокойно вызывать методы главной модели для работы с базой данных в этом же контроллере, которые уже написаны в главной модели (типо findAll, findOne, Save и т.д.) и в вносить все необходимые данные в базу данных через них?
>>901701 Проблема в том, что все свойства модели будут заполняться именно в контроллере, который будет с ней работать. Как сделать, чтобы они заполнялись в самой модели я не знаю.
>>901678 >синглтон Помню, как дали тесовое задание написать синглтон, а мне было лень и я просто отправил им синглтон из документации ПХП, они сказали что он не верен.
И я вновь хотел поговорить про альтернативы PHPStorm. Atom мне казался самой здравой, но он тормозит, вообще всё тормозит, кроме SublimeText, но его очень долго допиливать, а некоторые пакеты платные.
А мне хотелось бы: просмотр БД, гит, быструю навигацию и подсказки по классам, интеграцию с таскменеджерами, удалённые подключения, синхронизацию, подсветку разного синтаксиса в одной вкладке. Ну и скорость работы по быстрее, т.к. шторм притормаживает у меня, но всё равно быстрее чем этом.
Есть ли варианты, говорят что эклипс и нетбинс ещё хуже?
Вот я настраиваю платежную систему. Она постом принимает данные. То есть как бы ждет что пользователь введет на моем сайте в форму какие-то данные, и нажмет кнопочку.
Но у меня на сайте уже есть несколько платежных систем, которые работают немного не так. И получается что при нажатии на кнопку, пользователь попадет лишь на еще 1 окно с кнопкой. Надеюсь все поняли это тупое пояснение.
В общем мне нужно скипнуть процесс нажатия пользователм второй раз кнопки "оплатить" так как все данные мне и так известны, и пользователю ничего вводить не нужно.
Нужна такая схема: 1. Пользователь сначала в общей форме на моем сайте вводит например сумму, выбирает в селекте платежную систему и жмет "оплатить"
2. Я на ходу скриптом формирую массив, и высылаю его постом на сайт платежной системы
3. И теперь то с чем у меня затуп, а именно как при этом еще и пользователя вместе с этим запросом туда отправить? Как будто это он засабмитил из формы данные???
>>901965 Да я вот тоже уже пытался гуглить, и на ум приходит только паршивый редирект с помощью js, то есть я всё таки оставляю прослойку в виде формы, в которой сам заполняю все поля, и тут же эту форму отправляю с помощью js. Юзер в теории ничего не заметит. Но если у него какой-то там браузер с отключенным яваскриптом то НАЖМЕТ ЕЩЕ 1 КНОПОЧКУ, что уж тут поделать :(
>>902041 Ну за тебя нужно код написать или просто подсказку достаточно? Сделай вместо >while ($row = $stmt->fetch()) что-то типа: $newsArray = $stmnt->fetchAll(); далее массив переверни и его уже foeache'm выводи.
Другой способ более элегантный наверное, это еще на этапе селекта задавать сортировку с помощью 'SELECT * FROM news ORDER BY id DESC' http://www.w3schools.com/sql/sql_orderby.asp
>>902075 Да какой я программист, макака на хуевой работе, которая говнокод пишет в духе того что я тебе выше предложил с сортировкой массива вместо правильного селекта, PDO я вообще ниразу не щупал даже в жизни. ООП не знаю толком и те кто решил правильно задачу Оп-а со студентами и с обменником имеют за месяц больше скиллов уже чем я за год, такие дела. Но спасибо на добром слове.
Еще дам совет, ты можешь попробовать генерировать ссылки у себя же на странице с гет запросом к своему же скрипту
site/news.php?sort=DESK
и потом собираешь у себя в скрипте этот параметр
$sorting = $_GET['sort'];
$query = "SELECT * FROM news ORDER BY id {$sorting}";
>>902151 >с сортировкой массива вместо правильного селекта Я тут мимокрок, но вот что это значит? Это как и зачем? Тебя заставляют такое писать что ли?
>>902151 Да в принципе зачем что то менять, если это работает. Но я сейчас пытаюсь сделать регистрацию и чет не получается. Хочу взять из таблицы login и сравнить его с логином из <input>
Делаю это так: ( для начала хотя бы его вывести через echo)
$loginbd = $pdo->query('SELECT login FROM users'); echo($loginbd);
На что мне пишет ошибку "Catchable fatal error: Object of class PDOStatement could not be converted to string in /home/u720408973/public_html/register.php on line 14"
Понимаю, что скорее всего не так обращаюсь к таблице, но в интернете не нашел ПОДРОБНЫХ гайдов по этой теме. Что не так с кодом?
>>902173 >$loginbd = $pdo->query('SELECT login FROM users'); >echo($loginbd); Так ты же делаешь запрос, но не записываешь ответа. Правильней будет как-то так: $sql = 'SELECT login FROM users'; $sth = $this->dbh->prepare($sql); $sth->execute(); $sth->fetchAll(\PDO::FETCH_ASSOC); SQL запрос выноси в отдельную переменную, так легче будет проверять правильно ли ты его написал. Короче глянь урок у ОПа, там где-то было про PDO написано.
>>902173 Алсо, опять же читай что оно тебе там в ошибках выдает. >"Catchable fatal error: Object of class PDOStatement could not be converted to string in /home/u720408973/public_html/register.php on line 14" >Object of class PDOStatement could not be converted to string А говорит он тебе, мол ты пытаешься обратиться к объекту как к строке, но это же не строка и вообще так низзя(на самом деле такое можно замутить, но про магию тебе еще рано знать). Т.е. в переменное у тебя все еще объект.
>>902244 Немного запорол: $sql = 'SELECT login FROM users'; $sth = $logindb->prepare($sql); $sth->execute(); $sth->fetchAll(\PDO::FETCH_ASSOC); Что непонятно - гугли методы. Фетч ассоц это опция что бы ассоциативный массив тебе вернулся, если что.
>>898502 (OP) Всем привет. Начал писать фреймворк с нуля, в целях обучения, и с литературой по этой неме как то в сети не очень. Колеги посоветовали смотреть как работают другие фреймворки, но хотелось бы более подробного описания, так как на разных фреймворках индивидуальный подход, и не совсем понятно почему разработчики выбрали именно такой путь. Если кто знает хорошую книгу (или хотя бы статейку), буду очень благодарен.
>>901073 >>901690 > Просмотрел > Прочитал > Досматриваю > про ООП смотрю. Программировать ты так не научишься.
>>902289 Велосипедистов полон тред. Книги/статейки тебе не хватит, нужно разбираться с ООП, разделением ответственности, снижением связанности, тестируемостью кода и главное - писать код. Тогда MVC и паттерны не будут казаться чем-то притянутым за уши, ведь тот же MVC - это одна из реализаций принципа разделения ответственности. Если ты будешь понимать, что и зачем отделять, то разобраться с новым (адекватным) фреймворком не составит труда. Я могу посоветовать начать с пасты ОПа про ООП ну и решать задачи оттуда.
>>902151 > $sorting = $_GET['sort']; > $query = "SELECT * FROM news ORDER BY id {$sorting}"; Здесь SQL-инъекция.
>>902164 Пикрил - недавняя директива от начальника.
Я образно конечно сказал, но суть в том, когда нужно сложный селект сделать с подзапросами и всякими агрегациями прямо в мускуле, я не могу с этим справиться за 5 минут (и даже за час), и поэтому приходится делать простой селект или несколько, что бы собрать всю инфу и форичами хуярить перебирая массивы и уже там дергать/суммировать/пересчитывать что тебе нужно.
Вчера три часа пытался скормить майскулу двумерный массив, полученный из экселя. Сегодня ещё буду. В http://php-include.ru/stati/array-php-mysql всё подробно написано, но не помогает. Там сначала в двух циклах подготавливается МЕГАЗАПРОС, а потом он вылетает с ошибкой "проверьте синтаксис". А как его проверишь, когда он реально километровый?
ОПу привет и с нг (я где-то полгода назад активно учился в треде, так что хотел сказать спасибо за помощь).
На работе достаточно много свободного времени (как впрочем и дома), поэтому возвращаюсь к учебе. В планах повысить навыки: git (очень важно), подтянуть js/верстку, а также доделать testhub (когда-то начал, но забросил).
Первый вопрос по гиту. Нет ли каких-то практических гайдов, задач? Потому что очень неудобно тупо зубрить теорию. Гитбук когда-то читал, и думал что неплохо разбираюсь в гит, но оказалось, что разбираюсь плохо, так что нужно довести до ума эту тему. Гитбук попытаюсь заучить наизусть, но хотелось бы чего-то более увлекательного, может кто подкинет.
Рассказываю такой пример из практики: Есть большой сайт/сервис. Решили сделать его клон с немного другим функционалом, дизайном и сео-направленностью. (Кстати обсуждали вопрос по синхронизации бд, в итоге решили просто перезаливать дампы по крону, но я думаю что есть явно более интересные способы решения этой задачи, скиньте почитать по этой теме). Проблема так же в том, как синхронизировать изменения в системе контроля версий? На главном сайте один программист пофиксил баг. Как мне перенести изменения на клонированный сайт? Пока с трудом (сейчас расскажу подробнее почему с трудом) смог вытянуть изменения через git diff, но что-то мне подсказывает, что делаю что-то не так.
Так вот, каждый программист работает с отдельной версией сайта (папочки dev1, dev2, и т.д.), но гит-репозитории создаются либо локальные (на рабочем компьютере программиста), либо на удаленном сервере, это зависит от того, как настроена ide. Я работаю например с версией dev1, захожу через ssh и там уже работаю с гитом через командную строку. То есть мой нетбинс синхронизирует файлы с сервером, но с гитом я работаю напрямую. Чувак работает через PhpStorm, у него репозиторий на своем компе. Соответственно я не могу зайти к нему в папку dev2 на сервере и посмотреть лог и дифф, как делаю в своей папке. Продакшн в папке prod, в нем только мастер, который сливается раз в месяц. Центральный репозиторий (откуда я вытягиваю изменения, и куду пушу, то есть тот который remote -v) в папке git. Но в этой папке нет рабочих файлов, там внутренности гита (branches, HEAD, config и т.д.), соответственно я могу посмотреть разве что git log на текущей ветке master, но не могу переключиться на другую ветку и посмотреть в ней историю, пишет fatal: This operation must be run in a work tree
Решил проблему через жопу, через клонирование центрального репозитория себе на комп во временную папку git remote add -t target_branch origin ssh://... git checkout -b target_branch git pull origin target_branch
В общем, нужно подтягивать знание гита, реквестирую интересные мануалы типа задач от опа (новых задач кстати не появлялось за последние полгода?).
>>902481 Расскажи как устраивался, с каким багажом в итоге начал по собеседованиям гонять и вообще как они проходили, куда и кем взяли? Помотивируй что ли к смене работы. А то сидишь вот так на своем дне и боишься дернуться, а то опять представляю как год без работы сычевать придется.
>>902473 Попробовал вообще до одной строчки таблицу обрезать. Всё равно ошибка
Ошибка при добавлении в таблицу 1c_test: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ') VALUES ('Баков Валентин Сергеевич','10366','' at line 29
Запрос: INSERT INTO `1c_test` (`Организация`, `Всего начислено`, `северные`, `Оклад по дням`, `Оклад по часам`, `Оплата больничных листов`, `Оплата больничных листов за счет работодателя`, `Оплата по среднему заработку`, `Отпуск учебный`, `Оплата отпуска по календарным дням`, `Отпуск за свой счет`, `Отсутствие по невыясненной причине`, `Районный коэффициент`, `Пособие по уходу за ребёнком до 1.5 лет`, `Дни неоплачиваемые согласно табелю`, `Месячная премия`, `Компенсация отпуска при увольнении по календарным дням`, `Всего удержано`, `Удержание по исп. листу процентом`, `Удержание по исп. листу процентом до предела`, `Удержание по исп. листу фикс. суммой`, `НДФЛ`, `Всего выплачено`, `Перечислено в банк (аванс)`, `Через кассу (аванс)`, `Перечислено в банк (под расчет)`, `Через кассу (под расчет)`, `Конечное сальдо`,) VALUES ('Баков Валентин Сергеевич','10366','','10366','','','','','','','','','','','','','','1348','','','','1348','9018','4100','','4918','','')
Таблица 1c_test имеется, в ней только один столбец id. В запросе 28 полей и 28 значений в VALUES. Что этой скотине от меня нужно?
>>902485 В итоге плюнул и сделал через foreach. тоже не сахар и вроде как за такое ругают, но блин. Три часа вчера пытался "по хорошему" сделать, три часа сегодня - ну не получается. А так сразу получилось.
>>902498 Я хотел вот попасть на стажировку/обучение в Noveo, но нихуя не смог пройти тестирование их автоматическое, так что до устного даже не допустили, хотя пол года назад летом, когда думал просто джуном к ним устроиться прошел это же автоматическое у них, но завалил устное.
В общем сложно это всё. Как анончики в соседнем треде про количество строк жалуются, что сложно писать фулл тайм на быдлоработе быдлокод, а потом вечером еще и работать над собой и при этом не выгорать за 2 недели такого ритма.
Если заставят въебывать все праздники как обычный месяц то уйду наверное в январе.
>>902270 Вот так оно должно быть? <?php $sql = 'SELECT login FROM users'; $loginbd = $pdo->query($sql); $sth = $loginbd->dbh->prepare($sql); $sth->execute(); $sth->fetchAll(\PDO::FETCH_ASSOC); ?>
Ошибка такая: Fatal error: Call to a member function execute() on a non-object in....
почему это так сложна :c??? Нельзя ли в самой таблице настроить столбец на параметр уникальности? Что бы одного и того же значения не могло быть?
>>902532 >$loginbd = $pdo->query($sql); У тебя точно есть переменная, в которую записан объект PDO? И почему тебе так хочется именно query лепить? >Fatal error: Call to a member function prepare() on a non-object in.... Ну да, ты пытаешься вызвать метода объекта, но у тебя я так понял даже нет $pdo.
$dsn = "mysql:host=$host;dbname=$db;"; $opt = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC); $pdo = new PDO($dsn, $user, $password, $opt); var_dump($pdo); $sql = "SELECT * FROM test"; $stmt = $pdo->prepare($sql); $stmt->execute(); $sth = $stmt->fetchAll(); var_dump($sth); У меня на локалке все работает. Ты точно файл подключил? Вообще у тебя такие вопросы, мне кажется ты явно не с того конца начал учить пыху.
>>898502 (OP) Господа, а если я буду год верстать под вордпресс, я дорасту до мидла? И если да, то на мидла в пхп, жиес или верстке или просто вем миддла?
>>902610 Ну так понимаешь, ты таким образом ебанешься вкатываться в погромирование, так его не учат. Если тебе просто приспичило что-то сделать, то или найди кого кто сделает, или ищи готовые решения. Или же учись кодить, можешь поделать задачки ОПа например.
Тебе рано работать с базой данных. Тебе надо начать с изучения PDO, а перед ним наверно синтаксис самого пхп подучить, работу с объектами.
Другие аноны стремятся чему-то научиться, ты же не хочешь учиться, а просто хочешь чтобы за тебя бесплатно сделали твою работу, за которую тебе платят зарплату. С таким отношением ты далеко не уйдешь в нашей сфере.
Постить тут свой код ты конечно можешь, но я точно решать твои задачи за тебя не буду.
ОПчик, тебе норм будет если я решение студентов тебе на почту пришлю? В смысле ссылку на гитхаб, а то там ряд вопрос накопился, удобней будет по почте общаться, а не тред засорять.
С каких это пор стало достаточно года опыта, чтобы претендовать на должность "миддла"? Сами эти градации обычно соответствуют опыту, но уровень требуемых навыков разный в разных компаниях. То есть если в вашей компании года опыта достаточно, чтобы стать миддлом, то ты можешь стать миддл-верстальщиком под вордпресс в ООО рога и копыта.
Вкатится он только в компанию, где нет нормального отбора на собеседовании, то есть в компанию где нет хоть сколько-нибудь квалифицированных разработчиков.
> Как анончики в соседнем треде про количество строк жалуются, что сложно писать фулл тайм на быдлоработе быдлокод, а потом вечером еще и работать над собой Иди на другую работу тогда. Или может быть, ты уже достиг потолка и сейчас пик твоей карьеры.
В одном проекте я видел такое решение: используется общий код для 2 проектов, при этом View (и JS/CSS) и Controller у каждого свои, а Model и БД - общая. Ну и естественно во многих местах ифы (если это проект1, то делать так, а если проект2, то иначе) натыканы. В БД также некоторые поля в 2 копиях (показывать страницу на проекте1, показывать на проекте2).
Даже если использовать 2 разных БД, все равно с общим кодом наверно было бы удобнее работать.
Из минусов - надо более внимательно править код, понимая что изменения могут отразиться и на другом проекте.
Идея сделать отдельный независимый репозиторий, в общем плохая. Замучаешься переносить изменения, и этот процесс невозможно автоматизировать, так как код может понадобиться адаптировать при переносе. Если будешь гуглить, это называется "port patches", портирование измененей в несвязанных друг с другом репозиториях.
Я бы советовал найти кого-нибудь, на кого можно переложить портирование изменений, это ручная не автоматизируемая на 100% работа.
В гите есть ветки, теоретически можно было бы вести 2 проекта в 2 ветках, но я ни разу с таким не сталкивался, и уверен, что будут какие-то сложности. Ветки все же предназначены для того, чтобы добавлять какие-то новые фичи, а не делать отдельный проект. Хотя гит позволяет, используя cherry-pick вместо merge выбирать, какие изменения перенести.
Также, теоретически, можно было бы попробовать разбить код проекта на уникальную часть и общую, и общую часть (части) вынести в отдельные библиотеки, которые подключаются в проекте1 и проекте2. Но это по сути усложненная вариация первого подхода.
Еще, я знаю, вордпресс позволяет вести несколько сайтов, но там по моему код общий, просто у каждого сайта своя копия БД. То есть тебе не очень подойдет.
Теперь насчет переноса изменений. Такая потребность часто бывает, ну например при обнаружении бага в Windows 10 майкрософт вынужден патчить и более старые ОС, как я понимаю, там вручную переносят и адаптируют патч под каждую ОС.
Ты можешь попробовать такой подход. Сделать дифф изменений в проекте1 (получится так называемый патч), вручную исправить этот патч (убрав ненужное) и применить к проекту2.
Я сам, если честно, до такой степени в гите не разбираюсь. Но я понимаю, что гит в общем не рассчитан на работу с 2 независимыми репозиториями. Я бы не советовал тебе изучать все его тонкости, а только те команды, которые могут пригодиться в твоей задаче.
По синхронизации БД: лучше всего было иметь одну БД. Также, в MySQL есть репликация, аж 2 вида: statement-based (с мастера на слейв идет поток SQL запросов) и row-based (с мастера на слейв идет в бинарном виде содержимое изменившихся строк и ячеек).
> Так вот, каждый программист работает с отдельной версией сайта (папочки dev1, dev2, и т.д.), но гит-репозитории создаются либо локальные (на рабочем компьютере программиста), либо на удаленном сервере, это зависит от того, как настроена ide.
Так эти репозитории связаны? Они создаются через git clone или разработчик тупо копипастт файлы и создает новый репозиторий из них?
Обычно делают так: есть центральный репозиторий. Разработчик делает git clone, получает копию. Потом периодически делает git pull, чтобы забрать новые изменения от других людей. Когда надо что-то добавить в код, он создает ветку, пишет код в ней, пушит в центральный репозиторий и отправляет старшему на проверку. Тот смотрит, если все ок, мерджит его в мастер.
Иногда работают без веток, разработчик сразу пушит в мастер, если люди ответственные и внимательные то так тоже можно.
Из твоего описания я не понял, как это устроено у вас.
Тебе советую перечитать про удаленные репозитории и ветки, команды git branch, git pull/push. Что-то у меня ощущение, что ты плохо понимаешь, как все устроено.
Попробую немного пояснить.
Коммит - это набор всех файлов + дополнительная информация (вроде комментария, автора и тд). То есть когда ты делаешь коммит, гит, условно говоря, создает копию всех твоих файлов и сохраняет ее в папочку .git. разумеется, там все по-умному оптимизировано и сжато и при коммите репозиторий не увеличивается в 2 раза.
Коммит нельзя отредактировать, но можно удалить и закоммитить заново, при этом у него может поменяться хеш.
Ветка - это по сути последовательность коммитов. Можно в любой момент начать новую ветку с любого коммита в репозитории. В начале есть одна ветка master, дальше ты можешь от какого-то коммита отпочковывать новые ветки.
push работает так: он сравнивает список коммитов в локальной ветке и удаленной ветке с тем же именем и отправляет коммиты, которых нет на удаленном сервере. Если удаленно такой ветки нет - она там создается.
pull работает так: делает fetch изменений с удаленного сервера, затем merge.
fetch сравнивает список коммитов локально и удаленно в текущей ветке и скачивает отстутсвующие локально коммиты в origin/master. Ты можешь далее вмерджить их к себе в master.
Не понимаю, зачем тебе лезть в личные репозитории других разработчиков. Если тебе нужен код, который есть у них, но нет в центральном репозитории, есть 2 варианта:
- попроси их поместить код в отдельную ветку и запушить в центральный репозиторий - подключи их репозиторий как удаленный (git remote add) например под именем dev2 и с помощью git fetch dev2 master ты скачаешь коммиты того разработичка в локальную ветку dev2/master, где можешь их просмотреть или смерджить.
Сделать git remote add можно только если репозиторий удаленно доступен по какому-то протоколу.
> Центральный репозиторий (откуда я вытягиваю изменения, и куду пушу, то есть тот который remote -v) в папке git. Но в этой папке нет рабочих файлов, там внутренности гита (branches, HEAD, config и т.д.), соответственно я могу посмотреть разве что > git log на текущей ветке master, но не могу переключиться на другую ветку и посмотреть в ней историю, пишет
Это репозиторий без рабочей копии, то есть используется просто как хранилище кода (коммитов) и состоит из папки .git. зачем тебе в нем что-то смотреть? Ты делаешь fetch/pull и получаешь все изменения из него к себе.
Не надо лазать по чужим репозиториям. Надо научиться работать с удаленными репозториями и ветками. В гитбуке есть глава по теме.
Лучше в тред. так будет соблюдаться очередь, плюс у нас есть аноны, которые не пишут в треде, но читают его и им может быть пригодится что-то из написанного.
>>902482 > Расскажи как устраивался Просто взял и устроился без левых мыслей.
> с каким багажом Хорошим (по сравнению с другими "соискателями") багажом по php: написал оповский файлообменник с 95% поставленных багфиксов, поверхностно изучил два фреймворка (юи и симфони). Слабыми по js. Отличными по sql. Верстка само собой.
> как проходили собеседования Их должно быть больше одного? Чувак по скайпу минут пять примитивщину по теории (неймспейсы, замыкания, отличия между версиями php, внешние ключи, виды джойнов, движки mysql, нормализация, транзакции, области видимости в js, доктайп и т.п.).
> кем взяли? Веб-разработчиком.
> помотивируй Понятия не имею, кто ты и какие у тебя цели. У меня была цель уехать из ненавистной мухосрани в культурный красивый город (который меня впрочем успел достать шумом и суетой в метро). Теперь хочу а) уютный домик в тихом пригороде б) карьерный рост как показатель прокачки моего irl персонажа. Умную бабу еще хочу, но тут придется обломиться наверное.
>>902687 > используется общий код для 2 проектов, при этом View (и JS/CSS) и Controller у каждого свои, а Model и БД - общая Это пожалуй был бы оптимальный вариант. Вообще я спрашиваю чисто для себя, для будущего опыта. На практике проект в таком состоянии, что действительно остается махнуть рукой и вносить вручную все изменения параллельно в обе версии. Бд спроектирована плохо, и для некоторых таблиц должны быть разные данные, заполняемые каждая из своей админки. Вернее часть полей общая, часть конфликтующая между проектами. То есть нужно переделывать структуру бд. Код сильно завязан на структуру базы, придется много переписывать. Ну и нет уверенности, что проекты будут в дальнейшем синхронизироваться, скорее всего на втором сайте будет добавляться новый функционал, старый убираться.
В общем суть в том, что пару месяцев назад проект форкнули, на главной версии продолжают фиксить ошибки, и мне нужно как-то параллельно синхронизировать багфиксы на втором проекте (дай бог чтобы не появился третий-четвертый). В принципе работы немного и это несложно (там меньше сотни коммитов, за неделю разгребу), но труд не благородный.
> Ты можешь попробовать такой подход. Сделать дифф изменений в проекте1 (получится так называемый патч), вручную исправить этот патч (убрав ненужное) и применить к проекту2. Да, я же и пошел таким путем. Хотя запутался в репозиториях и ветках. Про cherry-pick (видимо речь про вишенку с торта, считающуюся почему-то лакомством) слышал, но не знал что можно переносить коммиты между репозиториями.
По гиту основы знаю (пуш, пулл, коммит), но с ветками и работой с удаленными репозиториями пока слабо. > Коммит нельзя отредактировать, но можно удалить и закоммитить заново, при этом у него может поменяться хеш. --amend по-моему, но это только для последнего коммита, насчет удаления коммитов из "средины" истории даже не знаю, хотя наверное это не часто на практике
> То есть когда ты делаешь коммит, гит, условно говоря, создает копию всех твоих файлов и сохраняет ее в папочку .git Начал перечитывать гитбук, этот момент кстати не совсем понятный. Походу при коммите делается копия не всех файлов, а только измененных. Те которые не менялись, там ставится что-то типа ссылки что ли. >The major difference between Git and any other VCS (Subversion and friends included) is the way Git thinks about its data. Conceptually, most other systems store information as a list of file-based changes. > if files have not changed, Git doesn’t store the file again, just a link to the previous identical file it has already stored. Git thinks about its data more like a stream of snapshots. https://git-scm.com/book/en/v2/Getting-Started-Git-Basics Я если честно плохо понимаю, что такое "снимки" (snapshots), чем они отличаются от "list of file-based changes". Кажется, суть в том, что другие скв хранят именно диффы для каждого файла, а гит рассматривает папку как "a miniature filesystem". Как будто я знаю принципы работы файловых систем и мне это о чем-то говорит. Короче, не знаю почему они делают такой акцент на этом, и как это может пригодиться на практике.
############################################ По поводу того как у нас организована работа с гитом. Мне конечно сложно объяснить, потому что сам плохо понимаю, но попробую. На удаленном сервере (где-то во Франкфурте) есть папочка /var/www/sites/project1
В ней список директорий 1) prod - папка продакшн, в ней есть репозиторий, но только с веткой master. В нее пушит тимлид когда сделает ревью по внесенным правкам. То есть я не могу зайти в папку prod и посмотреть в ней диффы нужных мне изменений, их здесь еще нет. Это не центральный репозиторий. Центральный репозиторий - в папке git (см. пункт 3). 2) папки dev1, dev2, ... - копии проекта для каждого разработчика. У меня в папке dev1 есть репозиторий, я по ssh захожу в эту папку и из нее делаю пулл/пуш в центральный репозиторий в папке git (см.пункт 3). В папке dev2 (коллеги) есть рабочая копия проекта, но нет репозитория, потому что он прямо со своей машины пушит в папку git (смотри пункт 3) 3) git (без точки) это папка, из которой я и другие разработчики тянут изменения, и куда все пушат изменения. То есть в своем репозитории я выполню git remote -v, выведет /var/www/sites/project1/git Таким образом только здесь есть все ветки, в том числе нужная мне target_branch, где есть искомый коммит. Но я не могу посмотреть эти изменения прямо в репозитории (или не знаю как). Спуллить не могу, потому что полезет туча конфлик... А, все, понял ошибку, я забыл что кроме пулла есть фетч. Вернее о существовании знал, но не употреблял на практике. Вот вам и голая теория без упражнений.
Эх, ладно, я пошел перечитывать гитбук. Может я возьму его дотошностью, что поделать. Хотя было бы интереснее решая практические задачи, а не зазубривая последовательность букв наизусть.
От вас хрен дождешься годноты, что-то сам нагуглил. https://githowto.com/ru Сейчас проверю, хотя там скорее всего пять хелловорлдов а потом просьба где-то зарегистрироваться и что-то купить. Вот еще что-то http://rypress.com/tutorials/git/index
>>902833 Погодите емана. Получается цикл с массивом - это когда условие цикла - весь массив, и по очереди проверяется каждый элемент массива как условие работы цикла?
>>902833 Первый аргумент, это массив по которому ты хочешь пройтись. Допустим массив $cars; Второе это - псевдоним для текущего элемента цикла. Т.е. foreach($cars as $car) { echo $car; } Выведет тебе все элементы массива, т.е все машины. Если у тебя ассоциативный массив, то можно так сделать: foreach($cars as $key => $value) { echo "Марка автомобиля :{$key} модель: {$value} ; } В key у тебя будет индекс элемента массива, в value - его значение.
>>902833 >>902837 Цикл обхода массива это немного не то что другие циклы, в которых ты задаешь условие: тип повторять цикл пока условие верно.
Тут у тебя цикл всегда выполнится столько раз, сколько у тебя в массиве элементов.
Ну и с помощью as ты показываешь в какую переменную на каждой итерации у тебя в переменную $car будет складываться текущий элемент массива.
Так как в массиве есть не только значения но и ключи, то есть более сложный вариант, когда у тебя текущий элемент массива раскладывается на две переменные: на ключ и значение - as $key => $value
хеш коммита определяется из хеша файлов и метаданных (включая время). --amend по сут создает новый коммит с новым хешем.
> Начал перечитывать гитбук, этот момент кстати не совсем понятный. Походу при коммите делается копия не всех файлов, а только измененных. Те которые не менялись, там ставится что-то типа ссылки что ли.
Это уже детали и оптимизации. Есть другой подход, когда мы сохраняем начальное состояние, а потом в каждом коммите сохраняем изменения (diff, patch) в сравнении с предыдущим. Нетрудно написать на bash такую примитивную систему управления версиям.
Гит так не делает, он к каждому коммиту прикрепляет все файлы из рабочей области. Ну а дальше уже идут оптимизации: информация о файлах записывается в виде хешей (по которому в хранилище можно найти тело файла), и если файл не поменялся, то его хеш остается неизменным и новый объект в БД не добавляется.
Вообще, гит построен вокруг хешей. Хеши являются идентификаторами для содержимого файлов, коммитов и других объектов внутри хранилища (хранилище - громко сказано, оно реализовано как папки с файлами внутри .git/objects).
Ну хеши - это уже детали, важно понимать что для любого коммита мы можем извлечь все относящиеся к нему файлы.
Кстати, ветки - это файлики в .git/refs/heads, которые содержат хеш "головы", последнего коммита ветки. Остальные коммиты ищутся благодаря тому, что в каждом коммите есть хеш родительского коммита.
> Я если честно плохо понимаю, что такое "снимки" (snapshots), чем они отличаются от "list of file-based changes".
snapshot это когда ты условно говоря при каждом коммите копируешь все файлы в отдельную папку. snapshot значит "снимок", то есть копия.
diff это когда ты при коммите сравниваешь текущее состояние с предыдущим и сохраняешь разницу между ними.
А дальшу уже идут оптимизации: гит вместо сохранения самих файлов каждый раз сохраняет в коммите их хеши, и добавляет те, которых там еще нет, в хранилище. Если файл не изменился то у него тот же самый хеш и он второй раз в хранилище не добавляется.
То есть там такой алгоритм коммита (я упрощаю конечно):
- взять хеши всех файлов - добавить каждый в хранилище, если там нет файла с таким хешем - записать все хеши в объект коммита - сгенерировать хеш от коммита и записать сам объект коммита в хранилище под этим хешем - записать в указатель на "голову" текущей ветки хеш нового коммита
На самом деле там все еще немного сложнее, там в коммит записывается не просто список всех хешей файлов, а ссылка (хеш) объекта tree, который как бы соответствует папке. Но это уже детали.
> Короче, не знаю почему они делают такой акцент на этом, и как это может пригодиться на практике.
Возможно они хотят поделиться тем, как оригинально устроен гит внутри.
> prod - папка продакшн, в ней есть репозиторий, но только с веткой master. В нее пушит тимлид когда сделает ревью по внесенным правкам.
Это скорее всего репозиторий только для деплоя, то есть выгрузки изменений на сайт. зачем тебе в него лезть? Он скорее всего содержит копию мастера из основного репозитория.
> папки dev1, dev2, ... - копии проекта для каждого разработчика. Ну это уже получается личное дело разработчика, как туда помещать файлы. Тебе к ним лезть незачем.
> Таким образом только здесь есть все ветки, в том числе нужная мне target_branch, где есть искомый коммит. Но я не могу посмотреть эти изменения прямо в репозитории (или не знаю как).
Тебе надо скачать нужные ветки к себе и смотреть у себя. pull ты делать не обязан, для просмотра хватит и fetch.
> Спуллить не могу, потому что полезет туча конфлик Ну это потому что ты пуллишь удаленный мастер в свой. Но ты мог бы пуллить или фетчить именно нужную ветку оттуда и конфликтов не будет, если ты ничего не коммитил в нее. В origin/branch не коммитят и потому fetch не вызывает конфликтов.
fetch копирует новый коммиты из удаленной ветки branch в origin/branch у тебя. А pull дополнительно мерджит origin/branch в локальный branch.
origin/branch - это как бы локальная копия удаленной ветки
Тебе надо читать именно про ветки и удаленные репозитории. Точно помню, что там есть глава.
foreach это специальный цикл для перебора всех элементов массива. на каждом шаге берется очередной элемент и его ключ и значение копируются в указанные тобой переменные и выполняется тело цикла.
Условие там не массив, а "пока мы не перебрали все элементы по очереди, продолжать цикл". То есть если в массиве 100 элементов, тело цикла выполняется 100 раз.
as это обязательная часть синтаксиса:
foreach ({ выражение, дающее массив} as {переменная, куда копировать ключ} => {переменная, куда копировать значение элемента}) { тело }
В "выражение, дающее массив" можно писать не только переменную, но любое выражение, которое возвращает массив, например:
>>902650 >Ну и названия колонок. Ладно, что на русском, но зачем такие длинные? Так выгружается из 1С. Я подумал и решил оставить. Программе до лампочки, а мне не приходится возиться с переименовыванием выгруженных данных сначала в некие "программные", а потом в понятные пользователю и выводимые в браузер (Месячная премия -> monthlyBonus -> Месячная премия).
ОПчик, расскажи про замыкания. Я вроде и понимаю что это, но очень условно. Можешь пояснить на примере какой-то реальной задачи? Ну такое что бы реально полезно, а не сферический пример. Алсо та жа хуйня про лмбду, функциональщину всю эту, можешь так же по хардкору разложить? А то выглядит интересно, но хоть убей не могу понять хоть что-то.
Сап, пхп боги. Имею хороший опыт ООП на Java, понимание алгоритмов и вообщем хороший уровень Computer Science. Как долго займёт изучение синтаксиса php и всех сопутствующих технологий/фреймворков? Есть возможность тратить на обучение 5-6 часов в день. Сколько по времени(дни/недели) займёт путь от старта до первого собеседование. Ваше примерное мнение из личного опыта. thx
>>903042 >Сап, пхп боги. Имею хороший опыт ООП на Java, понимание алгоритмов и вообщем хороший уровень Computer Science. Если это правда, то никаких проблем не должно возникнуть же, за пару недель должен осилить. >сопутствующих технологий/фреймворков? Сопутствующие технологии это что вообще? Ну, SQL, 1-3 нормальные формы знаешь же? Фреймворки все знать не надо. Если хочешь посмотреть самые популярные для продакшена - смотри Yii2 и laravel, там в принципе хорошо надо знать толко ООП и MVC паттерн. В принципе, если ты реально так хорош как говоришь можешь сходу Zend учить, самый илитный фреймворк же.
Я работал с разными БД,а с MVC в теории знаком. Не хочу без работы сидеть, в Java сейчас на вакансию джуна 300 человек в моем городе, а заявок менее чем ноль. Думал вкатиться во фронт-энд, начать получать какой-то опыт, потому что php вакансий вроде побольше, а потом сделать свитч в джаву. Какие могут быть проблему с трудоустройством, аноны? Какие контры обходить третей дорогой? Или есть смысл без опыта влезть хоть в какую-то щель? thx
>>903076 >Я работал с разными БД Работать с разными БД это одно, сам SQL то знаешь? CRUD, Left, inner join, primary - foreign key - знакомые слова? >а с MVC в теории знаком Ну, тогда начни с того что бы быть с ним знакомым не в теории. У ОПа есть хорошие задачки по MVC.
>>903032 Неистово удваиваю этого парня. Тоже хочется узнать.
>>901669 >Только надо понимать, что это библиотека для ассертов (утверждений, которые всегда верны), а не проверки например пользовательских данных из формы.
Нифига себе, я ее не по назначению, оказывается, использовал! Думал удобно и легко так - прошелся по нужным переменным, и дальше уверен что всё корректно (ну или сразу исключение кинет). Тогда вопрос еще, пожалуйста: как валидовать валидировать? пользовательский ввод? В PHP есть специальные функции (isset, empty, gettype, isbool и т.п.), это оно? А если мне, например, нужно: а) $_POST['date'] : например, дата только этого года б) $_POST['productname'] : строка длиной от 3 до 15 символов, например, не содержащая определенные символы в) $_POST['value'] : например, строго число, укладывающееся в диапазон INT(11) UNSIGNED и вдобавок, не равное нулю (для вставки в БД) г) $_POST['price'] : число с плавающей точкой (например 19.56)
Почему в этой функции выводится правильный результат: <? $x = 2; $n = 3; function myDegree($x, $n) { if($n == 0) { return 1; } if($n>0) { return $x*myDegree($x, $n-1); } } echo myDegree($x, $n);
Есть же условие, если $n == 0 вернуть 1, а она возвращает 8, но $n же должно стать 0 если от 3 несколько раз отнять 1 и когда она станет равно 0 функция должна вернуть 1, но она возвращает 8, почему?
>>902657 >е достиг потолка и сейчас пик твоей карьеры. >и сейчас пик твоей карьеры. >и сейчас пик твоей карьеры. >и сейчас пик твоей карьеры. >и сейчас пик твоей карьеры. >и сейчас пик твоей карьеры. >и сейчас пик твоей карьеры. >и сейчас пик твоей карьеры. >и сейчас пик твоей карьеры. >и сейчас пик твоей карьеры. >в днище-шараге на днище-зп
>>899522 >функции возвращающие false вместо выбрасывания исключений Почему ты считаешь, что выбрасывать исключения всегда лучше, чем возвращать false? Разве исключенря существуют не для исключительных ситуаций, которые никогда не должны происходить?
>>903459 Код покрытый исключениями выглядит в целом лучше, чем функция выбрасывающие ошибки в том месте где ты и не ожидаешь. С исключениями гораздо удобней контролировать поведения кода, опять же можно свои исключения выбрасывать. А контролировать ошибки бесконечной чередой if/else это сплошное уродство.
Старые ответыАноним30/12/16 Птн 15:56:33#198№903624
Тут можно было конечно сделать чуть оптимальнее - вместо пары методов generateDayPower/consumeDayPower сделать один метод, который возвращает положительное или отрицательное значение (вклад в баланс). Это позволило бы сократить код.
> function PowerStation() { > PowerNetElement.apply(this, arguments); как я помню, у электростанции мощность всегда одинакова и потому тут в конструкторе должен быть 1 аргумент.
Аналогично, у SolarPanel должен быть 1 аргумент - дневная мощность, у дома - число квартир, и тд.
Дом нет особого смысла представлять как массив квартир, так как у нас тут нет необходимости работать с отдельными квартирами и можно просто сделать дом одним обычным элементом сети. То есть не надо пытаться моделировать те детали, которые не важны для расчетов.
Насчет линии электропередач - если она наследуется от элемента сети, стоило все же вызвать его конструктор, указав 0 в качестве мощности.
> @param {(PowerElement|PowerStation|SolarPanel|House|PowerLine)} Можно просто писать название базового класса.
> var tmpNetElements = this._netElements.filter(function(element) > return ! ("getTransmittedPower" in element); Я думаю, для отсеивания линий передач лучше было бы проверять конструктор через instanceof PowerLine, либо сделать в PowerNetElement метод isPowerline. Первое наверно проще.
> if (instockPower >= demandPower - power) { > } else { Тут стоило применить Math.min или max вместо if.
> 'purchaseCost': purchaseCost + ' тугр.', Я думаю, лучше возвращать просто число, чтобы с ним можно было делать какие-то действия.
> if (typeof(report[key]) == "number") { > report[key] = Util.convertkWToMW(report[key]); Вот такие преобразования - верный повод создать ошибку. Лучше везде использовать одну, жетлательно стандартую единицу - например, Ватт-час.
Привет, анон, хочу вкатиться из школьного паскаля-ассемблера в psp-js и, кажется, имею проблемы с ООП. Дело в том, что я не всегда четко представляю себе, что нужно делать объектом, когда нужно наследовать и даже, как это ни смешно, что именно от чего должно наследоваться. Где можно что-то хорошее почитать на эту тему? Кроме того, меня постоянно тянет оптимизировать придуманные структуры. Кто-то постоянно нашептывает мне: "Эта переменная не нужна, эта функция тоже, а вот это вообще можно побитово хранить". Я, конечно, сопротивляюсь, но четкого понятия о том, как организовывать данные в классе не имею. Возьмем код, проанализированный вот в этом посте >>903624. JS не знаю, так что, возможно, я просто ничего не понял. Там от класса-родителя наследуются, кроме прочих, PowerLine (линии электропередач), при этом в PowerLine не используются ни поля класса-родителя, ни его методы, таким образом, наследование чисто логические. Однако, вместо добавления нового поля _transmittedPower можно хранить величину в уже имеющемся _generateDayPower, и мне кажется, что это хорошо, в конце концов линии электропередач по условию тоже в некотором роде поставщики/потребители энергии, и я не вижу в таком использовании логической ошибки. Но так ли это? Вообще, когда нужно следить, чтоб лишних полей не было, а когда можно махнуть на них рукой? Где почитать про это? Вот как я бы организовал данные: http://pastebin.com/TBGN0e5M Написано на условном языке, но должно быть понятно.
>>903788 >Где можно что-то хорошее почитать на эту тему? Ты нигде об это не прочитаешь, оно только с опытом приходит, как инкапсуляция. Можешь почитать про object relation mapping, если осилишь конечно же.
>>903822 Этот код ни о чем еще не говорит, и вообще отчет скорее говорит о том, что там какие-то древние сайты у них, рассказывают мол что такое SQL инъекция, XSS уязвимость. Спорить можно разве что по поводу, действительно ли хаккеры типа тех же Fancy Bear аффилированы с российскими госскруктурами ну прямо очевидное-невереоятное, но то уже политота.
Утверждают что использовались трояны на Питоне и Powershell.
Хотя в атаке использовались примитивные средства, трудно представить чтобы это были обычные хакеры. Они обычно ищут денег или славы, а тут надо было рассылать тысячи фишинговых писем, потом перерыть тонны скучных почтовых сообщений, чтобы найти интересные вещи и слить их в СМИ и в викиликс. Очень много труда затрачено. Причем взлом сделан чуть ли не год назад.
Вывод знака зодиака: http://ideone.com/Fg7RMI > Насчет поиска знака зодиака, я думаю, проще было сделать массив вида ['Козерог', 21, 1] и сравнивать числа, не перекодируя их в даты. Тогда и номер месяца можно убрать, если его можно получить из индекса знака зодиака в массиве? Это позволило избавится от цикла и сразу по индексу обращаться к нужному подмассиву с информацией о знаке зодиака. А дальше проверка: если номер дня на входе больше, чем номер дня в подмассиве - отдаём этот знак, если номер дня на входе меньше - отдаём предыдущий знак.
Дерево > Также, можно написать код без объектов Node и рекурсии, используя стек (стек для хранения записей, которые мы позже выведем, после того как разберемся с их детьми). Подход без рекурсии на базе стека действительно оказался простым: https://jsfiddle.net/0gpbzo8y/ но не совсем понятно, как выводить список вложенным, а не просто показывать список посещённых элементов. Сделал очень костыльно с добавлением ключа depth к каждому элементу массива: https://jsfiddle.net/0gpbzo8y/2/
Алсо я вспомнил про задачу на джуна, которую тут вбрасывали пару тредов назад, приложил скрин к посту. Как правильно реализовать третью часть? На запрос вида /catalog/samsung/s7 мне нужно выдавать товар, у которого path = s7, path родителя samsung, path его родителя = catalog. Я додумался только до того, чтобы генерировать вложенный sql-запрос. Например, для запроса /catalog/dvd/lg будет генерироваться такой: select * from items where path = 'lg' and parent_id = ( __select id from items where path = 'dvd' and parent_id = ( ____select id from items where path = 'catalog' __) ); Это очень плохое решение? Запрос генерирую так: http://ideone.com/pTS09b
N рабочих дней > Автоматически рассчитать даты праздников, увы, нельзя, так как их переносят из-за выходных. Я понял. Допустим, я получил, неважно как, список всех нерабочих дней в виде массива элементов вида 'd-m-y', решение будет примерно таким http://ideone.com/go0Mfo ?
Про алгоритмическую сложность отпишу отдельно, спасибо большое за ответ!
Ну и информация из открытых источников про российские подразделения компьютерных взломщиков https://meduza.io/feature/2016/11/07/rossiyskie-vooruzhennye-kibersily - в общем-то у других стран тоже такие есть. Надо лучше защищать критическую инфраструктуру и соблюдать меры безопасности, а не валить все на иностранных хакеров
Аноны, видели бы какую ужасную новую версию сайта для покупки билетов сделало РЖД. Раньше там был старый web-1.0 дизайн, и в таблице поездов строчка занимала где-то 2 см в высоту. Сейчас какие-то дебилы решили сделать дизайн посовременнее и теперь строчка занимает сантиметров 10 в высоту. То есть если раньше на экран спокойно помещалось 10-15 поездов, легко было сравнить цены, наличие мест и время в пути, то теперь получается длинная простыня, которую надо прокручивать. Слов нет, насколько плохо сделали. Дизайнер идиот и видимо сам билеты никогда не покупал
А еще в новом дизайне при выборе города в автокомплите клавишей enter, а не мышью, форма сабмитится. А еще дату нельзя ввести с клавиатуры, а можно только выбрать. Хотя я еще много лет назад делал виджет, в котором дату можно вводить в любом формате вроде "5 января" или "01.12.16"
>>903617 Но смотри, допустим, твой код выполняет пять операторов подряд, которые ты заключил в try catch, и чтобы реагировать на ошибку каждого оператора отдельно, тебе придётся либо выбрасывать в каждом своё исключение и обрабатывать его в виде нескольких катчей, либо обёртывать каждый отдельный оператор в свой try catch. Разве это удобно?
Потому что почти в каждой функции (кроме самых простых) может произойти ошибка и писать if после каждого вызова функции утомительно. Более того, из-за необходимости проверять результат мы не можем вкладывать вызовы функций вроде
func1(func2(..))
Исключения решают обе этих проблемы. Функция либо возвращает результат, либо выбрасывает исключение, если не может его получить. А что делать с исключением - решает вызывающий.
В принципе можно возвращать false вместо исключения тогда, когда мы ожидаем, что может быть ошибка и пишем там if. Но для каждой функции это делать утомительно.
Ну и на практике пропускают проверки. Посмотри сколько только в этом треде кода без проверки результатов вызова.
В большинстве случаев исключения никто и не ловит. В случае 5 операторов подряд, может быть и так, что выгоднее возвращать false, а уже в коде, где они вызываются, выбрасывать однотипное исключение в каждом из 5 случаев. Но можно и try/catch. В этом случае в других местах, где вызываются эти функции, нам if писать не придется.
И еще надо помнить, что если мы пишем throw в функции чтения файла, то мы выбрасываем например IOException, а если throw в функции загрузки пользователей из файла - то уже пишем UserLoadException. Хотя во многих случаях, когда мы точно не будем ловить исключение, можно использовать и просто Exception.
2. Не надуманный пример с 5-ю ифами, а рельная задача - нужно сделать так, чтобы все ошибки логгировались. В случае с исключениями можно сделать один try/catch на уровне фронт-контроллера и логгировать ошибки. Без исключений нужно постоянно помнить о том, что возвращает функция в случае ошибки, как получать сообщение об ошибке (где-то json_last_error, где-то curl_error) и вручную логгировать, используя копипаст по всему проекту. Можно, конечно, сделать логирующий декоратор, но ему ещё нужно знать, какая функция что возвращает при неудаче + этим декоратором будет усыпан весь проект, вместо одного try/catch на самом верхнем уровне.
>>904050 >И еще надо помнить, что если мы пишем throw в функции чтения файла, то мы выбрасываем например IOException В том то вот и плюс. Ты можешь в нормальном виде отобразить код ошибке, ее текст и проконтролировать поведение кода в случае исключения одновременно. В то время как ловля ошибок ифами это уродство и костыль. Зачем вообще это делать, если есть специальный, удобный инструмент исключений?
>>903900 Осторожно, политота: Не понимаю смехуечков. Что такого смешного в том, что бы гебня РФ использовала хакиров? Да господи, я знаю случаи вербовки хороших айтишников Службой Безопасности Украины, чего уж тогда про РФ говорить? Тем более, если уж посмотреть на тех же FancyBears, то их жертвы внезапно прохоядят по категории "врагои Россиюшки"
Вы не поможете с опен-сервером пожалуйста, не знаю куда обратиться... От администратора запускал, только для чтения галочку снимал, антивируса нет и никогда не было, брендмаузер отключен.
2016-12-31 14:50:59 -------------------------------------------- 2016-12-31 14:50:59 Начало процедуры запуска сервера 2016-12-31 14:50:59 Файл C:\Windows\system32\drivers\etc\hosts недоступен для записи 2016-12-31 14:50:59 Отключите использование HOSTS файла или настройте права доступа 2016-12-31 14:50:59 Сбой запуска!
Кстати возвращать null (в примере с json_encode) плохо еще тем, что причину ошибки надо получать отдельной функцией.
Насчет логгирования - обычно функции, которые возвращают false еще и выдают какую-то ошибку (которая идет в логи), но так делают не все. С системой ошибок конечно в PHP полный бардак.
Более того, в своем классе исключений можно делать дополнительные поля, например для хранения отдельно имени файла, который не удалось открыть или еще чего-то.
Да в общем-то многие развитые страны так делают. Можно вспомнить предположительно Израиль, уничтоживший трояном иранские центрифуги. Странно было бы не иметь нужных специалистов когда компьютеры так широко используются.
О боже, оказывается галка все еще есть! Смотрите. Снимаю галку, он требует права админа, нажимаю продолжить... Открываю снова свойства - снова галка стоит! Что за бред? Я за администратора же зашел.
>>904111 Мало быть администратором, начиная с шиндоус7 надо быть СУПЕР-АДМИНИСТРАТОРОМ. А в десятке этот марразм до пиздеца довели. Из-за этого я с десятки в итоге перешл на семерку, а когда шин7 перестанут поддерживать перейту на прыщи. Ибо блевать тянет от этого идиотизма от майкрософт.
>>904121 Я уже всех заебал наверное. Админа получил, права выставил, комп перезагрузил, включил - запустил сервер от администратора, нажимаю включить - и опять тоже самое.
Ты не пробовал читать инструкцию по установке твоей чудо-сборки? И кстати, лучше было бы не использовать сборки, а в первый раз самому все руками поставить. скорее всего чудо-сборку надо запускать от имени админитратора.
Можно ли в js в функции конструкторе объявлять заранее свойства (this.foo = null), чтобы нагляднее было их видеть? А то они вразброс по методам объявляются и сразу инициализируются.
>>904050 > В большинстве случаев исключения никто и не ловит. >>904093 То есть их назначение, с твоей точки зрения, просто информировать о сбое? Но непойманное исключение прекратит выполнение программы, причём — покажет голую информацию о коде пользователю, разве это допустимо? Представь, что у тебя в участке кода вызывается несколько методов класса, в каждом из которых может возникнуть ошибка, с которой нужно будет предпринимать какое-то действие: изменить порядок будущего исполнения программы или её свойства, вывести сообщение пользователю, откатиться на ранее сохранённый (безопасный) этап работы с сайтом. Чтобы различать ошибку каждого отдельного метода, придётся либо использовать много try..catch'ей, либо вызывать в каждом методе свой тип исключений, либо вызывать один тип исключений с разными кодами в них (и потом использовать switch для выбора конкретного действия); мне кажется, в такой ситуации простейший if, проверящий успех/неуспех (false) операции, выглядит проще и красивее.
>>904055 > В случае с исключениями можно сделать один try/catch на уровне фронт-контроллера и логгировать ошибки. А если ошибку нужно не просто логгировать, а предпринять с ней какое-то действие? Причём на том участке кода, который применяет метод, в котором произошла ошибка?
>>904314 В такой ситуации все исключения просто передают наверх, контролирующему коду. Так что трукатчить приходится один раз на модуль, при вызове цепочки функций. А тот код уже решает, что делать: возможна ли дальнейшая работа, нужно ли передать исключение выше, нужно ли падать, или можно перезапускать модуль до победного (например ожидая пока сеть не восстанет), или же можно просто проигнорить неотработку модуля и изменить поведение программы.
"Также, можно написать код без объектов Node и рекурсии, используя стек (стек для хранения записей, которые мы позже выведем, после того как разберемся с их детьми). Алгоритм примерно такой: - кладем в стек все записи, у которых parentId === null (корни деревьев) - цикл { - снимаем из стека верхнюю запись - выводим - ищем в массиве всех ее детей по parentId - кладем их в стек }
Вообще, многие рекурсивные алгоритмы можно заменить на алгоритм с циклом и стеком или очередью. Но он может стать сложнее."
В PHP есть структуры данных стек и очередь: php.net/manual/en/spl.datastructures.php , но можно обойтись и массивом. Кому интересно, собираю простые задачки на деревья/рекурсию, они отсортированы здесь по возрастанию сложности: http://pastebin.com/raw/SppMQayQ
Спасибо шо обучили похапе, люди добрые. Переехал из мухосрани в ДС - нашел работу. Правда плотют мало, но мне хватает ибо хату снимать не надо и жрачку покупать.
>>904732 1) По условию требуется, чтобы программа выводила NIL, если элемент не найден. 2) У тебя программа ищет ещё и дубликаты, а в условии написано "Выход: индекс", то есть один элемент. Для выхода из функции можно использовать return. Ну и функцию лучше бы назвать linearSearch, сортировка вставками это совсем другое.
>>904614 > теперь не нужно вспоминать, что ты правил в файле когда комитишь. Это просто визуализация git diff, такие плагины есть под каждый нормальный редактор кода.
Ага, позже нашел подобное в Komodo и NetBeans. Но таки в Visual Studio Code реализовано очень круто. Вообще программа очень шустрая, дистрибутив весит мало и работает без установки. Приятный отзывчивый интерфейс, особенно в сравнении с бобами и штормом. Можно обмазываться плагинами, куча их. В общем все как ты любишь, Анон. Серьезный конкурент Атому, Саблайму и прочим Нотпадам++. Рекомендую к ознакомлению.
Парни, объясните, как вы обеспечиваете инкапсуляцию сущностей?
Я посмотрел немного кода по первой задаче, но там пиздец. У всех в Student.php свойства public. Только один написал геттеры/сеттеры, но назвал методы не setId, а insertId (где он вообще видел такое???)
прочитал док и не понял: в php 5 есть вообще модификаторы доступа к методам и свойствам или они только в 7.1 появились?
>>905058 >Мне она показалась еще очень недоработанной В отзывах о последнем на данный момент релизе пишут, что по сравнению с предыдущим - небо и земля.
>необходимость обмаза плагинами - это говорит о недоработанности IDE. Я не согласне с этим утверждением. Т.к. необходиость обмазываться плагинами - это особенность, а не показатель недоработанности. Такой подход позволяет настроить программу следуя индивидуальным предпочтениям. Популярность Саблайма и Нотпада+ пример тому.
>>905028 > прочитал док и не понял: в php 5 есть вообще модификаторы доступа к методам и свойствам или они только в 7.1 появились? Область видимости для методов и свойств доступна с 5-й версии. В 7.1 появились модификаторы доступа для констант класса.
Геттеры/сеттеры это не самая главная часть инкапсуляции. Инкапсуляция - это о том, как прятать детали реализации и выставлять наружу лишь интерфейс: http://learn.javascript.ru/internal-external-interface А сущность Студент это ведь глупое хранилище данных, какие там детали реализации? Валидируется оно другим объектом, заносится в базу - тоже. Я руководствовался такими соображениями, когда сдавал своих студентов. Хотя, сейчас бы наверное использовал геттеры/сеттеры.
>>905071 >Я не согласне с этим утверждением. Т.к. необходиость обмазываться плагинами - это особенность, а не показатель недоработанности. Такой подход позволяет настроить программу следуя индивидуальным предпочтениям. Популярность Саблайма и Нотпада+ пример тому. Ну, это на любителя. А мне не нравится, что несмотря на заявленную поддержку пхп надо для работы с ним еще обмазываться плагинами, в то время как в нетбинс или пхпшторм это все искаропки. Кстати грузится оно у меня дольше чем визуал студия 15, лол.
>>905073 Мне нетбинс понравился, следующий проект буду в нем делать. А ты чаще полузуешься, нетбинсом или штормом? Полноценная визуал студия не устраивает?
>>905876 Я сам не пойму, т.к. шарю точно не больше твоего. Хотя кажись если ты вызываешь какой-то стандартный класс из из своего класса, то без слешей не работает, по крайней мере у меня тут http://ideone.com/9iSzfm к примеру без слешей точно не работает
>>905842 Оператор use. Но в данной ситуации (т.к. мы вызываем класс из корневого неймспейса) лучше использовать \PDO. Книга Мэта Занстры из шапки Начало Главы 5 можешь глянуть
> почему в одном случае есть \ перед PDO, а в другом его нет Если обратного слеша нет и всё работает - значит ты находишься в контексте глобального пространства имён. То есть вверху файла не указан неймспейс. Но если ты добавишь неймспейс для файла, к примеру Foo, то тайп-хинт PDO будет восприниматься PHP в пределах этого неймспейса как Foo\PDO. Поэтому для встроенных классов лучше использовать обратный слеш как указание на то, что класс нужно искать в глобальном пространстве имён (если ты в будущем планируешь использовать неймспейсы в проекте).
Поясните за index.php в корневой директории папки с сайтом. Почему он вызывается по умолчанию при вводе url сайта в браузере? Типа мой апач так настроен, что при вводе URL сайта по дефолту запускается index.php если таковой имеется, и дерево папок и файлов, если такого нет? Где и как это можно изменить, если, например, я хочу, чтобы по дефолту вызвался huindex.php? я ващпе rookie тут у вас здеся, и не очень прохавал, как именно работает апач и какие функции выполняет. (если он тут вообще каким то боком связан). В качестве web-сервера использую XAMPP, если это имеет значение.
>>906015 Говорить что ты используешь XAMPP в качестве веб-сервера - не правильно. XAMPP это сборка из веб-сервера, интерпретатора PHP и чего-нибудь еще. У тебя функции веб-сервера выполняет апач, именно он отвечает за то, какую страницу покажет браузеру и при вводе URL, и те вещи которые ты описал.
>>906015 >Поясните за index.php в корневой директории папки с сайтом. Почему он вызывается по умолчанию при вводе url сайта в браузере? Типа мой апач так настроен, что при вводе URL сайта по дефолту запускается index.php если таковой имеется, и дерево папок и файлов, если такого нет? Да. Точнее не по дефолту, а если ты заходишь на корень сайта. >Где и как это можно изменить, если, например, я хочу, чтобы по дефолту вызвался huindex.php? А вот не помню, где-то в настройках апача же. Только зачем тебе это? Лучше почитай про правила в .htacces
Затем обнаружил этот http://php720.com/, здесь тоже дошел до массивов. Собственно, хочу спросить - какой лучше, где чем отличается?
И еще. Возможно ли на моём этапе заняться хоть какой-либо практикой? Что вообще здесь можно сделать? Я заучиваю эти команды, функции, операторы, как найти определенную строку, то да се. А смысл то? Я чувствую как все это уже вытекает из меня. Практики хочется. К примеру, пхп это же язык для сайтов? Возможно ли сайт что-ли сделать? Найти какой-нибудь бесплатный или дешманский хостинг, я не знаю.
>>906046 >Я чувствую как все это уже вытекает из меня. Ну это норм, только заучивать не надо, надо практиковаться и тогда все само дойдет. >Практики хочется. К примеру, пхп это же язык для сайтов? Возможно ли сайт что-ли сделать? Найти какой-нибудь бесплатный или дешманский хостинг, я не знаю. Читай уроки ОПа, как сделаешь самые простые задачки приступай к студентам. Вот там напрактикуешься вдоволь. Задания выполняй на локальном сервере.
>>906065 А что по учебникам? Тот, который в оп-посте - более дружелюбный, и объясняют лучше. В том, который я обнаружил - больше инфы, но она какая-то сухая, и объясняют так, что раз с 10 догонишь, что хотят донести.
Не поможете пожалуйста? После выделенной команды(функции, оператора) Весь выводящийся ( кстати, как это называется, ну, то, что выводится из моих команд?) сместился на середину. Как это исправить? Вернуть после CMD команды на показ моего содержимого диска все налево?
>>898502 (OP) Господа, как можно этот запрос короче на чистом сокле написать? Мне надо чтобы поля, чьи плейсхолдеры равны нулям, вообще не выбирались, а оставшиеся поля выбирались как обычный запрос. рейтаните сокл запрос. на работе написал. http://ideone.com/kEdhTe
>>906469 Так как количество параметров произвольно, то одного SQL вряд ли хватит, нужно строить запрос средствами PHP. У меня попроще вышло: http://ideone.com/ZCcvJr Чтобы вызвать $this->db->placehold c произвольным количеством параметров можно использовать call_user_func_array или spread оператор.
>>906481 У него параметры функции могут быть null'ами, а не значения в базе.
>> someTypeOfSolarPanel.prototype = Object.create(SolarPanele.prototype); >ты тут делаешь наследование, но тогда хорошо бы в someTypeOfSolarPanel() вызывать конструктор класса-предка. А то кто-то допишет в него код (или модифицирует), а потом будет гадать, почему этот код не срабатывает в наслднике. Если нет каких-то причин не делать этого, то стоит вызвать конструктор предка. То есть конструктор предка заполняет нужные ему поля, конструктор наследника - нужные наследнику. Я предпологал что отдельный тип панели, это тот в котором "железно" определена мощность. Если этой причины не достаточно, то я просто переопределю конструктор класса-предка написав:
function someTypeOfSolarPanel(power) { SolarPanele.apply(this, arguments); }
>> for (var element in electricalnetwork.elements) { >> if (electricalnetwork.elements[element] instanceof PowerLine) { >Если делать это циклом то нет гарантии что мы продадим энергию по максимальной цене. Лучше отсортировать предложения по выгодности и продавать начиная с самой дорогой линии. Теперь можно написать программу по расчету самого выгодного предложения. Линия электропередач может посчитать какую сумму можно получить за определенное количество ватт PowerLine.countPrice(power), учитывая свою пропускную способность. Осталось только поместить эти данные в массив и отсортировать его. Но не будет ли это уже другой задачей?
У меня, кстати, есть решение по которому я сначала делал эту задачу, где происхдоит "имитация" торговли: https://jsfiddle.net/qvdz8y5j/ Такой вариант задачи мне нравится больше, потому что мы не просто считаем возможные варианты прибыли\мощностей, а действительно меняем систему.
>14. напиши функцию, определяющую тип переменной. Результат должен быть одной из строк: 'undefined', 'boolean' (для true/false), 'null', 'number', 'string', 'function', 'array', 'array-like', 'object' >https://jsfiddle.net/5q3r473h/ https://jsfiddle.net/5q3r473h/3/
>15. Напиши функцию неглубокого копирования объектов и массивов >https://jsfiddle.net/uyey3at1/ >> var clone = new object.constructor; >Тут есть подвох: JS не гарантирует что свойство хранит ссылку на фукнцию-конструктор. Оно хранит ее по умолчанию, но разработчик может перезапистаь его, а также забыть правильно прописать его при наследовании. Ну я думаю, что это можно оставить, но стоит помнить про такие вещи. В самой задаче копировать до такой степени не требовалось. Значит в js нету универсального решения чтобы получить имя класса - нужно писать самому под каждый отдельный случай?
>> for (property in object) { >Ты склонируешь не только свойства объекта, но и всех его прототипов. если у объект в прототипе есть метод, он будет перебран в этом цикле (у встроенных в JS объектов вроде массивов методы защтщены от этого и не перебираются циклом, а у пользовательских классов перебираются). Так это же хорошо что клонируется и прототип объекта, разве нет? Я думал, что прототип, это что-то, что принадлежит каждому образцу класса\объекта. Разве это не нужно клонировать?
>>898502 (OP) На многих видеотубах можно встретить такой адрес: domenname.com/video/34054 Что здесь что вообще? video это контроллер? А какой тогда запускается action? Не может же быть в контроллере action под каждый id видео. Или video это имя переменной а 34054 это её значение и обрабатывает этот запрос какой-то контроллер и его экшен по умолчанию?
>>906588 >domenname.com/video/34054 Можно так определять параметр. Например, в микро-фреймворке слим можно делать вот так: use \Psr\Http\Message\ServerRequestInterface as Request; use \Psr\Http\Message\ResponseInterface as Response; require 'vendor/autoload.php'; $app = new \Slim\App; //get() - метод, который в слиме делает разбор строки запроса $app->get('/video/{name}', function (Request $request, Response $response) { $name = $request->getAttribute('name'); //получаем название видоса / ну ты понел, тут стандартная работа с базой, шаблонами и прочее / }); $app->run(); Ютуб вроде на джанго написан, там такой роутинг делает
>>906514 быстренько накидал вариант на компе с экранной клавой http://ideone.com/G5xn2Q будет ли корректно работать call_user_func_array([$this->db, 'placehold'], $args);?
Как нормально интегрировать слим и твиг? http://ideone.com/SNttHo Я думал таким образом будет передаваться объект с твигом в колбек get'a, но не работает. $app->get("/", function (Twig_Environment $twig) { echo $twig->render('index.php'); });
Помогите пожалуйста с задачей на школьный рост из массива. http://ideone.com/EcokkU Суть такова - почему цикл не перебирает каждый элемент массива? Он вообще не работает.
>>906784 > Как нормально интегрировать слим и твиг? За тебя уже интегрировали: https://github.com/slimphp/Twig-View Это пакет-надстройка, которая пишет в тело PSR7-ответа то, что отрендерил твиг:
> $app->get("/", function (Twig_Environment $twig) {
написать
> $app->get("/", function ($req, $res, $args) use ($twig) {
но это глупо, слим использует контейнер, поэтому всё, что тебе нужно - это зарегистрировать твиг в нём. Класс App создаёт/получает контейнер и привязывает его контекст к callable: https://github.com/slimphp/Slim/blob/3.x/Slim/App.php#L234 bindTo это аналог bind из JavaScript
Кстати есть ещё шаблонизаторы, использующие нативный синтаксис PHP, например Plates. Такие шаблонизаторы очень просты в интеграции и использовании и не требуют от тебя изучать новый синтаксис.
>>906830 >Лучше початать исходник, а не ожидать магии от фреймворка. Если честно, мне крайне тяжело читать исходники фреймворка. Ну, хоть документацию нормально понимаю, хоть и не дочитал ее, видимо того и не знал что там твиг уже есть. >Как вариант в твоём случае можно пробросить твиг в обработчик роута через замыкание. То есть вместо Но все же я не понял почему мой способ не сработал, объекты реквест и респонд передаются же.
>>906864 >>906863 А PhPDesigner это что? Интегрированная Среда Разработки это что-то типа CMD.EXE на винде? Я просто гуглю, гуглю, и ни бельмеса не понимаю.
>>906843 Что делает слим, очень грубо и упрощённо: class Request {} class Response {} handler(new Request, new Response); // Передаёт аргументы в созданный тобой хендлер.
Созданный тобой хендлер: function handler(Twig $twig) { ____return $thig->render('template'); }
Очевидно, что в таком случае ничего не работает как минимум из-за тайп-хинтов. У Кантора про замыкания и контекст вызова хорошо расписано, ещё советую первые 6-7 задачек на JS из ОП-поста порешать. С чем тут ещё могут быть проблемы - не знаю.
>>906868 Да вроде тоже ИДЕ, но впервые вижу её. Вижу, ты совсем нюфаня. Короче, ИДЕ - это как текстовый редактор (читай, блокнот), но с кучей фишек.
Блокнот <<< редактор (типа саблайм, нотпад++ и тп) <<<<<< ИДЕ.
Второе - подсветка, маленькие ништяки типа автозаполнения уже введённых команд, но ещё не идеха.
Третьи - шторм, нетбинс(хуёвый дизайн и меньше плюшек), - полноценные станции для работы. Можно прикрутить компилятор, есть поддержка всяких плагинов и дерево файлов. Короче, в ней можно работать с целым проектом.
>>906871 >handler Что за хендлер? Ну я понимаю, что создаются переменные с объектами реквест и респонс. >Созданный тобой хендлер: Это ты имеешь ввиду $app->get()? Все равно не понял почему я туда не могу так же передать твиг. Без тайп-хинтов все тоже самое.
> Все равно не понял почему я туда не могу так же передать твиг. Потому что аргументы хендлеру передаёшь не ты, а слим. Короче, ты бы не задавал таких вопросов если бы понимал замыкания и как передавать функцию в функцию. Как это исправить я написал в предыдущем посте.
>>906886 >Потому что аргументы хендлеру передаёшь не ты, а слим. Не понял. Метод get умеет принимать реквест и респонд? > Короче, ты бы не задавал таких вопросов если бы понимал замыкания и как передавать функцию в функцию. Как это исправить я написал в предыдущем посте Да, тут ты явно прав.
Все понятно. Но этот array_rand выдает ИНДЕКС массива. А получить ЭЛЕМЕНТ, зная индекс? Я так понял, я должен гуглить это самостоятельно? Но как вообще подобное гуглить?
Поставил ПХПшторм - он там не читает русские буквы. Да и к ПХПдесигнеру привык как-то, он делает все то, что ты описал. Правда не знаю что такое компилятор и дерево файлов, но вроде переменные подсказывает, и такое.
>>904556 лся несколько часов с jsfiddle, но так и не смог залить туда новую версию. Соответственно, не могу поделиться, товарищ >>901673 ! Накодил почти все пункты (с использованием AngularJS, так как он мне понравился, буду дальше его копать тоже.). Очень хочу узнать твое мнение, но не знаю как показать результаты.*
Мучался несколько часов с jsfiddle, но так и не смог залить туда новую версию. Соответственно, не могу поделиться, товарищ >>901673 ! Накодил почти все пункты (с использованием AngularJS, так как он мне понравился, буду дальше его копать тоже.). Очень хочу узнать твое мнение, но не знаю как показать результаты.
>>906911 >он там не читает русские буквы Не называй это русскими буквами никогда. Это называется кириллица, и говорить "русские буквы" это признак тотального ньюфажества.
>>898502 (OP) Ребят посмотрите пожалуйста код, а то всем знакомым либо лень, либо просто не хотят помочь. Я уже несколько часов пытаюсь заставить роутер работать. Пытаюсь сделать базу mvc под задачу со студентами. http://rgho.st/7B9SFlwq9
>>907362 Я бы начал разбираться с гитом но я просто не могу пока этот код работать не заставлю. Так что не суди строго чувак мне этот код снится уже. Ошибки в синтаксисе искал, всё круто. В рекваерах рылся и кажется проблема там но на ларакасте именно всё так же.
Выполняю первое упражнение на регекспы. сначала идет +7 или 8, за ними ровно 10 цифр, между которыми может быть любое число скобок, минусов, пробелов Вот с этим вот "между" проблема. В качестве решения использую [()\-\s] , но это приходится вставлять несколько раз по ходу регекспа — некрасиво. В итоге получилось нечто уровня /^(\+[()\-\s]7)|8[()\-\s]([0-9][()\-\s]){10}$/u Есть два вопроса: есть ли способ реализовать это "между" лучшим путём, чем вставкой кода "эти символы могут встречаться, а могут и нет" после каждого обычного закодированного символа? Второй вопрос: в начале регеспа написал (\+[()\-\s]*7)|8, то есть фактически (\+7)|8, вроде бы всё чётко, но почему-то номер +8 234 5678901 прошёл регексп. Если кому-то здесь не лень возиться с регеспами, не подскажите, как так вышло?
>>898828 >Увы, пока неправильно. Вроде, после прочтения в гугле, начинаю что-то понимать, а не думать на угад. Единственное непонятно почему в 3-ем примере возвращается window а не z, ведь fn это ссылка на метод объекта.
>>898828 >> Нужно стараться решать самому >Да, верно, нужно самому гуглить, искать документацию, так как например на работе никто не будет за тобой по пятам ходить и решать проблемы за тебя. Но если ты не смог сам найти решение, то наверно смысла дальше ждать нет. Можно попросить подсказку или спросить, в каком направлении дальше гуглить. Мы тут все-таки не злодеи, поможем, тем более если человек сам старается найти решение. Я должен пройти по сложному пути чтобы в будущем быть сильнее. В прошлый раз меня это научило тому что преодолевая трудность, я экономлю больше времени, чем не не делая этого.
Поясните за паттерн dependecy injection conteiner. Я так понял, это когда всякие вещи типа соединения с БД, подключаемых плагинов и прочего просто сначала засовываешь в одну сущность, а потом из нее вызываешь по необходимости?
>>907493 Классы не должны создавать другие классы, потому что тогда возникает тесная связь - это очень плохо, код становится сложно расширяемым. Чтобы этого не было, мы юзаем Dependency Injection - классы просто получают готовые экземпляры классов и сами ничего не создают. Все хорошо работает, пока у нас есть только index.php и пара классов. Но когда приложение становится сложным, у нас появляются контроллеры, рутеры, классы бд, вьюхи и еще куча всего. Все это хозяйство должно создаваться в бутстрап файле, откуда расходится по соответствующим классам через Dependency Injection. Бутстрап файл быстро раздувается, там куча всяких параметров и даже ненужных классов, которые не юзаются. К примеру мы вызвали контроллер страницы 1, а создаются контроллеры и цепочка классов для страницы 2, которые потом не используются. Это естественно все не очень хорошо. Или мы хотим поменять один из классов, который получает один из контроллеров, тогда надо лезть в этот бустрап код, искать где создается класс, менять его на другой, создавать для него всю цепочку зависимостей. Все эти проблемы решает Dependency Injection Container. Там просто все зависимости описаны в явной форме. Захотел поменять класс - описал его в конфиге вместе с зависимостями, и поменял там же в конфиге у контроллера завимость на новый класс. Все работает, старый код мы не меняли. Кроме того Dependency Injection Container не создает всю тучу классов как бутстрап файл, он создает только нужные для конкретного случая. Страничка будет грузиться быстрее, меньше памяти съедать.
>>907509 Максимум криво написано. Чому bootstrap не юзает композер для создания классов? Зачем все эти require в коде? Метод load в роутере получает файл, но создает сам себя? Пиздец логика. routes.php работает с переменной, про которую вообще ничего не знает? Бляя, я уже за голову хватаюсь. Все максимум криво. Карта роутов должна быть либо в конфиге, либо в самом роутере прописана, либо отдаваться роутеру посредством dependency injection и интерфейса. Но никак не через этот кривой механизм с require и неизвестной переменной.
>>907518 Композер сразу подключают, тогда всех этих десятков require у тебя в коде не было бы. Late static binding там вообще не нужен, у тебя нет классов, наследующих от роутера. new self() вполне хватило бы. Не работает из-за кривых роутов видимо, делай var_dump(trim($_SERVER['REQUEST_URI'], '/'));
>>907523 Я ларакаст смотрю там он позже композер ставит. А разве меж new self и static разница есть? У чувака оттуда new static. Var_dump делал уже, чтобы протестить на обычный индекс возвращает '/' и роутер находитю
>>907524 Я не смотрел твой ларакаст, у чувака скорее всего причина была, его использовать, может у него наследования там. У тебя не вижу, у тебя роутер единственный класс. Композер сразу подключают, потому что потом тебе придется по всем файлам рыться и удалять эти require и менять все .php на названия классов. Если в ларакасте сразу не подключили, то чувак просто не понимает смысл композера.
>>907528 Переписать метод Request:uri(). Он у тебя возвращает не то, что routes.php ждет. При наличии аргументов нужно парсить урл и извлекать часть. Я тебе для этого var_dump и сказал делать.
>>907533 Лол, этот код просто аргументы у uri съедает. Ты var_dump не делаешь что-ли? Вообще чего ты добиться пытаешься? Если чтобы /about на about редиректил, то тебе .htaccess сначала нормально написать надо. У тебя там стоит просто index.php индексом считать, но про редиректы ничего нет.
>>907533 RewriteEngine on DirectoryIndex index.php # If requested resource exists as a file or directory go to it RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule (.) - [L]
# Else rewrite requests for non-existent resources to /index.php RewriteRule (.) index.php?route=$1 [L]
Вот так это делается. Тогда при вызове /about?arguments ты будешь получать редирект на index.php. В $_GET у тебя будет route=about, а аргументы ты можешь вытащить из REQUEST_URI.
>>907537 Ты просто роутер делаешь, не представляя как оно все вместе работает. Если .htaccess не делает редирект, то тебе никакой роутер не поможет, апач его просто запускать не будет.
Поясните за такой вот ньюанс. Я тут как-то вкидывал код, по моему про вектор, и там у меня был допустим родительский класс сотрудник, в которого были вынесены все общие для его наследников методы.
Но мне сказали что мол нельзя что бы родители что-то там знали о потомках, ВУТ? Что это блин значит и вообще как тогда наследоваться, если родителям не пихать общие для наследников методы / поля?
Класс ничего не может знать про своих наследников. Причина очень простая: когда ты пишешь класс, ты ведь не можешь заглянуть в будущее и угадать, кто и как будет писать классы-наследники.
Ты не можешь вызывать методы и обращаться к полям, которых нет в текущем классе.
Но есть специальный способ указать, что в наследнике должен быть определенный метод. Для этого надо сделать абстрактный метод - метод, у которого нет тела и который наследники обязаны реализовать, подробнее: http://php.net/manual/ru/language.oop5.abstract.php
Другой вариант - сделать в классе-предке реализацию метода по умолчанию и предоставить потомкам возможность переопределить его. В отличие от варианта с абстрактным методом, они это делать не обязаны.
Звездочки потерялись в коде, скобки в регулярке не нужны.
> Тогда при вызове /about?arguments ты будешь получать редирект на index.php Неточно. Редирект - это HTTP ответ с кодом 3xx, и визуально его можно увидеть по изменению адреса страницы в браузере. Тут не редирект, а rewrite (переписывание URL), просто настройка, как на сервере будет обрабатываться запрос.
Также, надо писать [L, QSA] чтобы не терялись параметры после знака вопроса в URL. route=$1 лучше убрать, исходный URL доступен в REQUEST_URI.
> Единственное непонятно почему в 3-ем примере возвращается window а не z, ведь fn это ссылка на метод объекта. В JS нет такой сущности, как "ссылка на метод объекта". Там и методов-то нет - это просто функции, сохраненные в поле объекта. Потому конструкция
var fn = obj.method;
Просто копирует функцию из поля method в переменную. И при вызове fn() функция вызывается с this = window.
this определяется тем, какой конструкцией вызвана функция:
- obj.method() - this = object - method() - this = window - method.apply(x) - this = x
> Я должен пройти по сложному пути Может тогда можно попросить какую-то дополнительную задачку, которая поможет разобраться? Или например можно спросить, что почитать по теме.
Знак | имеет очень низкий приоритет, то есть /a|bc/ значит /(a)|(bc)/, а не /(a|b)c/. У тебя получается выражение /(^+7)|(8xxxxxx)/. То есть неправильное, в общем-то выражение. И потому неправильный номер его прошел. Сгруппируй символы с помощью круглых скобок.
Также, после плюса минус идти не может.
Остальное в регулярке верно.
> В качестве решения использую [()\-\s] , но это приходится вставлять несколько раз по ходу регекспа — некрасиво. Я не знаю способов, как обойтись без этого.
Удобнее было бы выкладывать на сайты вроде github, чтобы мы могли просмотреть код не скачивая. Если кода немного, можно просто склеить в один файл или и выложить на pastebin.
Если есть возможность, лучше выкладывать код туда, где его можно просмотреть не скачивая.
Если не работает - ставь вар-дампы и эхо (или используй отладчик, если умеешь), чтобы увидеть, что выполняется, в каком порядке, и чему равны переменные.
Проверь, включен ли вывод ошибок, если нет, смотри логи Апача.
В конфиге стоит писать только параметры, которые может менять пользователь. Параметры вроде опций PDO пользователь менять не должен.
> require Router::load('routes.php') > ->direct(Request::uri()); Непонятная конструкция, в первый раз такое вижу. Лучше это писать в несколько строчек, а не пытаться упихнуть в одну.
Также, советую поменьше использовать статические методы. Вместо Request::uri() лучше сделать обычный объект. Запросов ведь в теории может быть и несколько, или мы можем захотеть для теста создать временный объект запроса.
В путях надежнее указвать абсолютные пути.
Способ определения конфига в routes.php мне не очень нравится так как там непонятно, откуда берется переменная $router. Не лучше ли было хотя бы функцию с аргументом $router?
При отстутвии роута надо выдавать страницу 404, а не исключение.
> с использованием AngularJS, так как он мне понравился, буду дальше его копать тоже Не представляю, как использовать фреймворк для создания SPA приложений ради показа окон, так что интересно посмотреть.
IDE = редактор кода + управление файлами в проекте + отладчик + умное автодополнение + иногла проверка синтаксиса + вызов интерпретатора + иногда управление сервером и тд. Этакий комбайн, содержащий в себе инструменты для работы над проектом. До появления IDE это было в виде отдельных программ.
Заметь, что если используется анонимная функция, то в ней $this указывает на DI контейнер:
> If you use a Closure instance as the route callback, the closure’s state is bound to the Container instance. This means you will have access to the DI container instance inside of the Closure via the $this keyword
Вообще код оформлен плохо и тяжело читаемый. Например, зачем писать длинные строки, когда можно писать каждое действие на своей строке?
Вместо call_user_func_array([$this->db, 'placehold'] лучше сделать функцию, которая принимает массив значений.
Нет никакий защиты от SQL инъекций, то есть значения ключей массива вставляются прямо в запрос без проверок. Если это форма поиска то лучше указать возможные значения критериев поиска. Или даже сделать условия поиска в виде объекта.
Ничего не говорящие названия переменных вроде $a, $v. Зачем-то из массива удаляются элементы во время цикла по нему.
Непонятно, что делает explode. Что-то удаляет из строки?
Непонятно зачем там стоит return false. Кто-то будет проверять результат вызова? Скорее всего нет.
- сделай аякс-голосование за статью (лайк/дизлайк). Запрос отправляется на сервер и возможны разные варианты: голос принят, пользователь уже голосовал, пользователь не имеет права голосовать, и тд. Серверная часть не особо важна, можно просто сделать простой php-скрипт, возвращающий случайно один из вариантов.
- сделай форму с аякс- и обычными проверками полей по мере ввода данных. Желательно не хардкодить правила проверки, а например, передавать в код JSON-конфиг с правилами валидации полей, либо указывать их как data-атрибуты HTML-тегов, то есть сделать универсальное решение.
- если делать аякс-запросы с индикатором загрузки, выводом ошибок, то можно обнаружить, что каждый раз приходится копипастить похожий код. Попробуй написать функции, упрощающие отправку AJAX запросов.
Можно использовать jQuery, можно другие библиотеки. Дублирования кода надо избегать.
>>907780 private на конструктор ставят, чтобы нельзя было создавать класс через $instance = new Class(). Тогда тебе придется написать в классе функцию: public static function fromParameters($parameter1, $parameter2) { if (self::parametersAreValid($parameter1,$parameter2) { return new self(); } throw new \Exception('ошибка создания класса'); } Т.е. класс можно будет создать только через специальный метод. И только если параметры пройдут валидацию, иначе кинет exception. Вариант с публичным конструктором ничего такого не гарантирует.
>>907789 а зачем это вообще нужно? То есть выставка private на конструкторе делает по сути весь класс приватным? Что бы левый вася в коде не мог не просто к полям и методам твоего класса обращаться, а вообще не мог класс создать прям? И только если входные данные верны, то тогда даем возможность создать класс?
>>907873 >тот же нет, все равно все через роутер пойдет бля, судя по хтакцсесу у тебя на index.php и, соответственно, на роутинг - уходят только те пути, по которым не найдено реальных файлов или папок. Если файл или папка по пути из запроса существуют - апач должен отдать их, а не реврайтить на index.php. Копай в эту сторону, если че - спрашивай
>>907792 Да, именно так. Левый вася вынужден будет использовать наши статитеские функции для создания класса, и не накосячит, потому что там валидаторы. Если валидация не прошла, экземпляр класса он просто не получит.
Пока ничего не исправлял, просто попытался прикрутить доктрину, чтоб все работало. Не понял зачем нужен кеш и прокси. Вообще мне кажется, что я как-то неправильно использую доктрину, ну или просто это проект слишком маленький. Не могу понять как вытащить куки из $app. Делаю дамп и нахожу там ее значение, а request stack пустой и в сам request не пойму как пробиться. В общем. надеюсь исправить все до проверки опом. https://github.com/anotherCodeMunkey/fileshare
сам подумай, какая разница между обьектом с приватными свойствами, но с геттером-сеттером для каждого свойства, и обьектом с публичными свойствами, но без дурацкого бойлерплейта геттеров-сеттеров.
инкапсуляция имеет смысл только тогда, когда у тебя нет сеттеров, а вместо них методы с поведением сущности. Если у сущности поведения нет - нафиг этот бред
>>905028 Есть все в php5. Все свойства должны быть приватными или защищенными, иначе будет порнография с левыми васями, меняющими свойства в твоем классе как угодно. С сеттером ты по крайней мере имеешь контроль над поведением Васи, который не поломает функциональность класса и свой код. Если решишь вдруг, что свойство нельзя менять или менять можно только по определенным правилам - дописываешь просто проверку в сеттере.
>>907920 > не хочет принимать моё решение Так как твоя программа направильно находит символ для последовательности ["O", "Q", "R", "S"];
> $missing = ord($findMissingLetter[0]); Но ведь в этой переменной не хранится код пропущенной буквы, а ты называешь её missing. $bd и $db вносят путаницу, как и цикл foreach, который не использует ни $key, ни $value. 2 счётчика не нужны, достаточно одного, который будет проходить поэлементно, сравнивать код текущего символа с кодом следующего и если их разница != 1, то - получить код текущего символа, увеличить на единицу - вернуть символ, соответствующий коду.
>>907756 >Все ссылки на англ, но с примерами кода. По поводу твига. Уже читал эту ссылку https://www.slimframework.com/docs/features/templates.html но так как тут описано у меня не работает. Команда composer require slim/twig-view не работает, говорит что чего-то не хватает(завтра могу скрин показать). Однако, я посмотрел в стандартном пакете слима уже есть твиг, и я попросту создал новый объект твига по инструкции из его документации, закинул в контейнер и юзаю в колбеке. При этом у меня нет в объекте app методов типа view или twig, получилось работать либо из контейнера, или из замыкания. Алсо еще вопрос по слиму. В колбеке не работает стандартная ф-я header(), ну что бы сделать переадресацию после записи в БД. Т.е. раньше я делал по принципу "если через пост-запрос что-то пришло, то сделать запись в БД и перенаправить на ту же страницу с помощью header('Location: /'). Я так понимаю, тут это как-то по своему реализовано, как мне сделать правильно?
>>907753 >> Единственное непонятно почему в 3-ем примере возвращается window а не z, ведь fn это ссылка на метод объекта. >В JS нет такой сущности, как "ссылка на метод объекта". Там и методов-то нет - это просто функции, сохраненные в поле объекта. Потому конструкция > >var fn = obj.method; > >Просто копирует функцию из поля method в переменную. И при вызове fn() функция вызывается с this = window. Но почему копирует? Функция это же объект и должна быть ссылка!
В мануале мазилы, кстати, сказано, что это методы, но наверно это так просто для простоты написано. Поэтому я их так назвал.
>Может тогда можно попросить какую-то дополнительную задачку, которая поможет разобраться? Или например можно спросить, что почитать по теме. Если у вас есть время, то давайте. Только не очень большую. Я в принципе начинаю понимать, просто нужно убедиться что нету пробелов в понимании, как например с примером выше.
Как разрешить такую ситуацию? То есть объект я создаю 1 раз, а потом 2 ссылки на него могу добавить в массив. Как избегать такого? Или это из разряда о чем не стоит греть голову? И так нормально оставлять, ведь никто не будет пытаться 1 объект 2 раза затолкать в 1 и то же место?
>>907979>>907994 как же я всё таки плох в программировании не смотря на то, что уже год работаю веб-мартыхой :( Смотри какое я дерьмо нагородил и оно еще и не работает почему-то http://ideone.com/8MjoiC
Наверное потому что нельзя форичить пустое своей свойство, в общем хоть с работы увольняйся и сиди дома вот так с анончиками постигай пробелы в пхп и ООП.
Алсо что касается второго твоего примера, я вот не совсем понял почему у тебя там нет ошибки. Ведь любой код насколько я знаю который может выкинуть исключения должен быть обернут в try блок (читал только об этом, сам не юзал)
У меня во всяком случае тупое выкидывание исключения давало эрор.
>>907997 Ты там какой-то дикой хуйни нагородил. Алсо, я так понял не работает потому что ты сравниваешь по типу, т.е. раз ты проверяешь объект === объект то $exist всегда будет тру, лол. Попробуй просто по значению == сравнивать, хотя все равно не понятно зачем ты такую хуиту нагородил.
>>908002 О, братишка. Меня один знакомый агитирует утроится к нему в говноконтору макакировать на битриксе. Только я сомневаюсь потому что из каждого утюга кричат что битрикс это лютейший говнокодище и явно не лучший выбор что бы начать карьеру. Можешь пояснить за суть?
>>908006 бля, я хз как в кратце рассказать про это говнище. там просто все хуево и все не так, как должно быть в 2к17, лол. например, шаблон, который тянет компонент - может лежать примерно в 20 (!!!!!!!!) разных местах - и ты хуй когда узнаешь, где он, пока не переберешь все пути. ну там конструкторы в которые передается 25 переменных по ссылке - классика. мешанина логики, хтмл/цсс - на каждом шагу. ВСЕ БЛЯТЬ ЧЕРЕЗ ЖОПУ, ПОНИМАЕШЬ? открываешь блять какой-нибудь класс, а там хуяк - 2к строк кода с ифами уровней на 15 вложенности ОЛОЛО СУКА НЕ НАПОМИНАЙ БЛЯТЬ СКОРО НА РАБОТУ СУКА!!!11111))))))
Аноны не желает кто нибудь стать ментором за небольшие деньги помогать учить анону. Теорию впаривать не надо будет почти просто решать со мной задания ОПа, хочу файлообменник сделать своим первым пет проектом и в диплом его отправить.
>>908017 Да я то понял. Только нахуя это все гет-запросом передавать?(я понял он это имел ввиду). И если данные так разрастаются - почему нельзя аргументы передавать массивом и нормально внутри метода это разбирать? Это же не тяжело такое по людски реализовать.
Поясните вот за это. Как 0x1A стало 26? Как это работает? Как считается? Я начал гуглить, но там охуительные истории, в которых нужно ещё разбираться, пока шишка не встанет. Поясните кратко. Кто-нибудь. Пожалуйста.
Скажите, правильно ли из метода модели, вызывать при надобности напрямую другой метод другой модели например? Или архитектурно правильнее будет например что бы модель вызывалась только из контроллера и ты из контроллера обращался в модель, получал от неё что-то и потом запрашивал другую модель?
Вопрос наверное звучит глупо и труднообъяснимо, но иногда когда я пишу на своём неуважаемом в этом треде Codeigniter'e у меня возникает именно желание из модели вызывать другую модель, такие дела.
private на конструкторе запрещает создание объектов за пределами класса. Мы можем сделать публичную статическую функцию и создавать (или не создавать) объекты через нее. Или может это класс, реализующий паттерн Utility class и его объекты нельзя создавать в принципе.
Проверку параметров можно сделать и в конструкторе. А вот статический метод позволяет например вместо создания объекта иногда делать что-то другое, например, возвращать ранее созданный (паттерн Singleton).
Проверку параметров можно сделать и в конструкторе. А вот статический метод позволяет например вместо создания объекта иногда делать что-то другое, например, возвращать ранее созданный (паттерн Singleton).
надо настроить htaccess, чтобы обращения к существующим файлам не обрабатывались в index.php. Обычно это флаги RewriteCond ... -f и -d. Также, можно определять файлы по расширению или по шаблону URL.
Кеши бывают разные, думаю речь о кеше метаданных. Доктрина читает информацию о замапленных классах и полях либо из yml-конфигов, либо из аннотаций в PHP комментариях. Процесс чтения не бесплатный и требует времени на разбор, и т.д. Соответственно есть идея кешировать эти метаданные, чтобы не читать их каждый раз при запуске скрипта.
Есть 2 режима:
- девеломпент (режим разработчика) - доктрина будет проверять дату модификации PHP файла и автоматически перегенерировать кеш при изменении или при отсутствии. - продакшен (рабочий режим) - время обновления PHP файла не проверяется. Кеш сам не создается. Метаданные нужно создать явно, вызвав специальный метод подогрева кеша. Это можно делать например, скриптом, при деплое (выгрузке кода на сайт). При деплое мы вкладываем код в новую папку, генерируем в ней кеш и переключаем веб-сервер на эту папку. А затем удаляем старую.
Есть и другие кеши, например, кеш DQL-запросов. Разбор DQL кода и преобразование в SQL не бесплатное. Надо настроить кеширование этих данных (тут заранее их сгенерировать нельзя и кеш генеруется по мере надобности).
Ты должен рассмотреть существующие варианты реализации кеша, и выбрать наиболее оптимальный.
Бывают еще другие кеши - например, кеширование результатов запроса - держись от них подальше, от них больше проблем, чем пользы.
Теперь насчет прокси. Прокси-классы - это автоматически сгенерированные классы-наследники сущностей (моделей из твоего кода), обеспечивающие "ленивую" загрузку. Прокси-класс можно создать, указав тип и id сущности ($em->getReference(...)). При попытке вызова любого метода прокси-класса, кроме getId(), сначала произойдет подгрузка данных из БД в поля объекта и только потом будет вызван метод предка. То есть твой код не должен ничего знать о ленивой загрузке, это делается прозрачно.
Зачем нужна ленивая загрузка? Представь, у тебя есть объект Комментарий, у него поля Статья и Пользователь. Если нет ленивой загрузки, мы должны в каждый комментарий проставить эти объекты (а у них могут быть свои связи) и в итоге мы должны будем загрузить базу целиком в память. Вместо реальных Статьи и Пользователя мы создаем прокси и эконоим на запросах в БД, при этом при обращении к ним данные сами подгрузятся, как будто они всегда там и были.
Почему нужно наследование? Чтобы объект-прокси был совместим с исходным классом и проходил тайп-хинты, проверки на instanceof итд. По правилу Лисков (объект-наследник можно использовать вместо объекта-предка).
Почему кодогенерация? Ну не руками же писать.
Как и с кешем, там тоже есть 2 режима, автоматическая генерация и генерация явным вызовом скрипта.
Прокси-классы создаются разумеется на диске в отдельной папке.
> Не могу понять как вытащить куки из $app. Возможно надо это делать в контроллере или функции-обработчике.
> Команда composer require slim/twig-view не работает, говорит что чего-то не хватает(завтра могу скрин показать). Надо разобраться, почему.
> Однако, я посмотрел в стандартном пакете слима уже есть твиг, и я попросту создал новый объект твига по инструкции из его документации, Если это skeleton, то это сборка, я бы советовал лучше Слим учиться с нуля через композер ставить.
> В колбеке не работает стандартная ф-я header(), ну что бы сделать переадресацию после записи в БД. Там у response можно заголовки добавлять и может даже есть метод для редиректа. Ты должен заполнять объект response, а не выводить все сам.
> Но почему копирует? Функция это же объект и должна быть ссылка! Конечно, копирует ссылку на функцию.
> В мануале мазилы, кстати, сказано, что это методы, но наверно это так просто для простоты написано. Поэтому я их так назвал. Возможно что даже где-то в стандартах JS есть слово "методы". Но фактически-то это просто функции, записанные в поля объекта (хотя в ES6 для них есть специальный синтаксис в определении класса). Тут имеется в виду "this указывает на объект, если метод был вызван с помощью синтаксиса obj.method()".
Кстати, там упомянут еще метод bind() из ES5, создающий функцию, вызвающую исходную функцию с заданным this.
> Если у вас есть время, то давайте. Только не очень большую. Я в принципе начинаю понимать, просто нужно убедиться что нету пробелов в понимании, как например с примером выше.
Вот по возрастанию сложности:
Задача 1: написать функцию bindContext(fn, that). Она создает новую функцию, которая при вызове вызывает fn с указанным this и переданным аргументами. То по сути есть привязывает произвольное значение this к функции. Использование:
function x(a, b, c) { return [this.test, a, b, c]; }; var that = { test: 1 }; var result = bindContext(x, that)(10, 20, 30); console.log(result); // [1, 10, 20, 30]
Использовать Function.prototype.bind из ES5 нельзя.
Задача 2: сделать функцию addProperty(object, name, initialValue) для создания приватных свойств с геттерами и сеттерами на объекте или прототипе объекта.
Функция добавляет 2 метода для доступа к свойству - геттер getName() и сеттер setName(x). Само свойство должно быть приватным и не доступно для внешнего кода никаким образом.
Использование:
var obj = { }; addProperty(obj, 'test', 10); console.log(obj.getTest()); // 10 obj.setTest(100); console.log(obj.getTest()); // 100 console.log(obj); // { getTest:..., setTest: ...} - самого свойства тут нет
Аналогично можно добавлять свойства в прототип:
function User() { }; addProperty(User.prototype, 'name', 'Иван'); var u1 = new User; console.log(u1.getName()); // 'Иван'
Задача 3: сделать функцию для добавления в объект или прототип нового метода addMethod(object, name, fn). Использование:
var o = {}; addMethod(o, 'test', function () { return 1; }); console.log(o.test()); // 1
Это конечно просто и неинтересно, потому добавим еще один пункт: если мы добавляем метод в класс-наследник, должна быть возможность обратиться к методу из класса-родителя с помощью слова super(...) (если предка нет, то бросаем RuntimeError или что-нибудь аналогичное):
function Parent() { this.x = 1; }; Parent.prototype.test = function (a) { return [this.x, a]; }; function Child() { Parent.call(this); this.x = 2; }; Child.prototype = Object.create(Parent.prototype); addMethod(Child.prototype, 'test', function (a) { var array = super(20); array.push(a); return array; });
var ch = new Child; console.log(ch.test(30)); // [ 2, 20, 30]
В JS такой возможности вызывать родительский одноименный метод нет, так что она нам пригодится.
Я еще хотел сделать пример с наследованием от встроенного класса (например, Array), но судя по статье http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/ (англ), это требует каких-то сложных хаков. Невозможность наследования от встроенных классов напоминает нам, что в JS все же есть прототипы, а не полноценное наследование в привычном виде.
Хорошая задачка получилась, правда? Может ее в список задач как бонусную добавить?
По умолчанию in_array делает нестрогое сравнение (аналогично ==), то есть просто проверяет что у объектов одинаковые значения полей, а не то что это один и тот же объект. Надо использовать специальный третий аргумент, см мануал.
Там код как учат в старых учебниках по PHP: куча глобальных переменных и функций. Ни ООП нормального, ни MVC. Ни PSR. Стиль кода - адский.
Сама архитектура там "ядро - компоненты" вместо MVC (как в древних CMS). Соответственно в "компоненте" вместе идут и модель, и контроллер, и в запущенных случаях, вью.
Посмотри официальные уроки от Битрикса с примерами кода (ну чтобы не говорили, что это я сочинил):
Обрати внимание на стиль кода, var из PHP4, и кучу рутинного кода по копированию файлов. Какой еще композер? Изобретай свой велосипед.
Данные передаются в каких-то массивах неизвестной структуры.
И еще это: > Модуль необходимо создавать в кодировке windows-1251, при установке его на сайт с кодировкой UTF-8 происходит автоматическая перекодировка. > Помните, что только языковые файлы из папки /ru/ конвертируются в кодировку сайта
То есть часть сайтов у них в одной кодировке, часть в другой.
В общем, погуглите сами, не ленитесь. Ну и может какой доброанон, который знаком с этим своеобразным миром, еще какие-нибудь интересные ссылочки подкинет.
Там массивы и используются, только я боюсь там постоянно надо вар-дампом выяснять, что в них находится. Я такое в Друпале видел, сотни массивов, нигде толком не документировнных, и что самое интересное, часть элементов иногда есть, а иногда нет, и понять, почему, очень непросто.
Кроме десятичной системы, где используется 10 цифр, и после числа 9 идет число 10, есть и другие. В 16-чной системе 16 цифр (0-9, a-f) и перед числом 10 идет число f. В двоичной - две цифры (0, 1) и перед 10 идет 1.
Важно понимать, что числа те же самые, просто записываются в другом виде и другими цифрами.
Можно, почему нет. Только у тебя там наверно не модели (представляющие какие-то сущности), а сервисы (классы, содержащие разные методы с бизнес-логикой).
> Вопрос наверное звучит глупо и труднообъяснимо, но иногда когда я пишу на своём неуважаемом в этом треде Codeigniter'e у меня возникает именно желание из модели вызывать другую модель, А, ты про CI? Там "моделями" называют ведь классы работы с БД? Тогда лучше сделать сервис, и пусть он вызывает методы 2 моделей. А вообще, CI за образец брать не стоит, там же ад, контроллеры наследуются от основного класса фреймворка.
12. Некая сеть фастфудов предлагает несколько видов гамбургеров
> https://jsfiddle.net/y28h2o2b/2/ > if (Error.captureStackTrace) { Ага, в JS так просто не унаследуешь исключения, и вообще встроенные классы, известная проблема. Напоминает о том, что в JS все же прототипное ООП, а не классическое.
>>908072 Только сейчас шел с трени и хотел в треде про друпал спросить. Потому что год назад ходил на недельное обучение по друпалу и охерел от того что там всё как-то очень непонятно было. Всё на хуках каких-то построено, сложно было для меня. Правда тогда вроде как вышла новая версия в которой обещали друпал перекатить на ООП и что теперь в нем всё будет ок. Интересно в треде кто-нибудь работает на нем?
>А, ты про CI? Там "моделями" называют ведь классы работы с БД? Ну по большей части да, простейшая логика на игнайтере выглядит так примерно:
Модели - просто наверное эволюция functions.php, в которых лежат методы которые что-то отдадут / посчитают /сохранят / изменят. Никаких сущностей там нет. Там по вездесущему $this есть гигантский синглтон, в котором можно вот так вот всё вызывать.
Контроллер - в 50% случаев спрашивает что-то у модели и передает это во вьюху, еще в 30% смотрит что пришло в каком-нибудь $_POST/$_GET и отдаст после обработки в модель что бы та засейвила в базу. Собственно это управляющая логика с проверками и прочим.
Вьюха - эволюция template.php в котором html смешан с <?=$name?> и так далее.
Собственно простейшие приложухи на этой логике делать безумно просто и быстро (я бы даже студентов написал наверное за час на игнайтере, они как будто под него и сделаны, но попытаюсь написать абстрагировавшись от игнайтеровского стиля), но иногда такая вот цепочка уже не работает. Например сделал юзер заказ, есть моделька которая засейвит в базу то что был совершен заказ. И есть "моделька" которая сгенерирует pdf например с квитанцией на оплату.
И тут передо мной открываются собственно 2 пути, либо вызывать из одной модели другую сразу, либо возвращать данные в контроллер из первой, и эти же данные передавать из контроллера во вторую при вызове.
Это наверное как раз когда у тебя уже не хватает игнайтеровских "рамок" а рвется наружу простое ООП, где классы должны обмениваться друг с другом данными как им удобно, а не обязательно быть загнанны в такой вот "MVC паттерн".
В общем спасибо тем кто прочел этот высер. Постараюсь дальше расти и вкуривать ООП, что бы мочь нормально написать с 0 студентов и прочий файлообменник.
Также, набор условий для поиска можно представить в виде объекта. Это требует описания класса, зато код будет намного понятнее, можно добавлять методы, и в классе можно написать подробные комментарии. Коллеги будут благодарны.
> https://github.com/fidnex/filehost/blob/master/dump.sql#L24 > FOREIGN KEY fk_parent (`parent_id`) REFERENCES comments (`id`) > ON DELETE SET NULL, То есть при удалении родителя дети становятся комментариями верхнего уровня? Так и задумано или ошибка?
Это по какой-то задаче вопрос или в общем? Если в общем, то обычно серый оверлей предназначен для 2 целей:
- сконцентрировать внимание пользователя на попапе - не позволить ему случайно нажать что-то вне попапа (по хорошему надо еще перемеситить фокус ввода на попап и не давать табом уйти из попапа)
Чтобы делать сайты, учи параллельно HTML - тогда ты сможешь делать простые статичные странички. А когда ты захочешь сделать их интерактивными, тебе понадобятся классы, база данных, MVC - все то, что ты будешь изучать в курсе по PHP, когда дойдешь до студентов. И поиск строк тоже понадобится.
Опция DirectoryIndex в конфиге Апача задает, какие файлы должны использоваться, если URL соответствует папке на диске. Там обычно стоит DirectoryIndex index.php index.html
> А сущность Студент это ведь глупое хранилище данных, какие там детали реализации? Это у тебя простой проект. А на практике получаются такие вещи:
- у Студента нужна Дата Модификации, и она должна проставляться при любых изменениях - надо вести историю изменения какого-нибудь свойства - есть какое-то свойство, которое вычисляется из другого или еще как-то от него зависит (в таких случаях лучше конечно не делать вообще такое свойство, но иногда оно нужно по соображениям оптимизации БД)
Лучше ставить в функции return индекса, а вывод делать снаружи. А то твою функцию в программе использовать нельзя и что-то делать с результатом работы.
Надо разобраться с общими идеями асинхронной работы (stream_set_blocking, stream_select), затем посмотреть React\Stream и React\EventLoop ну и дальше смотреть сокеты.
Как правило, исключения по умолчанию никто не ловит и делается один обработчик непойманных исключений который логгирует их и выводит страницу-заглушку 503 для пользователя.
В одном методе нет смысла писать throw и catch так как проще поставить if. Throw пишут для случаев, когда выбрасывается в одной функции, а ловится в другой.
Рассматривай исключение как способ функции сказать вызывающему ее, что что-то не так и она не может выполнить свою работу.
> Но непойманное исключение прекратит выполнение программы, причём — покажет голую информацию о коде пользователю, разве это допустимо? В PHP если поставить display_errors = 0, то покажет пустую страницу (и запишет исключение в лог). Но это лучше чем продолжать выполнять код. Конечно, это ошибка проектирования PHP: непойманное исключение должно приводить к выводу страницы-заглушки с HTTP кодом 503. Плоховато разработчики PHP знали HTTP тогда.
Обычно ставят один глобальынй обработчик исключений, который их логгирует и выводит заглушку 503.
В десктопных приложениях иногда обработчик показывает окно с предложением отправить сообщение об ошибке разработчикам.
> Представь, что у тебя в участке кода вызывается несколько методов класса, в каждом из которых может возникнуть ошибка, с которой нужно будет предпринимать какое-то действие: изменить порядок будущего исполнения программы или её свойства, вывести сообщение пользователю, откатиться на ранее сохранённый (безопасный) этап работы с сайтом. Чтобы различать ошибку каждого отдельного метода, придётся либо использовать много try..catch'ей, либо вызывать в каждом методе свой тип исключений, либо вызывать один тип исключений с разными кодами в них (и потом использовать switch для выбора конкретного действия); мне кажется, в такой ситуации простейший if, проверящий успех/неуспех (false) операции, выглядит проще и красивее.
На практике без исключений придется ставить if после каждого вызова функции и это утомительно. То, что ты привел - это частный случай, и там можно либо ставить catch, либо ифы. В случае с catch, если исключения разные, можно даже написать так:
Иногда можно исплоьзовать if вместо исключения, если мы ожидаем, что будет ошибка и если гарантированно проверяем резлуьтат вызова функции. Но по умолчанию надо использовать исклюяения, так как они ипозволяют например писать цепочечные вызовы:
> 1. В PHP функции, не выбрасывающие исключение при исключительных ситуациях как минимум уродливы тем, что часть из них возвращает null, а часть - false Это плохо спроектировано. Ошибок вообще не должно быть, только исключения, так как неправильно продолжать выполнение кода при необработанной ошибке.
> Не надуманный пример с 5-ю ифами, а рельная задача - нужно сделать так, чтобы все ошибки логгировались. В случае с исключениями можно сделать По идее конечно PHP сам умеет логгировать ошибки, есть опции, и по умолчанию они даже включены.
Чтобы разобраться в ООП, надо изучать теорию, читать примеры, решать задачи. В ОП посте есть учебник PHP, там есть глава по ООП с задачами. Советую их решить, можно не на PHP.
После основ можно браться за более сложные конструкции, изучать паттерны, смореть код компонентов Симфони.
Наверно есть какаие-то книги по ООП, но я не знаю, какие.
> Дело в том, что я не всегда четко представляю себе, что нужно делать объектом, То, что является частью предметной области, о чем задача. Тебе надо сделать учет сотрудников компании - сделай класс Сотрудник, объект которого представляет одного сотдрудника. Делаешь программу бронирования номеров в отделе - будут классы Номер, Постоялец, может класс Бронь и тд.
Вообще, при решении задач на ООП нало ответить на такие вопросы:
- какие сущности (модели) нужны для решения задачи (Номер, Постоялец)? - какие у них есть свойства (Номер - число мест, этаж, стоимость и тд)? - что они умеют/с ними можно делать? (т.е. какие будут методы) - как они связаны между собой (Бронь связывает Номер и Постояльца)
Иногда кроме классов-моделей делают еще классы-сервисы, которые не представляют никакой сущности, но содержат методы с бизнес-логикой. Обычно это классы вроде КалькуляторСкидки, ПроверятельВведенныхДанных, СохранятельВБазу, МенеджерБронированияОтеля и т.д.
> когда нужно наследовать и даже, как это ни смешно, что именно от чего должно наследоваться.
Наследование A от B значит, что A - это улучшенная/измененная версия B. Используют наследование, когда есть однотипные классы, отличающиеся поведением. Например, у нас есть класс Компания и ГосУчреждение - возможно, их стоит унаследовать от ЮридическоеЛицо, если в задаче это даст какую-то выгоду. Например, если у них есть общие свойства и методы, можно перенести их в базовый класс.
> Кроме того, меня постоянно тянет оптимизировать придуманные структуры. Кто-то постоянно нашептывает мне: "Эта переменная не нужна, эта функция тоже, а вот это вообще можно побитово хранить". Если не нужна, то и не храни. Насчет побитово - в PHP это точно не дает выгоды, только запутывает код. Но вообще, при инкапсуляции, неважно как хранятся данные внутри класса, так как снаружи это не видно, ты исплоьзуешь методы для доступа к ним, и они скрывают сложность внутренней реализации.
> Там от класса-родителя наследуются, кроме прочих, PowerLine (линии электропередач), при этом в PowerLine не используются ни поля класса-родителя, ни его методы, таким образом, наследование чисто логические. А, я специально сделал задачу, которая сложно ложится на ООП. В реальных задачах редко бывает так же, как в учебнике, все иделально.
Тут надо определиться, что общего есть у сущностей и что мы вынесем в базовый класс. Это такие особенности:
- все элементы сети можно подключить к сети - все элементы сети вносят вклад в баланс (хотя PowerLine - неизвестно сколько, пока мы не рассчитаем баланс. Проще всего присвоить ей нулевую мощность и считать покупку/продажу отдельно).
Я бы сделал так:
- базовый класс - ЭлементСети, конструктор NetworkElement(daypower, nightpower), умеет сообщать вклад в энергобаланс днем и ночью (getDayPower/getNightPower). При желании можно сделать один метод getPower(time). Конструктор принимает значения dayPower, nightPower. - наследники, вызывая конструктор предка, задают значения вклада в сеть
В наследники мы выносим то, что индивидуально для данного элемента. Ну например, у Жилого Дома потребление зависит от числа квартир и формула расчета - это как раз индивидуальная особенность дома. Я бы сделал так:
function House(apartments) { var dayPower = ...; bvar nightPower = ...; // вызываем конструктор предка NetworkElement.call(this, dayPower, nightPower); }
Если надо, можно еще добавить свойство и метод для получения числа квартир (в задаче не требуется).
Аналогично реализуется электростанция и солнечная панель.
PowerLine умеет передавать электричество, потому ей мы добавим свойства и методы для получения/задания цены и пропускной способности. Если цена не постоянная и зависит от чего-то (от налогов, времени года, цены на нефть и тд), может стоит ее не делать свойством PowerLine, а считать в другом классе.
Кстати, классы еще можно объединять через интерфейсы без наследования, но в JS их как бы нет.
> при этом в PowerLine не используются ни поля класса-родителя, ни его методы, таким образом, наследование чисто логические. PowerLine объединяет с другими то, что ее можно подключить в сети, и она вносит вклад в нее, но ради упрощения кода проще принять его равным нулю, так как посчитать его она не способна (она лишь передает энергию, но не принимает решение о закупке). Можно считать вклад снаружи и задавать через сеттер, но это ухудшит код - например при добавлении нового элеимента сети мы обязаны как-то перерассчитать этот вклад. Гораздо проще просто принять вклад равным нулю и считать его в другом месте.
Стоиит избегать случаев, когда в свойстве может быть недостоверное или устаревшее значение - это приведет к куче проблем.
> Однако, вместо добавления нового поля _transmittedPower можно хранить величину в уже имеющемся _generateDayPower, и мне кажется, что это хорошо, в конце концов линии электропередач по условию тоже в некотором роде поставщики/потребители энергии, и я не вижу в таком использовании логической ошибки. ЛЭП знает только пропускную способность и цену, но сколько фактически по ней передается, она посчитать не может так как не принимает решение о закупке.
> Но так ли это? Вообще, когда нужно следить, чтоб лишних полей не было, а когда можно махнуть на них рукой? Где почитать про это? Не знаю. Порешай задачки на ООП, можно наши, если тех, что есть мало, я придумаю еще.
> Вот как я бы организовал данные В общем логично, но с реализацией PowerLine не согласен.
> //PowerLine суть тоже поставщик/потребитель энергии, количество которой можно хранить в _generateDayPower, что логически вроде бы правильно (по-моему), поэтому нужно всего 1 дополнительное поле для стоимости Еще для пропускной способности надо поле. Пропускная способность != фактически переданный объем.
Исключение лишь значит что функция не может выполнить свою работу и вернуть результат, потому она прерывает свое выполнение и возвращает ошибку. Исключения дают отдельный (out-of-band) канал для передачи информации об ошибке, в то время как return false использует тот же канал (in-band) что и результат, и это менее удобно.
Давай я тебе дам задачу. Есть какая-то сущность и коллекция этих сущностей (Работник и список работников, Ученик и классный журнал, Товар и Корзина, Товар и Склад). Можешь представить их как объекты или массивы, если не знаешь ООП.
Напиши функцию, определяющую сколько сущностей в коллекции соответствуют какому-то критерию. Критерий может быть любой. Ну например, сколько учеников выше 160 см или имеют среднюю оценку или чья фамилия начинается на гласную.
Напиши функцию, отбирающую массив сущностей, соответствующих определенному критерию.
Напиши функцию, находящую лучшую сущность по произвольному критерию (ученик с самой большой суммой оценок, с самой короткой фамилией, самый тяжелый товар, товар с самой большой скидкой, итд)
Напиши функцию, принимающую на вход массив сущностей и возвращающую массив вычисленных из них по произвольной формуле значений (например: на вход подаем товары с ценой и скидкой, на выходе - массив цен с учетом скидки, на вход - ученики, на выходе - массив средних баллов).
Как ты представишь в своем коде "критерий" или "формула"? Как передать в функцию этот критерий?
> как валидировать пользовательский ввод? либо писать функцию/класс, принимающую данные и выдающую ошибки. Либо делать классы-примитивные ограничения и из них собирать комплексный валидатор. Ну и есть готовые библиотеки:
Вообще, советую попробовать идею с сборкой валидатора из примитивных объектов-ограничений. Ну например:
$validator = new Validator(); $validator->addConstraint('name', new Constraint\NonEmpty()); // имя обязательно $validator->addConstraint('name', new Constraint\MaxLength(100)); // длина имени не больше 100 $validator->addConstraint('birtyear', new Constraint\Range(1900, date('y'))); // год рождения $validator->addConstraint('gender', new Constraint\OneOf([GENDER_MALE, GENDER_FEMALE]));
$errors = $validator->validate($user);
// выводим список требований: echo getRequirementsAsText($validator); // - поле name обязательно // - поле name имеет длину не более 100 символов // ...
там дальше можно это все улучшать, например, добавить сообщения об ошибках с плесйхолдерами вроде "В поле {name} можно ввести не более {count} символов", генерацию форм по валидатору и тд.
Потому что авторы первого друпала, как и многих других CMS, были не очень хорошие программисты, им просто надо было на коленке слепить админку для управления сайтом, не беспокоясь о качестве кода. Потому они и наизобретали хуков и сделали все на массивах, потому что не смогли придумать архитектуру для большого, сложного проекта. Мы-то знаем, что хорошо организовать сложный код иногла можно с помощью ООП, а для веба хорошо подходит архитектура MVC.
> Правда тогда вроде как вышла новая версия в которой обещали друпал перекатить на ООП и что теперь в нем всё будет ок. Вроде пока не перекатили все, хотя компоненты Симфони кое-где добавили.
Кодеигнайтер очень старый и там много неправильного. Весь этот $this->load заменяется автозагрузчиком и DI контйенером.
> И тут передо мной открываются собственно 2 пути, либо вызывать из одной модели другую сразу, либо возвращать данные в контроллер из первой, и эти тебе надо разделить сервисы на 2 слоя, и высокоуровневые сервисы управляют более низкоуровневыим. То есть есть сохранятель в базу, есть посылатель писем, а есть высокоуровневый сервис, который ими командует.
> а рвется наружу простое ООП, где классы должны обмениваться друг с другом данными как им удобно, а не обязательно быть загнанны в такой вот "MVC паттерн". MVC никак не говорит, что модель должна быть представлена однотипным набором классов и 1 слоем. Ты по моему тут делаешь ошибку. MVC лишь говорит что надо разделить отдельно бизнес-логику, вывод данных и обработку пользовательских действий/запросов.
>>908070 >Надо разобраться, почему. Вот такая ошибка. Не очень понимаю что не так. >Если это skeleton, то это сборка, я бы советовал лучше Слим учиться с нуля через композер ставить. Не знаю что за скелетон, устанавливал командой composer require slim/slim "^3.0" как тут написано.
Разобрался как в слиме отправить заголовок. Вот такой вот хинт: return $response->withAddedHeader('location', '/some_page'); Правда может и лучше можно сделать, хуй знает.
>>908244 { "require": { "slim/slim": "^3.0", "twig/twig": "~2.0" } } Вот такое у меня там написано. Правда не пойму в чем проблема, если и без того в стандартном пакете слима есть твиг.
Зачем нужен пхп, если есть божественный js и нода? Я понимаю, что популярно и нужна обслуга, чтобы все это сопровождать, но почему бы просто не выучить что то, за что будут платить больше чем 40к в веб студии, тем более, что это ненамного сложнее? Нет, серьезно, хочу ответа
>>908266 Скорость работы. Нода близка к компилируемым языкам. А где там пхп? Меньше говнокода. Его, конечно, тоже много, но и не близко в сравнении с пхп. Ну и "чувак асинхронность".
>>908273 >пространства имен Ну это такое, да. >ООП ES6/TypeScript к твоим услугам. Ну и поговаривают, что функциональное программирование для богов, ооп не нужно, но это не точно
>>908276 >ES6/TypeScript к твоим услугам А шо, оопэшные фишки ЕС6 кто-то использует? А то я смотрю там все продолжают ебаться с прототипами. >Ну и поговаривают, что функциональное программирование для богов, Не смеши меня. Что в пыхе, что в жыэс функциональщина крайне куцая. Иди расскажи всяким лиспоебам про свою функциональщину, пускай пацаны посмеются. >ооп не нужно, но это не точно У тебя монокль шоли выпадает? Не, я на самом деле ничего против жс не имею. Просто это кому как. Меня тошнит от его расхлябанности, от того что все делается методом стучания хуем по клаве. Ну вот для своих задач не слезу с пыхи ради него. Лучше уж аспа или джанго.
> table = createField(table); Тут наверно возвращать ничего не требуется из функции.
Решено верно.
>>901678 > Алсо вспомнил про синглтон - я когда читал учил ООП понял как его делать, но не очень понял зачем он вообще нужен.
Идея в том, что синглтон позволяет запретить создавать больше 1 экземпляра объекта. Где это нужно - сам не знаю. Использовать его для соединения с БД глупо, так как соединений с БД может быть больше одного, и вообще, надо использовать DI для таких вещей.
Упоминают синглтон часто так как это очень простой паттерн, и на некоторых собеседованиях спрашивают про паттерны, вот люди его и учат, чтобы им было что ответить. Можешь при случае, если речь зайдет, спросить собеседующего, чем сингтон лучше использования DI, почему не может быть более 1 соединения с БД, как при исплоьзовании синглтона дать классу другой объект соединения с БД.
Обычно делают класс для работы с формами, в нем задают набор полей (либо он создается на основе модели, для которой предназначена форма), а имея список полей, мы можем перенести данные из GET/POST в объект модели.
Валидацию удобно делать, сделав примитивные объекты-ограничения (NonEmtpy, MaxLength) и далее создавая правила валидации из таких объектов:
$validator = new Validator; $validator->addRule('name', new Constraint\MaxLength(100)); $errors = $validator->validate($data);
Иногда делают объекты, представляющие разные виды полей, и собирают форму из них:
$form = new Form; $form->add(new TextField('name', 'Имя')); ...
Можешь глянуть компоненты Symfony Validation и Symfony Forms.
> Нормально будет, если под форму на стороне разработчика будет создаваться модель со свойствами равными полям формы. Часто форма используется для редактирования модели из БД и отдельная модель формы не требуется.
> Тогда и номер месяца можно убрать, если его можно получить из индекса знака зодиака в массиве? Можно, хотя, возможно, читать такой список будет немного труднее.
> // PHP не поддерживает негативные индексы для массивов, поэтому приходится извращаться отрицательные индексы допустимы, только это будет отдельный элемент.
Решено верно.
Дерево
> https://jsfiddle.net/0gpbzo8y/ Вот таике длинные строки лучше разбить на две команды: > stack = stack.concat(list.filter(node => node.parentId === current.id));
> но не совсем понятно, как выводить список вложенным, а не просто показывать список посещённых элементов нужно класть в очередь еще и глубину
> Сделал очень костыльно с добавлением ключа depth к каждому элементу массива: https://jsfiddle.net/0gpbzo8y/2/ Наверно лучше не модифицировать объект, а создавать обертку вида { node: ..., depth: ... }.
Вместо обнуления счетчика (что неверно) надо брать depth из current.
> Алсо я вспомнил про задачу на джуна, которую тут вбрасывали пару тредов назад, приложил скрин к посту. Как правильно реализовать третью часть? На запрос вида /catalog/samsung/s7 мне нужно выдавать товар, у которого path = s7, path родителя samsung, path его родителя = catalog. Проще всего сделать несколько запросов, ища запись по path + parentId. Там глубина все равно не будет большая.
Твой код с подзапросами можно переписать на джойны, что даст больше свободы для оптимизации, но суть останется та же.
Твой код получается сложным и запутанным. В такой ситуации можно использовать Query Builder, но проще просто делать несколько запросов.
> (var i = 0; i < 6; i++){ лучше было сделать i < WIDTH.
> if (cell.classList.contains('marked')) { Вроде есть метод toggle() ?
Учти что mousedown срабатывает на любую клавишу мыши включая по моему нажатие на колесо.
Решено верно.
N рабочих дней
Да, алгоритм в твоем решении вполне допустим (хотя оно не проверяет субботу, воскресенье и отмененные выходные). Хотя его можно оптимизировать, а именно:
- найти ближайший праздник (если они отсортированы, то можно делением пополам) - вычислить, сколько до него дней - уменьшить $n на min($n, $daysLeft), одновременно прибавив дату
С выходными и отмененными выходными это конечно усложнится (хотя... может и тут можно как-то оптимизровать, например автоматически добавляя 2 дня на каждые 5). Не уверен, стоит ли заморачиваться.
Для больших интервалов (1000 рабочих дней) алгоритм будет не очень быстрый, но его можно оптимизировать для таких случаев:
- создать список, где рассчитано число рабочих дней в каждом месяце, неделе или может годе - дойти по дням до начала ближайшего месяца - пропустить нужное число месяцев - пройти остаток по дням
Тебе надо зайти в packagist.org и посмотреть, какие зависимости у указанных тобой версий. Скорее всего проблема в том, что для указанных тобой версий пакетов там в зависимостях стоит twig выше 2.1, но он помечен как нестабильный, а у тебя по умолчанию стоит настройка ставиить только стабильные версии. Надо либо ее отключить, либо поставить менее жесткие требования к версиям слима и слим-твига.
Либо у тебя старый PHP, и новый твиг на нем не работает. Все это указано в composer.json нужной библиотеки.
Ты требуешь новый слим версии выше 3, а для него наверно нужны новые библиотеки и новая версия PHP.
Примерно это композер и пытается сказать - он тебе даже пишет конкретные версии, которые подходят под требования.
Попробуй еще добавить опцию -v при выове композера - может он подробнее напишет?
Upload target path is not writableАноним08/01/17 Вск 18:45:39#465№908341
Я щас пизданусь нахуй. Как убрать флажок "только для чтения" в семерке что бы можно было записывать файлы в папку? Права всякие менял, собственников там, пробовал команду atrib -r, ничего не помогает.
Куда выложить сапера на ООП? Как-то не хочется без MVC на гит, слишком простой проект (хотя я его делал почти неделю). На jsfiddle никак не закинуть папки? Оп, может я на файлообменник в архиве кину проект?
>>908341 в яватреде кажется видел как анон неделю назад ебался с такой же проблемой. Сходи там посмотри как решили ну и на вскидку ты должен быть суперадмином на компе + загугли как попасть в управление файлами. (вангую что правой кнопкой по Мой_компьютер -> управление -> а дальше хз)
У меня один вопрос пока что. Почему-то неправильно центрируется попап (я знаю, что еще дополнительно надо сделать margin на половину offsetWidth/offsetHeight, вышесказанное можно раскомментировать в коде. Оставил так, потому что более видно проблему. Красный кружок - там где должен быть попап).
Еще в моем коде меня заботит алгоритм метода Field.prototype._openTheCell(), при большом поле он показывает не самые быстрые результаты (все может создаваться две секунды и больше).
>>898502 (OP) Ребят тут есть кто уже решает оповские задания? Хочу найти народ в команду по изучению PHP. Будем вместе сидеть в скайпе и кодить, так веселей и продуктивней, чтобы не лениться. Начнем с решения задачи про студентов. Есть базовый кастомный фреймворк и самописный MVC под него. Надо чтобы вы уже знали HTML, CSS, JS, PHP на элементарном уровне, чтобы отбросить теорию и приступить к практике. Оставляйте контакты я вам напишу.
ОП, читнул твою пасту: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md Поясни за Table Data Gateway. Непонятный момент такой: В TDG как я понял каждый класс отвечает за одну таблицу. Что делать, если таблиц куча и практически каждый запрос задействует множество сложных джойнов? Любой запрос получается не к одной таблице, а сразу к многим. Где писать тогда эти запросы и как организовывать TDG? Один класс TDG для всех таблиц сразу? А если еще любой insert сразу инсертит в несколько таблиц? Куда тогда эти инсерты пихать? Если делать один большущий класс TDG на все таблицы, то это полный беспорядок в коде будет.
>>908600 Не совсем, одна сущность - одна таблица, вот так вот. Т.е. вот есть у тебя студент - значит все методы для работы со студентом в БД лежат в одном классе. Так что насколько я понимаю, тут уже похуй что у тебя там с джойнами. Но лучше пусть ОП еще подскажет. Алсо, если дохуя сущностей-таблиц то лучше уже посматривать в сторону active record.
Насчет джойнов - надо смотреть по основной (с точки зрения логики) таблице.
Насчет инсертов - один SQL запрос не может инсертить в несколько таблиц сразу. Если при вставке в одну таблицу надо делать вставки еще в другую, то можно либо сделать как с джойнами, либо сделать класс-сервис, вызывающий методы двух TDG по очереди. Вариант с сервисом хорошо подходит, если там еще что-то кроме вставки в БД надо делать.
>>908737 А разве этив рекорд не нужен как раз что бы не было такого гемороя? Типа сделал CRUD в основном классе, а там уже наследуйся @ перегружай методы.
друзья, объясните нубу, как правильно деплоить проект, если работаешь в одного? есть локальный код, который запускается на локальном сервере, он потом пушится на гитхаб, затем мне бы хотелось чтобы он потом с гитхаба сливался на хостинг при обновлении.
я пока нашел teamcity, но он нереально огромный, тормозной (для моих задач) и мне там нужна всего одна функция выгрузки через фтп по некоему триггеру.
>>908913 Лел, разобрался с проблемой, оказывается это я просто не правильно указывал директорию(надо было еще к директории приписывать имя файла). Теперь парочка вопросов по поводу архитектуры файлообменника: 1. Можно ли из контроллере(т.е. в колбеке метода get() слима) сразу же отправлять принмаемый из формы файл в нужную директорию, или это грубое нарушение MVC и это надо обязательно отдавать в модель? Просто в первом случае несколько попроще будет, меньше всего в модель передавать. 2. Как хранить файлы на стороне сервера? Просто все кидать в одну папку типа uploads, или внутри как разбивать в зависимости от типа файла?
Сап, есть общие знания PHP, основных алгоритмов, мозги и свободное время (3-4+ часа в день). Хочется выйти на доход 20-25к/мес хотяб, причем желательно на фрилансе. Сколько до этого придется изучать?
И как лучше - с текущими знаниями выполнять какие-то заказы и параллельно изучать или сейчас все свободное время посвятить своим идеям/проектам и потом уже идти на известные сайты?
Привет, народ, нужна ваша помощь. Я нюфаня и еще плохо во всем разбираюсь, но очень хочу понять как работает вот эта концепция developers.google.com/drive/v2/reference/files/trash Я поставил предлагаемую библиотеку, вшитые туда примеры работают как надо, но описанные в мануале по ссылке при этом не работают. Может ли кто из знающих сказать что я делаю не так и что нужно сделать чтобы заработало?
>>907755 Про Angularjs: > как модуль {директива + контроллер + сервис}
Однако, чем больше я копаюсь в нем, тем чётче складывается представление что это шоколадная посыпка на торте из говна.
Я даже не знаю. Единственное, чему можно радоваться - пришло понимание MVC. (Теперь я понимаю, почему ОП советует копать фреймворки перед исследованием iпаттернов etc.) Спасибо, ОП.
Ты по моему не очень понимаешь, что такое AR. AR это когда класс/объект представляет и строку из базы данных, и содержит методы для ее загрузки/сохранения в БД. Ты уверен, что ничего не перепутал?
Опиши, что именно не так. Что ты делаешь, что должно произойти, и что происходит вместо этого. А ты привел только сообщение об ошибке, не написав как оно появляется.
Насчет прав на папку - если я не путаю, флажок "только для чтения" не запрещает запись и является лишь рекомендацией для программ. Чтобы его поменять, наверно надо иметь права на смену атрибутов папки.
Реально права задаются в ACL, это вкладка "доступ" в свойствах папки.
> 1. Можно ли из контроллере(т.е. в колбеке метода get() слима) сразу же отправлять принмаемый из формы файл в нужную директорию, или это грубое нарушение MVC и это надо обязательно отдавать в модель
Это плохая идея. Лучше иметь сервис, в котором есть методы для проверки и сохранения файла. Чтобы мы всегда могли бы программно сохранить файл (даже если мы не собираемся это делать, просто чтобы код был логически разделен на част). Вот тебе дополнительное задание, чтобы ты понял:
- сделай скрипт для командной строки, который загружает указанный файл в файлообменник и выводит ссылку на страницу с ним. Например, так:
Скрипт должен проверять файл так же, как он проверяется при загрузке через веб-интерфейс (например, не превышен ли размер). При этом избегай копипасты, код сохранения файла не должен быть скопипащен 2 раза.
> 2. Как хранить файлы на стороне сервера? Просто все кидать в одну папку типа uploads, или внутри как разбивать в зависимости от типа файла?
В комментариях к задаче написано, что надо учитывать при сохранении файла:
- безопасность, чтобы нельзя было сохранить файл с расширением php или именем htaccess - уникальность, чтобы у каждого файла было уникальное имя и они не перезаписали друг друга - желательно ограничить число файлов в папке и для этого раскладывать их в подпапки, например по дате или по номеру
Действительно ли, что много вакансий по php? На сайтах по фрилансу видел по работе в WP, по нему знания можно получить именно из литературы, приведённой в треде или что-то отдельно изучать?
В оп посте написано, что 6-8 месяцев уйдёт на изучение. Это пр каком режиме? Сколько времени нужно уделять? И за эти 6-8 месяцев какой уровень владения достигнут будет? Много ли самоучек? Или ВУЗогоспода просто со смехом ссут на таких вот?
>>909082 Хз, хуйня всё это, просто садись и учи уже блядь. Никто не гарантирует тебе нихуя тут ничего. Я учу с дикими перерывами два года уже. Если бы задротил нон стоп то за три месяца достиг бы всего что знаю за эти два года. Иногда тупо лень, иногда после работы уже нет сил нихуя делать (2 веб-мартышко работы сменил уже, на которых не особо качается скилл) пока учу всю хуйню из оп-поста.
Учить реально дохера. В вузе тоже тебе бы дали бы основы любого языка + какие-нибудь базы данных а дальше ебись сам. Другое дело что там за ручку водят и знания с тебя требуют, а тут ты сам себе хозяин. Если сложный вопрос вкидываешь то никто и не поможет ибо такие же нубы. ОП тоже не всегда доступен, но часто отвечает, ДО СИХ ПОР СПУСТЯ 3 ГОДА, за что ему респект.
Я бы не сказал, что в ВУЗе научат чему-то такому, чему ты не можешь научиться сам. Если ВУЗ не один из специализирующихся на компьютерных науках (вроде ИТМО итд, команды из которых выигрывают олимпиады по программированию) то там ничему особенному не научат.
Не водят в вузе за ручку. Препод нудным голосом читает что-то с листочка, а потом на практике требует от тебя написать код. Понятно что 95% либо списывают либо потом год ходят на пересдачи. Оставшиеся 5% вроде ОПа сами изучают что им нужно и легко сдают зачет.
Проще всего сделать на сервере клон репозитория с гитхаба и делать там pull чтобы забрать изменения с гитхаба.
Тут есть 2 варианта: можно этот репозиторий поместить в папку веб-сервера, но тогда надо закрыть доступ к папке .git (а то смогут скачать исходники из файлов репозитория) и в процессе обновления файлов могут быть баги (условно, половина файлов заменилась на новые, а половина еще старая).
Если ты используешь композер и поменял что-то в composer.lock, то тебе надо на сервере делать composer install для обновления зависимостей. На время обновления в папке vendor может отстутсовать часть файлов и сайт может потому не работать.
Для изменений в БД советую использовать скрипты миграции базы данных.
Если посетителей мало, то ошибку в момент обновления вряд ли кто заметит, но все равно, неаккуратно. Некоторые потому перед обновлением вешают на сервер заглушку с кодом 503, обновляют код, снимают заглушку. Разумеется, bash-скриптом, а не руками.
Я бы советовал учиться делать "бесшовный" деплой. Он делается так:
- делаем где-то отдельно клон репозитория - делаем pull, чтобы забрать последние новые файлы - создаем (скриптом) новую папку, например, /var/www/site/v2/ - в нее копируем код из локального репозитория - прогоняем composer install - выполняем прогрев (генерацию) кеша, если это нужно - в конфиге веб-сервера меняем корневую папку на новую - делаем серверу reload, чтобы он перечитал конфиг
При таком подходе мы атомарно переключаемся на новую версию кода и ни один клиент не должен получить ошибки.
В простейшем случае это делается баш-скриптами, но можно попробовать использовать и системы вроде ansible (хотя я их не использовал).
Опиши подробнее, что ты делаешь, что должно произойти, и что происходит не так. Ты даже не написал, как ты запускаешь код (через веб-сервер или в консоли). Не написал, есть ли что в логе ошибок. Чему у тебя равна настройка display_errors.
Скайп хотя бы номер телефона не требует. Хотя насколько я знаю, он уязвим и там легко можно блокировать аккаунты, а также определять IP. Так что лучше заведите отдельный аккаунт, который не жалко.
Паста длинная, просмотрел по диагонали. Человек советует изучать компьютерные науки очень основательно. На практике во многих областях настолько глубоких знаний не требуется, хотя потихоньку изучать их параллельно с работой не помешало бы.
Ну и не думай, что можно немножечко поучиться, потом начать работать и бросить обучение. Учиться надо и дальше, иначе твоя карьера быстро остановится, а может и пойдет вниз. IT - не работа на конвеере, где достаточно посмотреть и повторять увиденное.
Тут рядом в /pr/ есть тред от анона, изучающего с нуля Питон: >>868752 (OP)
Может кому-то, кто тоже с нуля изучает, будет интересно почитать.
Хочу только предупредить, хоть он и пытается с помощью своеобразной манеры письма притвориться обычным неграмотным аноном, на самом деле он как раз довольно сообразительный, и если у вас получается все не так быстро, как у него, то это нормально.
Течениие ООП несет меня в казахстан, помогите, что там не так ?
Алсо пока я не нахуярил кучу кучу бессмысленной хуйни предлагаю помочь мне и направить в нужное русло, чекните пока там всего нихуя строк и так же помогите разобраться в чем сейчас там ошибка:
>>909015 >Ты по моему не очень понимаешь, что такое AR. AR это когда класс/объект представляет и строку из базы данных, и содержит методы для ее загрузки/сохранения в БД. Ты уверен, что ничего не перепутал? Просто я видел такую фишку, когда делали абстрактный класс model в котором просто были сделаны операции типа insert и delete, а под конкретную типа student от него наследовались, называли это актив рекорд. >Опиши, что именно не так. Уже разобрался, моя ошибка. >Лучше иметь сервис, в котором есть методы для проверки и сохранения файла. Чтобы мы всегда могли бы программно сохранить файл (даже если мы не собираемся это делать, просто чтобы код был логически разделен на част). О, отлично. Только это же все равно логически относится к моделям, правда? >сделай скрипт для командной строки, который загружает указанный файл в файлообменник и выводит ссылку на страницу с ним. Наверное когда основный фичи сделаю, а то сейчас я не умею рабоать с консолью в пхп. >безопасность, чтобы нельзя было сохранить файл с расширением php или именем htaccess Такие файлы стоит просто запрещать загружать или же менять им расширение?
Начал писать свой пикчехостинг на ларавеле. Вопрос следующий: у меня есть возможность загружать картинку не только с компа, но и по урл. Написал свой формреквест, форма валидируется нормально, но пропускает одновременно оба заполненных инпута, с файлом и урлом (нужен xor). Вопрос: как лучше организовать проверку на xor, писать свой Rule или делать это на стороне контроллера?
>>909394 Тут вроде бывали ребята, которые даже не делая студентов устраивались, так то. Правда мне тяжело себе представить что это за работа может быть.
$creditBalance = 40000; / Долг анона перед банком / $percent = 1.03; / Банк начисляет 3% в месяц от суммы / $servicePayment = 1000; / А также 1000 рублей в месяц комиссии за обслуживание счета / $monthlyPayment = 5000; / Анон платит 5000 р в месяц, это все, что ему дает мама на завтраки / $paymentTotal = 0; / Сколько всего отдал банку анон /
/ Посчитаем расходы 20 раз на 20 месяцев вперед / for ($month = 1; $month <= 20; $month ++) { $creditBalance = ( $creditBalance * $percent ) + $servicePayment - $monthlyPayment; $paymentTotal = $paymentTotal + $monthlyPayment; if ($creditBalance >= $monthlyPayment) { echo "{$month} месяц спустя: долг = {$creditBalance} руб, выплачено всего {$paymentTotal} руб. \n";} else {$paymentTotal = $paymentTotal + $creditBalance; $creditBalance=0; echo "{$month} месяц спустя: долг = {$creditBalance} руб, выплачено всего {$paymentTotal} руб. \n"; break;}
Можете подсказать, почему конечный результат отличается?
Где phpMyAdmin хранит комментарии к ячейкам таблицы? Участвуют ли они в запросах? Есть ли разница в производительности запросов с ними / без?
Какой движок (MyIsam, InnoDb, другой?) подойдет лучше для ситуации:
2 таблицы, очень активная выборка из обеих (иногда даже ВСЁ берется), примерно 100000 записей в год в каждую (таблицы обнуляются каждый год! То есть максимум, который в них хранится = приблизительно 100000 записей).
Не будут ли тормозить запросы в конце года?
Я читал про статические таблицы, насколько понял, привел структуру полей в соответствие требованиям статической таблицы: поля типов char, int, decimal, date, timestamp (Если это важно для предыдущих вопросов).
Нормализация 3NF (Если это важно для предыдущих вопросов).
(Постарался все вопросы сразу задать, как просит ОП).
>>909578 Форматирование - пиздец, ты не должен if сдвигать на табуляциюя влево просто потому что, как и каждый последудющий элсиф просто так фо лулз)))
>>909595 Всё у тебя так с константами. Вообще пока не стал писать сложные проекты с хотя бы отдельными функциями можешь ничего никуда не выносить, просто хуярь скрипты целиком в 1 файле.
А так далее по логике. Я бы сначала проверял на пустоту, а потом бы уже фильтровал данные.
Далее у тебя идет цепочка ошибок, когда сработает всегда только 1 условие. А ты попробуй сделать так, что сколько незаполненных / неправильно заполненных полей, то столько и ошибок выводится сразу пользователю.
Далее: if($_SERVER['REQUEST_METHOD'] == 'GET'){ $sql = 'DELETE FROM post WHERE id = ' . $_GET['delete'] . ''; mysqli_query($connect,$sql); }
ну я будучи кем угодно могу удалить чей угодно пост и там уязывимость, я могу написать гет запрос delete=qweqweqwe получить эрор который мне потроха твоей базы выдаст с названиями таблиц, далее я пишу второй гет запрос в духе: delete=1;drop table bla bla и рушу твою гостевуху к хуям, надо тут хотя бы сделать проверку что бы ничего кроме чисел не пропускалось.
А в целом всё норм, доебаться можно много до чего, но если у тебя работает то и найс. Сам смотри как бы ты хотел улучшать.
аноны, молю о помощи. сижу пол дня над проблемой! данные не записываются в файл. Юзаю класс SimpleXMLElement вот код $dom = simplexml_load_file('DB/DB.xml');
Стена кода без разбиения на функции. тебе надо научиться разбивать код на отдельные действия и оформлять их как функции с четко определенными входными и выходными значениями. Позже - учиться разбивать код на классы.
Обработка ошибок сделана неправильно. Ты показываешь ошибку пользователю (которому она не нужна), но не пишешь в лог (который нужен тебе).
После успешной обработки POST запроса надо делать редирект, чтобы при обновлении страницы он не отправлялся заново.
Я бы советовал тебе подучить наш учебник (только то, что ты еще не знаешь), и затем делать студентов из ОП поста, или хотя бы почитать подробные комментарии в этой задаче.
Пока уровень очень слабый, тебе надо многому учиться. Код низкого качества, его тяжело поддерживать и понимать. Я например не могу сказать быстро, правильно ли сделана проверка данных, или нет.
> https://github.com/never3ver/fileshare/blob/master/copyBootstrap.php#L4 Вместо opendir/readdir лучше исплоьзовать scandir() (readdir выгоден если файлов очень много и не хочется их названия все сразу грузить в память, а хочется обрабатывать по одному). Либо же использовать ООП-ориентированный и немного странно спроектированный класс DirectoryIterator.
Для рекурсивного обхода папки есть еще RecursiveDirectoryIterator.
Скрипт не проверяет результат вызова copy (да и opendir) - PHP при ошибке по умолчанию просто выведет варнинг и продолжит скрипт.
> https://github.com/never3ver/fileshare/blob/master/test_fileshare.sql > `name` varchar(45) DEFAULT NULL, > `tmpName` varchar(45) DEFAULT NULL, > `type` varchar(45) DEFAULT NULL, Вот к таким колонкам стоило бы добавить COMMENT '...' с описанием, что именно там хранится, так как не очень понятно. И надеюсь, ограничение на 45 символов проверяется в коде?
В условия, кроме проверки наличия сжатого файла, стоит еще добавить проверку существования оригинала.
Я не помню, давал я тебе такую задачу или нет. Чтобы лучше понять идею MVC, надо бы сделать альтернативный контроллер и вью для той же задачи. А именно: надо сделать скрипт для командной строки, позволяющий загружать файлы в файлообменник с локального диска и выводящий ссылку (ссылки) на них. Пример использования:
(кстати, в некоторых линуксовых консолях ссылки распознаются и делаются кликабельными, очень удобно).
При желании можешь дополнить программу возможностью удаления файлов. Опции можно разбирать с помощью стандартной функции getopt() или поискать библиотеку, если хочется больше возможностей.
При решении этой задачи надо избежать дублирования кода и обеспечить его разделение так, чтобы Модель можно было бы использовать из любого окружения - как из веб-скриптов, так и из cli-скриптов.
Сейчас у меня ощущение, что бизнес-логика сохранения файла у тебя вписана в контроллер и повторно использовать этот код нельзя.
Для соединения с PDO стоит явно задавать кодировку как utf-8 (есть опция). И строгий режим MySQL.
https://github.com/never3ver/fileshare/blob/master/app/Helper.php#L17 > public static function createTmpName($dataGateway) { Вот тут вот передача сервиса как аргумента выглядит странно. Обычно ведь сервисы-зависимости передают через DI. Если тебе лень делать отдельный сервис ради генерации имени, можешь (пока приложение маленькое) вставить эту функцию в TableDataGateway. Также, не очень эффективно делать 1000 запросов подряд, лучше уменьшить число попыток до 10-20 и увеличить разнообразие генерируемых имен. Также, еще эффективнее может быть проверять уникальность всех 20 имен одним запросом. Хотя, если мы ожидаем что чаще всего совпадений не будет, то выгоднее как раз проверять по одному варианту.
Вообще, мне кажется, тебе придется сделать сервис, отвечающий за работу с файлами и в него уже все эти функции класть.
https://github.com/never3ver/fileshare/blob/master/app/Helper.php#L26 > public static function getImagePath($tmpName) { > return "../files/" . $tmpName; плохо, что дается относительное имя, которое зависит от текущего каталога (см getcwd()/chdir()). Лучше бы генерировать полное имя. Определять полный путь к папке проще всего через __DIR__ или опции в конфиге.
Также, надо различать путь к файлу на диске и URL файла. Это разные вещи.
Также, возможно, для удобства стоило бы делать имена менее случайными, используя в них что-то человекочитаемое, чтобы при каких-то проблемах не надо было ковыряться в именах из случайных символов.
Также, желательно ограничить число файлов в одной папке. Некоторые программы начинают тормозить от большого числа (тысячи, десятки тысяч) файлов в папке.
> $url = $this->router->pathFor('error'); > $response = $response->withStatus(302)->withHeader('Location', $url); лучше бы отображать страницу ошибки, а не редиректить на нее. Ведь тогда при нажатии F5 будет повторяться попытка загрузки файла. Редиректят обычно только после успешной обработки POST-запроса.
Тем более, ты редиректишь на страницу 404 ("не найдено"). Это нелогично, смотри сам:
- отправляем POST запрос, получаем ответ 302 (нужный контент размещен по другому адресу) - переходим на него и получаем 404 (такой страницы не существует)
Явно неправильно используются HTTP коды.
Также, в Слиме есть объект Request из PSR-7, представляющий HTTP запрос от браузера. Изучи его возможности, есть информация в том числе на русском языке, и старайся использовать везде, чтобы освоить получше. Вот, например, там предусмотрен интерфейс для представления информации о загруженных файлах: http://www.php-fig.org/psr/psr-7/#uploaded-files
Исплоьзуй этот интерфейс вместо $_FILES.
Насчет типа файла - надо понимать, что он предоставлен браузером пользвоателя и особо доверять ему не стоит (то есть его можно использовать, но только там, где это не угрожает безопасности. Например, не стоит верить, что там картинка, если там указано image/png. Но можно выводить на странице и отдавать в Content-Type пользователям переданный тип).
> $dataGateway = new FileDataGateway($this->db); FileDataGateway надо положить в DI контейнер в $app.
https://github.com/never3ver/fileshare/blob/master/app/FileInfo.php#L11 > public function getDataForTemplate() { Тут мне не очень нравится, что мы получаем из класса какой-то непонятный массив непонятного формата. Зачем массив, когда у тебя есть класс и можно сделать либо публичные свойства, либо методы-геттеры вроде isImage(), getPlaytime() и тд.
Хотя, можно сделать и метод, возвращающий массив вида ["Длительность" => "3:30"], для вывода циклом.
Преобразование в килобайты удобнее всего делать в шаблоне вызовом хелпера или функции. Не стоит помещать его в класс, так как его можно использовать и не имея файла.
> public function __construct($file) { Нужен тайп-хинт.
Также, неэффективно анализировать файл при каждом запросе. Эффективнее в момент загрузки проанализировать и положить данные в БД. Удобно их там хранить как JSON (так как это стандарт и он не привязан к языку PHP). При правильном проектировании класса FileInfo нетрудно добавить в него методы для сериализации/восстановления объекта из JSON-данных.
Кстати, в сфинксе неплохо бы подумать над тем, как добиться быстрых обновлений индекса. Чтобы не ждать, пока он по крону переиндексируется после загрузки файла. Обычно делают так:
Делают основной индекс + realtime-индекс для хранения данных, которые только что добавлены и пока не попали в основной индекс. Сфинкс также поддерживает воможность добавлять в realtime-индекс информацию об удалении файла, чтобы исключать его из результатов, даже если он еще есть в основном индексе.
> WHERE MATCH ('{$query}')"; Тут надо использовать плейсхолдеры.
https://github.com/never3ver/fileshare/blob/master/public/index.php#L101 Код отдачи файла надо бы вынести в отдельную функцию, а не писать стеной в контроллере. Также желательно предусмотреть поддержку отдачи файла без участия PHP, например, с помощью X-SendFile, если это расширение установлено.
> Bitrate: {{ bitrate/1024 }}kbps Тут стоит добавлять окруление, а то может получиться число с кучей знаков после запятой
> <img class="leftmargin" height="200" Это неправильно, ты принудительно растягиваешь маленькие картинки до 200px, они мылятся. Также, для превьюшки лучше сделать отдельную уменьшенную копию картинки, а не заставлять пользователя загружать огромную.
> <a href="/download/{{ file.getId() }}" Ссылку лучше формировать в отдельном методе, а не копипастить по всему коду.
> $response = $response->withHeader('Content-Disposition', 'attachment; filename=' . $file->getName()); Это надежно работает только для имен из ASCII-символов (а другие в заголовках использовать нельзя). Надо делать ссылку, которая заканчивается на исходное имя файла: /download/123/file.txt и правильно экранировать специсимволы ыроде пробелов в имени. У меня был на гитхабе урок про ссылки.
А теперь перейдем к интересной части - к тестам - потому что их мало кто рискует делать, а зря.
> require_once 'vendor/autoload.php'; Нужно вместо этого сделать файл bootstrap.php, который подключит автозагрузку и подготовит все нужные объекты. Объекты вроде PDO надо хранить в контейнере (можно взять его из Слима), параметры БД - в конфиге.
Поговорим еще про то, как именно писать тесты. Вот подводные камни, которых стоит избегать:
- надо стараться изолировать тесты от любых внешних зависимостей (вроде исплоьзования сети, базы, диска), где это возможно. Это снижает вероятность, что тесты упадут при наличии правильного кода. Ненадежные тесты - очень плохо, так как разработчику надоедает выяснять, в чем была причина падения.
- юнит-тест должен тестировать класс, а не его зависимости или внешние сервисы. Тест для FileInfo должен тестировать код в этом классе, а не проверять работу getID3.
- надо тестировать код так же, как его использует пользователь. В юнит тестах - использовать публичные инфтерфейсы класса и не закладывать в код знание каких-то внутренних особенностей работы класса. Это сделает тесты более надежными и их придется реже переписывать при изменениях в классе.
- надо понимать, что код будет меняться, и стараться не закладывать в тест подробности, которые не влияют на тестирование. Ну к примеру, не стоит закладывать в тест, что длина случайного имени файла равна 45 символам, так как это число могут поменять. А вот факт, что функция генерирует имя, и оно уникально, скорее всего не поменяется. Это и надо проверять.
- не стоит отдельно писать проверки под то, что проверяется средствами языка - например, тайп-хинтами.
Вообще, тесты обычно пишутся исходя из задания (на больших проектах часто ТЗ большое и подробное - и его дают и разработчику, и сразу же тестерам). Есть задача "сделать функцию, которая получает на вход X и выдает Y" - вот мы и пишем тесты, которые дают что-то на вход и проверяют результат. Ну и иногда еще пишут негативные сценарии, которые дают неправильные данные на вход и проверяют, что функция сообщит об ошибке. Это ведь тоже иногда требуется тестировать.
Ну и если мы обнаружили ошибку в коде, мы можем после ее закрытия написать тест, проверяющий, что она больше не встречается - это называется регрессионный тест.
В общем, вопрос "а как это протестировать" - это отдельная задача, требующая порой творческого подхода. Давай попробуем на примере твоего кода разобраться. Начнем с простого класса Helper.
Возьмем функцию createTmpName() и попробуем сформулировать, как бы мы поставили задание, если бы поручили ее написание, например, какому-нибудь стажеру:
- функция должна генерировать имя для сохранения файла - имя должно быть уникальным - имя не должно совпадать с ранее сохраненными в БД
Попробуем написать на основе этого тестовый сценарий. Что мы можем тут протестировать с разумными затратами сил?
Самый простой тест - smoke test - это просто вызвать функцию. Это уже проверяет, что в ходе ее работы не возникло ошибок, даже если мы не проверили результат. Это лучше, чем ничего.
Далее, попробуем проверить результат. Как проверить, что возвращенное значение - имя файла? В Линуксе имена файлов могут содержать почти любые символы, разве что кроме слеша и спецсимвола с кодом 0 (NUL). Проверить, что это строка, можно тайп-хинтом на результате функции. Мы конечно можем проверять, что строка имеет определенный формат, но это такие вещи, которые могут меняться со временем. Я бы ограничился проверкой того, что строка не пустая, а если будут какие-то баги, связанные с неверным форматом этой строки, тогда добавил бы дополнительные проверки.
Также, имя должно быть уникальным. Это требование в принципе нелегко проверить. Ведь функция может 10 раз вернуть уникальное имя, а на 11-й повториться. Проверить это нереально, потому я бы ограничился минимальной проверкой: сгенерировал бы 2 имени и проверил, что они не равны. Это пишется быстро и позволяет отловить часть ошибок.
Там еще есть требование на несовпадение с сохраненными именами, но его сложно проверить. Для этого нам надо вставить какие-то имена в базу, но у нас нет метода для этого, так как коду он не нужен. Я думаю, это требование лучше будет проверить интеграционным тестом, который загрузит несколько файлов подряд и убедится, что они не затерли друг друга.
В твоем тесте заложена проверка длины имени и набора символов. Я бы советовал ее убрать или сделать более свободной (например, проверять что нет запрещенных спецсимволов и проверять только минимальную/максимальную длину).
getImagePath()/getFilePath() можно проверить с помощью smoke test - то есть просто вызвать и проверить, что результат не пуст. Хотя, там такая простая логика, что ее можно не проверять.
convertBytesToKilobytes - аналогично или подав на вход число и проверив, что результат находится в определенном диапазоне. Или тоже не проверять.
По поводу FileInfoTest:
В данном случае протестировать класс проще всего, собрав пару образцов файлов (или создав их во временной папке перед тестом) с известными свойствами и дав их на вход классу FileInfo. Стоит предусмотреть, что представление данных может меняться со временем (раньше было 2.300Kb, стало 2Кб).
Вместо мока для File можно просто создать такой объект.
Важно написать тест так, чтобы тестировать именно FileInfo, а не getID3. То есть мы тестируем, что данные от getId3 передаются, но проверять, чему равна длительность аудиофайла, смысла особого нет.
Если ты сделаешь сериализацию/десериализацию объекта FileInfo, то можно будет написать тест, проверяющий, что свежесозданный и восстановленный из JSON объекты возвращают одинаковые данные.
> $this->assertInstanceOf('File', $fileStub); Это ты тестируешь PhpUnit, а не свой код.
> public function testGetRowCount() { > $this->assertEquals(2, $this->getConnection()->getRowCount('fileshare')); Опять же, ты тут тестируешь не свой код, а правильно ли данные восстановлены из дампа.
В случае класса TDG, удобно тестировать код + БД по примерно такому принципу:
- сохранить сущность в БД - убедиться, что она ищется по id - убедиться, что количество сущностей увеличилось - удалить сущность - убедиться, что она не ищется - проверить количество сущностей
Вот, что еще можно покрыть тестами:
- isTmpNameExisting() - в случае добавления сфинкса - проверить, что сфинкс находит добавленные записи и не находит удаленные - проверить код сохранения файла: что добавленный файл можно найти в БД и можно найти на диске
В тестах с использованием БД надо подумать о восстановлении/очистке тестовой базы перед каждым тестом, либо написать тесты, не зависящие от состояния БД.
Ну и еще конечно можно бы написать интерфейсным тесты, имитирущие использование сайта через браузер.
Лучше стараться все, что связано с авторизацией, заключать внутри класса, и не выносить жти знания наружу. То есть внешний код в идеале не должен даже знать, как работает авторизация, а только обращаться к сервису: "проверь, залогинен ли пользователь", "зарегистрируй этого пользователя", итд.
Тут можно использовать разные подходы:
- можно сделать зоной ответственности класса только куки. То есть вставлять пользователя в БД не его задача, его задача - выставить и проверить куки. - можно сделать зоной ответственности класса вообще регистриацию и авторизаию пользователей. То есть внешний код вызывает методы вроде "зарегистрировать пользователя", "залогинить пользователя", "получить текущего пользователя", а этот класс внутри у себя решает, что надо сделать, какие куки выставить, что в базу через TDG вставить.
> value="<?= isset($search) ? htmlspecialchars($search) : '' ?>" isset - это проверка на null или проверка, что переменная существует? Второго быть не должно, а первое лучше делать через is_null или === null.
> Показаны результаты поиска по запросу: <?= $search ?></div> Тут не XSS? Конечно, она и есть. Попробуй вставить в поле поиска <s>test. И перечитывай урок по XSS.
Насчет пагинации, я помню, что я тебе сначала говорил перенести ее в один класс, потом назад, в шаблон, конечно, все равно как-то сложновато пока выглядит. Можно было сделать так: возвращать из Paginator массив с описанием всех ссылок примерно такого вида:
Шаблон проходит по массиву циклом и выводит пагинацию.
Так мы оставляем логику вычисления, какие номера показывать, в пагинаторе, а HTML-код будет в шаблоне.
Если тебе тоже кажется, что код сейчас запутанный, то можешь его так попробовать упростить.
По этой задаче, мы ее вроде давно уже делаем, и у тебя файлообменник еще есть. Подумай, может тебе захочется доработать авторизацию и пагинацию. Остальное все в принципе нормально работает. Если совсем времени нет, то можно конечно не доделвать, но я бы советовал его поискать.
Если будешь сдавать, напомни, что почти все готово.
Анон, помоги с задачей по ООП из уроков доброкуна. Задача с департаментами, в которых работают манагеры и т.п. офисный планктон. Не могу сообразить, как спроектировать классы. Дайте подсказаньки. Не нужно решения, нужно направление.
Перед тем как приступить к файлообменнику, пытаюсь изучать компоненты симфони и писать юнит-тесты. Для практики взял студентов, которых уже делал раньше. 1) Здесь тестирую форму: https://github.com/kubk/students/blob/master/tests/StudentTypeTest.php#L33 Правильно ли я понимаю, что это неправильно и лучше тестировать отдельно юзера? Тем более, что моя форма не добавляет новых правил. Как вообще понять, что тестировать, форму, Type, сущность? Где тут я тестирую свои классы, а где классы симфони?
Ну и вообще в коде довольно много сомнительных мест, так что буду рад любым замечаниям.
Silex использую из-за удобных сервис-провайдеров, которые скрывают в себе все конфигурационные заморочки. Настраивать компоненты вручную не так-то просто: https://symfony.com/doc/current/components/form.html#twig-templating Кстати в Silex класс App наследуется от контейнера: https://github.com/silexphp/Silex/blob/master/src/Silex/Application.php#L42 Как мне кажется, было бы удобней, чтобы контейнер можно было передавать в App как зависимость (так сделали в Slim). Можно сделать отдельный файл, где создаётся контейнер и инициализируются зависимости, которыми можно пользоваться, не поднимая класс приложения с его системой событий и роутингом. Или в Silex всё не так просто и мне нужно разобраться с EventManager'ом, чтобы понять, зачем они предпочли наследование?
Ананасы решил тут написать модуль для опенкарта Есть небольшой опыт кодинга на питоне и жс, но теперь прийдется дрочить и php так как опенкарт на нем. Начал я устанавливать LAMP и охуел напарртачил с апачем и phpmyadmin теперь они выкидывают ошибки при гуглении которых он меня нахуй посылает. Пасаны а нельзя для всей этой параши установить изолироное окружение? я теперь этот ебучий апач и mysql переустановить не могу .
>>909617 >>909618 >>909619 >>909623 Спасибо, подумаю что к чему. Честно говоря, я захэллоуворлдил yii2 и сейчас читаю документацию, целясь на тестхаб, так что тесты наверно отложу до него, тем более они присутствуют в задаче, а вот остальное постараюсь доделать. Хотя после знакомства с большим фреймворком меня не покидает ощущение, что это всё никому не нужные велосипеды. Все твои ответы я себе сохраняю, так что простыня про тесты обязательно пойдет в дело. Еще раз спасибо.
Когда заливаешь комит на гитхаб, тот делает запрос на указанный тобой адрес, а там скриптик что-то проверяет и делает пулл мастер ветки Как-то для сайтика приятеля такое писал: http://pastebin.com/FDvJrrD0
И так нужна ваша помощь аноны, пытаюсь загрузить файл на сервер и записать имя файла в таблицу mysql. Получается только загрузить файл на сервер. Вот скрипт
Кто-нибудь читал Зандстру? У вас тоже первые главы прошли лигео и просто по 50 страниц в день а там где началась вода про паттерны то: КАКАЯ ЖЕ ВОДА, ГДЕ КОНКРЕТИКА?? СПЛОШНЫЕ РАССКАЗЫ О ТОМ КАКИЕ ПАТТЕРНЫ КРУТЫЕ И КАК Я БУДУ СЧАСЛИВ КОГДА НАЧНУ ИХ ИСПОЛЬЗОВАТЬ, АЛО, ДА Я УЖЕ НЕСЧАСЛИВ ЧИТАЯ ВСЮ ЭТУ МУТЬ.
> как долго хранятся скрипты на идеоне? Не знаю. Если они тебе нужны, советую скачать себе на диск, а также забекапить куда-нибудь. Например на gist.github.com.
>>910108 Так я хотел её хотя бы вывести на экран чтобы убедиться, что всё работает. Все ж запросы должны в индекс посылаться, а значит я должен увидеть строку "/asdf". Или я чего-то не понял?
>>910152 совсем не апачееб, но хуй знает что ты делаешь не так, может у тебя мод_реврайт не включен, может хтаксес не туда засунул, может надо /index.php, слеш добавить в начало тоись. Всякая хуйня бывает, десу.
>>910233 >>910239 >>910241 Так в задачу про Вектор захерачить департаменты и сотрудников в этот класс вместо массива или нет? Так то там форичем офк их обойти и всё.
Хотел обведенные функции вынести в отдельный класс, но потом подумал что тогда вообще за класс у меня если в нем только геттер и сеттер остались, в общем оставил. Сложно кароче всё это ребят.
Не только. Массивы - это универсальная структура, которая заменяет такие структуры, как вектор, стек, очередь, хеш-таблича, множество, из других языков. И потому у массива может быть много применений.
Но, если у тебя есть какая-то сущность, то ее лучше представить в виде объекта. Это делают не из слепой веры в ООП, а потому что это дает преимущества:
- в отличие от массива, у объекта известен список полей - эти поля можно документировать комментариями - у объекта могут быть методы - у объекта можно реализовать инкапсуляцию (сокрытие) данных и логики внутри объекта - в тайп-хинтах можно использовать имя класса
Соответственно, правильно написанный ООП-код легче читать и поддерживать, чем код, где передаются массивы непонятных форматов.
Итераторы нужны там, где мы хотим перебирать что-то, что не можем или не хотим поместить в массив. Например, список файлов на диске (их может быть много и на загрузку их списка в массив уйдет много времени), или последовательности чисел, генерируемые на ходу.
SplObjectStorage нужен, если ты хочешь сделать "множество" или "хеш-таблицу", где ключом будут объекты. Ну то есть ты хочешь сделать "список"объектов или сопоставить объекту какие-то данные.
Это по моему написано в уроке, но я напишу еще раз. При решении задачи на ООП, надо решить:
- какие у нас будут сущности (для которых мы делаем классы). Ну тут просто - явно сущностями будут Сотрудник, Департамент, Компания. Больше в задаче вроде ничего нет. - какие у них будут свойства? Например, у Сотрудника есть как минимум такое свойство, как ранг. Тут важно выбрать свойства правильно и отличать исходные свойства от вычисляемых из них значений. Ну например, если у нас есть сущность Студент и список его оценок, то мы не будем делать отдельное свойство среднийБалл, так как его всегда можно посчитать исходя из списка оценок. - что можно делать с сущностями, то есть какие у них есть методы? Ну тут как минимум можно добавлять в Депаратмент нового Сотрудника, а также узнавать разную информацию: сколько Сотрудник выпил кофе, сколько произвел документов. Тут еще важно понять, к какому классу надо отнести тот или иной метод. - как сущности связаны? Ну например, в Департаменте работают Сотрудники, и эту связь надо отразить в коде (например, сделать массив работающих в Департаменте сотрудников).
Кроме классов-сущностей, бывают еще классы-сервисы. Они не представляют никакую сущность, и у них почти нет свойств, они просто содержат полезные методы. Обычно их называют "сервис чего-нибудь", "менеджер чего-нибудь", "хелпер чего-нибудь", "калькулятор чего-нибудь". В задаче таким классом может быть АнтикризисныйКомитет, который берет Компанию и применяет к ней разные меры.
Вот как-то так. Если по-прежнему непонятно, задавай дополнительные вопросы.
Это нужные велосипеды. Если бы ты не попробовал сделать приложение с нуля, ты бы сейчас плохо понимал, что делает фреймворк внутри. Ну и еще оно помогает изучить вещи вроде автозагрузки, работы с БД, MVC, вывод таблиц, работу с формами. Не так и мало!
Класс с 2 методами (add/get) вполне допустим, почему нет? Наверняка в будущем в него еще добавят.
Методы, которые ты упомянул, нет особого смысла выносить в другой класс, так как они работают только со свойствами Департамента и логичнее всего их в нем и держать.
>>910323 >>910307 Говнокодил всю ночь, проверяйте пожалуйста. http://ideone.com/yH35aJ В правильности решения не сомневаюсь даже, интересует целесообразно ли я классы описал и применил всякие ООП - фишечки
О, вот у тебя интересные вопросы, давай разберемся.
Для начала почитай вот это вот, это к другой задаче, но тоже про тестирование: >>909619
> Тем более, что моя форма не добавляет новых правил. Как вообще понять, что тестировать, форму, Type, сущность? Где тут я тестирую свои классы, а где классы симфони?
У тебя там по сути используется класс форм Симфони, ты к нему добавляешь только конфигурацию полей и правила проверок. И тестировать код Симфони смысла нет (у них есть свои тесты), тут можно разве что тестировать твою конфигурацию.
В принципе, от такого тестирования может быть польза, например, это позволит отловить опечатку при правке конфигурации формы.
Но ради этих тестов нам надо многое усложнять. Например, ты вынужден делать мок TDG, и при этом ты в тест закладываешь знание о принципе работы формы (ты знаешь, что она обращается именно к методу findByToken/findByEmail и делаешь для них мок). Это не очень хорошо, так как в будущем при рефакторинге это могут поменять и тест придется переделывать. Ведь техническое задание на форму вряд ли говорит "форма должна вызывать метод fidByEmail", оно говорит "форма должна сравнить введенный email с имеющимися в базе".
Более того, ты там подменяешь сервис в DI контейнере. То есть закладываешь в тест знание того, к какому именно сервису обращается форма. Лучше бы не подменять данные в контейнере, а создать самому нужный сервис и передать в него мок вместо TDG.
Соответственно для того, чтобы убрать это знание о внутреннем устройстве формы, нам надо отказаться от мока и использовать вместо этого тестовую базу с заранее известными значениями токенов и email, которые мы сможем заложить в тест.
Но с базой тоже не все так просто, во-первых, ее структура будет меняться и надо соответственно обновлять тестовые данные (хотя возможно, это сделают миграции), во-вторых, надо перед каждым тестом очищать и заполнять базу, в-третьих со временем этих данных будет много, в четвертых, тест и данные для него (пользователь в тестовом дампе БД) находятся далеко друг от друга. Наверно, тут удобнее поступить по-другому:
- перед тестом вставить одного или нескольких пользователей с известным email/токенов (можно, делать это, открыв и не коммитя транзакцию) - после теста удалить (или откатить транзакцию)
Тестовые пользователи понадобятся нам и в других тестах, так что метод регистрации пользователя пригодится еще не раз.
Тут конечно есть свой подвох: может произойти ошибка при вставке пользователя из-за неуникальности email или токена (если из-за какой-то ошибки старый тестовый пользователь останется в базе). Для борьбы с ним можно например сделать увеличивающийся счетчик, и использовать его при генерации email или токена. Ну или восстанавливать базу к известному состоянию перед тестом.
То есть мы пытаемся точнее имитировать поведение пользователей, которые регистрируются. Но тогда, опять же, возможно, лучше подняться на уровень выше, и тестировать форму через тестирование пользовательского интерфейса (то есть заполнять форму в эмуляторе браузера). Так мы протестируем больший объем кода. У меня ощущение, что тут лучше бы подошел браузерный тест, так как кода и логики в самой форме почти нет.
Как я понимаю, сам сервис авторизации у тебя довольно хорошо скрывает подробности внутренней реализации. Он принимает на вход HTTP запрос или модифицирует HTTP ответ. Тут явно можно использовать loopback-тест (loopback - это когда выход устройства, например, звуковой или сетевой карты, подключают ко входу и получают возможность протестировать как приемник, так и передатчик сигнала одновременно).
Loopback-тест можно попробовать реализовать так:
- создать студента - вызвать метод "проверить регистрацию" с пустым запросом и убедиться что он показывает не-залогиненность пользователя - вызвать метод "залогинить студента" и сохранить ответ с куками - вызвать метод "проверить регистрацию", и подать на вход ранее созданный ответ, убедиться что мы получаем правильного студента - вызвать метод "разлогинить студента", и сохранить ответ - подать этот ответ на вход методу "проверить регистрацию"
Единтсвенная сложность тут - нужно научиться превращать HTTP-ответ в HTTP-запрос, то есть перенести куки с учетом возможности их удаления. Немного можно было бы упростить себе жизнь, если бы сервис авторизации работал не с Request/Response, а с коллекциями кук.
Также, нам понадобится возможность записи в БД, так как сервис при регистрации пишет в нее.
Твой тест, увы, опять полагается на внутреннюю реализацию. Ну вот посмотри:
А что, если завтра мы начнем использовать 2 куки? Что, если вместо токена мы начнем использовать email + цифровая подпись? Хеш от пароля? Тест придет в негодность.
Увы, ты тестируешь не публичный интерфейс, а детали внутренней реализации.
Насчет PaginatorTest - там мало логики, нелегко понять, что тестировать. Я бы просто тестировал по-минимуму, вроде "вызвать функцию getPageCount() и убедиться что она вернула число больше нуля". Твой тест тоже годится в принципе и позволит обнаружить ошибки в расчете числа страниц.
Насчет markSearch. Давай подумаем, какие ошибки в принципе могут быть в этой функции? Скорее всего, там будет что-то такое:
- часть данных теряется - искомое слово не выделяется
Соответственно, попробуем, опять же, не закладывать знание о том, каким тегом выделяется искомое слово, а, например, напишем тест, ищущий любой тег (или просто угловые скобки) справа/слева от искомного слова. Или, если мы хотим еще сильнее абстрагироваться от деталей реализации (а вдруг слово выделяется не тегом?) - можно просто проверять, что при наличии искомого слова функция возвращает другой результат.
Ну и дополнительно можно проверить, что функция не теряет слова при выделении.
И еще, что спецсимволы экранируются.
Насчет pathToRoute - мне кажется, там незачем писать мок, можно просто ограничиться вызовом функции и при желании, проверить, что результат похож на URL. Там же логики в ней - одна строчка, стоит ли ради нее мок писать?
> Здесь у конструктора есть опциональный параметр, отвечающий за то, как помечать найденную строку: > Но по сути выходит, что аргумент добавлен исключительно ради тестов. Это плохо, лучше переделать тест. Иногда приходится добавлять в код какие-то тестовые опции (я например использовал опции для отключения кеширования), но лучше этого избегать. Тут надо взвешивать плюсы/минусы.
> Настраивать компоненты вручную не так-то просто Есть такое, у некоторых библиотек сложная инициализация, но там все же ООП, тайп-хинты и DI, так что иногда можно и так догадаться. Согласен, что там сложно, там еще и рефлекшен используют, чтобы имя файла с классом определить.
> Как мне кажется, было бы удобней, чтобы контейнер можно было передавать в App как зависимость (так сделали в Slim). Можно сделать отдельный файл, где создаётся контейнер и инициализируются зависимости, которыми можно пользоваться, не поднимая класс приложения с его системой событий и роутингом. Ну это микрофреймворк и в таком случае можно отдельно сделать заполнение контейнера, а отдельно - простановку роутов. Хотя в принципе, если run() не вызвать, то роуты не сильно мешают. Даже наоборот, они могут понадобиться, если ты захочешь генерировать URL, это же часть конфигурации.
То есть я думаю, тебе все же нужен $app целиком с роутами даже для CLI скриптов.
> Или в Silex всё не так просто и мне нужно разобраться с EventManager'ом, чтобы понять, зачем они предпочли наследование? Думаю, ради упрощения, микрофреймворки обычно и выглядят как один объект.
> Ещё ты советовал использовать собственный класс на куках тем анонам, которые использовали slim'овский CSRF guard: https://github.com/slimphp/Slim-Csrf > И symfony/security-csrf тоже использует сессию, однако TokenStorage внедряется по интерфейсу Согласен, можно. А может такой класс уже даже есть? Просто мне кажется, сессию заводить ради одного значения - перебор.
Код самого приложения я не проверял, только тесты.
Вообще, ты правильно делаешь, что изучаешь и Симфони, и тестирование. Это поможет, если тебе придется работать с большими и сложными приложениями.
Я бы проверял еще что $e instanceof класса, отвечающего за HTTP ошибки, так как код 404 теоретически может быть и в не HTTP исключении. (например, код ошибки в PDOException).
Ну и ты там при 404 ошибке отдаешь страницу с кодом 503. Надо проверять, что исключение содержит HTTP код ошибки и возвращать его наверно.
Бля сука помогите с ебучим апачем... Как его нахуй настроить? Накидайте ссылок, на язык похуй, главное что бы пропроще, куда какие файлы править и куда копировать.... СУКААА , БЛЯ ЭТОМУ АПАЧУ 80 лет, разве нельзя было сделать ему морду блять нормальную... сукааа как же мне печет блять
>>910893 xampp ставь, там все настроено уже. Если отдельный надо, то посмотри просто в xampp конфиг файл, скачай отдельно апач и настрой похожим образом. Отдельные настройки легко гуглятся.
>>910491 По поводу теста формы тоже прихожу к выводу, что раз мы пытаемся "точнее имитировать поведение пользователей, которые регистрируются", то тест формы логичнее сделать более высокоуровневым (функциональным), в противном случае выходит, что тестируются валидатор и форма Cимфони. Но тогда вопрос по функциональным тестам: в случае с валидацией формы нужно ли проверять, какие именно поля формы не проходят валидацию? Или достаточно проверить, что не происходит редирект на главную? В первом случае нужно использовать парсер html и подумать о том, как написать селектор для вычленения количества ошибок формы без привязки к вёрстке.
> Также, нам понадобится возможность записи в БД, так как сервис при регистрации пишет в нее. Совсем забыл об этом в тестах класса AuthService, а ведь он использует TDG. Странно, что PHPUnit мне не выдал ошибку из-за того, что использую у мока метод save, который никак не описал во время создания этого мока. Видимо, это из-за того, возвращаемое этим методом значение никак не проверяется (вдобавок метод save ничего и не возвращает: https://github.com/kubk/students/blob/master/src/StudentGateway.php#L60 ). Наверное лучше, чтобы методы, вставляющие данные в БД, что-то возвращали? Например lastInsertId. А в классе, который использует TDG, проверять, что это значение = число, отличное от нуля (или бросать исключение в самом методе save и ничего не возвращать?). Не могу понять, что лучше здесь. И по поводу БД, выходит, что AuthService должен использовать не мок TDG, а сам класс TDG, которому будет передаваться PDO-соединение c новой базой (которую будем постоянно чистить и перезаполнять)? А в тестах регистрации у AuthService нужно проверять, что в БД появилось новое значение? В этом >>909619 посте ты писал, что надо стараться изолировать тесты от любых внешних зависимостей вроде базы, если это возможно.
> А что, если завтра мы начнем использовать 2 куки? Что, если вместо токена мы начнем использовать email + цифровая подпись? Хеш от пароля? Тест придет в негодность. Угу. Только вот если мы будем использовать хеш от пароля, к примеру, то это уже будет совсем другой способ регистрации/логина. Например для регистрации будем пользоваться функцией password_hash, для логина будем брать из request'а email, находить пользователя с таким email в БД, проверять через password_verify его пароль. Вырисовывается отдельная абстракция. Думаю, отдельная абстракция должна быть и для цифровой подписи, с которой я не знаком. Ещё необходимо добавить абстракцию над теми данными пользователя, по которым мы его идентифицируем (в примерах использования symfony/security видел слово credentials), раз токенов может быть больше одного. Поначалу писал что-то подобное, но когда получил фабрику абстрактных интерфейсов, то вспомнил, что требуется по заданию и завернул в один класс, забив на гибкость. Выходит, что в моём случае AuthService - слишком громкое название и его как минимум нужно переименовать в CookieAuthService. Вообще да, в тесте AuthService у меня 2 или 3 приватных метода, что уже говорит о том, что я тестирую не интерфейс, а пытаюсь лезть в кишки. Подумаю над улучшением, пока в голову ничего не приходит.
> Немного можно было бы упростить себе жизнь, если бы сервис авторизации работал не с Request/Response, а с коллекциями кук. Но тогда контроллеру нужно быть умнее и передавать в методы сервиса не просто реквест, а куки, ну и потом эти куки класть в response. Хотя, работать с HTTP это прямая обязанность контроллера.
>> Свой CookieTokenStorage вместо дефолтного SessionTokenStorage > А может такой класс уже даже есть? Готовой реализации я не нашел, вот тут https://github.com/symfony/symfony/issues/13464 юзеры с меткой contributor вроде пришли к выводу, что куки подходят для хранения токена, но issue по-прежнему висит. Ревьюверов то код стайл не устраивает, то избыточные аннотации @see
> Я полез в исходники фейкера и обнаружил там такой же подход: К сожалению, об этом почему-то не написано в доках, только в коде. Оттуда об этом и узнал.
Суть лупбек теста понял, target у UniqueEmailValidator'а изменил. За БД возьмусь, когда придумаю, что делать с AuthService. Может есть на примете какие готовые библиотеки для авторизации, которые можно глянуть и поучиться?
Доделываю свой первый проект на Ларе (хостинг картинок). Сегодня вот доделал вывод картинок аякс-запросами. Начал было делать пагинацию на поганом жиквери, как меня одёрнуло: в Ларавеле же уже есть прекрасная пагинация из коробки, зачем мне опять писать велосипед (пагинацию и на пыхе, и на джс писал уже тысячу раз до этого)? В итоге блок с ссылками на страницы я каждый раз при смене страницы подгружаю заново с сервера. Вопрос: насколько это вообще хорошая практика? С одной стороны, зачем подгружать по сути лишние данные каждый раз, а с другой — зачем писать по сути лишний код, так? Хотелось бы услышать мнение здешних спецов.
>>911485 Вроде норм все, есть только стилистические огрехи. > if($height>$anonHeight){ Делай отступы между символами вроде знаков равенства и т.п. - if($height > $anonHeight){ Непонятно почему в задачке про генератор стихов ты додумался до конструкции типа $word1 = ($massiveWord1[array_rand($massiveWord1)]); А в задачке про генератор имен ты делаешь отдельную переменную $random >echo "------<br>"; Тут наверное удобней будет использовать тег <hr>, он просто выводит горизонтальную строку как блочный элемент.
>>911495 Спасибо за ответ. Не совсем понял >А в задачке про генератор имен ты делаешь отдельную переменную $random если не трудно, можешь написать как это должно выглядеть? Я старался придумать самый короткий вариант ну типа чтоб строк как можно меньше было. Ничего другого не смог придумать.
Думаю, лучше использовать готовый вариант. Ведь пагинатор может не показывать все страницы сразу (вроде "1 2 3 ... последняя"), а только часть, и показывать другие номера при переходе. Реализовать самому это конечно можно, но потребует написания дополнительного кода.
> В итоге блок с ссылками на страницы я каждый раз при смене страницы подгружаю заново с сервера. Я надеюсь, что тут имеется в виду, что этот блок приходит вместе с самим контентом, а не получается отдельным запросом - делать 2 запроса было бы неэффективно.
То, что другой анон сказал насчет оформелния - тоже верно, привыкай оформлять код правильно, смотри второй пост треда.
В генераторе стихов можно упростить код, если сделать массив с шаблоном стиха вида [$word1, [' '], $word2, [' '], $word3...] и обходить его циклом foreach.
А это для тех, кто не хочет изучать ООП, MVC, фреймворки, тестирование, решать задачи про вектор, студентов, файлообменник и тестхаб, а хочет учиться по видеокурсам "PHP за 24 часа без занудной теории".
Если что-то не получается, подробно напиши, что ты сделал и что ожидал, и что вышло вместо этого. И что в логе ошибок Апача написано.
Морду не делают, так как она не нужна, это же сервер, а не игра по метанию свиней в стены. Если ты освоишь командную строку и конфиги, ты увидишь, что зачастую этот подход удобнее и требует меньше времени. Ну например, конфиг легко скопировать, сгенерировать, искать слова в нем, а попробуй то же самое сделать с графическим интерфейсом.
Ну и никто не запрещает тебе самому написать конфигуратор Апача. Могу предложить сделать приложение на HTML/JS и превратить его в обычное приложение с помощью Electron. Уверен, на любом собеседовании твой конфигуратор произведет хорошее впечатление.
Когда я писал "имитировать поведение пользователя", я имел в виду не только поведение человека с браузером, но и код который например вызывает методы какого-то класса. Я просто хотел сказать, что надо не опираться за знание внутренних особенностей кода, а использовать только предоставленные интерфейсы.
Что касается формы, тестирование через браузер позволяет протестировать все слои кода насквозь, так как данные формы могут как-то дополнительно обрабатываться и преобразовываться на пути от браузера к БД.
> в случае с валидацией формы нужно ли проверять, какие именно поля формы не проходят валидацию? Стоит, если это не требует больших затрат. Так как это делает тест более точным.
> В первом случае нужно использовать парсер html и подумать о том, как написать селектор для вычленения количества ошибок формы без привязки к вёрстке. Без привяки к верстке, к сожалению, обойтись не выйдет. Лучше всего привязываться к каким-то атрибутам, которые не поменяются. Но это редко возможно. По факту при смене верстки, скорее всего, придется переделывать тесты (это недостаток браузерных тестов).
Обычно эмуляторы браузера позволяют искать элементы по разным критериям: id, текст, CSS/XPATH селекторы.
Чуть-чуть можно облегчить жизнь, вынеся селекторы элементов из кода тестов отдельно (в классы PageObjects), чтобы их было проще менять.
У меня была еще такая идея: сделать специальные классы (например, test-form-error) или атрибуты, которые относятся только к тестам и которые мы не трогаем при смене верстки. Можно пробовать привязываться к текстам на элементах, но они тоже могут меняться.
> Странно, что PHPUnit мне не выдал ошибку из-за того, что использую у мока метод save, который никак не описал во время создания этого мока Видимо неперехваченный метод работает, как обычно.
> Наверное лучше, чтобы методы, вставляющие данные в БД, что-то возвращали? Это не имеет значения.
> И по поводу БД, выходит, что AuthService должен использовать не мок TDG, а сам класс TDG, которому будет передаваться PDO-соединение c новой базой (которую будем постоянно чистить и перезаполнять)? Получается что да, если мы не хотим полагаться на знание, какие именно методы вызываются.
> А в тестах регистрации у AuthService нужно проверять, что в БД появилось новое значение? Можно, но лучше бы попробовать через тот же сервис найти ранее зарегистрированного пользователя, а не лезть в базу в обход него.
> надо стараться изолировать тесты от любых внешних зависимостей вроде базы, если это возможно В данном случае с базой получается проще.
> Только вот если мы будем использовать хеш от пароля, к примеру, то это уже будет совсем другой способ регистрации/логина. Например для регистрации будем пользоваться функцией password_hash, для логина будем брать из request'а email Не, ты путаешь. Я имел в виду, мы можем поменять формат кук. Вместо одной куки с токеном использовать, например, email + хеш пароля, которые мы берем из объекта Student. Или id + специальная подпись. Тогда внешний интерфейс AuthService не меняется, но внутренняя логика становится другой.
Тут лучше всего генерировать авторизационные куки с помощью сервиса, и потом ему же их и скармливать. Так мы избегаем внедрения в тест знаний о его внутреннем устройстве.
> Вырисовывается отдельная абстракция. Думаю, отдельная абстракция должна быть и для цифровой подписи, с которой я не знаком. Такую абстракцию уже предоставляет AuthService.
> Ещё необходимо добавить абстракцию над теми данными пользователя, по которым мы его идентифицируем (в примерах использования symfony/security видел слово credentials) В Симфони абстракция нужна, так как они не знают, как будет выглядеть объект пользователя и как будет реализована авторизация.
> Выходит, что в моём случае AuthService - слишком громкое название и его как минимум нужно переименовать в CookieAuthService. Можно. Тогда логично будет и передавать ему сразу куки вместо всего Request.
> Но тогда контроллеру нужно быть умнее и передавать в методы сервиса не просто реквест, а куки, ну и потом эти куки класть в response. Добавь тайп-хинт, чтобы нельзя было перепутать, и пусть так и делает. В случае с куками ответа, можно просто получить коллекцию кук из Response и передать этот объект.
> Может есть на примете какие готовые библиотеки для авторизации, которые можно глянуть и поучиться? Не знаю. Есть библиотека Симфони, но она очень-очень переусложненная, я к ней даже подходить лишний раз не хочу. Фаерволлы какие-то, куча абстракций.
Можно поискать в packagist.org по словам auth, authentication и тд.
> HTMLElement.prototype.find Не советую совать свой код в стандартные прототипы - так как это может конфликтовать c другими библиотеками (или существующими функциями, или добавленными в будущем), да и неаккуратно как-то.
Да и нелогично передавать context, если ты вызываешь find на конкретном элементе.
Поиск по id не учитывает context. Не позволяет искать в отсоединенных (не вставленных в DOM) элементах.
> if (!(result instanceof HTMLElement)) { Это логичнее делать при задании функций, так как там мы знаем, результат какого типа вернет функция. То есть сделать как-то так:
pipe() - надо написать самому (получается функциональное программирование).
> var elems = context.getElementsByTagName("*"); Это может быть не очень эффективно, если элементов много. Выгоднее может быть обходить ДОМ рекурсивно.
> if (elems.className == selector) { Тоже неправильно. Надо проверять наличие класса, а не полное совпадение.
Для вписывания кода в Reporter стоит применить Heredoc или Nowdoc, чтобы код был читабельным и можно было ставить кавычки. А еще лучше, конечно, сделать отдельный PHP-шаблон: http://www.phpinfo.su/articles/practice/shablony_v_php.html (а если еще заморочиться, можно сделать перекодировщик HTML в plain text для ideone, вырезающий теги).
У Organisation, возможно, не нужно поле name (хотя и вреда от него нет).
> public function getOrgTotals() { > $info = new stdClass(); Вот тут мне не нравится использование объекта как массива. Может, лучше было сделать массив? А еще можно просто сделать в организации методы для суммирования по департаментам.
Чуть-чуть попробовать упростить код в однотипных методах можно так:
или применив array_reduce к массиву департаментов (заодно голову поломаешь). Увы, в PHP-синтаксисе код получается не очень компактный, но в языках со стрелочными функциями получается как-то так:
> class Names { Название неправильное, правильно NameGenerator. Ведь твой класс - это не хранилище имен.
В классе Employee есть небольшая проблема, что тот, кто его наследует, должен не забыть в конструкторе задать 3 значения зарплаты, и тд. Но где гарантия, что он не забудет? Решить проблему можно, создав в базовом классе абстрактные функции вроде getBaseSalary() и тд., которые наследники будут обязаны определить.
> как более элегантно забить работягами я просто не знаю: Сделать функцию-парсер выражения вроде ['1ме2', '5ме3', ...].
Чтобы понять паттерны, надо изучать код компонентов Симфони или подобный сложный код, и там уже находить паттерны, и тогда читать про них. Тогда, я думаю, будет понятнее. Ну и надо не слепо везде использовать паттерны, а оценивать, выгодно это или нет.
Ну возьмем паттерн Фабрика. Вот просто так он выглядит как бессмысленное усложнение, зачем создавать объект Фабрикой, когда есть $x = new Class? Молодец, если ты так думаешь вместо зубрежки, то ты уже не безнадежен. Но не все так просто. Представь,что объект создается не в твоем коде, а где-то в сторонней библиотеке, которую ты править не можешь. Как тогда заставить ее создавать объекты нужного нам класса? Вот тут бы и пригодилась бы Фабрика, если бы автор того кода сделал возможность передать в его класс нашу Фабрику.
$number = 0; / Сколько человек в классе выше анона /
/ Перебираем всех одноклассников / foreach ($classmates as $name => $height) { echo "Имя: {$name}, рост: {$height} см.\n"; if ($height < $anonheight){ $number++; echo "В классе {$number}"; } / Тут надо добавить проверку, выше или ниже этот человек, чем анон, и подсчитать число тех, кто выше / }
echo "В классе {$number} человек выше анона\n";
Сравнение с ростом анона почему-то не работает, тех, кто ниже не видно, выше оказываются все.
>>911673 Вообще, когда делал пагинатор на джс в одном из проектов, писал с тем условием, что в джс с бэкенда приходит всего один параметр: номер последней страницы. А там уж скрипт сам проставляет многоточия в нужных местах, disabled на стрелочки и тд. НО, т.к. это был пагинатор для модулей в админке, требование "работать без джс" не было. Сейчас же для себя я решил обязательно это требование включить.
>Я надеюсь, что тут имеется в виду, что этот блок приходит вместе с самим контентом Конечно.
>Также, советую проверить, что реализация загрузки с аяксом учитывает описанные в этой статье требования: Спасибо. Будет интересно для практики сделать статус-бар загрузки контента не НА сервер, а С сервера.
А ещё интересно было бы узнать, как по уму реализовывать хранение конфига системы в базе данных (как у всех популярных cms). Интуитивно я бы сделал примерно так: отдельная модель UserConfig с отдельной таблицей в БД, сохраняющая при каждом изменении данные в кэш. При просрочке кэша, соответственно, туда их снова заносит из БД. Всё это дело выполняет отдельный миддлвэр для всех запросов к серверу. В том направлении мыслю?
>>911673 Спасибо за проверку и советы. Решил последнюю задачу из раздела "строки". Программа работает верно, на разные палиндромы, но реализация так себе. Можно как то улучшить? http://ideone.com/rbYtIA
>>911846 Код лучше выкладывать на ideone, так ты облегчишь жизнь проверяющим. Вот твой код: http://ideone.com/728DSl Читай ошибку: "Undefined variable: anonheight" Переменные $anonHeight и $anonheight - разные, регистр имеет значение. Ну и ты скорее всего всего знак в if'е перепутал, там сравнение if (ростТекущего < ростАнона), то есть ты ищешь тех, кто ниже анона.
>>911857 >>910137 Спасибо, нашёл свой код февральский, что-то охота взяла допилить его. офигеть, прямо помню половину постов и писавших, как плотно сидел тогда в тредах, сколько ж воды с тех пор утекло, на работу устроился, опушка, кстати, благодаря тебе
Вот я понял, что TableDataGateway работает с таблицей в БД. И мы пишем на каждую таблицу свой класс, и все удобно. А что теперь делать, если нужно получить запросом данные из нескольких таблиц? А? А? Слишком сложно это все.
Это зависит от установленных в твоей системе шрифтов.
> @import "css/style.css" screen; А почему не просто <link href> ?
> src="../src/field.js?f Не надо дописывать странные символы, лучше научиться очищать кеш и пользоваться соответствующей опцией в инструментах разработчика.
Код конечно получился доволно запутанным, из-за того, что вместе объединены игровая логика, вывод информации, и обработка событий. Такой большой код надо как-то разделять на части. У тебя много методов, и не очень понятно, как они и в каком порядке вызываются.
Ну например, если посмотреть на класс Field, то трудно понять, как им вообще пользоваться, как правильно инициализировать. Если бы вся инициализация была в конструкторе, было бы проще (хотя конечно DOM элементы может быть удобнее создавать отдельным методом, чтобы мы могли создать объект, и только потом создать таблицу). Еще есть такой вариант: сделать класс FieldBuilder, который при вызове метода createField() вернет объект класса Field, который управляет полем. Таким образом, управлять полем нельзя, пока оно не создано (а в твоем коде мы можем вызывать публичные методы вроде getCells() когда поле еще не создано). В общем, тут есть разные варианты.
Или, например, можно было бы попробовать разбить код хотя бы на 2 класса: один отвечает за работу с DOM, другой - за игровую логику.
Для хранения информации об открытых клеточках, есть 2 подхода, можно хранить данные об открытых клетках. рсположении мин, и тд, в JS-переменных (массивах и словарях), а можно хранить их в DOM, в виде классов и атрибутов. У тебя вроде бы используется второй.
> Field.prototype.getField = function() { > return this._gameContainer.querySelector("#field"); Вот тут как минимум 2 проблемы:
- поле _gameContainer не инициализируется в конструкторе, значит мы должны как-то догадаться, когда уже можно вызывать этот метод, а когда еще нельзя - вместо того, чтобы искать элемент каждый раз, может быть выгоднее просто хранить ссылку на него
> Field.prototype.getFace = function() { > return this.getHeader().querySelector("[id$='face']"); Это какой-то переусложненный подход. Для смены свойств элемента надо использовать классы. А вот писать в коде только часть идентификатора - плохо, так как поиск по danger-face не найдт эту строку.
Если посмотреть на классы Field и Popup, то можно увидеть взаимные зависимости: Field вызывает Popup, а тот вызывает Field. В данном случае это говорит о неудачном проектировании. У тебя получился не универсальный попап, а попап, работющий только с объектом Field. Сделать зависимость односторонней можно, если например в Popup передавать коллбек, который будет вызван при нажатии на кнопку. Тогда Popup не будет ничего знать о Field и не будет к нему как-то привязан.
> Field.prototype.getTd = function(x, y) { Этот метод сделан неэффективно. Ты ищешь все td, потом перебираешь их. Лучше сделать один из вариантов:
- искать td по table.rows.cells[j] - сделать словарь, поместить в него ссылки на ячейки и искать как cells[y][x]
> Field.prototype.getTimerElem Не очень понятно, зачем этот метод сделан публичным, а не приватным.
> this.getField().oncontextmenu = this._playerRightClick.bind(this); Неправильно. Событие oncontextmenu соответствует открытию меню, а не нажатию правой кнопки. Например, меню можно открыть клавишей на клавиатуре. Для отслеживания клика правой кнопкой надо использовать onmousedown/up (оно срабатывает для всех кнопок мыши, а не только для левой).
> if (!this._isCorrectTd(td, event, 3, ["opened", "hint"])) { Не очень понятно, почему мы передаем список классов. Разве функция "можно ли поставить флаг" сама не знает, по каким клеткам можно кликать? Какой смысл выносить это знание из функции?
Я бы сделал примерно так:
если (!можноПоставитьФлаг(...)) { ... } поставитьФлаг();
То есть выделил бы действия вроде "поставить флаг", "проверить, можно ли стаивть флаг" в отдельные функции (или даже в отдельный класс, работающий с DOM). А у тебя код содержит не высокоуровневые действия, а детали реализации:
если (клетка содержит классы X и Y) { ... }; добавить класс Z;
Получается, ты в одной функции смешиваешь игровую логику и работу с DOM. Из-за этого код хуже читается. Также из-за этого получается небольшое дублирование кода.
То же самое и в других местах:
> if ((!cells.classList.contains("flag") && > cells.classList.contains("mine")) || А ведь можно написать if (!this._hasFlag(cell) && this._hasMine(cell)) ...
> face.removeAttribute("id"); > face.setAttribute("id", id); Лучше просто face.id = .... А еще лучше классы менять.
> //Отменить contextmenu событие по-умолчанию > event.preventDefault(); Почему-то это стоит в конце функции и не всегда срабатывает.
Классы для цифр лучше было бы пометить префиксом, для понятности и защиты от конфликтов (что кто-то еще сделает такой класс), например: mines-1, mines-2, как-то так.
> cells.sort(Util.shuffle) Лучше было сделать Util.shuffle(cells), так как отдельно от sort твоя функция Shuffle смысла не имеет.
> Field.prototype._createHeader = function(container) { Тут наверно тоже стоило бы использовать HTML-шаблон или вставить элементы в HTML код самой страницы. Не очень выгодно создавать дивы вручную. А вот ячейки удобнее генерировать программно.
Насчет цифр - можно было просто поискать нужный шрифт. Хотя вариант с картинками тоже годится, лучше наверно было бы использовать css-спрайт (картинку со всеми цифрами сразу). Вместо подгрузки картинок через создание новых img каждый раз выгоднее наверно просто менять css-классы или src у существующих объектов.
> _playerWinCheck Имя функции начинается с глагола. "Что сделать?"
> new Popup(templatePopup.innerHTML, this); > var outerTemplatePopup = popup.createPopup(message, container); Показ попапа требует вызова конструктора и createPopup(), но непонятно, почему например, шаблон передается в конструктор, а container - в createPopup(). Надо подумать, куда лучше какой аргумент передавать.
Для победы лучше бы не требовать ставить флажки, а просто проверять что все, кроме мин, открыто. А флаги просто оставить для игрока, чтобы он мог отмечать клетки.
Насчет криво расположенного попапа - а ты пробовал пользоваться инструментами разработчика (Ctrl + shift + I) и посмотреть, какие css-свойства применены к попапу и к элементу, относительно которого он позиционируется?
> Field.MAX_NUMBER = 999; > function Field(settings) { Немного непривычно смотрится, когда ты сначала присваиваешь свойство функции, а только потом объявляешь. Там есть какие-то правила, что объявления функций могут выполняться до остального кода, но лучше бы просто переставить строчки местами.
> function Field(settings) { Если настроек немного, то наверно проще было передавать их отдельными аргументаими. Хотя твой вариант тоже годится.
> var timer = setInterval(function() { > clearTimeout(timer); Не соответствуют друг другу функции. Также, clearTimeout разбросан несколько раз по коду, а правильнее было бы сделать функцию _stopTimer().
> safetyCells = this._openTheCell(safetyCells); Непонятно, зачем результат возвращается и сохраняется назад в массив.
startNewGame не очищает старый таймер. Что, если он останется и продолжит работать?
В общем, тебе надо подумать над разделением кода работы с DOM и игровой логики. Хотя бы на уровне функций, а лучше - на уровне 2 классов.
Хотя тут больше фронтенда, чем ПХП, всё-таки спрошу.
Аноны, кто-нибудь пилил шаблонизатор, который работает с отдельными tpl-файлами? Суть в том, что сейчас использую такой. Самопильный, естественно.
Шаблонизатор не просто вставляет переменные в текст, а в самих tpl-файлах есть условия и вызовы других функций (например, `date`).
Единственная проблема -- файлы подключаются (и, соответственно, выводятся) через `include`. Есть необходимость переделать шаблоны и связанные с ними классы так, чтобы можно было получать результат работы шаблонизатора, но не выводить его сразу.
Но суть вопроса в том, что я понятия не имею как это сделать. `file_get_contents`+`eval` ломается при первом PHP-теге. На этом мои идеи иссякли… Я даже не знаю как это гуглить -- все результаты, кроме работающих на `include`, это плагины для Вордпресс, лол.
>>912479 ЯННП. Как ты запускаешь свой самописный шаблонизатор, если ты сразу инклудишь шаблоны? Кажется, ты просто вынес html-код в отдельные файлы, а не используешь шаблонизатор.
>чтобы можно было получать результат работы шаблонизатора, но не выводить его сразу
ob_start(); // Отправляешь на вывод что угодно $your_output = ob_get_clean();
В your_output лежит все, что должно было улететь на вывод (но не улетело).
>`file_get_contents`+`eval` ломается при первом PHP-теге. И правильно делает. Если хочешь писать свой шаблонизатор со своими тегами, никаких php-тегов в шаблонах быть не должно.
>>911674 Я хуй его знает что с ним делать нахуй, где эти ебучие логи смотреть, и что значить освоить командную строку? Там же миллиард програм которые работают через емулятор терминала , смотри анон, https://www.digitalocean.com/community/tutorials/apache-ubuntu-14-04-lts-ru делаю полностью по инструкции , просто напросто копирую пися в писю , когда перехожу оно мне выводит просто пустую страницу и пиздец, залупа гнида пидарска, ебать его в рот, говно ебаное. Во вторых нахуй, логи блять пидарские нахуй, я так понял что их тут смотрить ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined Шо блять значить {APACHE_LOG_DIR} , я сука должен жить полноценой жизнью дроча апач и знать что обознчают апачивские макросы? Ну это все хуйня, анан. Самое больше что меня страшить это mYsql, апач то можно удалить из бубнты, а вот мускул ты хуй удалишь из ОС, это хуже вируса герписа в нервной системе. Я блять в отчьянии, нельзя блять обойтись без блять хоть без апача, ебать его в рот?
>>912514 >Как ты запускаешь свой самописный шаблонизатор, если ты сразу инклудишь шаблоны? Есть класс `Templater`. В конструктор передаются все данные, в виде массива `array('post' => $post)`. В моём случае это необходимо, ибо в разных tpl-файлах могут запрашиваться какие-либо данные -- для JS в шапке, для HTML в теле… Ну, а Templater#show - просто экспортирует переменные функцией `extract` и инклюдит файл, имя которого передано аргументов в Templater#show. Как-то так: > new Templater($data_array)->show('head')->show('top')->show('form')->show('thread'); и т.д. Таким образом: -- каждый элемент ассоциативного массива становится переменной, к которой шаблоны имеют доступ. ПРОФИТ! -- в метод Templater#show вторым аргументом можно передавать данные для именно этого файла. ПРОФИТ! -- не ебёшься с данным каждый раз. ПРОФИТ! -- можно няшно чейнить :з
>ob_start(); >$your_output = ob_get_clean(); Знал про эти функции, но как-то боялся контроль вывода испортить. После `ob_get_clean` вывод вернётся в нормальное состояние? Всякие 'echo' не будут просить слить буфер?
Так я тот гондон что плакал что у него не работает апач. Я все похерил, устновил xampp, xampp запустился. Что мне делать что бы я увидел свои свои файлы на .php, делать виртуальный хост? Я блять пиздец как боюсь править конфиги что бы эта хуйня опять не ебнулась.
>>912640 Прочитать мануал xampp? В той директории, куда ты его установил, должна быть папка, в которую ты можешь положить свой срипт. В браузере он будетдоступен примерно по такому адресу "localhost\project\script.php". Иногда требуется перезагрузка xampa/wampa, чтобы новый скрипт стал доступным.
>>912646 У меня ебучий линукс и я в душе не ебу куда он установиливает программы( на самом деле размазывает по всей файловой системе), да.Читаю мануал, вообще нет как запускать скрипты, может в глаза ласкаюсь, но не могу найти. И такой вопрос что такое phpmyadmin? Создал пользовтеля в нем, попытался под этим пользовтелем зайти из консоли, выдает Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' . Загуглил эту ошибку, я так понял что не установлены инструменты для работы с mysql и вот теперь сижу думаю как их установить все не сломать к хуям.
Как организовать моментальный вывод сообщений, как в чате? Я знаю только один способ, проверять базу данных каждую секунду и в случае появления новых сообщений выводить их. Но как можно сделать по другому без этого?
Вечер, мастера по регуляркам. Выполняю задание по ним, а именно: >Автозамена. Напиши скрипт, заменяющий определенное слово на другое (например, слово «дурак» на «хороший человек» в фразе «ты дурак»). Скрипт должен не пропускать слово, если оно написано буквами в разном регистре (ДуРАк), с заменой русских букв на похожие английские (а -> a), или через пробелы («ты — д у р а к») Понятно, что надо написать "между каждым из символов может встречаться символ пробела", но можно ли это сделать, не вставляя поиск пробела после каждого символа в искомой строчке? Например: сделать не '/д//sу//sр//sа//sк//s/', а что-то типа '/(дурак)//s(предыдущий символ может содержаться в любом количестве в группе символов перед ним)/', где курсивом отмечена некая магическая команда регулярных выражений, которую я пропустил в мануалах. Перечитал весь мануал, но так и не нашёл ответ на вопрос. Существует ли такая команда? Было бы жутко удобно.
Твой шаблон - это просто PHP файл. Нужно использовать ob_clean/ob_get_clean(). Для чистоты хорошо бы еще поддержать обработку исключений и в catch/finally поставить ob_end_clean.
Нет, это нелогично. И в запросе обычно всегда есть какая-то основная сущность, ну например, выбираем коммент и приджойниваем его автора - можно положить в класс для комментов.
Нужны вебсокеты. Страница устаналивает постоянное соединение с демоном на сервере, и тот присылает ей информацию о новых сообщениях, например, по протоколу WAMP (Web Application Message Protocol).
phpmyadmin - это клиент для сервера баз данных MySQL, сделанный как PHP приложение. Начинающему я бы советовал сначала освоить клиент для командной строки mysql, а то приходят потом люди на работу и не понимают, как миграцию написать, привыкли таблицы через кнопочки править.
Для начала можешь изучить вообще, что такое "клиент" и "сервер".
> Загуглил эту ошибку, я так понял что не установлены инструменты для работы с mysql Плохо гуглил, это сокет для соединения с сервером mysql, значит он не установлен или не запущен.
Это функция, которую передают в другую функцию и та вызывает переданнную. Потому и "функция обратного вызова" - не мы ее вызываем, а функция, в которую мы коллбек передали.
Зачем они нужны? Был такой вопрос и ответ на него выше в посте >>908094
Попробуй решить написанные там задачи (про отбор по критерию).
>>912085 Да, всё дело в регистре, надо внимательнее относиться, спасибо. Знак "меньше" я просто для проверки ставил, хотел увидеть, выведет ли того, кто ниже.
>>912907 > Начинающему я бы советовал сначала освоить клиент для командной строки mysql, а то приходят потом люди на работу и не понимают, как миграцию написать, привыкли таблицы через кнопочки править. Ну охуеть просто, ну у тебя и советы. Кому всралась эта командная стока, если всем надо "ЧТО БЫ ПРОСТО РАБОТАЛО!!" .Командную строку я немного знаю но не вижу причин изучать ее больше, один хер открываешь вакансии, а там фреймворк ебет фреймворк фреймвоком, да да я знаю основы вся хуйня. И тем более что я спрашиваю если я установлю mysql в ос, они будут пользоваться одной базой mysql консоли и phpmyadmin? И почему постояные посылы в шапку? Говно шапка, главная проблем что вся эта поебота - php, mysql, апач, composera и куча компонентов которые надо лепить в кучу и за этого у ньюфагов просто мозги взрываються.
>>913088 >>Кому всралась эта командная стока Тебе. Она помогает разобраться и понять как устроено то, с чем ты работаешь.
>я установлю mysql в ос, они будут пользоваться одной базой mysql консоли и phpmyadmin? Вообще да, но с таким настроем, у тебя вообще ничего работать не будет.
>И почему постояные посылы в шапку? Потому что сядь и изучи уроки, вместо того чтобы постоянно плакать.
>главная проблем что вся эта поебота - php, mysql, апач, composera и куча компонентов которые надо лепить в кучу и за этого у ньюфагов просто мозги взрываються Сначала разберись с чем-то одним, вместо того, чтобы хвататься за все сразу. По всем перечисленным пунктам у опа есть уроки. Все есть в шапке.
>>913240 >>913151 Зачем вы вообще ему отвечаете? Он ничего не хочет изучать, наивно полагая, что все инструменты должны "просто работать", после того, как нажмёшь одну большую кнопку. Только вот за что платить такому спецу? Его ведь может заменить любой.
>>913250 Хм, почему то js-никам не нужно идеально знать устройство операционой системы, наврно по этому js и так прет вверх. Я же не виноват что в linux по ебанутому организовано удаление программ. После целого дня ебли и удалениячисти, чисти! зависимостей мускул стал. Если запущен phpMyAdmin, то доступ к баз ене возможен?
>>913250 Да, это конечно ближе к делу, но все-таки по миру видно что php слабеет, а вот js наоборот поднимается, такой конечно пиздец это "единые решения" вокруг одного языка (я про реакты и ноды)
Ага, вот что получается если запущен phpMyAdmin, до доступа к mysql через консоль не возможен, по этому оно и выкидывало "Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock" приходиться останавливать phpMyAdmin, потом перезапускать service mysql и только после этого будет доступ через консоль. Так, буду и дальше держать вас в курсе. >>913240 >Ну и пошёл нахуй тогда. Целую тебя в твои румяные ягодички.
> Ага, вот что получается если запущен phpMyAdmin, до доступа к mysql через консоль не возможен, по этому оно и выкидывало Это ты уже от себя выдумываешь, потому что видимо не понимаешь даже как клиент связывается с сервером. Проблемы могут быть только если в MySQL стоит ограничение на 1 соединение (что вряд ли), и даже в этом случае ошибка будет в том клиенте, которые пытается подключиться вторым.
> Так, буду и дальше держать вас в курсе. Больше пользы тебе будет, если ты разберешься, почему именно что-то не работало и как это исправить. Ну например при ошибке cannot connect стоит как минимум проверить:
- установлен ли сервер - запущен ли он - указано ли в конфиге сервера, что надо слушать данный сокет
Ты не путай верстальщиков и разработчиков на Javascript.
В linux во многих дистрибутивах (Дебиан, Убунту) установка и удаление программ организовано лучше, чем в винде. Расскажи, как в винде одной командой вроде apt-get install php5 установить PHP? Надо идти искать сайт, скачивать архивы, распаковывать, проверять антивирусом, устанавливать, жать далее, ой, надо было запустить от администратора.
Ты бы мог привести как хороший пример Андроид или iOS. Там тоже используется пакетный менеджер (как в линуксах), просто для пользователя там сверху к нему прикручен магазин приложений с графическим интерфейсом, а админы люди суровые и больше любят командную строку, а не прокручивать сотни картинок и смотреть на тормозящие анимации.
> Если запущен phpMyAdmin, то доступ к баз ене возможен? Это ни на что не влияет. MySQL поддерживает несколько соединений одновременно.
Да не выйдет ничего у него с таким подходом, как только надо будет сделать что-то не по туториалу, он сядет в лужу и будет полдня разбираться, в итоге все удалит и установит заново в надежде что оно само заработает.
> если всем надо "ЧТО БЫ ПРОСТО РАБОТАЛО!!" Либо изучай теорию, либо нанимай того, кто ее изучил. Тогда будет "просто работать".
> И тем более что я спрашиваю если я установлю mysql в ос, они будут пользоваться одной базой mysql консоли и phpmyadmin? Ты сам-то понял, что написал? ЧТо значит "база mysql консоли"? ЧТо значит "база phpmyadmin"? phpmyadmin - это клиент для доступа к серверу БД. В нем нет никаких баз. Я тебе вчера написал, прочитай что такое клиент и сервер, ты даже это поленился сделать.
Также желательно хотя бы в общих чертах понимать, что такое TCP-соединение, IP-адрес, localhost, номер порта, unix-domain сокет.
>>912907 >Твой шаблон - это просто PHP файл Поэтому мне и было интересно: можно-ли как-нибудь, кроме буферизации вывода, запустить код, но получить результат в переменную, а не на выход как это обычно бывает между PHP-тегами. И тебе спасибо ^_^
>>913501 >Это ты уже от себя выдумываешь, потому что видимо не понимаешь даже как клиент связывается с сервером По логике вещей ты прав, но я проверял и не один раз. >установлен ли сервер, запущен ли он , указно ли в конфиге Вот это я не знаю как гуглить, а во всех книгах просто пишут как работать с оболочками, я даже не знаю чем отличается клиент от сервера мускула, мда.
PHP >= 5.3 нормально себя ведёт, если объявлять функции внутри функций? Эти локальные функции не вытекают наружу? Протестировал на PHP 5.6, но хочу быть уверен. Не нагуглил.
>>914045 >В PHP все функции глобальные Тогда почему этот код с 1й пикчи вывел 'passed'? `function_exists` накрыла-бы его, если-бы область видимости потекла.
>Попытка создать ту же функцию второй раз приведет к фатальной ошибке >пик2 Или я неправильно тестирую, или у меня неправильный PHP.
это единственный способ впихнуть результат в эхо, чтобы он был вида 5 * 5 = 25 ? если вместо резалта впихнуть выражение, как я бы в питоне сделал, то он пишет хуету какуюто
ребят, нюфажный вопрос совсем: вот я скачал нотепад++, а ещё у меня есть vs code, как теперь там запускать пхп программы? чтобы видеть какой-то результат. Нотепад просит какой-то ехе файл постоянно, а вс коде генерирует tasks.json в котором надо прописать что-то, а что - хз.
Для запуска программ нужен интерпретатор PHP. В шапке есть уроки:
- про командную строку - про установку PHP
Прочитай их и сможешь запускать программы. Когда тебе надост возиться с консолью, настрой запуск интерпретатора в своем редакторе, если он это поддерживает.
Кроме серверных СУБД, бывают еще встраиваемые БД. Это когда серверов/клиентов нет и код, управляющий БД, встроен в приложение (например как библиотека), и только это одно приложение может работать с этой базой данных. Данные, как правило, в таких случаях хранятся в файле. Например, так работает встраиваемая БД sqlite, которая используется в андроиде, скайпе, хроме для хранения истории, настроек и прочего. В PHP, кстати, она тоже доступна.
> Вот это я не знаю как гуглить
Управление сервисами (фоновыми процессами) сделано по-разному в разных дистрибутивах. В современных Дебианах и Убунту используется systemd, так что стоит гуглить его:
По поводу проверки email на уникальность: я бы назвал класс не UniqueEmail, а StudentEmailUnique, так как это ограничение работает только для одного класса - студента. Если ты хочешь сделать универсальное ограничение, работающее с любыми сущностями, то тебе придется усложнить класс, добавив в него такие свойства:
- имя поля для email. По имени поля нам надо как-то получить сам email (ведь модель данных в форме может быть массивом, объектом со свойствами, объектом с методами, объектом с ArrayAccess). Для этого можно использовать класс PropertyAccessor, метод getValue(). - callable, которая принимает на вход id объекта и email и ищет, есть ли такой в базе.
Также, вместо callable можно сделать интерфейс вроде EmailCheckInterface с методом doesEmailExists($email, $id) и передавать в Constraint реализацию этого интерфейса.
Можешь посмотреть код этого ограничения и его валидатора, если интересно.
Насчет обработки HTTP-исключений - я тут глянул документацию http://silex.sensiolabs.org/doc/2.0/usage.html#error-handlers и по моему, мы немного изобрели велосипед, так как какая-то обработка там уже есть по умолчанию. Ну например, HTTP-код на Response выставляется автоматически, а переданный тобой игнорируется.
Тут https://mighty-ridge-55042.herokuapp.com/ поиск по фамилии зависит от регистра букв. Возможно, неправильно выбран COLLATION для поля таблицы или не установлена какая-то настройка MySQL.
При попытке при регистрации отправить email test@пример.рф код падает с ошибкой 503.
Для пометки найденного слова есть тег <mark>
В тесте HTML-экранирования тоже немного заложены детали реализации: ты проверяешь конкретную мнемонику, вроде & lt ; . Но угловую скобку можно записать и по-другому, кодом вроде & # 1234 ; или & # 001234 ; . Конечно маловероятно, что алгоритм htmlspecialchars поменяется, но можно было бы сделать тест так:
- передаем строку с тегом и проверяем, что на выходе тега нет - вызываем html_entity_decode на результате и проверяем, что получилась исходная строка - аналогично можно проверить кавычки, а также отдельные угловые скобки, что их нет в результирующей строке
> public function getRegisteredStudent(ParameterBag $parameterBag) Тут аргумент лучше назвать $cookiesBag, а то нельзя понять, что туда передавать. А еще лучше конечно бы не писать конкретный класс в тайп-хинте, а указать, что нам подойдет любой класс, из которого можно получать значения (включая массив), но к сожалению, в PHP так сделать (пока) нельзя.
> public function rememberStudent(Student $student, ResponseHeaderBag $headers): ResponseHeaderBag Здесь незачем возвращать Bag, так как этот тот же самый объект, что подается на вход. То, что ты его возвращаешь, значит, что пользователь функции должен теперь этот Bag как-то засунуть в HTTP ответ (ведь тут не гарантии, что это тот же объект, что на входе).
Более того, это может привести к ошибкам, посмотри на код:
> $unregisteredStudentHeaders = $authService->unregister($rememberedStudentHeaders); Можно подумать, что тут $rememberedStudentHeaders не изменяется.
> public function studentsAreTheSame(Student $studentA, Student $studentB): bool Я думаю, это все же не должно быть в классе авторизации. У него нет задачи сравнивать студентов. Полтьзователь должен сам догадаться, как их сравнивать.
В Доктрине, где есть Entity Map, достаточно сделать $a === $b. В других случаях надо сравнивать id, token или другой уникальный идентификатор.
> public function unregister Правильнее logOut или forget (антоним remember).
> $result = $this->authService->getRegisteredStudent($parameterBag); > $this->assertFalse($result instanceof Student); Лучше assertEmpty или assertNull.
Так, в общем, тесты неплохо сделаны.
> Вспомнил про аннотацию @depends, поможет ли она сделать testLoopback более читаемым или только размажет его по методам? Мне не очень нравится, так как код становится менее очевидным (и кстати, я про нее не помнил, пришлось лезть в гугл). Да и экономит ведь это всего одну строчку.
> Ещё меня удивило насколько мало пришлось менять кода ради того, чтобы изменить способ регистриции. В этом и смысл инкапсуляции и разделения отвественности.
Насчет авторизации по паролю - лучше в куке (и в БД) хранить не пароль, а его соленый хеш. Чуть безопасней, а так у тебя пароль в открытом виде на каждый запрос шлется. И в базе лежит. Украли базу/куки - и можно идти проверять, подойдет ли этот пароль к почте, к соцсетям итд.
По хорошему конечно надо еще формы регистрации и авторизации обрабатывать через HTTPS. А то любой провайдер или владелец вайфая может все в открытом виде смотреть.
Я вижу, у тебя много js-файлов. Вручную вписывать их в код в нужном порядке становится утомительно, да и это неэффективно, грузить много мелких файлов через Интернет.
Можешь прочитать про системы модулей в JS: CommonJS и AMD. Затем - про сборщики и загрузкичи модулей вроде WebPack.
Советую настроить сборщик так, чтобы в режиме разработки не требовалась бы сборка и файлы бы грузились напрямую (это упрощает и ускоряет отладку), а в режиме релиза собирался бы единый JS файл. Некоторые делают так, что собрка используется всегда, но это по моему только все усложняет: нужны карта файлов и отладчик, который ее поддерживает, надо ждать, пока все соберется, и тд.
Кстати, ты используешь jsDoc, если ты используешь (или использовал бы) IDE, которая его поддерживает, это может улучшить качество автодополнения кода, и иногда IDE даже может проверять код на правильность за счет аннотаций. PhpStorm/WebStorm вроде это умеет.
https://github.com/enotocode/minesweeper.mvc/blob/master/app/Cell.js Тут surroundinMines наверно выгоднее не хранить, а сделать у Field метод для его расчета. Вообще, странно, у тебя у Клеточки есть своство "isMine", но оно в MinesweeperGame не проставляется. Очень странно.
openCells, возможно, стоило сделать словарем для более эффективного поиска, вроде this.openCells[y][x] = true;
Наличие словаря избавит нас от необходимости писать сложные алгоритмы пересечения списков клеток.
В классе MinesweeperGame, возможно, лучше было бы сделать 2 словаря:
- cells[y][x] с объектами Cell с информацией о клеточке - openedCells[y][x] с true/false, показывает, открыта ли клетка - markedCells[y][x] true/false
Можно сделать даже один словарь, если сделать у ячейки свойства isOpened/isMarked, но тогда их можно случайно поменять, и MinesweeperGame об этом не узнает (и не сгенерирует событие). Но можно конечно договориться так не делать. Или можно сделать Cell тоже источником событий, и подписываться на событие открытия, но это конечно немного усложнит жизнь.
А у тебя этого нет, у тебя одна и та же клеточка может быть представлена 2 разными объектами в minedCells и openCells, это, мне кажется, нехорошо. Ведь тогда, если мы хотим что-то у клеточки поменять, мы должны найти все экземпляры и поменять нужное свойство. Лучше делать, что 1 сущности соответствует ровно 1 экземпляр объекта.
Вот если бы ты убрал из Cell свойство isMined, и сделал класс CellCoords с свойствами x/y, ты бы мог создавать их сколько угодно, потому что они представляют координаты ячейки, а не саму ячейку. А ячейка должна быть в одном экземпляре.
Может тебе задачку на ООП дать про отель, если ты эти вещи не очень понимаешь?
Вообще, когда нас модель сложная и содержит много объектов (например, для программы управления отелем будут классы Отель - Номер - Постоялец - Бронь - Счет за услуги), нам так или иначе надо отслеживать изменения в них, по всему дереву иерархии, и иметь поток информации об изменениях (вроде "Номер 302 поменял статус на Забронирован") И вариантов-то тут не так и много:
- либо в каждый класс добавлять события, генерировать их при изменениях, и класс-владелец подписывается на события в объектах, которые он хранит внутри -либо мы должны использовать angular-подобную схему, когда мы сохраняем копию объектов, и после изменений сравниваем граф (набор) объектов с сохраненной ранее копией, находим разницу и генерируем события изменения. Это требует меньше кода, но работает медленнее и ест больше памяти. - либо react-подобную, когда изменения нас не интересуют, так как мы каждый раз пересоздаем дерево DOM полностью. Но тут надо понимать, что события нам все равно могут понадобиться, например, для реализации функции undo - либо использовать иммутабельные объекты (объекты, которые нельзя изменять). То есть при любом изменении на поле мы создаем новый экземпляр поля и новый экземпляр клеточки. Это требует очень много памяти, зато мы в любой момент можем сохранить текущий объект Поле в переменную, и знать что он и клеточки в нем никогда не изменятся. Удобно, если нам нужно иметь полную историю всех изменений в модели. Перемещение на 5 ходов назад получается очень простым, надо просто взять старый экземпляр Модели из истории и поставить как текущий.
(если у тебя будет время, можешь со всеми этими методами поэкспериментировать, или хотя бы подумать, как это реализовать).
В общем, для начала надо правильно реализовать Модель (классы, которые хранят состояние игры и клеточек). Где что хранится, как что можно узнать.
https://github.com/enotocode/minesweeper.mvc/blob/master/app/GameEvent.js Здесь, ты может сам заметил, проблема в том, что у разных событий может быть разное число аргументов, с разными названиями. Получается, надо либо сделать разные классы событий, либо сделать один класс, а аргументы передавать словарем вроде { cell: someCell, action: SomeClass.ACTION_OPEN }
Использование нескольких классов требует больше кода, но позволяет ставить jsDoc, добавлять кастомные методы и свойства в события.
Ну например, для событий GAME_OVER target не очень-то и нужен, так как подписчик должен знать, на какой объект он подписывался.
Еще, наверно, стоило бы начать раскидывать файлы по папкам.
Еще у тебя нет разделения на публичные/приватные методы и свойства. Из-за этого сложнее читать код, так как непонятно, какие методы внутренние, а какие выставлены наружу.
В JS нет приватных свойств, иногда просто имя начинают с подчеркивания, чтобы показать, что оно приватное.
Событие названо неправильно. Откуда View знает, что при клике по клетке она меняет статус? Это знает только модель. Событие должно быть названо BrowserView.EVENT_CELL_CLICK_LEFT или как-то так. Видишь, как смысл сразу меняется?
Я советую события сделать константами не на GameEvent, а на соотв. классах.
> var browserView = new BrowserView(); > browserView.attach(mswprGame); А почему мы модель не передаем сразу в конструктор? Есть какая-то причина? Чтобы ее можно было заменить на другую? Это конечно иногда может быть полезно (если у нас 1 вью и несколько игр, между которыми можно переключаться), но я не уверен, что у тебя это работает.
Ну и насчет 2 независимых контроллеров (ModalController/BrowserViewController) - вот я не уверен, а не лучше ли сделать ModalController/ModalView подчиненными BrowserViewController, чтобы он им говорил, когда показать диалог, а не они сами решали? А то просто у тебя получается что диалог сам решает, когда ему открываться, как-то иерархия управления размывается, это как 2 директора на одном предприятии.
Ну и соответственно логику кода понимать сложнее, так как они вдвоем могут подписаться на одно событие и каждый по-своему обрабатывает его.
Вот допустим ConsoleController можно сделать независимым, так как он никак с BrowserViewController не пересекается. Но ModalView ведь перекрывает BrowserView на экране, они не независимы друг от друга.
Вот если бы статус игры выводился в отдельной независимой области экрана - тогда можно было бы сделать контроллер/вид независимыми. Но тут, мне кажется, они должны быть подчиненными.
BrowserViewController очень сильно привязан к BrowserView, оно в общем-то логично, но я поймал себя на мысли, что может быть, проще было его код во View и поместить? Хотя тогда конечно мы не можем прицепить к вью какой-то альтернативный контроллер (например такой. который позволяет только следить за игрой, но запрещает делать ходы или такой, который слушает нажатия клавиш). Хотя опять же, вот допустим, если мы присоединяем контроллер только для просмотра - а зачем тогда во вью мы подписываемся на клики по полю, если они никому не нужны? У меня конечно правильного ответа, как надо сделать, пока нет, только такие мысли.
> ConsoleController.prototype.updateCellStatus = function(status, cell){ Проще наверно просто пересоздавать строку с нуля, а не мучаться с заменой отдельного символа. там все равно отрисовка этого поля потом больше времени займет.
А так тебе приходится держать копию данных модели и синхронизировать ее постоянно. Сложно и есть риск что-то забыть.
> @param {string} forGloryOfArgumentsGod - Number of mouse button (event.witch) on mousedown Лучше было сделать отдельный метод setMouseButtonListener с этим аргументом.
>В классе MinesweeperGame, возможно, лучше было бы сделать 2 словаря: >- cells[y][x] с объектами Cell с информацией о клеточке >- openedCells[y][x] с true/false, показывает, открыта ли клетка >- markedCells[y][x] true/false
Если я воспользуюсь словарями, тогда мне не нужен объект Cell, и не нужно хранить экземпляры отдельно в cells[y][x]? Ведь координаты открытых и отмеченных клетках будут в словарях. Можно добавить третий словарь mines[x][y] и отказаться от класса Cell и от cells[y][x]? В этом случае можно использовать CellCoords, как принятый формат передачи координат?
>>914262 >А, еще неплохо бы твоего сапера выложить в сеть на беспллатные github pages
> var regexp = new RegExp('^\\s|\\s$', 'iu'); > Uncaught SyntaxError: Invalid flags supplied to RegExp constructor 'iu'
Я поправил. Почему-то мой Хромиум не ругался.
>И консоль не работает, я пишу game.open('a3') , и мне выводится пустая строка. Я что-то включить забыл?
Скажи пожалуйста версию своего хромиума? Не могу понять, в чем может быть проблема. Скачал дистрибутив с гитхаба, открыл в опере/хроме/фф - game.open('a3') работает. Позже попробую на другой машине потестить.
В качестве обучения, пишу сервис для сайта. Там на странице есть кнопки, по нажатию срабатывают js скрипты и аяксом подгружают php файлы. Что-то вроде: ajax({ url:"update.php", type:"POST", async: true, data:{}, success: function (data) {}, error: function (error1) {}, })
В php просто написан код и его результат выводится через echo. Есть два вопроса: 1) "подгружают php файлы" - как-то совсем криво звучит. Как это называется правильно? 2) у меня используется несколько отдельных файлов, по одному на каждую кнопку. И в половине из них прописано подключение к MySQL базе. Параметры все одни и те же. Я правильно понимаю, что нужно сделать одну функцию, и каждый раз её писать? Но где именно её нужно писать? В отдельном файле и прописывать import в начале каждого php файла?
Вопросы явно взаимосвязаны. В общем, я не понимаю вот этого вот момента.
ОПушка помогай! Решил я запилить валидацию через силекс, да не простую, а с custom validation constraints и за одно еще и формы подкинуть. Только вот я уже пару дней читаю официальную документацию симфони и все мои потуги ни к чему не приводят. Может быть ты подскажешь как лучше прикрутить к силексу кастомные констранты, чтобы был доступ к доктрине.
Мучаюсь над проектированием. Решил сделать класс Game с игровой логикой и Field связанный с dom. Пробую хранить объект Field в поле объекта Game. Не понятно у кого из них будут какие свойства (количество мин, длина, высота, значение времени) и что делать когда пользователь решил начать игру заново, создавать ли новый field или чистить его.
> Если одновременно выбраны А и один из [K,L,M], то стоимость выбранного продукта уменьшается на 5% "Выбранный продукт" это же один из K, L, M? Сделал именно так.
> Решил сделать класс Game с игровой логикой и Field связанный с dom. То,что связано с DOM - это View. Я тебе советую сначала целиком спроектировать модель, и только потом браться за View/Controller.
Тут я вижу 2 таких варианта:
- сделать логику игры в классе Game (хранение статуса, определение победы, раскрытие клеточек), и сделать класс-модель Field, хранящий только состояние поля (например, информацию о клеточках, минах, флажках) - сделать и логику игры, и состояние поля в одном классе Game
Также, у нас есть разные варианты хранения информации о клеточках (есть ли там мина, стоит ли флажок и тд):
- хранить информацию в классе Game или Field - хранить ее в объектах Cell, которые хранятся внутри Game/Field
То есть получается выбор из нескольких вариантов. И при выборе нам надо продумать, как мы будем собирать информацию об изменениях и как генерировать события.
В Constraint доступа к Доктрине быть не должно, так как этот объект представляет собой Ограничение, и хранит относящуюся к нему информацию. Доступ к Доктрине может потребоваться в ConstraintValidator, который проверяет значение на соответствие Ограничению.
Вбиваем в Гугл: "symfony constraint validator dependency"
> How to Create a custom Validation Constraint > Constraint Validators with Dependencies
> If your constraint validator has dependencies, such as a database connection, it will need to be configured as a service in the Dependency Injection Container. This service must include the validator.constraint_validator tag so that the validation system knows about it:
> ...
То есть объявляется сервис в контейнере, указывается его имя класса в validatedBy(), по этому имени он потом ищется.
Поскольку наш ConstraintValidator создается где-то в недрах компонента Validator, то мы не можем так просто передать ему аргументы в конструктор. Потому мы должны вместо этого объявить его как сервис в DI container, чтобы Validator взял этот сервис из контейнера. Ну и очевидно, что в контейнере мы можем прописать любые правила инициализации нашего валидатора и аргументы конструктора.
Там также требуется пометить сервис валидатора специальным тегом. Я думаю, этот тег нужен не просто так, какой-то компонент Симфони ищет все сервисы-валидаторы по этому тегу и что-то с ними делает. Не люблю, когда остаются какие-то пробелы в понимании темы, потому сделаем поиск в коде Симфони по "constraint_validator":
Обрати внимание, что это FrameworkBundle - часть Симфони, а не часть независимого от Симфони компонента Validator. То есть работать это должно только в Симфони.
На первый взляд, ничего не понять. То есть понятно, что код ищет все сервисы-валидаторы по тегу, получает их имена классов (или алиасы), кладет в массив и сохраняет этот массив в контейнере как аргумент для создания сервиса validator.validator_factory. Выглядит как костыль какой-то. Непонятно:
1) зачем тут нужен какой-то CompilerPass? 2) зачем помечать сервисы-валидаторы тегом validator.constraint_validator? Без него что, нельзя получить сервис из контейнера по имени класса?
Начнем с первого вопроса. В Симфони ради оптимизации сделана так называемая "компиляция DI контейнера" (о контейнере: https://symfony.com/doc/current/components/dependency_injection.html , о компиляции: http://symfony.com/doc/current/components/dependency_injection/compilation.html ). Там сервисы в контейнер добавляются не в коде, а через конфиги (services.yml), и в каждом бандле (части фреймворка) может быть свой конфиг с объявлением сервисов. Чтобы при каждом запуске приложения (читай, при каждом HTTP-запросе от браузера) не читать и не анализировать кучу конфигов с определениями сервисов из всех бандлов (долго), Симфони "компилирует" собранную информацию в один php-файл и кладет его куда-то в папку cache (если у тебя есть DI container от Симфони, то поищи этот файл, он назвается как-то вроде devContainer.php и посмотри его содержимое). При втором запуске Симфони просто загружает файл с скомпилированным контейнером, не тратя время. Если используется opcache, это работает очень быстро, мы получаем все удобства DI контейнера с сложными конфигами и кучей сервисов без ущерба производительности.
Это сгенерированный на основе конфигов код, и видно что получение/создание сервисов (методы вроде get_xxx) сделано очень минималистично и эффективно.
Бандлы могут вмешиваться в компиляцию и добавлять "CompilerPass" - класс, который что-то делает с контейнером перед компиляцией в файл. То есть тут используется CompilerPass, чтобы один раз составить список сервисов-валидаторов при компиляции, сохранить его в контейнере как аргумент для validator_factory и при последующих запусках не тратить на это время.
Возвращаясь к компоненту Validator и тегу. Напомню, что Симфони состоит из Компонентов, которые можно использовать независимо от самого фреймворка, и бандлов, которые являются частью фреймворка. Эта validator_factory, которая умеет искать сервис-валидатор в контейнере по id, который возвращает validatedBy() - это часть Симфони (FrameworkBundle). А что, если мы используем компонент Validator без Симфони? Как можно тогда передать зависимости в валидатор?
Очевидно, Симфони передает эту фабрику в компонент Валидатора через DI, чтобы компонент Валидатора использовал ее вместо встроенной. Если изучить код, видно, что в эту фабрику через конструктор передается список имен классов и id сервисов валидаторов из контейнера (этот список конструирует AddConstraintValidatorsPass при компиляции контейнера). И соответственно эта симфониевская фабрика умеет искать сервисы в контейнере.
Вот мы и нашли ответ, зачем нужно ставить тег в контейнере: тег в контейнере нужен, чтобы фабрика знала, какие именно классы надо искать в контейнере, и под какими именами там зарегистрирован сервис.
Также, мы нашли ответ на вопрос, как сделать то же самое без Симфони: написать свою фабрику валидаторов, умеющую искать сервис в DI контейнере (или получающую валидаторы откуда-нибудь еще, например, через конструктор).
И наконец, мы нашли пример использования паттерна Фабрика. Фабрика нужна, чтобы переопределить способ создания валидатора в чужом коде.
Вообще, такие трюки в Симфони часто используются: в компоненте (который должен работать без Симфони) делается какая-то минимальная реализация, а уже в Симфони делается более мощная реализация, интегрированная с другими частями Симфони.
На мой взгляд конечно получилось немного сложновато. Это то, за что любят критиковать ООП-подход и паттерны. Но зато получилось очень гибко.
Если у тебя есть время, я бы советовал поковырять компоненты DependencyInjection, Validator, Form, посмотреть, как они устроены. Вместе с книгой Фаулера PoEAA это даст тебе понимание, как и зачем используются паттерны на практике, в отличие от дурацких статей вроде "3 самых простых паттерна для собеседования".
Ну и ты будешь лучше знать возможности и особенности этих компонент.
Ну и помни, что паттерны надо использовать только там, где это оправданно, а не вставлять везде "чтобы было".
> Только вот я уже пару дней читаю официальную документацию симфони и все мои потуги ни к чему не приводят. При чтении обязательно различай Компоненты Симфони (независимые от фреймворка) и их использование вместе с фреймворком - фреймворк может расширять их возможности, как мы видели на примере выше.
Твоя регулярка ищет строки, где идет 11 цифр подряд без символов между ними. Тебе надо написать другое:
- сначала напиши выражение "ровно 1 цифра и любое число скобок, минусов, пробелов за ней" - затем возьми это выражение в скобки и допиши к ним "повторяется ровно 10 раз"
> 1) "подгружают php файлы" - как-то совсем криво звучит. Как это называется правильно? Это звучит неправильно, так как никакие php файлы с сервера в браузер не передаются. Передается результат их работы. Правильно сказать "JS-код отправляет AJAX запрос на сервер для получения HTML кода и вставки его в страницу (в дерево DOM страницы)". Заметь, что я не упомянул PHP, так как клиентский код не знает ничего о том, какой язык программирования используется в серверном коде - это нельзя узнать, не имея доступа к серверу.
> 2) у меня используется несколько отдельных файлов, по одному на каждую кнопку. И в половине из них прописано подключение к MySQL базе. Параметры все одни и те же. Я правильно понимаю, что нужно сделать одну функцию, и каждый раз её писать? Но где именно её нужно писать? В отдельном файле и прописывать import в начале каждого php файла?
Удобно сделать файл bootstrap.php, который инициализирует какие-то вещи, которые нужны всем скриптам. И подключить его в начале каждого из файлов. В нем может быть как просто код, так и объявлены полезные функции.
Ну и замечу, что если ты хочешь профессионально программировать на PHP, тебе надо изучать больше (ООП, MVC, фреймворки), например, используя учебник и уроки из ОП поста. Судя по твоему описанию, у тебя пока уровень изучения основ PHP.
> прописывать import В PHP нет такого ключевого слова. require наверно имелся в виду.
> Если я воспользуюсь словарями, тогда мне не нужен объект Cell, Может быть и нужен. Удобно сделать объект, представляющий одну клеточку, и хранить в нем, открыта ли она, есть ли мина, есть ли флажок.
> Можно добавить третий словарь mines[x][y] и отказаться от класса Cell и от cells[y][x]? В этом случае можно использовать CellCoords, как принятый формат передачи координат? Можно попробовать и так. Трудно пока сказать, в чем недостатки.
> Я поправил. Почему-то мой Хромиум не ругался. У меня версия 46, ей может год или около того. Может в твоем браузере флаг u что-то значит, может в нем сделано игнорирование неизвестных флагов. В любом случае он там быть не должен.
Теперь все вроде работает, в том числе и из консоли. Впрочем, вот чего не хватает:
- координаты почему-то отсчитываются с 0, а не с 1 - в консоли не пишутся события в игре вроде "клеточка x открыта" или "игра проиграна", трудно понять, что именно изменилось - если за раз открывается несколько клеточек, в консоль вываливается куча изображений поля - при STATUS_LOSE можно ходить, при этом под полем появляется несколько диалогов
>>914937 Почему-то ideone уже не разрешает мне редактировать свой файл, а в коде закралась ошибка. Приходится постить ещё раз: http://ideone.com/EgMwYH
>>915198 Задание дают прежде всего тебе, чтобы ты понял, готов ли ты по знаниям и опыту или нет. Если сам не осиливаешь - значит рано. Задачу можешь вбросить конечно.
>>915227 задание понятно. а как решить непонятно >>915231 Я уже кое-что накидал через array_walk_recursive, но там то ключи не меняются, то на выходе получается одномерный массив. Задание: Написать функцию, которая будет принимать 3 параметра
1) Любой вложенности массив (array) = $datas 2) Что ищем (string) = $key 3) На что заменяем (string) = $value
На выходе мы должны получить массив $datas в котором заменены все элементы (ключи, значения) $key на $value. Будет + если решите задачу без использования циклов.
>>915489 >Нихуя не понимаю, как включить --enable-mbstring в PHP Его нельзя "включить". То что ты написал - дополнение к конфигуратору при самостоятельной компиляции интерпретатора. Узнать как именно конфигурировалась среда перед компиляцией твоего интерпретатора можно из окна информации (php -i в консоли или .php файл с вызовом функции phpinfo()). В большинстве источников интерпретатор скомпилирован с поддержкой mbstring, все что тебе нужно это установить пакет phpVER-mbstring (где VER - версия интерпретатора). В версиях для виндоус большинство дополнений идут вместе с бинарниками (смотри папку ext в корневой директории интерпретатора), тогда нужно указать в php.ini путь до дополнения, которое хочешь подключить. Поищи в своем php.ini по слову extension и убери комментарий в строке с нужным дополнением.
>а что не перекатываетесь? ОП делает перекаты когда отвечает на большинство постов в треде.
> https://github.com/someApprentice/Students/blob/master/students.sql#L31 Вот тут у колонки стоит COLLATE utf8_bin. COLLATE задает правила сортировки и сравнения строк и utf8_bin значит, что строки сравниваются с учетом регистра. То есть поск например потребует ввода имен в правильном регистре. Я думаю, по умолчанию лучше бы использовать utf8_unicode_ci, которая нечувтствительна к регистру.
https://github.com/someApprentice/Students/blob/master/app/Controller/Controller.php Вот тут конечно методы вроде getPageQuery или getSortQuery явно не очень правильно смотрятся. Ведь это базовый контроллер, и в него надо класть только то, что может пригодиться в любом контроллере, а это явно функции для вывода таблицы постранично. Возможно, стоило их поместить в унаследованный класс BaseTableController, возможно - вынести в Helper. В принципе, это исправлять не надо, но в дальнейшем надо задумываться, куда лучше поместить метод.
https://github.com/someApprentice/Students/blob/master/app/Controller/IndexAction.php Вот тут что-то странное, 2 вызова render. Мы сначала выводим страницу 'templates/index.phtml', а затем под ней зачем-то выводим 'templates/list.phtml'. Это как минимум странно выглдяит. Ведь мы уже вывели подвал страницы, и после него пытаемся вывести таблицу, которая явно не под подвалом должна быть.
Действия Search и просто вывод таблицы стоило бы объединить. Это ведь почти одно и то же, и там и там есть и сортировка, и пагинация, проще использовать один код и в нужных местах просто поставить пару if.
> $this->render('templates/search.phtml', compact('query', 'pager')); > } else { > $this->render('templates/search.phtml'); Вот это тоже странно. Один и тот же шаблон может вызываться как с переменными, так и без. Как писать надежный код, если ты даже не знаешь, передана такая переменная или нет? Да и неудобно, перед любым использованием переменной надо писать if (isset()). Не стоит так делать, тут стоит оставить единственный вызов render, в который передаются все нужные переменные.
Теперь посмотрим на использование класса Pager. Это класс, в котором много неудачных решений:
Во-первых, бросается в глаза, что почему-то все аргументы не передаются в конструктор сразу. 2 аргумента, ($records, $recordsCount) задаются уже после создания. Получается, чтобы получить работоспособный объект, нам надо не только создать его, но и вызвать еще 2 метода, и как об этом догадаться, если мы видим класс впервые? Надо внимательно изучать код, тратить время.
В принципе, аргументы можно задавать через методы вида setSomething(), но обычно так делают только с необязательными аргументами, класс будет работать корректно и без их указания.
Но для пагинации число записей - это ключевой параметр, так как именно на его основе считается число страниц. Странно, что он не задается в конструкторе.
Во-вторых, почему-то для передачи аргументов в конструктор используется массив. Причем вместе идут как параметры, которые не зависят от номера страницы (query, sort), так и номер текущей страницы. Тоже нелогично, мне кажется, что номер страницы должен идти отдельно.
Опять же, если мы хотим использовать класс, то неочевидно, что за массив надо ему передать, и что в нем должно быть.
Как мы помним, лучше всего, когда у каждого класса своя зона ответственности, одна какая-то задача. Какие задачи решает класс Pager?
- предоставляет информацию о текущем и общем числе страниц - генерирует ссылку на страницу с указанным номером - генерирует ссылку и стрелочку для сортировки - хранит список выбранных записей ($records)
Что касается "хранит список выбранных записей", это явно лишнее. Это выглядит как не его задача. Непонятно, зачем было это в него засовывать. Так в него можно засунуть например и getCurrentUser().
Теоретически конечно можно переименовать его в класс TableHelper, экземпляр которого соответствует выводимой на странице таблице, и потому он знает и параметры сортровки, и хрант список записей, и номер страницы. Но тут-то он называется Pager.
Также, есть такая задача как "генерирует ссылку на нужную страницу". В принципе, это имеет отношение к пагинации, но ведь это тоже отдельная задача. Вот представим, нам где-то в другом месте кода надо получить ссылку на таблицу с определенной сортировкой (например, ссылку для заголовка таблицы). Как ее получить? Через класс Pager, но для его использования нам надо указать, сколько всего в базе записей. Хотя мы всего лишь хотели сгенерировать ссылку, и для этого знать число записей не нужно. Это показывает, что возможно, генерация ссылок - отдельная задача, которую стоит вынести из класса Pager.
Как можно вынести генерацию ссылок, чтобы Pager ей не занимался? Есть много способов:
- сгенерировать ссылку снаружи и передавать в класс шаблон ссылки вида /index.php?sort=name&page={page}, в который подставляется номер страницы - передавать в класс анонимную функцию-генератор ссылок вида function ($page) { ... }, которая должна по номеру страницы вернуть ссылку - просто сделать отдельный метод в другом классе, и вызывать его из Pager - передать в Pager объект-генератор ссылок
Все эти вещи, конечно, увеличат объем кода. Но зато будет универсальный класс, который можно подключить на любую страницу. В простом приложении, возможно, лучше не заморачиваться и просто сделать все в одном классе.
Ну и наконец, название выбрано неудачно. Можно подумать по названию, что это универсальный класс для любых страниц, но на самом деле он работает только с таблицей студентов. Раз так, надо назвать его StudentPager или как-то так.
В общем, я бы советовал подумать над исправлением этого класса. Сильно усложнять все наверно не стоит, но чуть-чуть исправить недостатки желательно. Возможно, стоит просто переделать его из класса пагинации в класс, помогающий выводить таблицу. Тогда стоит подумать, а можно ли сделать этот класс универсальнее, чтобы он помогал выводить любые таблицы с сортировкой и пагинацией.
LogOut лучше бы делать отправкой POST-запроса (например, пустой формы с одной кнопкой). Это ведь изменяет состояние залогиненности, а GET запросы обычно не меняют состояние сервера.
Далее, я бы хотел обратить внимание на LoginHelper и посмотреть, хорошо ли в нем соблюдаются принципы ООП. Видимо это класс, отвечающий за регистрацию и сопутствующие действия (логин, регистрация, проверка залогиненности, разлогинивание). В соответствие с принципом инкапсуляции было бы хорошо убрать все знание о том, как технически реализуется залогинивание/разлогиивание, в этот класс. Я тут вижу даже 2 варианта:
- класс может отвечать только за залогинивание/разлогинивание, то есть работу с куками, и не обращается к БД со списком студентов - класс обращается к БД, и кроме залогинивания/разлогинивания, умеет еще искать текущего пользователя и регистрировать пользователей, может еще менять им пароль
В принципе, второй вариант позволяет спрятать в этот класс подробности того, как генерируется и сохраняется пароль. Тогда, если мы захотим поменять алгоритм хеширования, нам достаточно будет подправить только этот класс, не трогая остальной код. Или если мы захотим разобраться в алгоритме, нам достаточно будет посмотреть на этот класс.
Тут я вижу небольшие нарушения принципа инкапсуляции:
- наружу выставлены методы generateSalt/hashPassword, хотя лучше бы все, что относится к работе с паролями, спрятать внутри класса - в классе Student есть метод setPassword(), который знает, что для сохранения пароля надо сгенерировать соль и захешировать пароль. Лучше бы убрать этот метод и использовать для смены пароля LoginHelper::setPassword().
В общем-то инкапсуляция есть, но немного знания о том, как ставить пароль, все же вылезло из класса.
https://github.com/someApprentice/Students/blob/master/app/Model/Validators/StudentValidations.php Константы вроде const GENDER_MALE = "Man"; логичнее поместить в студента. Это ведь его свойства, а не свойства валидатора. В валидатор можно поместить константы вроде VALIDATE_MIN_LENGTH, относящиеся к валидации. Представь, что нам не нужна валидация и мы удалим этот класс. И вместе с ним удалятся константы, обозначающие пол, хотя они-то нам еще нужны.
Может стоит сделать метод validateByRules($entity, $rules, $errorList) в базовом классе?
Хотя, конечно, не очень понятно, как тогда там вставить код, меняющий правила проверки пароля:
> if ($field = 'password' and $editMode) {
Возможно, надо сделать правила более гибкими, чтобы мы могли бы как-то в правилах добавить флаг "это поле может быть пустым" или через правила передать дополнительный параметр в функцю-валидатор.
> <a href="?<?= $pager->getLinkForPage(1) ?>" Тут хорошо бы экранировать спецсимволы, так как в ссылке может встретиться &, а это спецсимвол в HTML.
В общем, решение меня устраивает, если не считать класса Pager. Надо его хотя бы немного переделать.
Ну и дальше стоит начинать изучать фреймворки, хотя бы микрофреймворки для начала, так как тут мы писали все с нуля, и на практике конечно эффективнее учиться использовать готовый код. Также потом стоит глянуть шаблонизатор twig и, может быть, библиотеку-data mapper для работы с БД под названием Doctrine.
Как это не с чем? Там в каком-то банке получается по моему 61270 или около того. Судя по ответу 61529 в твоей задаче, у тебя примерно на 300 рублей ошибка.
Попробуй поставить сумму кредита 1000 или 4000 и посмотри, что получится. Должно быть 2030 и примерно 6123 рубля соответственно.
Я думаю, проблема в том, что когда там в последний месяц остается мелочь вроде 500р, ты платишь $rest + $finalMonthly, и 500 рублей входят в каждую из переменных. То есть, платишь лишнее.
Также, oneTimePayment надо прибавлять до вычисления процентов, а не после.
Что нужно знать что бы удалено взяли на удаленую работу? Если опыта работы нет , это импосибл? Пробежался так по вакансиям, я так понял всем похуй че ты там знаешь , главное что бы запилил высоконагруженый проект или гитхаб с двумя тысячами фоловеров. МНЕ НУЖЕН РЕЗУЛЬТАТ!!! Как вам книга Никсона "Создаем динамические сайты"? А то я очень долго дрочил питон и на выхлопе сделал пару недосайтов на джанго, очень уродливые и хелоувордные, очень хочется работать на результат.
>>916540 На нагруженный проект обычно берут сеньора с опытом подобных проектов и не на удаленку, а в офис. На удаленке как раз проекты не такие сложные обычно, уровня написать плагин к вордпресу или новую фичу в админку cms.
http://ideone.com/KwY3R3 Анончики, не могу понять, почему если я вывожу в цикле каждое $value, то оно показывается именно правильным, со всеми правками. А когда вывожу весь массив $textarray после цикла, то выводится старый, как если бы я ничего не делал с $value.
>>916540 Ты - это я. Я даже удивился такому сходству. Просто точь-в-точь и про питон и про гит, и вопросами задаюсь такими же.
>>916540 Книга Никсона дает самые азы, пригодится только если например не знаешь как вообще подойти к задачке про студентов от ОПа. Результат она тебе не даст.
$query = $this->db->placehold(" SELECT id_trening, new, online, time, discount, instr_name, price, name, url, date, country, city, mesto FROM __timetable WHERE 1 $data");
>>918766 >>918767 Я не ОП, но делал MVC сапера. Очевидно View отвечает только за отображение того что ему передают, значит хранить все данные о игровом поле и минах нужно в модели.
>>918883 Ну, скажем так. Есть такое негласное правило - если что-то можно сделать без регулярок, то лучше делать без регулярок. Но на самом деле очень удобный инструмент если хорошо освоить.
Почему в докуиентации к preg_math указывают такую форму - int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] ) А в примере пишут preg_match('/(foo)(bar)(baz)/', 'foobarbaz', $matches, PREG_OFFSET_CAPTURE);? Что квадратные скобки не нужны, эта функция возвращает массив? Что обозначает preg? Зачем такие дурацкие сокращения, дико раздражает после JS?
Кароч, анансы я вообще нихуя не понял, что выплюнет функция preg_match зависит от флага? Во всех учебниках пишут что функции должны поддерживать только одну простую фунцию, а не превращаться в универсальные комбайны с встроеным печатным станком, или я все не правильно понял?
Там объясняется, что значат квадратные скобки и string.
> Что квадратные скобки не нужны Они обозначают необязательные аргументы, которые можно не указывать.
> эта функция возвращает массив? Перед определением функции написано int, то есть возвращает целое число. Подробнее ты можешь прочесть в мануале по функции:
> Возвращаемые значения > preg_match() возвращает 1, если параметр pattern соответствует переданному параметру subject, 0 если нет, или FALSE в случае ошибки.
Число обозначает число найденных сопадений текста с регуляркой, и так как она ищет только первое совпадение, то число может быть либо 0, либо 1. False возвращается при ошибке, например, если указан флаг u, но переданная строка не является корректной строкой в кодировке utf-8 (подробнее про кодировки в моем уроке https://github.com/codedokode/pasta/blob/master/cs/strings.md )
> Что обозначает preg? Это префикс от расширения (PCRE), в которое входит функция. Думаю, что оно значит Pcre REGexp, где PCRE значит "perl-compatible regelar expressions", то есть "регулярные выражения, совместимые с теми, что используются в языке Perl". Раньше в PHP были еще ereg-функции, Extended posix REGexp, "расширенные регулярные выражения из стандарта POSIX".
Да, у регулярных выражений есть несколько диалектов, с небольшими отличиями.
Если ты посмотришь, то там есть еще другие функции с префиксом preg, например preg_split.
То есть это не разработчики PHP сами написали свой движок регулярных выражений, они взяли готовую библиотеку PCRE на языке Си и сделали возможность вызывать ее функции из PHP.
> Зачем такие дурацкие сокращения, дико раздражает после JS? Подозреваю, это пришло из языка Си, где обычно функцию назывют как модуль_функция().
Она и есть простая функция, дополнительные опции просто немного влияют на ее работу. Например, позволяют искать совпадение не с начала строки, или позволяют в результаты добавлять отступ в байтах от начала строки, где было найдено совпадение.
Первый раз слышу про такое правило. Регулярки как раз позволяют сделать поиск или замену текста одной строчкой, а без них пришлось бы писать сложный цикл.
Ширина и высота поля относится к модели. Ну представь, что ты допустим уберешь View вообще, просто программно вызываешь методы модели из своего кода. И что, игра сможет работать, не зная ширину и высоту поля? Вряд ли.
Вся игровая логика находится в модели. Контроллеры отвечают за выполнение пользовательских команд (за вызов нужных функций модели по команде пользователя), а вью - за отображение данных из модели.
Вот это нехорошо, что ты для первого числа пишешь отдельную ветку кода. Лучше бы обрабатывать все аргументы одинаково. Например, всегда класть текущее число в $number, и при обнаружении математического знака, и $op == '' переносить из $number в $result.
Если (текущий символ - математический знак), то { - если $op пустой, то копируем $number в $result - если $op не пустой, выполняем записанную в нем операцию - затем копируем текущий знак в $op - затем, если текущий знак - это равно, то выводим результат }
Для отладки программы я бы советовал натыкать там echo, чтобы видеть, какие значения куда записываются.
> if ($number % 100 >= 11 && $number % 100 <= 14) { > $wordForm = $word3; Тут лучше было сразу писать return $word3;
> $lastThree = round(($number / 1000 - floor($number / 1000)) * 1000); > settype($lastThree, "integer"); Тут надо было написать $number % 1000. Вместо settype лушче использовать intval, хотя я не понимаю, чем float тут плох.
> $text .= smallNumberToText($parts[2], 0) . " " . $million . " "; Тут наверно части фразы было удобнее класть в массив и позже склеивать.
> if ($parts[1] % 10 == 1 || $parts[1] % 10 == 2) { > $text .= smallNumberToText($parts[1], 1) . " " . $thousand . " "; > } > else { > $text .= smallNumberToText($parts[1], 0) . " " . $thousand . " "; Тут не нужен этот if, так как проверка на последнюю цифру уже есть в функции smallNumberToText(). Число 0/1 обозначает род, и слово "тысяча" всегда женского рода.
$value - это не элемент массива, а копия его значения. Ты менял только копию, а не сам элемент в массиве.
&$value приводит к тому, что в $value помещается ссылка на знаечние элемента и изменение $value меняет сам элемент в массиве. Имей в виду, что тут есть подвохи (например, после окончания цикла в $value сохраняется ссылка на последний элемент массива и можно нечаянно что-то туда записать). чтобы избежать ошибки, рекомендуется после окончния цикла уничтожить переменную $value через unset($value).
> $first = mb_substr($text, 0, 1); > $up = mb_strtoupper($first); > $other = mb_substr($text, 1); Тут лучше было использовать поменьше промежуточных переменных. Ну например, вместо $up можно было внось использовать $first.
> foreach ($textarray as &$value) { лучше было назвать переменные как $sentences и $sentence.
Ну да, работодатель предпочитает людей, которые умеют разрабатывать веб-сайты, и которые могут с первого дня включиться в работу. Если ты это еще не умеешь, то тебе стоит либо учиться дальше (и наш тред предлагает для этого различные задания), либо искать работу в какой-нибудь веб-студии, где ищут стажера-передвигальщика-меню.
Ну и надо понимать, что удаленная работа - это по многим пунктам выгоднее, чем обычная (свободнее график, не тратишь время на транспорт, меньше контроля, может быть более высокая зарплата), и соответственно отбор туда может быть строже.
> Как вам книга Никсона "Создаем динамические сайты"? Код там плохой и некачественный, и по моему, с уязвимостями. Почитать ты можешь, но не пиши код как в книге.
> и на выхлопе сделал пару недосайтов на джанго, очень уродливые и хелоувордные, очень хочется работать на результат. Тебе надо как минимум пройти учебник из ОП-поста (или аналогичный), и сделать задачи про студентов и файлообменник (или аналогичные). Ну и HTML/CSS/JS. Если ты знаешь Джанго и ООП, то это не должно представлять для тебя большой сложности.
> $chars = preg_split('//u', $text, -1, PREG_SPLIT_NO_EMPTY); > $chars[0] = mb_strtoupper($chars[0]); Тут наверно проще было отрезать первый символ через mb_substr, но твой вариант тоже годится.
> $match = preg_replace('/(^\s+)/u', '', $match); Для отрезания пробелов есть ltrim, rtrim и trim.
> preg_replace('/\s([,.;:!?])(?![?.])\s/ui', Чтобы поддерживать многоточия, можно еще использовать такое решение: [,.;:!?]+
Еще у тебя там ошибка (внизу выводится), обращение к пока не существующей переменной, ее надо исправить: PHP Notice: Undefined variable: result in /home/VnUvhY/prog.php on line 27
Это нужно, если ты собираешь и копилируешь PHP из исходников. В этом случае ты вначале запускаешь конфигурационный скрипт, и ему указываешь этот параметр.
Если ты скачал уже собранный PHP под Windows, то включить расширение можно 2 способами:
- если это расширение шло в комплекте с PHP (mbstring идет), надо просто в файле конфигурации php.ini его включить через опцию extension=... - если это расширение не идет в комплекте, надо найти его собранную версию (обычно это dll-файл) на windows.php.net, выбрать совместимую с твоим PHP версию, скачать, поместить в папку ext внутри папки PHP, и включить в php.ini
> HTMLElement.prototype.getElementsByClassName = getElementsByClassName; Я тебе советую не трогать прототипы встроенных объектов и не подменять браузерные функции. Вместо этого лучше сделать свою функцию и вызвать ее. А она уже может проверять наличие встроенной getElementsByClassName() вызывать ее либо предоставлять альтернативное решение.
> Будет + если решите задачу без использования циклов. Тебе надо изучить список стандартных функций для работы с массивами и попробовать найти подходщие.
А так, решается циклом + рекурсией.
А вообще, задание немного наркоманское. Я не могу представить, где было бы оправданно делать такие замены в массиве неизвестной структуры.
> А если я описываю классы и это конструкторы? А конструктор - это разве не функция? Я не вижу смысла писать как-то нестандартно, когда есть стандартный и более короткий способ.
> В примерах документации мозиллы объявлено так: Скорее всего автору кода просто так больше нравится писать. В JS нет единого стиля оформления кода и потому каждый пишет, как он хочет. Я видел и более странные формы вроде
const func = () => { ... };
Просто кто-то любит новые ключевые слова и стрелочки. Хотя читать тяжеловато.
Да, но это такой тонкий аспект, что я бы не советовал его использовать. Я например не помню наизусть все правила, относящиеся к области видимости функций и переменных. Как и правила подстановки точки с запятой в конце строки.
>> Если одновременно выбраны А и один из [K,L,M], то стоимость выбранного продукта уменьшается на 5% > "Выбранный продукт" это же один из K, L, M? Сделал именно так. Думаю, да, хотя там правила местами туманно сформулированы.
Классы, представляющие собой виды скидок, стоило бы объединить - либо через наследование от абстрактного класса, либо через реализацию интерфейса.
А так тебе пришлось сделать 2 метода для добавления скидок: addCountDiscount(), addCombinationDiscount(). Это не позволяет нам добавлять новые виды скидок, не меняя класс Calculator. И получается калькулятор у тебя знает слишком много о скидках (какие виды скидок бывают), лучше бы он просто принимал объекты-скидки, не вникая в их устройство. Тут нарушается принцип единой ответственности - у каждого объекта своя зона ответственности и это не дело Калькулятора, по какому именно принципу определяется скидка. Это ответственность объекта-скидки.
Давай попробуем придумать, какой может быть интерфейс у объекта-скидки? Спрячу под спойлер, если ты сначала хочешь подумать сам:
- скидка должна получать список товаров, чтобы искать в нем те, к которым можно примениться - скидка должна также знать об использованных в предыдущих скидках товарах (чтобы соблюдать правило несуммирования скидок, при этом решение о несуммировании остается за объектом-скидкой. Мне кажется, это гибче, чем удалять товары из коллекции, хотя с другой стороны, может привести к копипасте кода по всем классам скидок)
Это понятно, и это простые вещи. А вот что должна вернуть функция применения скидки? Процент? Сумму скидки в рублях? Не, лучше возвращать подробную информацию, а именно:
- к каким конкретно экземплярам (не названиям!) товаров она была применена (заметим, там есть скидки, применяющиеся к нескольким товарам, и к общей сумме заказа) - чему равна скидка, вроде тут везде использованы проценты
Тогда например Калькулятор мог бы логгировать подробно процесс применения скидок. И в чеке мы бы могли перечислить примененные скидки.
Эту информацию можно возвращать как-то массивом, объектом, а можно даже хранить в самой скидке, но тогда у нас получится одноразовый калькулятор, который надо пересоздавать перед использованием.
Плохо, что у тебя методы вроде getSumAfterApplyingDiscount() возвращают сразу цену. Мне кажется, они берут на себя задачу Калькулятора, и зоны ответственности Калькулятора и Скидки у тебя не разграничены, они лезут в задачи друг друга.
В общем, я бы советовал начинать проектирование именно с Калькулятора и цикла применения скидок, расчета цены, кто за что отвечает, а потом уже под это затачивать остальные классы.
Что касается CountCriteria - я не уверен, что его надо было делать, для меня выглядит как ненужное усложнение. Лучше было бы просто переупорядочить правила. И вообще, если внимательно почитать задание, то там условие сформулировано очень неудачно. Это можно понимать так:
1) "если пользователь выбрал ровно 3 продукта, не использованных в других скидках, то ..." - тут как-то нелогично получается, что в задании нет слов "не использованных в других скидках". 2) "если пользователь выбрал ровно 3 продукта, ..." - тогда, если пользователь выбрал более 5 продуктов, скидка не применяется, что нелогично. 3) "если ни одна из предыдущих скидок не применена, и пользователь выбрал 3 или более продуктов, то применить одну из этих 3 скидок в зависимости от количества" - вот это с моей точки зрения самое логичное. Тогда выгоднее представить эти 3 скидки как один объект или комбинацию объектов, так как они взаимоисключающие. 4) "если пользователь выбрал 3 продукта одного типа, не задействованных в других скидках" - вроде в задании нет слов "одного типа"
Тебе надо для начала четко определить условия применения этих скидок.
И я считаю, эти 3 скидки на количество выгоднее сделать одним объектом, которому например передается массив количеств и соответствующих скидок. Потому что если применять их по порядку, то нам приходится городить критерии и последняя скидка как-то должна знать, что она последняя и к ней применяется другой критерий.
Что касается комбинирования скидок - вот я не уверен, что тут его стоило делать. Я такие штуки видел во фреймворках, кажется,что они позволяют строить произвольные комбинации объектов, но на практике это не особо нужно и проще в коде прописать нужное условие.
В общем, я предлагаю упростить код и уменьшить число классов и абстракций.
> $pc->hasNames Удачнее было назвать hasAllOfNames().
> $pc->getOneOfName(['a', 'Z']) == true Странно, что функция возвращает true/false, из названия кажется ,что возвращается продукт. Если ты хотел проверить на не-пустоту, лучше бы писать assert(!!$pc->getOneOfName(['a', 'Z']));
> public function getByName($name) Эта функция должна называться getFirstByName() либо же возвращать массив, а не один объект.
> public function getWithoutNames Лучше будет getAllExceptNames()
Тогда да, можно логику в Game, а работу с DOM отдельно. Но это уже полшага на пути к MVC, может стоит дальше в ту сторону и двигаться?
> Не понятно у кого из них будут какие свойства (количество мин, длина, высота, значение времени) Если игровая логика в Game, то и свойства должны там храниться.
> и что делать когда пользователь решил начать игру заново, создавать ли новый field или чистить его. Без разницы. Пересоздавать наверно проще.
Можно ли сделать функцию, которая будет принимать N аргументов, например сумму этих аргументов? При этом, чтобы функция не жаловалась, что аргументов мало.
>>908071 >> Но почему копирует? Функция это же объект и должна быть ссылка! >Конечно, копирует ссылку на функцию. Но функция находиться же в объекте obj! В чём подвох?
>>908071 >Задача 1: написать функцию bindContext(fn, that). Она создает новую функцию, которая при вызове вызывает fn с указанным this и переданным аргументами. То по сути есть привязывает произвольное значение this к функции. https://jsfiddle.net/Luubgokx/
>Задача 2: сделать функцию addProperty(object, name, initialValue) для создания приватных свойств с геттерами и сеттерами на объекте или прототипе объекта. https://jsfiddle.net/sh21j4p1/
>Uncaught TypeError: obj.getName is not a function at window.onload ((index):64) А что это тогда если не функция?
>Задача 3: сделать функцию для добавления в объект или прототип нового метода addMethod(object, name, fn). Использование: Сначала нужно сделать 2-ую...
>>908076 >12. Некая сеть фастфудов предлагает несколько видов гамбургеров > >> https://jsfiddle.net/y28h2o2b/2/ >> if (Error.captureStackTrace) { >Ага, в JS так просто не унаследуешь исключения, и вообще встроенные классы, известная проблема. Напоминает о том, что в JS все же прототипное ООП, а не классическое. Я её откуда-то скопировал... Кажется отсюда https://learn.javascript.ru/oop-errors
>>> this.menu = { >> SIZE_SMALL: {price: 50, calories: 20}, >тут дублируется значение константы, и это плохо. Лучше писать как
13. В одном городе есть электрическая сеть. К ней могут быть подключены > Я предпологал что отдельный тип панели, это тот в котором "железно" определена мощность. Если этой причины не достаточно, то я просто переопределю конструктор класса-предка написав:
>Идея примерно такая. Так как элементы сети имеют что-то общее (их можно добавить в сеть, и они вносят вклад в баланс), то логично их наследовать от общего предка либо связать интерфейсом (которых в JS нет). Это нам дает такие преимущества, как возможность добавить общие методы, возможность проверять принадлежность к элементам сети через x instanceof NetworkElement. С идеей я согласен.
...
>А сейчас у тебя копипаста из 4 методов getPower.
>Соответственно, если у всх элементов выработка статичная, логично сделать базовый класс:
>NetworkElement(dayPower, nightPower) ...только мне теперь кажется логичным что у каждого элемента сети должен быть один параметр, это мощность, и этот элемент должен сам определять дальнейшие условия определения этого параметра в соответствии\зависимости со средой (определение дневного\ночного времени).
//Нужен ли тут гетер, если к свойству можно обратиться через точку? NewtworkElement.prototype.getPower = function() { return this.power }
Но боюсь ради этого нужно будет переделывать всю программу. В другой раз.
>Для ЛЭП проще всего указать нули и учитывать их вклад отдельно (сделать у них методы для получения информации, сколько мощности доступно и по какой цене). Лучше исключить её из перебора потому, что ЛЭП тоже может вернуть какое-то количество мощности.
>> ElectricalNetwork.prototype.countPrice = function() { >> price += this.elements.countPrice(balance); >> balance = this.elements.countPower(balance); >Вот здесь нехорошо, что код расчета закупок/продаж размазан по 2 классам - ElectricalNetwork и PowerLine. Логичнее его оставить только в ElectricalNetwork. Ну подумай сам: кто принимает решение о закупке: электросеть или начальник на конкретной ЛЭП (или руководство удаленной сети, к которой подключена ЛЭП)? Логично, что решение принимают в центре, а на ЛЭП только сообщают, сколько нужно принять или передать, запрашивают цены и тд. Так как сама ЛЭП не знает про баланс энергии в сети. С другой стороны электросеть не знает о ценах и о внутреннем устройстве ЛЭП. Моя идея в том, баланс можно просто передать в условный счетчик. Ведь это для ЛЭП свойственно иметь и считать цену.
>14. напиши функцию, определяющую тип переменной. Результат должен быть одной из строк: 'undefined', 'boolean' (для true/false), 'null', 'number', 'string', 'function', 'array', 'array-like', 'object
>> switch(type) { >> case '[object Function]': >> return "Function"; >тут проще было использовать хеш: { '[object Function]': 'function', ... } Что подрузумевается здесь под словом 'хеш'? И какие ещё свойства должны быть за место ... ?
>> if ('length' in variable && '0' in variable) { >Недостаточно, надо бы проверить что там есть свойства от 0 до length - 1. >от 0 до length - 1 А как это выразить в условии?
>> for (property in object) { >тут есть подвох, for in перебирает не только свойства объекта, но и его прототипов. Если кто-то расширит стандартный Object.prototype, это свойство или метод попадет в цикл. Опять же, не могу понять что с этим не так: Если это клон объекта, то этот клон должен иметь тот же прототип что и "донор"(?). Лучше использовать for ... object.length вместо for in? Как тогда узнать имя свойства для клона?
>> if (typeof object[property] == 'object') { >> clone[property] = deepClone(object[property]); >typeof(null) дает object, а получить (null).constructor нельзя. Клонирование объекта с null в поле даст ошибку.
Или, если ты знаешь наверняка сколько это N, в любом языке:
function countSum(first = 0, second = 0, ... ) { ... } В общем, при написании функции можешь определить дефолтные значения аргументам, они перейдут внутрь при ее вызове.
Может, есть еще способы? Было бы интересно послушать.
Вкатываюсь в PHP. Поставил PhpStorm и Apache под линуксом. Смущает одна проблема: каждый раз перед запуском приходится деплоит каждый файл отдельно. Думаю, можно это делать как-то более удобно, но нагуглить не получается.
Что скажите насчет сохранения данных в бд на русском языке? Например есть выпадающий список стран, раньше я делал типа такого <option value="Russia">Россия</option> и на латинице сохранял в БД и приходилось потом делать лишние телодвижения для перевода в кириллицу. Нормально ли сразу на русском сохранять?
>>936400 > и на латинице сохранял в БД Так должна быть отдельная таблица стран, где России будет соответствовать какое-то id, это id и сохраняй в другую таблицу в виде внешнего ключа. Про нормальные формы слышал? Алсо мы в уже в новом треде >>936081
Это не чат! Также, перед Новым годом ОП довольно занят, а на праздниках будет появляться нечасто.
Это тред для начинающих. Не написал за свою жизнь ни одной программы и имеешь тройку по математике? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Netbeans PHP или PhpStorm (с ним будет удобнее).
Предыдущий тред был тут:
Мейлач лежит? Есть запасной тред: http://dobrochan.org/s/res/23225.xhtml#i46467
Что самое главное для программиста? Умение аккуратно оформлять код (читай второй пост, прежде чем писать код).
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП заходит редко, где-то раз в 2-3 дня, у него мало времени, не жди его, решай задачки дальше. ОП отвечает на все вопросы по его задачкам и учебнику, а вот насчет каких-то других вещей - только если останется время. Но в треде немало анонимных экспертов разного уровня, так что вряд ли вопрос останется без ответа.
У нас есть уроки по основам PHP, они собраны и выложены по адресу http://archive-ipq-co.narod.ru/ Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь, то надо начать с него. Он простой и понятный (по крайней мере в начале). Там есть задачи, их надо решать обязательно (чтобы стать программистом, надо писать код — иначе никак). Пости ссылки на решения в тред, мы их проверим, напишем замечания и дадим советы по улучшению.
Если не знаешь как решать, запости код, напиши в каком месте остановился и попроси подсказку.
Ты прошел весь учебник? Молодец, но это были лишь основы языка PHP, этого недостаточно. Вот что в идеале надо изучить еще: ООП, как работает веб-сервер, HTML/CSS, SQL, PDO, работа с таблицами в БД, работа с формами, MVC, git, composer, JS, фреймворки, автоматизированное тестирование.
Надо переходить к более серьезным задачкам, которые научат тебя всему этому.
- для начала прочти урок https://github.com/codedokode/pasta/blob/master/soft/web-server.md
- установи Апач + PHP (советы выше и ниже) и читай туториал http://php.net/manual/ru/tutorial.php
- Учи HTML/CSS и SQL, PDO, хотя бы основы
- Далее простая, но полезная задача сделать список студентов, в ней много полезных советов: https://github.com/codedokode/pasta/blob/master/student-list.md
- Более сложная задача сделать файлообменник на микрофреймворке Slim: https://gist.github.com/codedokode/9424217
- Еще более сложная и долгая задача на Yii/Symfony: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование https://gist.github.com/codedokode/a455bde7d0748c0a351a
- Если ты все решил, переходи к Symfony 2/Doctrine 2
- Почитать про паттерны http://designpatternsphp.readthedocs.org/ru/latest/README.html (если ты не изучил ни одного фреймворка, то это будет рановато), тут с примерами кода http://designpatternsphp.readthedocs.org/ru/latest/README.html . Имей в виду что без примеров использования их учить бесполезно - не поймешь, хочешь увидеть примеры использования паттернов - ковыряй исходники Симфони, например Symfony Forms. Не заучивай паттерны - смотри код и думай, зачем тут они использованы.
Чтобы делать эти задания, тебе надо установить Апач + PHP (можно заодно сразу и MySQL) на компьютер. Вот полезные инструкции:
https://github.com/codedokode/pasta/blob/master/soft/php-install.md
https://github.com/codedokode/pasta/blob/master/soft/apache-install.md
Может тебе понадобится пользоваться командной строкой, вот гайд https://github.com/codedokode/pasta/blob/master/soft/cli.md
Решения задач лучше показать мне, особенно на ООП,так как сам ты вряд ли увидишь все ошибки. Пости свой код на гитхаб и вкидывай ссылку в тред по мере решения. Я прокомментирую и укажу на ошибки.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания JS/HTML/CSS/SQL. Решай их параллельно с задачами выше.
- HTML/CSS: https://github.com/codedokode/pasta/blob/master/html/html.md
- JS: https://gist.github.com/codedokode/ce30e7a036f18f416ae0
- SPA (сложно): https://github.com/codedokode/pasta/blob/master/js/spa.md
- Проверялка решений на JS: http://dkab.github.io/jasmine-tests/
- MySQL: https://github.com/codedokode/pasta/blob/master/db/databases.md
Что почитать
- Мануал по PHP — http://www.php.net/manual/ru/langref.php
- Сайт phptherightway (перевод на русский: http://getjump.me/ru-php-the-right-way/ )
- По PHP: Профессиональное программирование на PHP Джордж Шлосснейгл
- По PHP: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- JS: learn.javascript.ru
- Про Git: https://git-scm.com/book/ru/v1
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
У ОПа нет аккаунтов и групп вконтакте, в фейсбуке, в твиттере, все "пхп-треды" там поддельные.
Платиновые вопросы
- Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко.
- Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета)
- Что надо знать чтобы найти работу - разработчику: PHP, SQL, HTML/CSS, JS, ООП, Git, композер, MVC, фреймворк. Верстальщику - HTML/CSS, JS, jQuery
- Можно подробнее про поиск работы, собеседования - нет, ОП писать не будет, но может кто из анонов захочет рассказать. Поищите тред перезвонивших, а также раздел /wrk/.
- Сколько времени надо изучать все это? - все зависит от тебя, но не меньше 6-8 месяцев
- Посоветуйте редактор кода - Sublime Text 3, Notepad++, PhpStorm
- Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию.
- Что самое главное для программиста? Умение аккуратно оформлять код.
- ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет.
- Подскажи сайты для поиска работы, я не умею гуглить? — hh.ru, geekjob.ru, moikrug.ru (склеен с brainstorage.me), fl.ru, upwork.com (бывший одеск). Имей в виду, что кроме фриланса есть еще постоянная удаленная работа (remote job) когда тебе не надо тратить время на поиск заказов и переговоры с неадекватными заказчиками.