Архив метки: php

Ошибка отлова исключения в PHP 7.0+

Столкнулся с интересной ошибкой сегодня. Есть вызов функции. Есть обертка try — catch в ней, а она не срабатывает! Гуглил. Долго думал.. Оказывается в PHP 7.0 и выше, изменилось дерево наследования ошибок на:

Throwable
  Error
    ArithmeticError
      DivisionByZeroError
    AssertionError
    CompileError
      ParseError
    TypeError
      ArgumentCountError
  Exception

Соответственно «всех выше теперь» — Throwable, и если хочется теперь словить «глобальную» ошибку, то блок вызывать нужно:

try {

} catch(\Throwable $e) {

}

PHP: определение языка пользователя сайта

В ранние годы, чаще всего определяли языка пользователя на основании его IP. Да и до исх пор так часто делают. И всё чаще ошибаются из-за широкого распространения VPN и всяческих аномайзеров. Поэтом правильнее будет полагаться на стандарт W3C, согласно которому браузер должен передавать на сервер в заголовке HTTP заполненую переменную HTTP_ACCEPT_LANGUAGE. Собстевенно в PHP оно попадает в $_SERVER[‘HTTP_ACCEPT_LANGUAGE’]. Далее остается распрасить переменную с сортировкой по «весам»:

        if (($list = strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']))) {
                    if (preg_match_all('/([a-z]{1,8}(?:-[a-z]{1,8})?)(?:;q=([0-9.]+))?/', $list, $list)) {
                        $language = array_combine($list[1], $list[2]);
                        foreach ($language as $n => $v)
                            $language[$n] = $v ? $v : 1;
                        arsort($language, SORT_NUMERIC);
                    }
        } else $language = array();

Получаем массив вида:

Array
(
    [ru-ru] => 1
    [ru] => 0.8
    [en-us] => 0.6
    [en] => 0.4
)

Установка капчи reCAPTCHA v2 на сайт без отправки формы

Технически задача выглядит так:  на клиенте пользователь нажимает галочку «Я не робот» (описание от Google), по нажатии какойто кнопки — проверяем деймтвитель но ли капча пройдена.

Html (клиент):

<script type="text/javascript">
              var onloadCallback = function() {
                grecaptcha.render('capcha_element', {
                  'sitekey' : 'ключ сайта',
                   hl : "ru"
                });
              };
</script>
<div class="border border-light p-5" id="RegistrationDiv">
    <p class="h4 mb-4 text-center">Регистрация</p>        
    <input type="email" name="RegisterFormEmail" id="RegisterFormEmail" class="form-control mb-4" placeholder="E-mail">    
    <input type="password" name="RegisterFormPassword" id="RegisterFormPassword" class="form-control" placeholder="Пароль" aria-describedby="RegisterFormPasswordHelpBlock">
    <small id="RegisterFormPasswordHelpBlock" class="form-text text-muted mb-4">Минимальная длина 8 симоволов</small>

    <div id="capcha_element"></div>    

    <button id="RegistrationStartButton" onclick="RegistrationStart()" class="btn btn-success my-4 btn-block">Зарегистрироваться</button>
</div>
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
<script>
function GetCapchaData(){
if (typeof grecaptcha.getResponse=="function") {
return grecaptcha.getResponse(); 
} else {
return ""; 
};
}
function RegistrationStart(){ 
$.post("registration.php", {
capcha: GetCapchaData()
})
.done(function( data ) {
});
}
</script>



PHP (сервер)

<?php
        $res=GetResponseCapcha($capcha);
        if (isset($res->success)):
           if ($res->success==true):
                  // капча пройдена, чтото делаем дальше
               else:
		 // это робот - пишем ошибку
           endif;
        endif;

function GetResponseCapcha($capcha){
    $ch = curl_init("https://www.google.com/recaptcha/api/siteverify");                
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $js["secret"]="секретный ключ сайта";
        $js["response"]=$capcha;
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $js);
        $res= json_decode(curl_exec($ch));    
   return $res;
}

SSL routines:ssl3_get_server_certificate:certificate verify failed

Эта ошибка может выходить на некоторых серверах при установце wss соединения WebSocket.

Решение:  отключаем проверку Peer

Amp\Loop::run(function () use (&$data) {
    $tlsc=new ClientTlsContext("");
    $tlw=$tlsc->withoutPeerVerification();
    $connectContext = (new ConnectContext)->withTlsContext($tlw);

    $connection = yield connect('wss://ezs.pupkin.ru:35502',$connectContext);                    
    yield $connection->close();                    
});

Многопоточность в PHP

Ну как известно штатно её нет (с). А иногда очень хочется. Предлагаемая пока альтернатива разработчками — функция Fork() которая по сути создает дочерний процесс, куда передает все открытые соединения, значения переменных и т.п. и продолжает в дочернем процессе с того момента с которого процесс в «родителе» начат.  Один нюанс — при завершении дочернего процесса — все соединения закрываются. Обходить этот нюанс можно посылая сигнал posix_kill(posix_getpid(), SIGHUP);  в дочернем процессе в случае нужды его завершить.

Таким образом «эталонная» реализация «многопоточности» в PHP будет выглядеть примерно так:

какойто код...
какойто код...
//предстоит длительная трудоемкая задача
$child_pid = pcntl_fork();
if ($child_pid==0){
	//длительная ресурсоемкая задача
	echo "--завершил дочерний процесс\n";
	posix_kill(posix_getpid(), SIGHUP);
} else {
	echo "--стартовал дочерний процесс\n";
};