Что не так в этом классе?

Привет. Написал два класса и хочу выложить на github, но, по классике, хочу, чтобы код был красивый и правильный.
Собственно, это мой первый опыт с ООП, и я хотел-бы услышать критику со стороны экспертов.
Первый класс для обработки Callback API от ВК:
spoiler

class VKCallback {
    private static array $json;

    public static function run() : void {
        if (!empty($json = json_decode(file_get_contents('php://input'), true))) {
            self::$json = $json;
        } else {
            self::close();
        }
    }

    public static function type(string $type, callable $function) : bool {
        return self::$json['type'] == $type ? call_user_func($function, self::$json) : self::close();
    }

    public static function on(string $string, callable $function) : bool {
        return strstr(strtolower(self::$json['object']['message']['text']), strtolower($string)) ? call_user_func($function, self::$json) : false;
    }

    public static function close(string $string = 'ok') : bool {
        echo $string;
        return fastcgi_finish_request();
    }
}


Второй класс - обычный класс для работы с VK API:
spoiler
class VK {
    private static string $access_token;
    private static float $v;

    public static function setup(string $access_token, float $v = 5.101) : void {
        self::$access_token = $access_token;
        self::$v = $v;
    }

    public static function sendMessage(array $array) : array {
        return self::API('messages.send', array_merge($array, ['random_id' => rand(0, 9999999999)]));
    }

    public static function API(string $method, array $array) : array {
        $array['access_token'] = self::$access_token;
        $array['v'] = self::$v;

        return json_decode(
            self::cURL('https://api.vk.com/method/' . $method . '?' . http_build_query($array)),
            true
        );
    }

    private static function cURL(string $url) : string {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0',
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_SSL_VERIFYPEER => false,
        ]);
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }
}

Вот, как я применяю это все дело:
spoiler
use VKCallback as App;

VK::setup('213');

App::run();

App::type('confirmation', function() {
    App::close('8c58af93');
});

App::type('message_new', function(array $json) : void {

    App::on('Пока', function (array $json) : void {
        VK::sendMessage([
            'user_ids' => $json['object']['message']['from_id'],
            'message' => 'Пока',
        ]);
    });

    VK::sendMessage([
        'user_ids' => $json['object']['message']['from_id'],
        'message' => 'Твой ID: ' . $json['object']['message']['from_id'] . ' | Ты сказал: ' . $json['object']['message']['text'],
    ]);
    App::close();
});

Собственно, это все заработало с первого раза, на что удивлению моему не было предела.
Но, повторюсь, хотелось-бы достичь полного перфекционизма. Правильно ли сделана вообще структура класса VKCallback? Можно ли объединить эти два класса в один, не нарушив одно из правил ООП? Я не прошу сделать что-то за меня, я был бы благодарен, если скажете недочеты.
  • Вопрос задан
  • 314 просмотров
Решения вопроса 1
HemulGM
@HemulGM
Delphi Developer, сис. админ
Согласен с другими ответчиками, это не совсем ООП. Лишь один принцип ООП используется - класс и то как просто namespace.
Вот примерно как ООП и VK API в Delphi:

Класс - группировка для методов Auth

TAuth = class(TVKEntity)
    /// <summary>
    /// Проверяет правильность введённого номера (возможность его использования для регистрации или авторизации).
    /// </summary>
    /// <param name="Phone">номер телефона регистрируемого пользователя</param>
    /// <param name="ClientId">идентификатор Вашего приложения</param>
    /// <param name="ClientSecret">секретный ключ приложения, доступный в разделе редактирования приложения</param>
    /// <param name="AuthByPhone">True — проверить правильность номера для авторизации,
    ///                           а не для регистрации нового аккаунта. По умолчанию: False.</param>
    function CheckPhone(Phone: string; ClientId, ClientSecret: string; AuthByPhone: Boolean = False): Boolean; overload;
    /// <summary>
    /// Проверяет правильность введённого номера (возможность его использования для регистрации или авторизации).
    /// С указанием текущих данных приложения ClientId и ClientSecret
    /// </summary>
    /// <param name="Phone">номер телефона регистрируемого пользователя</param>
    /// <param name="AuthByPhone">True — проверить правильность номера для авторизации,
    ///                           а не для регистрации нового аккаунта. По умолчанию: False.</param>
    function CheckPhone(Phone: string; AuthByPhone: Boolean = False): Boolean; overload;
    /// <summary>
    /// https://vk.com/dev/auth.restore
    /// </summary>
    function Restore(Phone, LastName: string): TResponse;
  end;


