Started methods implementation, added login and check auth state methods. Added project license.
parent
c6f0c7d8f7
commit
058bbfbc03
|
@ -1,3 +1,4 @@
|
||||||
.idea
|
.idea
|
||||||
vendor/
|
vendor/
|
||||||
composer.lock
|
composer.lock
|
||||||
|
tests/
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Bohdan Konkevych
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -5,9 +5,13 @@
|
||||||
"homepage": "http://git.devbones.com/Toloka/php-api",
|
"homepage": "http://git.devbones.com/Toloka/php-api",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.4",
|
"php": ">=7.4",
|
||||||
"psr/simple-cache": "^3.0",
|
"psr/http-client": "^1.0",
|
||||||
"psr/log": "^3.0",
|
"psr/simple-cache": "^1.0",
|
||||||
"psr/http-client": "^1.0"
|
"psr/log": "^1.0",
|
||||||
|
"symfony/dom-crawler": "^5.4",
|
||||||
|
"symfony/css-selector": "^5.4",
|
||||||
|
"dflydev/fig-cookies": "^3.0",
|
||||||
|
"symfony/polyfill-php80": "^1.26"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -22,6 +26,9 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"guzzlehttp/guzzle": "^7.5"
|
"cache/array-adapter": "^1.2",
|
||||||
|
"guzzlehttp/guzzle": "^7.5",
|
||||||
|
"monolog/monolog": "^2.8",
|
||||||
|
"cache/filesystem-adapter": "^1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,24 @@
|
||||||
|
|
||||||
namespace Toloka\PhpApi;
|
namespace Toloka\PhpApi;
|
||||||
|
|
||||||
|
use Dflydev\FigCookies\Cookie;
|
||||||
|
use Dflydev\FigCookies\Cookies;
|
||||||
|
use Dflydev\FigCookies\FigRequestCookies;
|
||||||
|
use Dflydev\FigCookies\SetCookies;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
use Psr\Http\Client\ClientInterface as HttpClientInterface;
|
use Psr\Http\Client\ClientInterface as HttpClientInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
use GuzzleHttp\Psr7\Utils;
|
||||||
|
use Symfony\Component\DomCrawler\Crawler;
|
||||||
|
use Toloka\PhpApi\Exception\Auth\AuthException;
|
||||||
|
use Toloka\PhpApi\Exception\Auth\InvalidAuthCredentials;
|
||||||
|
|
||||||
class Client implements ClientInterface {
|
class Client implements ClientInterface {
|
||||||
|
|
||||||
|
const CACHE_KEY_COOKIES = 'cookies';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hurtom Toloka base url.
|
* Hurtom Toloka base url.
|
||||||
*
|
*
|
||||||
|
@ -36,6 +48,11 @@ class Client implements ClientInterface {
|
||||||
*/
|
*/
|
||||||
protected LoggerInterface $logger;
|
protected LoggerInterface $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Cookies
|
||||||
|
*/
|
||||||
|
protected ?Cookies $cookies = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toloka client constructor.
|
* Toloka client constructor.
|
||||||
*
|
*
|
||||||
|
@ -44,14 +61,22 @@ class Client implements ClientInterface {
|
||||||
* @param LoggerInterface|NULL $logger
|
* @param LoggerInterface|NULL $logger
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $base_url = 'https://toloka.to',
|
string $base_url,
|
||||||
HttpClientInterface $httpClient,
|
HttpClientInterface $httpClient,
|
||||||
CacheInterface $cache,
|
CacheInterface $cache,
|
||||||
LoggerInterface $logger
|
LoggerInterface $logger
|
||||||
) {
|
) {
|
||||||
|
$this->base_url = $base_url;
|
||||||
$this->httpClient = $httpClient;
|
$this->httpClient = $httpClient;
|
||||||
$this->cache = $cache;
|
$this->cache = $cache;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
|
|
||||||
|
if ($cache->has(self::CACHE_KEY_COOKIES)) {
|
||||||
|
$persistent_cookies = $cache->get(self::CACHE_KEY_COOKIES);
|
||||||
|
if ($persistent_cookies instanceof Cookies) {
|
||||||
|
$this->cookies = $persistent_cookies;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,14 +90,79 @@ class Client implements ClientInterface {
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function login(string $login, string $password): void {
|
public function login(string $login, string $password): void {
|
||||||
// TODO: Implement login() method.
|
if ($this->isLoggedIn()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$body = http_build_query([
|
||||||
|
'username' => $login,
|
||||||
|
'password' => $password,
|
||||||
|
'autologin' => 'on',
|
||||||
|
'ssl' => 'on',
|
||||||
|
'redirect' => '',
|
||||||
|
'login' => 'Вхід',
|
||||||
|
]);
|
||||||
|
$request = new Request('POST', $this->getBaseUrl() . '/login.php', [
|
||||||
|
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||||
|
], Utils::streamFor($body));
|
||||||
|
$response = $this->httpClient->sendRequest($request);
|
||||||
|
if ($response->getStatusCode() === 302) {
|
||||||
|
// If redirected - this means successful login, can save cookies.
|
||||||
|
$cookies = SetCookies::fromResponse($response);
|
||||||
|
$this->saveCookies($cookies);
|
||||||
|
}
|
||||||
|
elseif ($response->getStatusCode() === 200) {
|
||||||
|
// In case of returned page - try to find an error and throw exception.
|
||||||
|
$crawler = new Crawler($response->getBody()->getContents());
|
||||||
|
$text = $crawler->filter('.forumline .row1 span.gen')->text(NULL, TRUE);
|
||||||
|
if (str_contains($text, 'Такий псевдонім не існує, або не збігається пароль.')) {
|
||||||
|
throw new InvalidAuthCredentials();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AuthException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function saveCookies($data) {
|
||||||
|
if ($data instanceof SetCookies) {
|
||||||
|
$cookies = [];
|
||||||
|
foreach ($data->getAll() as $cookie) {
|
||||||
|
$cookies[] = Cookie::create($cookie->getName(), $cookie->getValue());
|
||||||
|
}
|
||||||
|
$this->cookies = new Cookies($cookies);
|
||||||
|
}
|
||||||
|
elseif ($data instanceof Cookies) {
|
||||||
|
$this->cookies = $data;
|
||||||
|
}
|
||||||
|
$this->cache->set(self::CACHE_KEY_COOKIES, $this->cookies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyRequestCookies(RequestInterface $request): RequestInterface {
|
||||||
|
if (!$this->cookies) {
|
||||||
|
return $request;
|
||||||
|
}
|
||||||
|
foreach ($this->cookies->getAll() as $cookie) {
|
||||||
|
$request = FigRequestCookies::set($request, $cookie);
|
||||||
|
}
|
||||||
|
return $request;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function isLoggedIn(): bool {
|
public function isLoggedIn(): bool {
|
||||||
// TODO: Implement isLoggedIn() method.
|
$request = new Request('GET', $this->getBaseUrl());
|
||||||
|
$request = $this->applyRequestCookies($request);
|
||||||
|
$response = $this->httpClient->sendRequest($request);
|
||||||
|
if ($response->getStatusCode() === 200) {
|
||||||
|
$crawler = new Crawler($response->getBody()->getContents());
|
||||||
|
$menu_links = $crawler->filter('table.navie6fix a');
|
||||||
|
foreach ($menu_links as $menu_link) {
|
||||||
|
// If there is a link to profile, then we authorized.
|
||||||
|
if ($menu_link->textContent === 'Профіль') {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,7 +176,7 @@ class Client implements ClientInterface {
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getTopic(int $id): TopicInterface {
|
public function getTopic(int $id): TopicInterface {
|
||||||
// TODO: Implement getTopic() method.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@ interface ClientInterface {
|
||||||
* Check if user logged in.
|
* Check if user logged in.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
*
|
||||||
|
* @throws \Psr\Http\Client\ClientExceptionInterface
|
||||||
*/
|
*/
|
||||||
public function isLoggedIn(): bool;
|
public function isLoggedIn(): bool;
|
||||||
|
|
||||||
|
@ -48,6 +50,8 @@ interface ClientInterface {
|
||||||
*
|
*
|
||||||
* @return TopicInterface
|
* @return TopicInterface
|
||||||
* Object with populated topic data.
|
* Object with populated topic data.
|
||||||
|
*
|
||||||
|
* @throws \Psr\Http\Client\ClientExceptionInterface
|
||||||
*/
|
*/
|
||||||
public function getTopic(int $id): TopicInterface;
|
public function getTopic(int $id): TopicInterface;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Toloka\PhpApi\Exception\Auth;
|
||||||
|
|
||||||
|
class AuthException extends \Exception {}
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace Toloka\PhpApi\Exception\Auth;
|
namespace Toloka\PhpApi\Exception\Auth;
|
||||||
|
|
||||||
use Toloka\PhpApi\Exception\AuthException;
|
|
||||||
|
|
||||||
class InvalidAuthCredentials extends AuthException {
|
class InvalidAuthCredentials extends AuthException {
|
||||||
|
|
||||||
protected $message = 'Failed to login using given credentials.';
|
protected $message = 'Failed to login using given credentials.';
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace Toloka\PhpApi\Exception\Auth;
|
namespace Toloka\PhpApi\Exception\Auth;
|
||||||
|
|
||||||
use Toloka\PhpApi\Exception\AuthException;
|
|
||||||
|
|
||||||
class TooManyLoginAttempts extends AuthException {
|
class TooManyLoginAttempts extends AuthException {
|
||||||
|
|
||||||
protected $message = 'Too many login attempts performed. Try again later.';
|
protected $message = 'Too many login attempts performed. Try again later.';
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Toloka\PhpApi\Exception;
|
|
||||||
|
|
||||||
abstract class AuthException extends \Exception {}
|
|
Loading…
Reference in New Issue