За последние 24 часа нас посетили 5527 программистов и 430 роботов. Сейчас ищут 225 программистов ...

ООП Мой первый class. Не нарушаю ли я методологию?

Тема в разделе "PHP для новичков", создана пользователем mainprofilemail, 13 мар 2020.

Метки:
  1. mainprofilemail

    mainprofilemail Новичок

    С нами с:
    14 апр 2018
    Сообщения:
    73
    Симпатии:
    0
    Это мой первый class, не судите строго, а лучше подскажите где я ошибаюсь (а я ошибаюсь, чувство такое) и как правильно работать с классом

    Данный клас предназначен для полноценной работы Viber бота (перевожу из процедурного в ООП)

    PHP:
    1. //Подключаемся к БД
    2. $mysqli = new mysqli('ххх', 'ххх', 'ххх', 'ххх');
    3. //Кодировка данных из базы
    4. $mysqli->query("SET NAMES 'utf8' ");
    5. // В случае ошибки подключения передаём false
    6. $mysqli = $mysqli->connect_errno ? false : $mysqli;
    Инициализируем класс.
    PHP:
    1. $handler = new Viber_Bot_Handler($mysqli, API_TOKEN));
    Вот собственно сам класс:

    • getContents() - принимает данные от Вайбера
    • requestProcess() - покачто делает минимальную проверку на на тип присылаемого сообщения. Если тип text отвечает темже сообщением что нам пришло, в противном случае показываем ошибку
    • sendResponse() - отправляет готовый ответ
    PHP:
    1. class Viber_Bot_Handler{
    2.    
    3.     private $mysqli;
    4.    
    5.     private $api_token;
    6.     private $api_url = "https://chatapi.viber.com/pa/send_message";
    7.    
    8.     private $event;
    9.     private $mess_type;
    10.     private $mess_text;
    11.     private $sender_id;
    12.     private $sender_name;
    13.    
    14.     private $answers = [
    15.         "subscribed" => "Дякуємо, що підписались на нас!",
    16.         "conversation_started" => "Розмова почалась!",
    17.         "error_understand" => "Нажаль я вас не зрозуміла, напишіть будь ласка щось інше "
    18.     ];
    19.    
    20.    
    21.     function __construct($mysqli, $api_token){
    22.         $this->mysqli = $mysqli;
    23.         $this->api_token = (string) $api_token;
    24.         $this->getContents();
    25.     }
    26.    
    27.     /*
    28.         Получаем данные от Вайбера
    29.     */
    30.     public function getContents(){
    31.         $request = file_get_contents('php://input');
    32.         $input = json_decode($request, true);
    33.        
    34.         if(!$input){return}
    35.        
    36.         $this->event = $input['event'];
    37.         $this->mess_type = $input['message']['type']; // Тип присланного сообщения
    38.         $this->mess_text = $input['message']['text']; // Текст сообщения
    39.         $this->sender_id = $input['sender']['id']; // ID пользователя который нас пишет
    40.         $this->sender_name = $input['sender']['name']; // Имя пользователя который нас пишет
    41.        
    42.         $this->requestProcess();
    43.     }
    44.    
    45.     public function requestProcess(){
    46.        
    47.         $data['auth_token'] = $this->api_token;
    48.         $data['receiver'] = $this->sender_id;
    49.         $data['type'] = "text";
    50.         $data['min_api_version'] = 2;
    51.        
    52.         if($this->mess_type == 'text'){
    53.             $data['text'] = $this->mess_text;
    54.         }else{
    55.             $data['text'] = $this->answers['error_understand'];
    56.         }
    57.        
    58.         $this->sendResponse($data);
    59.     }
    60.    
    61.     /*
    62.         Отправляем готовый ответ пользователю
    63.     */
    64.     public function sendResponse($param){
    65.        
    66.         $resp_json = json_encode($param);
    67.        
    68.         $ch = curl_init($this->api_url);
    69.        
    70.         curl_setopt($ch, CURLOPT_POST, 1);
    71.         curl_setopt($ch, CURLOPT_POSTFIELDS, $resp_json);
    72.         curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
    73.        
    74.         $response = curl_exec($ch);
    75.         $err = curl_error($ch);
    76.        
    77.         curl_close($ch);
    78.        
    79.         // Если ошибку существуют показываем $err
    80.         // в противном случае $trsponse
    81.         return ($err) ? $err : $response;
    82.     }
    83. }
     
  2. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    6.518
    Симпатии:
    1.034
    Адрес:
    Лень
    ;
     
  3. mainprofilemail

    mainprofilemail Новичок

    С нами с:
    14 апр 2018
    Сообщения:
    73
    Симпатии:
    0
    может есть ещё что-то? Буду рад если тыкнешь меня носом в мои ошибки;)
     
  4. ElisDN

    ElisDN Активный пользователь

    С нами с:
    13 фев 2018
    Сообщения:
    606
    Симпатии:
    128
    1. По стилю кода и именованию полей придерживайтесь PSR-1 и PSR-12.

    2. В свежем PHP используйте типизацию.

    3. Не вызывайте методы вроде $this->getContents() в конструкторе, а сделайте отдельный метод вроде $handler->handle() для запуска.

    4. В $this->... присваивайте только настройки в конструкторе. Вместо присваивания промежуточных вещей $this->mess_text передавайте $input напрямую в методы вроде $this->requestProcess($input);

    5. Внутренние меоды делайте приватными.

    6. Не вызывайте лишний раз методы по цепочке друг из друга. Сделайте методы максимально независимыми от соседей и в методе handle() и управляйте процессом их вызова.

    7. Для сообщений об ошибках используйте исключения.

    PHP:
    1. declare(strict_types=1);
    2.  
    3. class ViberBotHandler
    4. {
    5.     private static array $answers = [
    6.         'subscribed' => 'Дякуємо, що підписались на нас!',
    7.         'conversation_started' => 'Розмова почалась!',
    8.         'error_understand' => 'Нажаль я вас не зрозуміла, напишіть будь ласка щось інше '
    9.     ];
    10.  
    11.     private string $apiToken;
    12.     private string $apiUrl;
    13.  
    14.     public function __construct(
    15.         string $apiToken,
    16.         string $apiUrl = 'https://chatapi.viber.com/pa/send_message'
    17.     ) {
    18.         $this->apiToken = $apiToken;
    19.         $this->apiUrl = $apiUrl;
    20.     }
    21.  
    22.     public function handle(): string
    23.     {
    24.         $input = $this->readInput();
    25.         $data = $this->generateResponse($input);
    26.  
    27.         return $this->sendResponse($data);
    28.     }
    29.  
    30.     private function readInput(): array
    31.     {
    32.         $request = file_get_contents('php://input');
    33.  
    34.         return json_decode($request, true, 512, JSON_THROW_ON_ERROR);
    35.     }
    36.  
    37.     private function generateResponse(array $input): array
    38.     {
    39.         $data = [];
    40.  
    41.         $data['auth_token'] = $this->apiToken;
    42.         $data['receiver'] = $input['sender']['id'];
    43.         $data['type'] = 'text';
    44.         $data['min_api_version'] = 2;
    45.  
    46.         if ($input['message']['type'] === 'text') {
    47.             $data['text'] = $input['message']['text'];
    48.         } else {
    49.             $data['text'] = self::$answers['error_understand'];
    50.         }
    51.  
    52.         return $data;
    53.     }
    54.  
    55.     private function sendResponse(array $data): string
    56.     {
    57.         $ch = curl_init($this->apiUrl);
    58.  
    59.         curl_setopt($ch, CURLOPT_POST, 1);
    60.         curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    61.         curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
    62.  
    63.         $response = curl_exec($ch);
    64.         $error = curl_error($ch);
    65.  
    66.         curl_close($ch);
    67.  
    68.         if ($err) {
    69.             throw new RuntimeException($error);
    70.         }
    71.  
    72.         return $response;
    73.     }
    74. }
    PHP:
    1. $handler = new ViberBotHandler($token);
    2.  
    3. try {
    4.     $response = $handler->handle();
    5.     echo 'Success: ' . $response;
    6. } catch (Exception $exception) {
    7.     echo 'Error: ' . $exception->getMessage();
    8. }
    А дальше уже можно декомпозировать вроде передачи $input снаружи и выноса отправки сообщений в ApiClient:
    PHP:
    1. class ViberBotHandler
    2. {
    3.     private static array $answers = [
    4.         'subscribed' => 'Дякуємо, що підписались на нас!',
    5.         'conversation_started' => 'Розмова почалась!',
    6.         'error_understand' => 'Нажаль я вас не зрозуміла, напишіть будь ласка щось інше '
    7.     ];
    8.  
    9.     private ApiClient $client;
    10.     private string $apiToken;
    11.  
    12.     public function __construct(ApiClient $client, string $apiToken)
    13.     {
    14.         $this->client = $client;
    15.         $this->apiToken = $apiToken;
    16.     }
    17.  
    18.     public function handle(array $input): string
    19.     {
    20.         $data = $this->generateResponse($input);
    21.         return $this->client->sendMessage($data);
    22.     }
    23.  
    24.     private function generateResponse(array $input): array
    25.     {
    26.         $data = [];
    27.         $data['auth_token'] = $this->apiToken;
    28.         $data['receiver'] = $input['sender']['id'];
    29.         $data['type'] = 'text';
    30.         $data['min_api_version'] = 2;
    31.  
    32.         if ($input['message']['type'] === 'text') {
    33.             $data['text'] = $input['message']['text'];
    34.         } else {
    35.             $data['text'] = self::$answers['error_understand'];
    36.         }
    37.  
    38.         return $data;
    39.     }
    40. }
    PHP:
    1. $client = new ApiClient($apiUrl);
    2. $handler = new ViberBotHandler($client, $token);
    3.  
    4. try {
    5.     $input = json_decode(file_get_contents('php://input'), true);
    6.     $response = $handler->handle($input);
    7.     echo 'Success: ' . $response;
    8. } catch (Exception $exception) {
    9.     echo 'Error: ' . $exception->getMessage();
    10. }
    К такому коду уже можно сочинять юнит-тесты.
     
    #4 ElisDN, 13 мар 2020
    Последнее редактирование: 13 мар 2020
    AlexProg, Dimon2x, romach и 2 другим нравится это.
  5. mainprofilemail

    mainprofilemail Новичок

    С нами с:
    14 апр 2018
    Сообщения:
    73
    Симпатии:
    0
    @ElisDN спасибо за подробное описание и примеры, сегодня же начну изучать полученную информацию и применять:):D:cool:
     
  6. Valick

    Valick Активный пользователь

    С нами с:
    12 авг 2018
    Сообщения:
    1.911
    Симпатии:
    325
    @mainprofilemail, тут ещё вот в чём загвоздка. Сам по себе класс это ещё не ООП. ООП это когда объекты взаимодействуют. Видите в последнем примере Дмитрий написал что объект ApiClient, передаётся в объект ViberBotHandler в качестве параметра.
     
    mainprofilemail нравится это.
  7. ElisDN

    ElisDN Активный пользователь

    С нами с:
    13 фев 2018
    Сообщения:
    606
    Симпатии:
    128
    Для понимания взаимодействия объектов советую видео по конструкторам и методам и похожий на ваш реальный пример написания геолокатора с продвинутой декомпозицией функциональности с таким же выносом HTTP-клиента.

    И аналогично по примеру написания реального приложения посмотрите процесс написания регистрации пользователей с юнит-тестами.

    Для дальнейшего развития себя как программиста советую изучать хорошие практики.
     
    AlexandrS и mainprofilemail нравится это.
  8. mainprofilemail

    mainprofilemail Новичок

    С нами с:
    14 апр 2018
    Сообщения:
    73
    Симпатии:
    0
    Спасибо, обязательно ознакомлюсь со всеми ссылками и рекомендациями. Вы очень хорошо объясняете и чётко доносите информацию (без воды). Спасибо за помощь и то что наставляете на верный путь.

    Кстати я начал тестировать и не знаю почему но приведение типов в некоторых местах невозможно. Я имею ввиду:

    PHP:
    1. private static array $answers = [];
    2.  
    3. private string $apiToken;
    4. private string $apiUrl;
    Parse error: syntax error, unexpected 'array' (T_ARRAY), expecting function (T_FUNCTION) or const (T_CONST) in /storage/ssd3/422/12855422/public_html/index.php on line 6

    С приведением в строки аналогичная ситуация.

    Ещё здесь:
    PHP:
    1. return json_decode($request, true, 512, JSON_THROW_ON_ERROR);
    Ошибка
    Warning: Use of undefined constant JSON_THROW_ON_ERROR - assumed 'JSON_THROW_ON_ERROR' (this will throw an Error in a future version of PHP) in /storage/ssd3/422/12855422/public_html/index.php on line 35

    Fatal error: Uncaught TypeError: json_decode() expects parameter 4 to be integer, string given in /storage/ssd3/422/12855422/public_html/index.php:35 Stack trace: #0 /storage/ssd3/422/12855422/public_html/index.php(35): json_decode('', true, 512, 'JSON_THROW_ON_E...') #1 /storage/ssd3/422/12855422/public_html/index.php(25): ViberBotHandler->readInput() #2 /storage/ssd3/422/12855422/public_html/index.php(82): ViberBotHandler->handle() #3 {main} thrown in /storage/ssd3/422/12855422/public_html/index.php on line 35

    Пришлось удалить два последних значения

    И ещё у нескольких методов убрал приведение типов потому что ожидался другой тип данных. Также и поступил со свойствами потому что это вызывало ошибку, но только почему не знаю. Можете подсказать?

    Версия php 7.2.18
     
    #8 mainprofilemail, 13 мар 2020
    Последнее редактирование: 13 мар 2020
  9. ElisDN

    ElisDN Активный пользователь

    С нами с:
    13 фев 2018
    Сообщения:
    606
    Симпатии:
    128
    Потому что типы полей добавили только в PHP 7.4
     
    mainprofilemail нравится это.
  10. mainprofilemail

    mainprofilemail Новичок

    С нами с:
    14 апр 2018
    Сообщения:
    73
    Симпатии:
    0
    Понял, спасибо большое.


    Кстати стал смотреть видео по вашей рекомендации, очень хорошие, мне нравится все четко и по делу