muxui
@muxui
пишу так, как другие боятся

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

Привет. Написал два класса и хочу выложить на 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? Можно ли объединить эти два класса в один, не нарушив одно из правил ООП? Я не прошу сделать что-то за меня, я был бы благодарен, если скажете недочеты.
  • Вопрос задан
  • 287 просмотров
Решения вопроса 3
kruslan
@kruslan
Это не ООП. От слова вообще. Ну и по самому коду - закопайте. Почитайте книжки про код (Макконнелл, Мартин и ко). Вот это будет очень полезно. Psr не забудьте посмотреть.
Ответ написан
@ivorobioff
Software Engineer
Вы используйте классы всего навсего как пространство имен для ваших функции, с таким же счастьем могли бы создать обычные функции в отдельных namespace-ах и обращаться к ним так же.

Если хотите начать с OOP то забудьте для начала про static.
Ответ написан
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

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

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

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