• В чем практический смысл использования интерфейсов в PHP?

    gscraft
    @gscraft
    Программист, философ
    Вам нужно смотреть в сторону принципов SOLID, разобраться, почему эти подходы оправданы. Интерфейсы же выполняют несколько задач, в связи с этими принципами.

    Во-первых, при разработке приложения, начиная с интерфейсов, программист больше сосредоточен на инкапсуляции, поскольку он в большей степени отвлечен и абстрагирован от конечной реализации, от способа хранения данных.

    Во-вторых, интерфейс гарантирует реализацию конечным программистом определенного поведения. Это очень широко применяется. Возьмите те же PSR: тысячи библиотек реализуют одни и те же подходы, и все программисты могут использовать одинаково библиотеки, основанные на этих интерфейсах, не задумываясь о конечной реализации классов.

    В-третьих, продуманный интерфейс позволяет в большей степени создавать взаимозаменяемые реализации — конечные классы. Тот, кто использует интерфейс при разработке класса обязан разработать все обязательства интерфейса.

    В-четвертых, при взаимодействии программных сущностей, опираясь на интерфейсы, разработчик делает сущности еще более заменяемыми (в том числе с применением таких паттернов как внедрение зависимостей, который, впрочем, может использоваться и без интерфейсов).

    Проектирование приложения на базе интерфейсов — полезная практика, хотя и не необходимая. Если пишется небольшое приложение и есть точная уверенность, что оно не будет дальше расширяться и меняться или если не предполагается командная работа, то интерфейсами можно пренебречь. Если приложение масштабно, будет разрабатываться и сопровождаться несколькими специалистами или предполагается его дальнейшее развитие — интерфейсы являются буквально необходимостью.

    Добавлю, к слову, о внедрении зависимостей. Действительно, этот подход может использоваться и без интерфейсов, тот факт, что инверсия управления и внедрение зависимостей используются чаще всего с интерфейсами — своего рода совпадение. Хороший пример: фреймворк Yii 2 или библиотека Pimple, там в качестве маркеров для внедрения зависимостей часто используются произвольные (или основанные на иных соглашениях) строки. Это к тому, что DI — необязательно самый яркий пример использования интерфейсов (хотя и более ценный, чем другие варианты).
    Ответ написан
    Комментировать
  • Как определить MIME-тип файла по содержимому?

    gscraft
    @gscraft
    Программист, философ
    Хм, определить MIME можно двумя способами: по расширению файла, если оно указано корректно, и по его содержимому. Если с расширением файла все просто, то файл придется читать, пытаясь сверить первые байты с имеющейся базой сигнатур форматов. Наверняка есть готовые библиотеки.
    Ответ написан
  • Что это - переменные среды, хочу портабл?

    gscraft
    @gscraft
    Программист, философ
    Все верно, если удалить пути из переменных окружения системы, то ни одно приложение системы не будет знать об этих путях, в том числе и командный процессор.

    По пути %USERPROFILE%\AppData\Roaming\npm хранятся исполняемые файлы установленных пакетов npm, которые предоставляют некоторые пакеты. Чтобы изменить этот путь, нужно использовать npmrc по пути установки Node.js. Подробнее — https://docs.npmjs.com/cli/v7/configuring-npm/folders

    Портативная установка подразумевает отсутствие связи с окружением системы, и верным решением будет использование окружения и командного интерпретатора OSPanel, конечно.
    Ответ написан
    2 комментария
  • Как можно избежать дублирование кода?

    gscraft
    @gscraft
    Программист, философ
    Масса способов. Самое простое, добавьте активным элементам общий класс и работайте с ним, а не с tabs или subTabs, tabs-triggers__item или subtabs-triggers__item раздельно. Плюс, Вам достаточно внести разницу, если она вообще нужна, только в корневой элемент, а не делать различными все классы по иерархии: subtabs-content__item и tabs-triggers__item — лишнее усложнение, достаточно tabs_trigger__item в обоих случаях. Чтобы задать разные стили, достаточно разделять по родителю, а точнее, задать особенности для дочерних вкладок. Плюс, Вам необязательно делать вкладки вложенными друг в друга, можно расположить их друг под другом. Что касается JS, не вызывайте функцию раньше, чем она объявлена, и лучше обернуть от глобальной области видимости:
    function initTabs() {
      // здесь весь нужный код
    }
    document.addEventListener('DOMContentLoaded', initTabs);
    Ответ написан
    Комментировать
  • Почему компонент рендерится дважды? Проблема в codesandbox?

    gscraft
    @gscraft
    Программист, философ
    Во-первых, самое главное, все значения, которые используете в React, должны быть обернуты, т.к. нельзя контролировать в полной мере обновление компонента, особенно если он будет обернут в дерево (находиться внутри других компонент). Т.е. используйте значения только из useState, переданных параметров props. useMemo не сможет распознать иных зависимостей.

    Во-вторых, useEffect должен использовать зависимости, чтобы не вызывался каждый раз. Если Вам нужно выполнение эффекта только при инициализации компонента, укажите useEffect(callback, []). useEffect без зависимостей практически бесполезен (и есть трюки, чтобы довести до однократного выполнения с помощью useRef).

    В-третьих, если Вам нужно значение вне компонента, как хранилище, используйте прокси (передача значения сверху-вниз из родительских компонент в виде props), например, или другие инструменты (Redux, MobX).

    Например,
    import React from 'react';
    
    function App(props) {
      return <a onClick={() => props.addIdCounter()}>{ props.idCounter }</a>
    }
    
    function RootComponent () {
      const [idCounter, setIdCounter] = React.useEffect(0);
      const addIdCounter = () => setIdCounter(idCounter + 1);
    
      return <App addIdCounter={addIdCounter} idCounter={idCounter} />;
    }
    Ответ написан
    Комментировать
  • Как исправить ошибку Parse Error: Expected HTTP/?

    gscraft
    @gscraft
    Программист, философ
    А зачем Вы WebSocket-серверу передаете http-сервер? Просто укажите порт:
    const wss = new WebSocket.Server({port: 80});
    — http лишний.
    Ответ написан
  • Почему XMLHttpRequest.getAllResponseHeaders() не показывает мои собственные кастомные заголовки?

    gscraft
    @gscraft
    Программист, философ
    Не все заголовки доступны из сценариев в случае разных доменов / cross-origin, для контроля этого существует специальный заголовок Access-Control-Expose-Headers, Вам нужно с сервера отправить 'Access-Control-Expose-Headers' с 'Abra-Kadabra'
    Ответ написан
    1 комментарий
  • Есть ли компиляторы для javascript?

    gscraft
    @gscraft
    Программист, философ
    Есть масса технологий разной степени готовности и области применения. Тут главный вопрос, чего Вы хотите получить и в каком окружении JavaScript? Браузер? Сервер Linux / NodeJS? Приложение рабочего стола? Это надо искать и пробовать. Навскидку, WebAssembly, JScript .NET, NectarJS или, скажем, Electron Packager. Ну и в ответе Алексей Уколовупаковщики.
    Ответ написан
    Комментировать
  • Почему пустой массив после запроса в базу данных sqlite?

    gscraft
    @gscraft
    Программист, философ
    У Вас в коде ворох неразберихи. Во-первых, дело не в запросе. А проверить запрос очень просто: логгируйте результат запроса в теле замыкания, где получаете player (можно заодно посмотреть, что хранится в members). Во-вторых, почему не выбрать сразу всех одним запросом? Запросы в цикле — моветон и лишние расходы. Предполагается
    const playerIds = channel.members.map(member => member.id);
    // Тут лучше использовать подстановку значений, см. документацию клиента базы данных
    const sql = `SELECT * FROM players WHERE user_id IN (${playerIds.join(',')})`;
    db.all(sql, (err, players) => resolve(
      players.map(player => `[**${player.level}**] ... ваша строка с пользователем ...`)
    ));

    В-третьих, players нужно определить внутри обещания, в теле Promise. В-четвертых, где и как Вы смотрите результат? Что и как получает ответ от return new Promise? Я полагаю, что у Вас это выглядит примерно так:
    function getPlayers() {
      // ...
      return new Promise(resolve => {
         // ...
         resolve(players);
      });
    }
    
    getPlayers().then(players => console.log(players));
    // или, если getPlayers() объявлено как async
    let players = await getPlayers();

    — то есть, Вы действительно получаете список игроков как результат Promise?
    Ответ написан
  • Почему не устанавливается Linux?

    gscraft
    @gscraft
    Программист, философ
    Дело, конечно, не в дистрибутиве, они все используют одни и те же утилиты, подсистемы для работы с диском. Трудно понять, почему не ставиться, не имея скриншота (а лучше — описания того, что делали) на этапе конфигурации диска. Общий совет такой: удалите все разделы на жестком диске, предназначенном для Linux и попробуйте создать 1 раздел ext4 для монтирования в корень на все доступное пространство, не меняя настроек по-умолчанию: удалили, создали раздел, выбрали точку монтирования "/" — все, далее. Если и в этом случае возникнет ошибка, пишите, будем разбираться.

    PS ах, да, посмотрите в BIOS, у вас стоит Legacy или UEFI загрузка? Попробуйте поставить приоритет UEFI. И еще, чем и как создавали загрузочную флешку?
    Ответ написан
  • Как создать ярлык запуска скрипта python?

    gscraft
    @gscraft
    Программист, философ
    Во-первых, можно ассоциировать Python-сценарии с интерпретатором (инсталлятор это делает, если не ошибаюсь). От администратора в Windows (pythonw — для запуска без консоли):
    assoc .py=PythonScript
    ftype PythonScript=path-to\python.exe "%1" %*
    assoc .pyw=PythonScriptWindow
    ftype PythonScriptWindow=path-to\pythonw.exe "%1" %*

    — в таком случае любой py / pyw будут запускаться двойным щелчком.
    Во-вторых, запуск через интерпретатор можно прописать в ярлыке, в его свойствах:
    5f66003cd4a26888664301.png
    В-третьих, как советуют рядом, создать bat файл, в котором будет по сути то, что в ярлыке, но это повлечет за собой окно терминала.
    В-четвертых, pyinstaller — неплохой вариант в целях автономности приложения. Сборка проходит быстро, ее можно настроить под локальное размещение и в один клик.
    Ответ написан
    5 комментариев
  • Почему если цикл в цикле, то идёт дублирование?

    gscraft
    @gscraft
    Программист, философ
    vladislav997, чтобы в print_r($var); не дублировались посты нужно воспользоваться инъекцией зависимостей и обязательно подключить пару серьезных библиотек, например, AR/ORM.

    Если серьезно, путей решения того, что хотите сделать, очень много. Проще всего — группировать, что в каком-то смысле и делают ORM, где сразу доступно $user->posts. Группировать можно и на уровне запроса к базе, и на уровне кода. Например.
    $vars = [];
    foreach($posts as $post)
      $vars[$post->author_id][] = $post->name;
    print_r($vars); // теперь у Вас записи группированы по пользователю, делайте что хочется,
    // например:
    foreach ($users as $user)
      if (isset($vars[$user->id]))
        print_r(implode(', ', $vars[$user->id]));

    — вообще, есть правило, старайтесь избегать двойного обхода массивов. А в данном случае Вы дважды обходили $posts внутри перебора $users.
    Ответ написан
    Комментировать
  • Как сократить условия php?

    gscraft
    @gscraft
    Программист, философ
    В цикле:
    foreach (range(2016, 2025) as $year)
      if (intval($_GET['year']) == $year)
        $jdns['year'] = (string) $year;

    Или без цикла:
    if (in_array(intval($_GET['year']), range(2016, 2025)))
      $jdns['year'] = (string) intval($_GET['year']);

    PS FanatPHP верно отмечает, если даты лежат последовательно в заданном диапазоне, проще сравнить условием больше-меньше:
    if (intval($_GET['year']) >= 2016 && intval($_GET['year']) <= 2025)
      $jdns['year'] = (string) intval($_GET['year']);
    Ответ написан
  • Sublime Text 3 как выключить автоматическое добавление скобок () после функций?

    gscraft
    @gscraft
    Программист, философ
    Так включить или выключить? Другого способа, кроме закрытия по открывающей скобке, нет. Разве что какие-то отдельные IDE-плагины смогут дополнить функцию из выпадающего списка и сразу добавить скобки, а иначе редактор никак не отличит, это часть текста или именно функция. Ну и потом, какая разница, нажать Tab, Enter или открыть скобку? За что отвечает опция:
    {
      "auto_match_enabled": true
    }

    По своему опыту скажу, лучше приспособиться, чем цепляться за такие мелочи привычек. Погоды снипеты и опции не сделают. Чем больше будете привыкать к разным окружениям в разных редакторах, тем проще будет мигрировать, не теряя время на такие мелочи.
    Ответ написан
    Комментировать
  • Стоит ли читать книгу С. Рао по C++?

    gscraft
    @gscraft
    Программист, философ
    Прочитали треть и не можете определиться? По-моему, однозначно не стоит читать Донцову, а книги по программированию не являются сюжетными блокбастерами от корки до корки. Читайте, на ходу практикуйте, если получается, понимаете, улавливаете — идите дальше. Не понимаете, возьмите еще одну книгу по предмету и тоже читайте. И третью книгу возьмите, если двух мало. А попутно и статьи по теме смотрите.

    Надо учитывать, что не все авторы-технари — хорошие педагоги. Некоторые излишне сюсюкаются с читателем, другие — наоборот, не учитывают, что кругозор разный, третьи просто не могут нормально излагать предмет. Поэтому читателю стоит самому быть немного учителем, додумывая за автора посредством других источников и иных способов изложения одного и того же.

    Моей первой книгой был Шилдт, но в его руководстве не было ничего о компиляторах, о среде работы, о сборке, и эту сухую теорию читал не раз, и не два, но изучение оставалось на бумаге. Прошло какое-то время, отвлекся от С++ вообще по основной учебе, потом добрался до Visual Studio 6-й, что ли, до статей, начал практиковать, и тут-то понял, что Шилд все-таки по мозгам проехался не без следа. Ругают его? Ну, сколько людей, столько и мнений. Педагог он не очень, а теорию излагает верно. Так же и тут. Больше зависит от багажа, от другой литературы.
    Ответ написан
    2 комментария
  • Цикл убивает браузер?

    gscraft
    @gscraft
    Программист, философ
    Видимо раньше Вы внутри цикла выполняли полезную нагрузку и отдавали рабочие ресурсы процесса между делом, сам того не подозревая. Везде, где используют бесконечный цикл, а не пользуются системой сообщений-событий для организации программы — организуют раздачу сообщений или таймаут вручную в обязательном порядке. Возьмите для примера приложения на Win API:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    — здесь цикл подвисает до получения сообщений от системы. Ну или другой пример (что пришел в голову), Python Pygame:
    while True:
      # код приложения
      tick(60) # отдадим время свыше необходимого фреймрейта системе

    Ну а на JS делать такое — моветон, совершенно неправильно. Поскольку любое JS приложение должно быть событийно-ориентированным, реагировать на действия пользователя, как и большинство UI-приложений. Если Вам нужно выполнять действие с интервалом, используйте:
    function main() {} // вместо Вашего напрасного цикла
    setInterval(main, 50); // вызывайте каждые 50 миллисекунд, например
    Ответ написан
    2 комментария
  • Как найти все теги и вытащить значения из них?

    gscraft
    @gscraft
    Программист, философ
    Смотрите документацию по BeautifulSoup
    div_elements = soup.find_all('div', {'class': 'with-overtask'})
    for div in div_elements:
      imgg = div.find('img')['src'] # ... и т.д.
    Ответ написан
  • Перекодировка в Python, как кратко записать?

    gscraft
    @gscraft
    Программист, философ
    А откуда есть такая строка? Она должна была поступить как вам как биты и сразу при поступлении конвертирована с помощью decode().
    b'\xd0\x94\xd0\xb0, \xd0\xbf\xd1\x82\xd0\xb8\xd1\x86\xd1\x8b \xd0\xbe\xd0\xbf\xd0\xb0\xd1\x81\xd0\xbd\xd1\x8b'.decode()
    Ответ написан
    Комментировать
  • Как отправить тело запроса с переносами строк?

    gscraft
    @gscraft
    Программист, философ
    Отображается без переносов где? Перенос строки — это символы, отображаемые собственно как новая строка, в Windows — \r\n (возврат каретки и новая строка по подобию печатных машин), практически во всех остальных случаях — \n. Вы можете легко проверить, что содержится в textarea, открыв текст в Блокноте, который не поддерживает \n. Ну или программным путем, /\r\n+/.test(textareaText):
    xpkTcSh.png
    По всей видимости отображение на каком-то из этапов меняет подход к переносам строк (где просматриваете строки базы данных?), но если программно этого не меняете, текст должен оставаться в том же виде.
    Ответ написан
    5 комментариев
  • Как вывести массив строк в textarea построчно?

    gscraft
    @gscraft
    Программист, философ
    document.getElementById('your-textarea').value = yourStringList.join('\n');
    Ответ написан
    1 комментарий