RabbitMQ: Отложеная доставка и время жизни пакета

Собрал наконец в одном классе, всё что мне нужно от RabbitMQ: а именно отправка сообщения в очередь, с отложеной доставкой, плюс уничтожение сообщения по прошествии какого то времени «не доставки».

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

<?php

//Необходимые классы
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;


class TRabbit extends TLog{ 
    public $host="";
    public $login="";
    public $password="";
    public $port="";
    public $connection=null;
    public static $isInstance=false; 
    // настройки касающиеся очереди
    public $passive=false;      // может использоваться для проверки того, инициирован ли обмен, без того, чтобы изменять состояние сервера
    public $durable=true;       // убедимся, что RabbitMQ никогда не потеряет очередь при падении - очередь переживёт перезагрузку брокера
    public $exclusive=false;    // используется только одним соединением, и очередь будет удалена при закрытии соединения
    public $autodelete=false;   // очередь удаляется, когда отписывается последний подписчик
    public $ttl=3000;           // время жизни пакета, через которое оно при недоставке дропнется,в милисекундах
            
    public function __destruct(){
        if (self::$isInstance==false) {        
          $this->connection->close();  
        }
    }    
    public function __construct(){
        self::$isInstance=true;
        $this->host= TConfig::GetConfigKeyValue("rabbitmq_host");
        $this->login= TConfig::GetConfigKeyValue("rabbitmq_login");
        $this->host= "localhost";
        
        $this->password= TConfig::GetConfigKeyValue("rabbitmq_password");
        $this->password="guest";
        $this->port= TConfig::GetConfigKeyValue("rabbitmq_port");        
        $this->connection = new AMQPStreamConnection($this->host, $this->port, $this->login, $this->password);        
    }    
    /**
     * Поставить в очередь сообщение
     * @global type $db
     * @param type $queue   - имя очереди
     * @param type $message - сообщение в json формате
     * @param type $delay   - отсрочка доставки в секундах (необязательно)
     * @param type $ttl     - время жизни пакета, через которое оно при недоставке дропнется
     * @return type
     */
    public function  Producer($queue,$message,$delay=0,$ttl=null) {  
     global $db;
        try {    
          $answer = AnswerStrucNext(false, "Failed to queue a RabbitMQ message");             
             $channel = $this->connection->channel();      
            $channel->exchange_declare(
                'delay',
                'x-delayed-message',
                false,            /* не создается очереь если нет её */
                $this->durable,   /* гарантированная доставка */
                false,            /* удаление очереди если пустая и нет слушателей */
                false,            /* internal */
                false,            /* не ждать ответа от сервера (т. е. продолжать получение) */
                [
                    'x-delayed-type' => ['S', 'direct']                    
                ]);
             if (is_null($ttl)) $ttl=$this->ttl;
             $channel->queue_declare($queue, $this->passive, $this->durable, $this->exclusive, $this->autodelete,false,
                array(
                        "x-message-ttl" => array("I", $ttl)
                    )                     
                );
             $channel->queue_bind($queue, "delay", $queue);
               if ($delay==0){
                  $msg = new AMQPMessage($message);		        
               } else {
                   $msg = new AMQPMessage($message, ['delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]);		
                   $msg->set('application_headers', new AMQPTable(['x-delay' => $delay*1000]));        
               };
          $channel->basic_publish($msg,"delay",$queue);                  
          $channel->close();
          $answer = AnswerStrucNext(false, "ok");             
       } catch(Exception $err) {
           $this->InsertLog(TLog::PT_PLAIN,"Ошибка постановки в очередь $queue (RabbitMQ)", TLog::R_Error, $message);
       };          
     return $answer;
    }
}

PHP: работа с файлами Excel

Одним из вариантов работы с файлами Эксель является библиотека PhpSpreadsheet.

Установка:

composer require phpoffice/phpspreadsheet

Пример использования:


/require_once('vendor/autoload.php');
 
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\{Font, Border, Alignment};
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

$oSpreadsheet = IOFactory::load("akt.xlsx"); // шаблон с шапкой

$oSpreadsheet->getProperties()
   ->setCreator('SSK, OOO')
   ->setLastModifiedBy('SSK, OOO')
   ->setTitle('Акт сверки с клиентом') 
   ->setSubject('Акт сверки с клиентом')
   ->setKeywords('кцмукмц ')
   ->setCategory('узщшамыуозк ')
;

$oSpreadsheet->setActiveSheetIndex(0);
$wSheet = $oSpreadsheet->getActiveSheet();

$wSheet->setCellValueByColumnAndRow(1, 3, "Клиент: " . $res->result->user);
$wSheet->setCellValueByColumnAndRow(1, 4, "Дата с: " . $res->result->from);
$wSheet->setCellValueByColumnAndRow(1, 5, "Дата по: " . $res->result->from);

$totalrow=8;

$balance=0;
foreach($res->result->table as $row) {
    $source="";
    if ($row->doc_reservation>0){$source="уцкацук м №".$row->doc_reservation;};
    if ($row->doc_charge>0){$source="ы укмыу №".$row->doc_charge;};
    if ($row->doc_payment>0){
        $source="уфыкмыукм №".$row->doc_payment;
        if ($row->connector>0){$source=$source.",фыумукау №".$row->connector;};        
    }; 
    
    $wSheet->setCellValueByColumnAndRow(1, $totalrow, $row->date);    //дата
    $wSheet->setCellValueByColumnAndRow(2, $totalrow, $source);    // источник     
    $wSheet->setCellValueByColumnAndRow(3, $totalrow, $row->debit);    // дебет
    $wSheet->setCellValueByColumnAndRow(4, $totalrow, $row->credit);    // кредит
    $wSheet->setCellValueByColumnAndRow(5, $totalrow, $row->balance);    // баланс
    $wSheet->setCellValueByColumnAndRow(6, $totalrow, $row->ezs);    // ЭЗС
    $balance=$row->balance;
    $totalrow++;
}

$wSheet->setCellValueByColumnAndRow(1, $totalrow, "Текущий баланс: " . $balance);
$wSheet->getStyle('A'.$totalrow)->applyFromArray([
    'font' => [
      'name' => 'Arial',
      'bold' => true,
      'italic' => false,
      'underline' => Font::UNDERLINE_DOUBLE,
      'strikethrough' => false,
      'color' => [
          'rgb' => '808080' 
        ]
    ],
    'borders' => [
        'allBorders' => [
            'borderStyle' => Border::BORDER_THIN,
            'color' => [
                'rgb' => '808080'
            ]
        ],
    ],
    'alignment' => [
        'horizontal' => Alignment::HORIZONTAL_CENTER,
        'vertical' => Alignment::VERTICAL_CENTER,
        'wrapText' => true,
    ]
]);
// установить цвет фона ячейки
$wSheet->getStyle('A'.$totalrow)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('00FF7F');
 

$oWriter = IOFactory::createWriter($oSpreadsheet, 'Xlsx');
$oWriter->save('php://output');

Отправка PUSH уведомлений в приложение IOS

Задача: в приложение на IOS отправить push уведомление из PHP

Решение:

public function SendPushIOS($token,$title,$message){     
     $rs=false;
     $notification_payload = [
        "aps" => [
            "alert" => [
                //"title" => "Зарядись!",
                "body" => $message,
               // "action-loc-key" => "PLAY"
            ],
            "badge" => 0,
            "sound" => "bingbong.aiff"
        ]
     ];     
        $token_key = $this->server_key_ios;
        //echo "!! token_id IOS: $this->server_token_id_ios\n";
        $jwt_header = [
                'alg' => 'ES256', 
                'kid' => $this->server_token_id_ios
        ];
        //echo "!!team_id:$this->server_team_id_ios\n";
        $jwt_payload = [
                'iss' => $this->server_team_id_ios, 
                'iat' => time()
        ];
        $raw_token_data = self::b64($jwt_header, true).".".self::b64($jwt_payload, true);
        $signature = '';        
        openssl_sign($raw_token_data, $signature, $token_key, 'SHA256');
        $jwt = $raw_token_data.".".self::b64($signature);
        // send push
        $request_body = json_encode($notification_payload);
        $endpoint = 'https://api.push.apple.com/3/device';
        $url = "$endpoint/$token";
        $ch = curl_init($url);
        //echo "!!server_bandle_id_ios:$this->server_bandle_id_ios\n";
        curl_setopt_array($ch, [
                CURLOPT_POSTFIELDS => $request_body,
                CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
                CURLOPT_HTTPHEADER => [
                        "content-type: application/json",
                        "authorization: bearer $jwt",
                        "apns-topic: $this->server_bandle_id_ios"
                ]
        ]);
        $result_curl=curl_exec($ch);
        $result = json_decode($result_curl);

        if (is_null($result)) {
            $log= new TLog($this->api);
            $log->InsertLogBoiler([
                "source_id"=> TLog::S_Messages,
                "comment"=>"Ошибка отправки PUSH IOS",
                "raw_package"=>curl_error($ch).$result_curl,
                "reason"=> TLog::R_Error
            ]);           
        } else { 
                if ((int)$result>0){
                //if ($result->success>0){
                  $rs=true;  
                };
        };
        

        curl_close($ch);        
     return $rs;
    }
 public function b64($raw, $json=false){
            if($json) $raw = json_encode($raw);
            return str_replace('=', '', strtr(base64_encode($raw), '+/', '-_')); 
    }   

Отправка PUSH уведомлений в приложение Android из PHP

Для того чтобы отправлять пуш уведомления, необходимо получить ключ установки приложения и знать «ключ сервера». Более подробная информация в консоли Firebase

public function SendPushAndroid($server_key,$token,$title,$message){
        $data = json_encode([
            "to" => $token,
            "notification" => [
                "body" => $message,
                "title" => $title,
                "icon" => "ic_launcher"
            ]
        ]);

        $url = 'https://fcm.googleapis.com/fcm/send';
        $server_key = $server_key;
        $headers = array('Content-Type:application/json','Authorization:key='.$server_key);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        $result = curl_exec($ch);
        var_dump($result);
        if ($result === FALSE) {
            die('Oops! FCM Send Error: ' . curl_error($ch));
        }
        curl_close($ch);        
    }    

PHP: Рисуем SVG

Один из вариантов генерации картинок svg «на лету». Капля, внутри буква, окруженная полукругами.

<?php

$blob_status=5; 
$pies=[1,2,3,4,5,6];

function GetColorPieByState($state){    
    switch ($state) {
        case 1:$color="#2afc24";break;  
        case 2:$color="#2afc24";break;  
        case 3:$color="#fc2424";break;  
        case 4:$color="#fc2424";break;  
        case 5:$color="#fc2424";break;  
        case 6:$color="#cccccc";break;  
        default:$color="#7DD8B5";break; 
    }
    return $color;
};

function String2Array($st){
    $st=mb_substr($st, 0, -1);
    return explode(",", $st);
};

function GetColorBlobByState($state){    
    $station_fill="#D1FFB4";
    if ($state==1) $station_fill="#2afc24";
    if ($state==2) $station_fill="#fc2424";
    if ($state==3) $station_fill="#fc2424";
    if ($state==4) $station_fill="#fc2424";
    if ($state==5) $station_fill="#cccccc";
    return $station_fill;
};

function SaveSvgBlobIco($blob_status,$pies,$filename){
    
}

Draw($blob_status,$pies);

function Draw($blob_status,$pies){
    
    $station_fill=GetColorBlobByState($blob_status);

    $svg="";
    $svg=$svg.'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="264">';
    $svg=$svg."<defs>";
    $svg=$svg."    <style>";
    $svg=$svg."        .blob_fill {fill:$station_fill;}"; /*Цвет капли*/       
    $svg=$svg.'    </style>';
    $svg=$svg.'</defs>';
    //Рисунок капли
    $svg=$svg.'   <path class="blob_fill" d="m199.10001,101.02451c0,90.53423 -99.52251,161.55548 -99.52251,161.55548s-99.5775,-76.21059 -99.5775,-161.55548a99.55,101.0245 0 0 1 199.10001,0z" id="svg_1"/>';
    //Буква й
    $svg=$svg.'   <text transform="matrix(7.0814, 0, 0, 6.46509, -382.002, -470.036)" xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="24" id="svg_3" y="94.56099" x="61.67799" stroke-opacity="null" stroke-width="0" stroke="null" fill="#EDFFFF">й</text>';

    $svg=$svg.'<g style="stroke:black;stroke-width:1">';

    function pt($x,$y,$rad,$gap,$r,$ang,$mns=-1){ 
      //global $x,$y,$rad,$gap;      
      $a=($ang-90)*$rad-$mns*asin($gap/$r); 
      return sprintf('%0.2f,%0.2f',$x+$r*cos($a),$y+$r*sin($a));
    }

    $x=100;
    $y=100;
    $r=60;  // внутренний радиус
    $rc=90; // внешний радиус
    $gap=20; // отступ между кусками
    $a360=360/count($pies); 
    $M_PI=3.14159265358; 
    $rad=$M_PI/180.;
    $out='';
    $ang1=0;      


    foreach ($pies as $connector) {  
      $dang = 1 * $a360;       
      $laf  = $dang > 180? 1 : 0; 
      $ang2 = $ang1 + $dang;      
      $out.= '<path d="M'.pt($x,$y,$rad,$gap,$rc,$ang1).'L'.pt($x,$y,$rad,$gap,$r, $ang1)."A $r,$r, 0,$laf,1 "  .pt($x,$y,$rad,$gap,$r,$ang2,1).
                      'L'.pt($x,$y,$rad,$gap,$rc,$ang2,1)."A $rc,$rc, 0,$laf,0, ".pt($x,$y,$rad,$gap,$rc,$ang1).'" style="fill:'.GetColorPieByState($connector).'" />'."\n";
      $ang1=$ang2;
    }
    $svg=$svg.$out;
    $svg=$svg.'</g></svg>';
    echo $svg;
};
?>
1 5 6 7 8 9 24