• Можно ли сделать два разных Sitemap для разных доменов но с одним корневым каталогом?

    @mafia8
    Файл .htaccess
    RewriteEngine On
    RewriteBase /
    
    RewriteRule sitemap.xml sitemap.php

    Файл sitemap.php
    $host=$_SERVER['HTTP_HOST'];
    if($host=='site1.ru') echo file_get_contents('sitemap_xml_site1_ru.xml');
    if($host=='site2.ru') echo file_get_contents('sitemap_xml_site2_ru.xml');
    Ответ написан
    Комментировать
  • Как скачать и установить Apache?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    гоподи, не надо никаких апачей, и уж тем более всяких опенсерверов, вампы, мампы и прочей мумбы-юмбы

    скачать пхп, распаковать, перейти в папку с пхп файлами, запустить команду
    c:\путь куда распакован РНР\php -S localhost:80
    Ответ написан
  • Как получить дробную часть числа?

    v3shin
    @v3shin
    Веб-шаман
    console.log(`123.321555`.split('.')[1].substr(0, 3));
    console.log(`123.32`.split('.')[1].substr(0, 3));
    console.log(`123.3`.split('.')[1].substr(0, 3));
    Ответ написан
    Комментировать
  • Кто-нибудь может помочь реализовать смену данных пользователя самим пользователем?

    delphinpro
    @delphinpro Куратор тега PHP
    frontend developer
    Сначала дернуть из базы пользователя по юзернейму (по паролю нельзя, он хеширован)
    Если запись нашлась – хорошо, пользователь есть

    SELECT * FROM users WHERE username=:username

    Получаем объект/массив с данными пользователя
    Примерно такой:

    $user = [
      'id' => 1,
      'username' => 'vasya',
      'password' => 'длинный хеш'
    ]


    Потом проверяем старый пароль из формы, правильный ли

    password_verify($old_password, $user['hash'])

    Если прошли проверку, значит логин и пароль вбили правильно, можно менять

    Делаем хеш нового пароля

    $hash = password_hash($new_password, PASSWORD_DEFAULT);


    И закидываем в базу

    UPDATE users SET password=:hash WHERE id=$user['id']
    Ответ написан
    Комментировать
  • Почему в Zeplin не отображается text-transform: uppercase?

    @cheeroque
    С тем, что дизайнер прямо так их и набрал, с зажатым шифтом на клавиатуре.
    Ответ написан
    Комментировать
  • Как проскроллить страницу до нужного элемента (ярлыка) с помощью Javascript (jQuery)?

    Zheleznov
    @Zheleznov
    const anchors = document.querySelectorAll('a[href*="#"]')
    
            for (let anchor of anchors) {
              anchor.addEventListener('click', function (e) {
                e.preventDefault()
    
                const blockID = anchor.getAttribute('href').substr(1)
    
                document.getElementById(blockID).scrollIntoView({
                  behavior: 'smooth',
                  block: 'start'
                })
              })
            }
    Ответ написан
    2 комментария
  • SVG background - как правильно сделать адаптивным?

    Ankhena
    @Ankhena Куратор тега CSS
    Нежно люблю верстку
    Фоновые изображения адаптируются с помощью background-size
    Или изменения размеров блока. Например, вставляются как фон псевдоэлемента и дальше регулируют размер псевдо, а фону задают contain, cover или 100% (или не 100)

    Единственно отличие между растром и SVG в том, что растр меняет свои пропорции, а SVG обычно нет.
    Чтобы менялись пропорции SVG у него должен быть атрибут preserveAspectRatio

    Вот примеры
    https://jsfiddle.net/0xrh6uvj/ - меняйте ширину окна с результатом

    ----
    Но вы не сделали песочницу на jsfiddle.net с вашим проблемным кодом и не ясно, что вообще вы там творите.
    Ответ написан
    6 комментариев
  • Где и как лучше прехватывать/обрабатывать исключения?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Главная вещь, которую надо понимать про исключения, это то что они бывают двух основных видов.
    После этого вся обработка становится совершенно естественной и очевидной.

    - Error exceptions, или по простому говоря - ошибки. Обычные ошибки при выполнении программы. Обычно код бросает их сам. Решение "обрабатывать все ошибки через set_exception_handler" будет вполне логичным.
    - Business logic exceptions - это не ошибка в строгом понимании этого слова, а скорее нормальное поведение программы. Ситуация исключительная, но только для бизнес-логики. Их всегда кидает программист.

    И вот просто тупо исходя из того что их два типа, уже можно сказать что единого ответа "где лучше" не существует.
    У каждого типа своя логика обработки. Но при этом, как только ты уложил в голове различия, эта логика становится совершенно очевидной:

    - Error exceptions почти никогда не ловятся через try-catch, по крайней мере на месте. За исключением редких исключительных ситуаций обработка ошибок производится в единой точке, обработчике ошибок
    - Business logic exceptions всегда ловятся через try-catch

    Отсюда мы видим, что

    - //PDOException при коннекте (эммм... я понимаю что пример, но блин, new PDO в конструкторе репы, серьёзно?ладно, мы сейчас не об этом) - это однозначно ошибка
    - //PDOException в запросе - это тоже ошибка, тут два раза думать не надо
    - условно пустое имя. Ну вот здесь мы уже переходим в область бизнес-логики. Коду тут без разницы, пустое имя, или полное. Это важно нам - программисту, пользователю.

    Но тут есть один, блин, тонкий момент.
    Валидация, по сути, пытается разорваться между всеми слоями приложения.
    С одной стороны, это функция Сущности (которую ошибочно называют моделью) - проверять валидность своих данных.
    С другой - если нам надо донести результаты валидации до пользователя, то как быть с переводами? Тащить в модель переводчик, серьёзно? Ну ок, ладно, возвращаем ключи для перевода. Хотя тоже как-то...
    Но вот проверка емейла на уникальность. Её-то где делать?
    В Сущности? И тащить в нее соединение с БД?
    На уровне БД? А где ловить тогда исключение? В сервисе? И ломиться через несколько уровней абстракции к сырому PDOException? Не вариант.
    Или, к примеру, для модели естественно проверять каждое поле отдельно, и кидать исключение. А для пользовательского интерфейса это неприемлемо - надо выдавать все ошибки валидации скопом, а не скармливать по одной.
    Вопросы...

    Но "где валидировать данные" - это отдельная тема, которая не относится напрямую к вопросу "где ловить ошибки".

    В данном случае я предлагаю оставить Сущность Юзер без валидации, а всю валидацию делать в сервисе.

    Хотя опять же - в современных фреймворках валидацию (Не будем показывать пальцем, но это был Ларавель) вообще делают еще до запуска контроллера, в миддлвари. Это кстати спорное решение, которое нарушает целостность модели. Если мы обращаемся к модели через другую точку входа, не контроллер, а, к примеру, создаем юзера через командную строку, то нам нужна точно такая же валидация. Запускать команды через мидлварь? В сущности, это мысль... Но всё равно, мы в итоге бизнес-логику размазываем между моделью и точками входа в неё, а это костыль.
    И при всём при этом оставлять Сущность Юзер совсем без валидации тоже как-то не комильфо... А если оставлять - то получится по сути дублирование кода.
    Вопросы, вопросы...

    Но вернемся к нашим баранам, в смысле юзерам.

    Начнем с того, что проверка имени на равенство пустой строке или нулю - это какой-то детский лепет (и кстати, почему ноль нельзя? вот у Маска ребеночка зовут X Æ A-12 - почему у кого-то не может быть имя "0.0"?).

    Отдельно побурчу насчет empty. Вообще, это один из самых сложных операторов, на нем спотыкаются все поголовно. В частности,
    function f($name){
        if (empty($name))...

    - это бессмыслица. Звучит, в переводе на русский, немного шизофренически: "пусть у нас будет переменная $name. Если у нас нет переменной $name...". Ну как нет, если мы только что ее в функцию передали?
    empty() проверяет переменную на существование И "пустоту". И в данном случае первая проверка будет бессмысленной. Никогда не надо писать бессмысленный код.
    Поэтому логичнее будет написать просто if(!$name). Хотя по нынешним временам это тоже говнокод. Что мы имеем здесь в виду? Имя не может быть пустой строкой? Пустым массивом? Нулём? null? false? А true или заполненный массив - это, получается, хорошее, годное имя?
    Лучше все-таки четче определять свои претензии. К примеру, проверять длину строки.

    У имени можно сделать миллион проверок: Минимальную длину, максимальную длину. Проверить что это строка, а не массив, в конце концов, и не булево значение.
    И так по каждому полю.
    То есть, по-хорошему, валидация данных на соответствие правилам бизнес-логики - это отдельный большой бизнес!

    При этом, по итогам валидации, исключение кидается строго одно, общее для всех ошибок.
    Которое мы и ловим в контроллере через трай.

    class ValidationError extends Exception{ ... };
     
    class User
    {
        private string $name;
    
        public function __construct(string $name)
        {
            $this->name = $name;
        }
    }
    
    class UserRepository
    {
        public function __construct(PDO $pdo)
        {
            $this->builder = $pdo;
        }
        public function add(User $user): User
        {
            $saved = $this->builder->query('INSERT INTO users (name) VALUES ("user")'); //PDOException
        }
    }
    
    class UserSerivce
    {
        private UserRepository $repository;
        private Validator $validator;
    
        public function new(array $data): User
        {
            $rules = [...] ;
            $errors = $this->validator->validate($data, $rules);
            if ($errors) {
                throw new ValidationException("", $errors);
            }
            $user = new User($data['name']); // в принципе, сучность может здесь бросить своё ValidationException
            return $this->repository->add($user);
        }
    }
    
    class UserController
    {
        private UserSerivce $service;
    
        public function store(array $data)
        {
            try {
                $user = $this->service->new($data);
            catch (ValidationError $e) {
                // рассказываем юзеру что он дурак
            }
            return redirect()->to('/' . $user->id);
        }
    }


    Но опять же, в современных фреймворках вручную этот catch не пишут, там уже при вызове контроллера мы либо в роутере, либо в миддлвари, либо еще где у черта в ступе - но, главное, в одном месте, чтобы не писать одно и то же каждый раз, ловим определенные исключения, скажем исключения которые потом сконвертируются в 4хх ошибки НТТР. И вот ошибки валидации тоже можем там ловить.

    Как можно заметить, вот весь этот длинный и путанный текст посвящен исключительно ошибкам бизнес-логики.
    Поскольку с ошибками кода всё куда проще - единый хендлер тупо их обрабатывает в одном месте, как описано в статье по ссылке @Spartak-2205
    За исключением редких случаев, когда они ловятся по месту. Когда ошибка некритичная, или есть сценарий обработки - например, попробовать выполнить то же действие еще раз.
    Ответ написан
    3 комментария
  • Где хранить сессии? SQLite? MySQL? Memcached? Redis? FS?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Храни в мускуле.

    Файлы, действительно - самый неудачный вариант. Сара Големон, отвечая недавно на подобный вопрос, написала
    File storage is only a default because the runtime doesn't know in advance what database engine or credentials you're going to use unless you tell it. So... ya know.... tell it.

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

    Редис и мемкеш - это кэш, а не хранилище. Подумай над тем, что такое кэш и для чего он используется. И подходит ли кэш для хранения сессий.

    Про скулиту ты все правильно написал. Те же файлы, вид сбоку.

    А про мускуль очень смешно. Какая-то прямо повальная датабазебоязнь. Откуда это "я вообще хочу минимизировать запросы к MySQL"? Что за ерунда про "соединение может оборваться"? И как ты вообще можешь сравнивать по производительности файл на диске, который открывается при каждом запросе, с демоном, который держит все данные в памяти и отдает по сокету?
    Ответ написан
    32 комментария
  • Может ли человек показать что у него есть аккаунт без пароля?

    yarkov
    @yarkov
    Помог ответ? Отметь решением.
    Открывайте devtools и меняйте код страницы как хотите.
    Ответ написан
    2 комментария
  • Как найти таблицу в нужной схеме MySQL?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Добрый день!
    Думаю, что данный вопрос отпадет, когда вы познакомитесь с технологией ORM и с таким фреймворком, как например, Hibernate. Если вкратце, то там есть опция, которая при запуске проверяет наличие таблиц и в случае отсутствия оных, создает таблицы автоматически. Но как правило, на production таблицы автоматически не создают.
    Обычно, пишут sql скрипты, которые впоследствии мигрируются при помощи таких фреймворков, как например, flyway.
    Таким образом, с учетом того, что вы работаете по принципу database first (т.е. вы создаете БД, схему и таблицу), то ситуации, когда не будет нужная таблица не должно быть. А даже если такая ситуация будет, то пускай выбрасывается исключение и приложение падает. Можете также ознакомиться с такими терминами. как database first, code first, model first.
    Ответ написан
    Комментировать
  • Покритикуйте код! Какие грубые и негрубые ошибки совершил?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Ну тут скорее ни одной почти строчки нормальной.
    • exit('Ошибка подключения к базе данных!'); дважды глупость. Пользователю сайта не интересно читать, что у тебя сломалось - база данных или деньги на пиво кончились. Как программисту, тебе эта бессмысленная фраза тем более бесполезна, она ничего не говорит о том, ЧТО КОНКРЕТНО сломалось, чтобы ты мог исправить
    • if ($numRows > 0) { бессмысленный кусок кода
    • while ($row = $result->fetch_assoc()) { заменяется на $result->fetch_all(MYSQLI_ASSOC)
    • $this->connect() коннектимся каждый раз, чтобы выполнить запрос, серьёзно?
    • А когда будет еще один класс, для другой таблицы, снова будешь писать код подключения к БД? И так в каждом?
    • Почему класс для работы с "позициями" называется DBh?
    • По сути это не класс, а набор функций. Если ты уберешь красивые слова class и this, то НИЧЕГО не изменится
    • SQL инъекции кругом
    • class Handler extends Dbh ВООБЩЕ непонятно зачем



    В общем, как-то так
    dbh.php
    class Dbh
    {
        public $conn;
    
        public function __construct($config)
        {
            mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
            $this->conn = new mysqli(...$config);
            $this->conn->set_charset('utf8mb4');
        }
    
        public function preparedQuery($sql, $params, $types = '')
        {
            $types = $types ?: str_repeat('s', count($params));
            $stmt = $this->conn->prepare($sql);
            $stmt->bind_param($types, ...$params);
            $stmt->execute();
            return $stmt;
        }
    
        public function selectResult($sql, $params, $types = '')
        {
            if (!$params) {
                return $this->conn->query($sql);
            }
            return $this->preparedQuery($sql, $params, $types)->get_result();
        }
        public function selectAll($sql, $params = [], $types = '')
        {
            return $this->selectResult($sql, $params, $types)->fetch_all(MYSQLI_ASSOC);
        }
        public function selectAssoc($sql, $params = [], $types = '')
        {
            return $this->selectResult($sql, $params, $types)->fetch_assoc();
        }
        public function selectRow($sql, $params = [], $types = '')
        {
            return $this->selectResult($sql, $params, $types)->fetch_row();
        }
        public function selectCell($sql, $params = [], $types = '')
        {
            $row = $this->selectRow($sql, $params, $types);
            return $row ? $row[0] : false;
        }
    }

    position.php
    class Position
    {
        protected $dbh;
    
        public function __construct(Dbh $dbh)
        {
            $this->dbh = $dbh;
        }
        // Получаем все позциии из БД и возвращаем их в массиве $output если записей больше нуля
        public function getAllPositions()
        {
            return $this->dbh->selectAll('SELECT * FROM positions');
        }
        protected function addPosition($content)
        {
            $count = $this->dbh->selectCell('SELECT count(*) FROM positions');
            if ($count < 10) {
                $this->dbh->preparedQuery("INSERT INTO positions (content) VALUES (?)", [$content]);
            }
        }
        protected function deletePosition($id)
        {
            $this->dbh->preparedQuery("DELETE FROM positions WHERE id = ?", [$id]);
        }
        //Поиск позиций в БД по столбцу content
        protected function searchPosition($content)
        {
            $content = "%$content%";
            return $this->dbh->selectAll('SELECT * FROM positions WHERE content LIKE ?',[$content]);
        }
    }

    config.php
    return [
          'db' => [
              'host' => '127.0.0.1',
              'username' => '',
              'password' => '',
              'dbname' => '',
              'port' => 3306,
          ],
      ];


    handler.php

    $config = require 'config.php';
    $dbh = new Dbh($config['db']);
    $position = new Position($dbh);
    
    switch ($_GET['action']) {
        case 'getpositions':
            $output = $position->getAllPositions();
            echo json_encode($output);
            break;
        case 'addposition':
            $content = $_GET['content'];
            $position->addPosition($content);
            break;
        case 'getsearchpositions':
            $content = $_GET['content'];
            $output = $position->searchPosition($content);
            echo json_encode($output);
            break;
        case 'deleteposition':
            $id = $_GET['id'];
            $position->deletePosition($id);
            break;
        default:
            header("HTTP/1.0 400 Bad Request");
    }
    Ответ написан
    2 комментария
  • Как из json сформировать массив?

    horbyshyn
    @horbyshyn
    1C-Bitrix developer
    var_dump($payment_details);


    А json_decode Вы помещаете в
    $simpla->order->payment_details
    , его распечатайте
    Ответ написан
    Комментировать
  • Таймер обратного отсчёта:?

    @haveacess
    HTML
    <div id="timer">
    	<strong>-:-:-</strong>
    </div>


    JS
    var timer_life = 90; //time in seconds (у тебя это будет 20 245 сек)
    
    var timer = setInterval(function () {
    			timer_life -= 1;
    
    			if (timer_life == 0) {
    
    				clearInterval(timer);
    				//next actions, when timer was stopped
    			}
    
    			let w = Math.trunc(timer_life/3600),
    					m = Math.trunc((timer_life - (w*3600))/60),
    					s = Math.trunc(timer_life - ((w * 3600) + (m * 60)));
    
    			$('#timer > strong').text(w + ':' + m + ':' + s); //#timer > strong - is you selector, change it please
    		}, 1000)
    Ответ написан
    7 комментариев
  • Почему меняется числовое значение после JSON.parse()?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    Number.MAX_SAFE_INTEGER == 253 - 1 == 9007199254740991
    1680365554570513200 > 9007199254740991
    Передавайте id как строку.
    Ответ написан
    Комментировать
  • Почему выскакивает ошибка php in_array() expects parameter 2 to be array?

    DevMan
    @DevMan
    ссылку на гугло или яндек транслейт дать, штоле?
    что может быть не понятного в "нужен массив, а передана строка"?
    Ответ написан
    Комментировать
  • Почему не срабатывает обратная перекодировка json?

    DevMan
    @DevMan
    и это абсолютно нормально и по стандарту.
    если вам упало видеть кириллицу, то есть флаг JSON_UNESCAPED_UNICODE
    Ответ написан
    Комментировать
  • Как распарсить строку?

    cr1gger
    @cr1gger
    Все дороги ведут в Рим — встретимся в Риме!
    Ответ написан
    Комментировать
  • Как из дробного числа (13.123456) убрать все, что перед точкой вместе с точкой?

    kirbi1996
    @kirbi1996
    Переведи в строку разбей по точке и возьми вторую часть, затем переведи в число. Это самое банальное что пришло в голову
    Ответ написан
    Комментировать