Класс - асинхронный обработчик запросов

TVKHandler = class
    const
      RequestLimit = 3; //Round(1000 / 3) + 10; //задержка между запросами 3 запроса в секунду + 10 мс страховка
  private
    FStartRequest: Cardinal;
    FRequests: Integer;
    FRESTClient: TRESTClient;
    FOnConfirm: TOnConfirm;
    FOnError: TOnVKError;
    FOnLog: TOnLog;
    FUseServiceKeyOnly: Boolean;
    FOwner: TObject;
    FOnCaptcha: TOnCaptcha;
    function DoConfirm(Answer: string): Boolean;
    procedure ProcError(Code: Integer; Text: string = ''); overload;
    procedure ProcError(E: Exception); overload;
    procedure ProcError(Msg: string); overload;
    procedure SetOnConfirm(const Value: TOnConfirm);
    procedure SetOnError(const Value: TOnVKError);
    procedure FLog(const Value: string);
    procedure SetOnLog(const Value: TOnLog);
    procedure SetUseServiceKeyOnly(const Value: Boolean);
    procedure SetOwner(const Value: TObject);
    procedure SetOnCaptcha(const Value: TOnCaptcha);
  public
    constructor Create(AOwner: TObject);
    destructor Destroy; override;
    function AskCaptcha(Sender: TObject; const CaptchaImg: string; var Answer: string): Boolean;
    function Execute(Request: string; Params: TParams): TResponse; overload;
    function Execute(Request: string; Param: TParam): TResponse; overload;
    function Execute(Request: string): TResponse; overload;
    function Execute(Request: TRESTRequest; FreeRequset: Boolean = False): TResponse; overload;
    property RESTClient: TRESTClient read FRESTClient;
    property OnConfirm: TOnConfirm read FOnConfirm write SetOnConfirm;
    property OnCaptcha: TOnCaptcha read FOnCaptcha write SetOnCaptcha;
    property OnError: TOnVKError read FOnError write SetOnError;
    property OnLog: TOnLog read FOnLog write SetOnLog;
    property UseServiceKeyOnly: Boolean read FUseServiceKeyOnly write SetUseServiceKeyOnly;
    property Owner: TObject read FOwner write SetOwner;
  end;


Класс - LongPoll сервера, работающий в отдельном потоке

TLongPollServer = class
   ...
    function Start: Boolean;
    procedure Stop;
    constructor Create; overload;
    constructor Create(AClient: TRESTClient; AMethod: string; AParams: TParams); overload;
    destructor Destroy; override;
    property OnError: TOnVKError read FOnError write SetOnError;
    property Interval: Integer read FInterval write SetInterval;
    property GroupID: string read FGroupID write SetGroupID;
    property OnUpdate: TOnLongPollServerUpdate read FOnUpdate write SetOnUpdate;
    property Client: TCustomRESTClient read GetClient write SetClient;
    property Method: string read FMethod write SetMethod;
    property Params: TParams read FParams write SetParams;
  end;


Основной класс - невизуальный компонент для разработчиков

TCustomVK = class(TComponent)
  private
    FOAuth2Authenticator: TOAuth2Authenticator;
    FOnLogin: TOnLogin;
    FPermissionsList: TPermissions;
    FGroupLongPollServers: TGroupLongPollServers;
    FAuthForm: TFormOAuth2;
    FAppID: string;
    FAppKey: string;
    FEndPoint: string;
    FHandler: TVKHandler;
    FBaseURL: string;
    FAPIVersion: string;
    FAccount: TAccount;
    FAuth: TAuth;
    FServiceKey: string;
    FUseServiceKeyOnly: Boolean;
    FIsLogin: Boolean;
    FOnError: TOnVKError;
    FOnLog: TOnLog;
    FOnErrorLogin: TOnVKError;
    FChangePasswordHash: string;
    FUsers: TUsers;
    FOnCaptcha: TOnCaptcha;
    FOnConfirm: TOnConfirm;
    FOnAuth: TOnAuth;
    function GetPermissions: string;
    procedure FAskCaptcha(Sender: TObject; const CaptchaImg: string; var Answer: string);
    procedure FAfterRedirect(const AURL: string; var DoCloseWebView: boolean);
    procedure FAuthError(const AURL: string; AStatusCode: Integer; var Cancel: WordBool);
    procedure FLog(Sender: TObject; const Value: string);
    procedure FVKError(Sender: TObject; Code: Integer; Text: string);
    procedure SetOnLogin(const Value: TOnLogin);
    procedure SetPermissionsList(const Value: TPermissions);
    procedure DoLogin;
    procedure SetAppID(const Value: string);
    procedure SetAppKey(const Value: string);
    procedure SetEndPoint(const Value: string);
    procedure SetPermissions(const Value: string);
    procedure SetHandler(const Value: TVKHandler);
    procedure SetBaseURL(const Value: string);
    procedure SetAPIVersion(const Value: string);
    procedure SetServiceKey(const Value: string);
    procedure SetUseServiceKeyOnly(const Value: Boolean);
    procedure SetOnError(const Value: TOnVKError);
    procedure SetOnLog(const Value: TOnLog);
    procedure SetOnErrorLogin(const Value: TOnVKError);
    procedure SetOnCaptcha(const Value: TOnCaptcha);
    procedure SetOnConfirm(const Value: TOnConfirm);
    procedure SetOnAuth(const Value: TOnAuth);
    procedure DoAuth(const AURL: string);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DoLog(Sender: TObject; Text: string);
    procedure Login(AParentWindow: TWinControl = nil);
    procedure CallMethod(MethodName: string; Params: TParams; Callback: TCallMethodCallback = nil);
    procedure GroupLongPollServerStart(GroupID: string);
    procedure GroupLongPollServerStop(GroupID: string);
    /// <summary>
    /// Универсальный метод, который позволяет запускать последовательность других методов, сохраняя и фильтруя промежуточные результаты.
    /// https://vk.com/dev/execute
    /// </summary>
    /// <param name="Code">код алгоритма в VKScript - формате, похожем на JavaSсript или ActionScript (предполагается совместимость с ECMAScript). Алгоритм должен завершаться командой return %выражение%. Операторы должны быть разделены точкой с запятой. </param>
    function Execute(Code: string): TResponse;
    property PermissionsList: TPermissions read FPermissionsList write SetPermissionsList;
    //Группы методов
    property Account: TAccount read FAccount;
    property Auth: TAuth read FAuth;
    property Users: TUsers read FUsers;
    //
    property AppID: string read FAppID write SetAppID;
    property AppKey: string read FAppKey write SetAppKey;
    property EndPoint: string read FEndPoint write SetEndPoint;
    property Permissions: string read GetPermissions write SetPermissions;
    property Handler: TVKHandler read FHandler write SetHandler;
    property APIVersion: string read FAPIVersion write SetAPIVersion;
    property BaseURL: string read FBaseURL write SetBaseURL;
    property ServiceKey: string read FServiceKey write SetServiceKey;
    property UseServiceKeyOnly: Boolean read FUseServiceKeyOnly write SetUseServiceKeyOnly;
    property IsLogin: Boolean read FIsLogin;
    property ChangePasswordHash: string read FChangePasswordHash;
    //
    property OnLogin: TOnLogin read FOnLogin write SetOnLogin;
    property OnError: TOnVKError read FOnError write SetOnError;
    property OnErrorLogin: TOnVKError read FOnErrorLogin write SetOnErrorLogin;
    property OnLog: TOnLog read FOnLog write SetOnLog;
    property OnCaptcha: TOnCaptcha read FOnCaptcha write SetOnCaptcha;
    property OnConfirm: TOnConfirm read FOnConfirm write SetOnConfirm;
    property OnAuth: TOnAuth read FOnAuth write SetOnAuth;
  end;


И конечно, все представления и структуры в виде классов

5e1c425e0b2f5671276122.png

Ну и вот работа такого класса в designtime

5e1c404cea6dd998958891.png

И код равный твоему

5e1c446dea3d6273622206.png

Конечно не образец идеального кода... Но что есть
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы