mirror of
https://github.com/Poniverse/Pony.fm.git
synced 2024-11-25 06:27:59 +01:00
#25: Rewrote authentication to use the Poniverse API PHP library.
This commit is contained in:
parent
e30400cab0
commit
ab9dd7a1eb
54 changed files with 444 additions and 4358 deletions
|
@ -173,6 +173,9 @@ class GenerateTrackFilesCommand extends CommandBase
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This ensures that any updates to the track record, like from parsed
|
||||||
|
// tags, are reflected in the command's response.
|
||||||
|
$this->track = $this->track->fresh();
|
||||||
return CommandResponse::succeed([
|
return CommandResponse::succeed([
|
||||||
'id' => $this->track->id,
|
'id' => $this->track->id,
|
||||||
'name' => $this->track->name,
|
'name' => $this->track->name,
|
||||||
|
|
|
@ -111,7 +111,7 @@ class ParseTrackTagsCommand extends CommandBase
|
||||||
$vars = $track->getAttributes();
|
$vars = $track->getAttributes();
|
||||||
|
|
||||||
foreach ($vars as $key => $value) {
|
foreach ($vars as $key => $value) {
|
||||||
if ($value == null) {
|
if ($value === null) {
|
||||||
unset($track->{"$key"});
|
unset($track->{"$key"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pony.fm - A community for pony fan music.
|
|
||||||
* Copyright (C) 2015 Peter Deltchev
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Exceptions;
|
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvalidAccessTokenException
|
|
||||||
* @package Poniverse\Ponyfm
|
|
||||||
*
|
|
||||||
* This exception indicates that an access token we attempted to introspect
|
|
||||||
* through the Poniverse API is expired or otherwise unusable.
|
|
||||||
*/
|
|
||||||
class InvalidAccessTokenException extends AccessDeniedHttpException
|
|
||||||
{
|
|
||||||
};
|
|
|
@ -20,12 +20,16 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Http\Controllers;
|
namespace Poniverse\Ponyfm\Http\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Input;
|
||||||
|
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||||
|
use Log;
|
||||||
|
use Poniverse\Lib\Client;
|
||||||
use Poniverse\Ponyfm\Models\User;
|
use Poniverse\Ponyfm\Models\User;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Config;
|
use Config;
|
||||||
use DB;
|
use DB;
|
||||||
use Illuminate\Support\Facades\Request;
|
use Request;
|
||||||
use Poniverse;
|
|
||||||
use Redirect;
|
use Redirect;
|
||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
|
@ -34,14 +38,16 @@ class AuthController extends Controller
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->poniverse = new Poniverse(Config::get('poniverse.client_id'), Config::get('poniverse.secret'));
|
$this->poniverse = new Client(config('poniverse.client_id'), config('poniverse.secret'), new \GuzzleHttp\Client());
|
||||||
$this->poniverse->setRedirectUri(action('AuthController@getOAuth'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLogin()
|
public function getLogin()
|
||||||
{
|
{
|
||||||
if (Auth::guest()) {
|
if (Auth::guest()) {
|
||||||
return Redirect::to($this->poniverse->getAuthenticationUrl('login'));
|
return Redirect::to(
|
||||||
|
$this->poniverse
|
||||||
|
->getOAuthProvider(['redirectUri' => action('AuthController@getOAuth')])
|
||||||
|
->getAuthorizationUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Redirect::to('/');
|
return Redirect::to('/');
|
||||||
|
@ -50,25 +56,22 @@ class AuthController extends Controller
|
||||||
public function postLogout()
|
public function postLogout()
|
||||||
{
|
{
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
|
|
||||||
return Redirect::to('/');
|
return Redirect::to('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOAuth()
|
public function getOAuth()
|
||||||
{
|
{
|
||||||
$code = $this->poniverse->getClient()->getAccessToken(
|
$oauthProvider = $this->poniverse->getOAuthProvider();
|
||||||
Config::get('poniverse.urls')['token'],
|
|
||||||
'authorization_code',
|
try {
|
||||||
[
|
$accessToken = $oauthProvider->getAccessToken('authorization_code', [
|
||||||
'code' => Request::query('code'),
|
'code' => Request::query('code'),
|
||||||
'redirect_uri' => action('AuthController@getOAuth')
|
'redirect_uri' => action('AuthController@getOAuth')
|
||||||
]
|
]);
|
||||||
);
|
$this->poniverse->setAccessToken($accessToken);
|
||||||
|
$resourceOwner = $oauthProvider->getResourceOwner($accessToken);
|
||||||
if ($code['code'] != 200) {
|
} catch (IdentityProviderException $e) {
|
||||||
if ($code['code'] == 400 && $code['result']['error_description'] == 'The authorization code has expired' && !isset($this->request['login_attempt'])) {
|
Log::error($e);
|
||||||
return Redirect::to($this->poniverse->getAuthenticationUrl('login_attempt'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Redirect::to('/')->with(
|
return Redirect::to('/')->with(
|
||||||
'message',
|
'message',
|
||||||
|
@ -76,47 +79,44 @@ class AuthController extends Controller
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->poniverse->setAccessToken($code['result']['access_token']);
|
/** @var \Poniverse\Lib\Entity\Poniverse\User $poniverseUser */
|
||||||
$poniverseUser = $this->poniverse->getUser();
|
$poniverseUser = $resourceOwner;
|
||||||
$token = DB::table('oauth2_tokens')->where('external_user_id', '=', $poniverseUser['id'])->where(
|
|
||||||
'service',
|
|
||||||
'=',
|
|
||||||
'poniverse'
|
|
||||||
)->first();
|
|
||||||
|
|
||||||
$setData = [
|
$token = DB::table('oauth2_tokens')
|
||||||
'access_token' => $code['result']['access_token'],
|
->where('external_user_id', '=', $poniverseUser->id)
|
||||||
'expires' => date('Y-m-d H:i:s', strtotime("+".$code['result']['expires_in']." Seconds", time())),
|
->where('service', '=', 'poniverse')
|
||||||
'type' => $code['result']['token_type'],
|
->first();
|
||||||
];
|
|
||||||
|
|
||||||
if (isset($code['result']['refresh_token']) && !empty($code['result']['refresh_token'])) {
|
$setData = [
|
||||||
$setData['refresh_token'] = $code['result']['refresh_token'];
|
'access_token' => $accessToken,
|
||||||
}
|
'expires' => Carbon::createFromTimestampUTC($accessToken->getExpires()),
|
||||||
|
'type' => 'Bearer',
|
||||||
|
];
|
||||||
|
|
||||||
if ($token) {
|
if (!empty($accessToken->getRefreshToken())) {
|
||||||
//User already exists, update access token and refresh token if provided.
|
$setData['refresh_token'] = $accessToken->getRefreshToken();
|
||||||
DB::table('oauth2_tokens')->where('id', '=', $token->id)->update($setData);
|
}
|
||||||
|
|
||||||
return $this->loginRedirect(User::find($token->user_id));
|
if ($token) {
|
||||||
}
|
//User already exists, update access token and refresh token if provided.
|
||||||
|
DB::table('oauth2_tokens')->where('id', '=', $token->id)->update($setData);
|
||||||
|
return $this->loginRedirect(User::find($token->user_id));
|
||||||
|
}
|
||||||
|
|
||||||
// Check by login name to see if they already have an account
|
// Check by login name to see if they already have an account
|
||||||
$user = User::findOrCreate($poniverseUser['username'], $poniverseUser['display_name'], $poniverseUser['email']);
|
$user = User::findOrCreate($poniverseUser->username, $poniverseUser->display_name, $poniverseUser->email);
|
||||||
|
|
||||||
if ($user->wasRecentlyCreated) {
|
|
||||||
return $this->loginRedirect($user);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to insert a new token row :O
|
|
||||||
|
|
||||||
|
if (!$user->wasRecentlyCreated) {
|
||||||
|
// We need to insert a new token row :O
|
||||||
$setData['user_id'] = $user->id;
|
$setData['user_id'] = $user->id;
|
||||||
$setData['external_user_id'] = $poniverseUser['id'];
|
$setData['external_user_id'] = $poniverseUser->id;
|
||||||
$setData['service'] = 'poniverse';
|
$setData['service'] = 'poniverse';
|
||||||
|
|
||||||
DB::table('oauth2_tokens')->insert($setData);
|
DB::table('oauth2_tokens')->insert($setData);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->loginRedirect($user);
|
|
||||||
|
return $this->loginRedirect($user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function loginRedirect($user, $rememberMe = true)
|
protected function loginRedirect($user, $rememberMe = true)
|
||||||
|
|
|
@ -21,18 +21,19 @@
|
||||||
namespace Poniverse\Ponyfm\Http\Middleware;
|
namespace Poniverse\Ponyfm\Http\Middleware;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use GuzzleHttp;
|
use Illuminate\Contracts\Auth\Guard;
|
||||||
use Illuminate\Auth\Guard;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Session\Store;
|
use Illuminate\Session\Store;
|
||||||
|
use League\OAuth2\Client\Token\AccessToken;
|
||||||
use Poniverse;
|
use Poniverse;
|
||||||
|
use Poniverse\Lib\Client;
|
||||||
use Poniverse\Ponyfm\Models\User;
|
use Poniverse\Ponyfm\Models\User;
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
|
||||||
class AuthenticateOAuth
|
class AuthenticateOAuth
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Poniverse
|
* @var Client
|
||||||
*/
|
*/
|
||||||
private $poniverse;
|
private $poniverse;
|
||||||
|
|
||||||
|
@ -46,9 +47,9 @@ class AuthenticateOAuth
|
||||||
*/
|
*/
|
||||||
private $session;
|
private $session;
|
||||||
|
|
||||||
public function __construct(Poniverse $poniverse, Guard $auth, Store $session)
|
public function __construct(Guard $auth, Store $session)
|
||||||
{
|
{
|
||||||
$this->poniverse = $poniverse;
|
$this->poniverse = new Client(config('poniverse.client_id'), config('poniverse.secret'), new \GuzzleHttp\Client());
|
||||||
$this->auth = $auth;
|
$this->auth = $auth;
|
||||||
$this->session = $session;
|
$this->session = $session;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +61,7 @@ class AuthenticateOAuth
|
||||||
* @param \Closure $next
|
* @param \Closure $next
|
||||||
* @param string $requiredScope
|
* @param string $requiredScope
|
||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws \OAuth2\Exception
|
* @throws Poniverse\Lib\Errors\ApiException
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next, $requiredScope)
|
public function handle(Request $request, Closure $next, $requiredScope)
|
||||||
{
|
{
|
||||||
|
@ -68,7 +69,7 @@ class AuthenticateOAuth
|
||||||
$accessToken = $this->determineAccessToken($request, false);
|
$accessToken = $this->determineAccessToken($request, false);
|
||||||
|
|
||||||
// check that access token is valid at Poniverse.net
|
// check that access token is valid at Poniverse.net
|
||||||
$accessTokenInfo = $this->poniverse->getAccessTokenInfo($accessToken);
|
$accessTokenInfo = $this->poniverse->poniverse()->accessTokenInfo()->introspect($accessToken);
|
||||||
|
|
||||||
if (!$accessTokenInfo->getIsActive()) {
|
if (!$accessTokenInfo->getIsActive()) {
|
||||||
throw new AccessDeniedHttpException('This access token is expired or invalid!');
|
throw new AccessDeniedHttpException('This access token is expired or invalid!');
|
||||||
|
@ -79,13 +80,14 @@ class AuthenticateOAuth
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log in as the given user, creating the account if necessary.
|
// Log in as the given user, creating the account if necessary.
|
||||||
$this->poniverse->setAccessToken($accessToken);
|
$this->poniverse->setAccessToken(new AccessToken(['access_token' => $accessToken]));
|
||||||
$this->session->put('api_client_id', $accessTokenInfo->getClientId());
|
$this->session->put('api_client_id', $accessTokenInfo->getClientId());
|
||||||
|
|
||||||
$poniverseUser = $this->poniverse->getUser();
|
/** @var Poniverse\Lib\Entity\Poniverse\User $poniverseUser */
|
||||||
|
$poniverseUser = $this->poniverse->getOAuthProvider()->getResourceOwner($accessToken);
|
||||||
|
|
||||||
$user = User::findOrCreate($poniverseUser['username'], $poniverseUser['display_name'], $poniverseUser['email']);
|
$user = User::findOrCreate($poniverseUser->username, $poniverseUser->display_name, $poniverseUser->email);
|
||||||
$this->auth->onceUsingId($user);
|
$this->auth->setUser($user);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,243 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pony.fm - A community for pony fan music.
|
|
||||||
* Copyright (C) 2015 Peter Deltchev
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Poniverse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AccessTokenInfo
|
|
||||||
*
|
|
||||||
* A container for the fields in the draft OAuth Token Introspection proposal.
|
|
||||||
*
|
|
||||||
* @link https://tools.ietf.org/html/draft-richer-oauth-introspection-06
|
|
||||||
* @package Poniverse
|
|
||||||
*/
|
|
||||||
class AccessTokenInfo
|
|
||||||
{
|
|
||||||
protected $token;
|
|
||||||
|
|
||||||
protected $isActive;
|
|
||||||
protected $expiresAt;
|
|
||||||
protected $issuedAt;
|
|
||||||
protected $scopes;
|
|
||||||
protected $clientId;
|
|
||||||
protected $sub;
|
|
||||||
protected $userId;
|
|
||||||
protected $intendedAudience;
|
|
||||||
protected $issuer;
|
|
||||||
protected $tokenType;
|
|
||||||
|
|
||||||
public function __construct($accessToken)
|
|
||||||
{
|
|
||||||
$this->token = $accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getToken()
|
|
||||||
{
|
|
||||||
return $this->token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function getIsActive()
|
|
||||||
{
|
|
||||||
return $this->isActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param bool $isActive
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setIsActive($isActive)
|
|
||||||
{
|
|
||||||
$this->isActive = $isActive;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getExpiresAt()
|
|
||||||
{
|
|
||||||
return $this->expiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $expiresAt
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setExpiresAt($expiresAt)
|
|
||||||
{
|
|
||||||
$this->expiresAt = $expiresAt;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getIssuedAt()
|
|
||||||
{
|
|
||||||
return $this->issuedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $issuedAt
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setIssuedAt($issuedAt)
|
|
||||||
{
|
|
||||||
$this->issuedAt = $issuedAt;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getScopes()
|
|
||||||
{
|
|
||||||
return $this->scopes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array|string $scopes
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setScopes($scopes)
|
|
||||||
{
|
|
||||||
if (is_array($scopes)) {
|
|
||||||
$this->scopes = $scopes;
|
|
||||||
} else {
|
|
||||||
$this->scopes = mb_split(' ', $scopes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getClientId()
|
|
||||||
{
|
|
||||||
return $this->clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $clientId
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setClientId($clientId)
|
|
||||||
{
|
|
||||||
$this->clientId = $clientId;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getSub()
|
|
||||||
{
|
|
||||||
return $this->sub;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $sub
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setSub($sub)
|
|
||||||
{
|
|
||||||
$this->sub = $sub;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getUserId()
|
|
||||||
{
|
|
||||||
return $this->userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $userId
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setUserId($userId)
|
|
||||||
{
|
|
||||||
$this->userId = $userId;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getIntendedAudience()
|
|
||||||
{
|
|
||||||
return $this->intendedAudience;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $intendedAudience
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setIntendedAudience($intendedAudience)
|
|
||||||
{
|
|
||||||
$this->intendedAudience = $intendedAudience;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getIssuer()
|
|
||||||
{
|
|
||||||
return $this->issuer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $issuer
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setIssuer($issuer)
|
|
||||||
{
|
|
||||||
$this->issuer = $issuer;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getTokenType()
|
|
||||||
{
|
|
||||||
return $this->tokenType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $tokenType
|
|
||||||
* @return AccessTokenInfo
|
|
||||||
*/
|
|
||||||
public function setTokenType($tokenType)
|
|
||||||
{
|
|
||||||
$this->tokenType = $tokenType;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pony.fm - A community for pony fan music.
|
|
||||||
* Copyright (C) 2015 Peter Deltchev
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use OAuth2\Client;
|
|
||||||
use Poniverse\Ponyfm\Exceptions\InvalidAccessTokenException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Poniverse
|
|
||||||
*
|
|
||||||
* Just for the sake of being sane without an autoloader
|
|
||||||
* this class is going to be a simple flat api class.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Poniverse
|
|
||||||
{
|
|
||||||
protected $clientId;
|
|
||||||
protected $clientSecret;
|
|
||||||
protected $accessToken;
|
|
||||||
protected $redirectUri;
|
|
||||||
protected $urls;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var OAuth2\Client
|
|
||||||
*/
|
|
||||||
protected $client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the class
|
|
||||||
*
|
|
||||||
* @param string $clientId
|
|
||||||
* @param string $clientSecret
|
|
||||||
* @param string $accessToken
|
|
||||||
*/
|
|
||||||
public function __construct($clientId, $clientSecret, $accessToken = '')
|
|
||||||
{
|
|
||||||
$this->urls = Config::get('poniverse.urls');
|
|
||||||
|
|
||||||
$this->clientId = $clientId;
|
|
||||||
$this->clientSecret = $clientSecret;
|
|
||||||
$this->accessToken = $accessToken;
|
|
||||||
|
|
||||||
//Setup Dependencies
|
|
||||||
$this->setupOAuth2();
|
|
||||||
$this->setupHttpful();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function setupOAuth2()
|
|
||||||
{
|
|
||||||
require_once('oauth2/Client.php');
|
|
||||||
require_once('oauth2/GrantType/IGrantType.php');
|
|
||||||
require_once('oauth2/GrantType/AuthorizationCode.php');
|
|
||||||
|
|
||||||
$this->client = new \OAuth2\Client($this->clientId, $this->clientSecret);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function setupHttpful()
|
|
||||||
{
|
|
||||||
require_once('autoloader.php');
|
|
||||||
$autoloader = new SplClassLoader('Httpful', __DIR__."/httpful/src/");
|
|
||||||
$autoloader->register();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setAccessToken($accessToken)
|
|
||||||
{
|
|
||||||
$this->accessToken = $accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAuthenticationUrl($state)
|
|
||||||
{
|
|
||||||
return $this->client->getAuthenticationUrl($this->urls['auth'], $this->redirectUri, ['state' => $state]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setRedirectUri($redirectUri)
|
|
||||||
{
|
|
||||||
$this->redirectUri = $redirectUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the OAuth2 Client
|
|
||||||
*
|
|
||||||
* @return \OAuth2\Client
|
|
||||||
*/
|
|
||||||
public function getClient()
|
|
||||||
{
|
|
||||||
return $this->client;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets data about the currently logged in user
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getUser()
|
|
||||||
{
|
|
||||||
$data = \Httpful\Request::get($this->urls['api'] . "users?access_token=" . $this->accessToken);
|
|
||||||
|
|
||||||
$result = $data->addHeader('Accept', 'application/json')->send();
|
|
||||||
|
|
||||||
return json_decode($result, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets information about the given access token.
|
|
||||||
*
|
|
||||||
* @link https://tools.ietf.org/html/draft-richer-oauth-introspection-06
|
|
||||||
*
|
|
||||||
* @param $accessTokenToIntrospect
|
|
||||||
* @return \Poniverse\AccessTokenInfo
|
|
||||||
* @throws InvalidAccessTokenException
|
|
||||||
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
|
|
||||||
*/
|
|
||||||
public function getAccessTokenInfo($accessTokenToIntrospect)
|
|
||||||
{
|
|
||||||
$token = $this->client->getAccessToken(
|
|
||||||
Config::get('poniverse.urls.token'),
|
|
||||||
Client::GRANT_TYPE_CLIENT_CREDENTIALS,
|
|
||||||
[]
|
|
||||||
)['result']['access_token'];
|
|
||||||
|
|
||||||
|
|
||||||
$request = \Httpful\Request::post($this->urls['api']. 'meta/introspect?token='.$accessTokenToIntrospect);
|
|
||||||
|
|
||||||
/** @var Httpful\Response $result */
|
|
||||||
$result = $request
|
|
||||||
->addHeader('Accept', 'application/json')
|
|
||||||
->addHeader('Authorization', 'Bearer '.$token)
|
|
||||||
->send();
|
|
||||||
$data = json_decode($result, true);
|
|
||||||
|
|
||||||
if (404 === $result->code) {
|
|
||||||
throw new InvalidAccessTokenException('This access token is expired or invalid!');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (200 !== $result->code) {
|
|
||||||
throw new \Symfony\Component\HttpKernel\Exception\HttpException(500, 'An unknown error occurred while contacting the Poniverse API.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$tokenInfo = new \Poniverse\AccessTokenInfo($accessTokenToIntrospect);
|
|
||||||
$tokenInfo
|
|
||||||
->setIsActive($data['active'])
|
|
||||||
->setScopes($data['scope'])
|
|
||||||
->setClientId($data['client_id']);
|
|
||||||
|
|
||||||
return $tokenInfo;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SplClassLoader implementation that implements the technical interoperability
|
|
||||||
* standards for PHP 5.3 namespaces and class names.
|
|
||||||
*
|
|
||||||
* http://groups.google.com/group/php-standards/web/final-proposal
|
|
||||||
*
|
|
||||||
* // Example which loads classes for the Doctrine Common package in the
|
|
||||||
* // Doctrine\Common namespace.
|
|
||||||
* $classLoader = new SplClassLoader('Doctrine\Common', '/path/to/doctrine');
|
|
||||||
* $classLoader->register();
|
|
||||||
*
|
|
||||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
|
||||||
* @author Roman S. Borschel <roman@code-factory.org>
|
|
||||||
* @author Matthew Weier O'Phinney <matthew@zend.com>
|
|
||||||
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
|
|
||||||
* @author Fabien Potencier <fabien.potencier@symfony-project.org>
|
|
||||||
*/
|
|
||||||
class SplClassLoader
|
|
||||||
{
|
|
||||||
private $_fileExtension = '.php';
|
|
||||||
private $_namespace;
|
|
||||||
private $_includePath;
|
|
||||||
private $_namespaceSeparator = '\\';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new <tt>SplClassLoader</tt> that loads classes of the
|
|
||||||
* specified namespace.
|
|
||||||
*
|
|
||||||
* @param string $ns The namespace to use.
|
|
||||||
* @param string $includePath
|
|
||||||
*/
|
|
||||||
public function __construct($ns = null, $includePath = null)
|
|
||||||
{
|
|
||||||
$this->_namespace = $ns;
|
|
||||||
$this->_includePath = $includePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the namespace separator used by classes in the namespace of this class loader.
|
|
||||||
*
|
|
||||||
* @param string $sep The separator to use.
|
|
||||||
*/
|
|
||||||
public function setNamespaceSeparator($sep)
|
|
||||||
{
|
|
||||||
$this->_namespaceSeparator = $sep;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the namespace seperator used by classes in the namespace of this class loader.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getNamespaceSeparator()
|
|
||||||
{
|
|
||||||
return $this->_namespaceSeparator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the base include path for all class files in the namespace of this class loader.
|
|
||||||
*
|
|
||||||
* @param string $includePath
|
|
||||||
*/
|
|
||||||
public function setIncludePath($includePath)
|
|
||||||
{
|
|
||||||
$this->_includePath = $includePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the base include path for all class files in the namespace of this class loader.
|
|
||||||
*
|
|
||||||
* @return string $includePath
|
|
||||||
*/
|
|
||||||
public function getIncludePath()
|
|
||||||
{
|
|
||||||
return $this->_includePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the file extension of class files in the namespace of this class loader.
|
|
||||||
*
|
|
||||||
* @param string $fileExtension
|
|
||||||
*/
|
|
||||||
public function setFileExtension($fileExtension)
|
|
||||||
{
|
|
||||||
$this->_fileExtension = $fileExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the file extension of class files in the namespace of this class loader.
|
|
||||||
*
|
|
||||||
* @return string $fileExtension
|
|
||||||
*/
|
|
||||||
public function getFileExtension()
|
|
||||||
{
|
|
||||||
return $this->_fileExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Installs this class loader on the SPL autoload stack.
|
|
||||||
*/
|
|
||||||
public function register()
|
|
||||||
{
|
|
||||||
spl_autoload_register([$this, 'loadClass']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uninstalls this class loader from the SPL autoloader stack.
|
|
||||||
*/
|
|
||||||
public function unregister()
|
|
||||||
{
|
|
||||||
spl_autoload_unregister([$this, 'loadClass']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the given class or interface.
|
|
||||||
*
|
|
||||||
* @param string $className The name of the class to load.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function loadClass($className)
|
|
||||||
{
|
|
||||||
if (null === $this->_namespace || $this->_namespace.$this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace.$this->_namespaceSeparator))) {
|
|
||||||
$fileName = '';
|
|
||||||
$namespace = '';
|
|
||||||
if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) {
|
|
||||||
$namespace = substr($className, 0, $lastNsPos);
|
|
||||||
$className = substr($className, $lastNsPos + 1);
|
|
||||||
$fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
|
|
||||||
}
|
|
||||||
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension;
|
|
||||||
|
|
||||||
require($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
4
app/Library/Poniverse/httpful/.gitignore
vendored
4
app/Library/Poniverse/httpful/.gitignore
vendored
|
@ -1,4 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
composer.lock
|
|
||||||
vendor
|
|
||||||
downloads
|
|
|
@ -1,5 +0,0 @@
|
||||||
language: php
|
|
||||||
before_script: cd tests
|
|
||||||
php:
|
|
||||||
- 5.3
|
|
||||||
- 5.4
|
|
|
@ -1,7 +0,0 @@
|
||||||
Copyright (c) 2012 Nate Good <me@nategood.com>
|
|
||||||
|
|
||||||
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.
|
|
|
@ -1,150 +0,0 @@
|
||||||
# Httpful
|
|
||||||
|
|
||||||
[![Build Status](https://secure.travis-ci.org/nategood/httpful.png?branch=master)](http://travis-ci.org/nategood/httpful)
|
|
||||||
|
|
||||||
[Httpful](http://phphttpclient.com) is a simple Http Client library for PHP 5.3+. There is an emphasis of readability, simplicity, and flexibility – basically provide the features and flexibility to get the job done and make those features really easy to use.
|
|
||||||
|
|
||||||
Features
|
|
||||||
|
|
||||||
- Readable HTTP Method Support (GET, PUT, POST, DELETE, HEAD, PATCH and OPTIONS)
|
|
||||||
- Custom Headers
|
|
||||||
- Automatic "Smart" Parsing
|
|
||||||
- Automatic Payload Serialization
|
|
||||||
- Basic Auth
|
|
||||||
- Client Side Certificate Auth
|
|
||||||
- Request "Templates"
|
|
||||||
|
|
||||||
# Sneak Peak
|
|
||||||
|
|
||||||
Here's something to whet your appetite. Search the twitter API for tweets containing "#PHP". Include a trivial header for the heck of it. Notice that the library automatically interprets the response as JSON (can override this if desired) and parses it as an array of objects.
|
|
||||||
|
|
||||||
$url = "http://search.twitter.com/search.json?q=" . urlencode('#PHP');
|
|
||||||
$response = Request::get($url)
|
|
||||||
->withXTrivialHeader('Just as a demo')
|
|
||||||
->send();
|
|
||||||
|
|
||||||
foreach ($response->body->results as $tweet) {
|
|
||||||
echo "@{$tweet->from_user} tweets \"{$tweet->text}\"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
# Installation
|
|
||||||
|
|
||||||
## Phar
|
|
||||||
|
|
||||||
A [PHP Archive](http://php.net/manual/en/book.phar.php) (or .phar) file is available for [downloading](http://phphttpclient.com/httpful.phar). Simply [download](http://phphttpclient.com/httpful.phar) the .phar, drop it into your project, and include it like you would any other php file. _This method is ideal smaller projects, one off scripts, and quick API hacking_.
|
|
||||||
|
|
||||||
<?php
|
|
||||||
include('httpful.phar');
|
|
||||||
$r = \Httpful\Request::get($uri)->sendIt();
|
|
||||||
...
|
|
||||||
|
|
||||||
## Composer
|
|
||||||
|
|
||||||
Httpful is PSR-0 compliant and can be installed using [composer](http://getcomposer.org/). Simply add `nategood/httpful` to your composer.json file. _Composer is the sane alternative to PEAR. It is excellent for managing dependancies in larger projects_.
|
|
||||||
|
|
||||||
{
|
|
||||||
"require": {
|
|
||||||
"nategood/httpful": "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
## Install from Source
|
|
||||||
|
|
||||||
Because Httpful is PSR-0 compliant, you can also just clone the Httpful repository and use a PSR-0 compatible autoloader to load the library, like [Symfony's](http://symfony.com/doc/current/components/class_loader.html). Alternatively you can use the PSR-0 compliant autoloader included with the Httpful (simply `require("bootstrap.php")`).
|
|
||||||
|
|
||||||
# Show Me More!
|
|
||||||
|
|
||||||
You can checkout the [Httpful Landing Page](http://phphttpclient.com) for more info including many examples and [documentation](http:://phphttpclient.com/docs).
|
|
||||||
|
|
||||||
# Contributing
|
|
||||||
|
|
||||||
Httpful highly encourages sending in pull requests. When submitting a pull request please:
|
|
||||||
|
|
||||||
- All pull requests should target the `dev` branch (not `master`)
|
|
||||||
- Make sure your code follows the [coding conventions](http://pear.php.net/manual/en/standards.php)
|
|
||||||
- Please use soft tabs (four spaces) instead of hard tabs
|
|
||||||
- Make sure you add appropriate test coverage for your changes
|
|
||||||
- Run all unit tests in the test directory via `phpunit ./tests`
|
|
||||||
- Include commenting where appropriate and add a descriptive pull request message
|
|
||||||
|
|
||||||
# Changelog
|
|
||||||
|
|
||||||
## 0.2.6
|
|
||||||
|
|
||||||
- FIX [I #85](https://github.com/nategood/httpful/issues/85) Empty Content Length issue resolved
|
|
||||||
|
|
||||||
## 0.2.5
|
|
||||||
|
|
||||||
- FEATURE [I #80](https://github.com/nategood/httpful/issues/80) [I #81](https://github.com/nategood/httpful/issues/81) Proxy support added with `useProxy` method.
|
|
||||||
|
|
||||||
## 0.2.4
|
|
||||||
|
|
||||||
- FEATURE [I #77](https://github.com/nategood/httpful/issues/77) Convenience method for setting a timeout (seconds) `$req->timeoutIn(10);`
|
|
||||||
- FIX [I #75](https://github.com/nategood/httpful/issues/75) [I #78](https://github.com/nategood/httpful/issues/78) Bug with checking if digest auth is being used.
|
|
||||||
|
|
||||||
## 0.2.3
|
|
||||||
|
|
||||||
- FIX Overriding default Mime Handlers
|
|
||||||
- FIX [PR #73](https://github.com/nategood/httpful/pull/73) Parsing http status codes
|
|
||||||
|
|
||||||
## 0.2.2
|
|
||||||
|
|
||||||
- FEATURE Add support for parsing JSON responses as associative arrays instead of objects
|
|
||||||
- FEATURE Better support for setting constructor arguments on Mime Handlers
|
|
||||||
|
|
||||||
## 0.2.1
|
|
||||||
|
|
||||||
- FEATURE [PR #72](https://github.com/nategood/httpful/pull/72) Allow support for custom Accept header
|
|
||||||
|
|
||||||
## 0.2.0
|
|
||||||
|
|
||||||
- REFACTOR [PR #49](https://github.com/nategood/httpful/pull/49) Broke headers out into their own class
|
|
||||||
- REFACTOR [PR #54](https://github.com/nategood/httpful/pull/54) Added more specific Exceptions
|
|
||||||
- FIX [PR #58](https://github.com/nategood/httpful/pull/58) Fixes throwing an error on an empty xml response
|
|
||||||
- FEATURE [PR #57](https://github.com/nategood/httpful/pull/57) Adds support for digest authentication
|
|
||||||
|
|
||||||
## 0.1.6
|
|
||||||
|
|
||||||
- Ability to set the number of max redirects via overloading `followRedirects(int max_redirects)`
|
|
||||||
- Standards Compliant fix to `Accepts` header
|
|
||||||
- Bug fix for bootstrap process when installed via Composer
|
|
||||||
|
|
||||||
## 0.1.5
|
|
||||||
|
|
||||||
- Use `DIRECTORY_SEPARATOR` constant [PR #33](https://github.com/nategood/httpful/pull/32)
|
|
||||||
- [PR #35](https://github.com/nategood/httpful/pull/35)
|
|
||||||
- Added the raw\_headers property reference to response.
|
|
||||||
- Compose request header and added raw\_header to Request object.
|
|
||||||
- Fixed response has errors and added more comments for clarity.
|
|
||||||
- Fixed header parsing to allow the minimum (status line only) and also cater for the actual CRLF ended headers as per RFC2616.
|
|
||||||
- Added the perfect test Accept: header for all Acceptable scenarios see @b78e9e82cd9614fbe137c01bde9439c4e16ca323 for details.
|
|
||||||
- Added default User-Agent header
|
|
||||||
- `User-Agent: Httpful/0.1.5` + curl version + server software + PHP version
|
|
||||||
- To bypass this "default" operation simply add a User-Agent to the request headers even a blank User-Agent is sufficient and more than simple enough to produce me thinks.
|
|
||||||
- Completed test units for additions.
|
|
||||||
- Added phpunit coverage reporting and helped phpunit auto locate the tests a bit easier.
|
|
||||||
|
|
||||||
## 0.1.4
|
|
||||||
|
|
||||||
- Add support for CSV Handling [PR #32](https://github.com/nategood/httpful/pull/32)
|
|
||||||
|
|
||||||
## 0.1.3
|
|
||||||
|
|
||||||
- Handle empty responses in JsonParser and XmlParser
|
|
||||||
|
|
||||||
## 0.1.2
|
|
||||||
|
|
||||||
- Added support for setting XMLHandler configuration options
|
|
||||||
- Added examples for overriding XmlHandler and registering a custom parser
|
|
||||||
- Removed the httpful.php download (deprecated in favor of httpful.phar)
|
|
||||||
|
|
||||||
## 0.1.1
|
|
||||||
|
|
||||||
- Bug fix serialization default case and phpunit tests
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
|
|
||||||
- Added Support for Registering Mime Handlers
|
|
||||||
- Created AbstractMimeHandler type that all Mime Handlers must extend
|
|
||||||
- Pulled out the parsing/serializing logic from the Request/Response classes into their own MimeHandler classes
|
|
||||||
- Added ability to register new mime handlers for mime types
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
require(__DIR__ . '/src/Httpful/Bootstrap.php');
|
|
||||||
\Httpful\Bootstrap::init();
|
|
|
@ -1,51 +0,0 @@
|
||||||
#!/usr/bin/php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the whole library into a single file
|
|
||||||
* as an easy drop in solution as opposed to
|
|
||||||
* relying on autoloader. Sometimes we just
|
|
||||||
* want to hack with an API as a one off thing.
|
|
||||||
* Httpful should make this easy.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function exit_unless($condition, $msg = null) {
|
|
||||||
if ($condition)
|
|
||||||
return;
|
|
||||||
echo "[FAIL]\n$msg\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the Httpful Phar
|
|
||||||
echo "Building Phar... ";
|
|
||||||
$base_dir = dirname(__FILE__);
|
|
||||||
$source_dir = $base_dir . '/src/Httpful/';
|
|
||||||
$phar_path = $base_dir . '/downloads/httpful.phar';
|
|
||||||
$phar = new Phar($phar_path, 0, 'httpful.phar');
|
|
||||||
$stub = <<<HEREDOC
|
|
||||||
<?php
|
|
||||||
// Phar Stub File
|
|
||||||
Phar::mapPhar('httpful.phar');
|
|
||||||
include('phar://httpful.phar/Httpful/Bootstrap.php');
|
|
||||||
\Httpful\Bootstrap::pharInit();
|
|
||||||
|
|
||||||
__HALT_COMPILER();
|
|
||||||
HEREDOC;
|
|
||||||
try {
|
|
||||||
$phar->setStub($stub);
|
|
||||||
} catch(Exception $e) {
|
|
||||||
$phar = false;
|
|
||||||
}
|
|
||||||
exit_unless($phar, "Unable to create a phar. Make certain you have phar.readonly=0 set in your ini file.");
|
|
||||||
$phar->buildFromDirectory(dirname($source_dir));
|
|
||||||
echo "[ OK ]\n";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Add it to git!
|
|
||||||
echo "Adding httpful.phar to the repo... ";
|
|
||||||
$return_code = 0;
|
|
||||||
passthru("git add $phar_path", $return_code);
|
|
||||||
exit_unless($return_code === 0, "Unable to add download files to git.");
|
|
||||||
echo "[ OK ]\n";
|
|
||||||
echo "\nBuild completed successfully.\n\n";
|
|
|
@ -1,27 +0,0 @@
|
||||||
{
|
|
||||||
"name": "nategood/httpful",
|
|
||||||
"description": "A Readable, Chainable, REST friendly, PHP HTTP Client",
|
|
||||||
"homepage": "http://github.com/nategood/httpful",
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": ["http", "curl", "rest", "restful", "api", "requests"],
|
|
||||||
"version": "0.2.6",
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Nate Good",
|
|
||||||
"email": "me@nategood.com",
|
|
||||||
"homepage": "http://nategood.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"require": {
|
|
||||||
"php": ">=5.3",
|
|
||||||
"ext-curl": "*"
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-0": {
|
|
||||||
"Httpful": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"phpunit/phpunit": "*"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Grab some The Dead Weather albums from Freebase
|
|
||||||
*/
|
|
||||||
require(__DIR__ . '/../bootstrap.php');
|
|
||||||
|
|
||||||
$uri = "https://www.googleapis.com/freebase/v1/mqlread?query=%7B%22type%22:%22/music/artist%22%2C%22name%22:%22The%20Dead%20Weather%22%2C%22album%22:%5B%5D%7D";
|
|
||||||
$response = \Httpful\Request::get($uri)
|
|
||||||
->expectsJson()
|
|
||||||
->sendIt();
|
|
||||||
|
|
||||||
echo 'The Dead Weather has ' . count($response->body->result->album) . " albums.\n";
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?php
|
|
||||||
// XML Example from GitHub
|
|
||||||
require(__DIR__ . '/../bootstrap.php');
|
|
||||||
use \Httpful\Request;
|
|
||||||
|
|
||||||
$uri = 'https://github.com/api/v2/xml/user/show/nategood';
|
|
||||||
$request = Request::get($uri)->send();
|
|
||||||
|
|
||||||
echo "{$request->body->name} joined GitHub on " . date('M jS', strtotime($request->body->{'created-at'})) ."\n";
|
|
|
@ -1,44 +0,0 @@
|
||||||
<?php
|
|
||||||
require(__DIR__ . '/../bootstrap.php');
|
|
||||||
|
|
||||||
// We can override the default parser configuration options be registering
|
|
||||||
// a parser with different configuration options for a particular mime type
|
|
||||||
|
|
||||||
// Example setting a namespace for the XMLHandler parser
|
|
||||||
$conf = ['namespace' => 'http://example.com'];
|
|
||||||
\Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf));
|
|
||||||
|
|
||||||
// We can also add the parsers with our own...
|
|
||||||
class SimpleCsvHandler extends \Httpful\Handlers\MimeHandlerAdapter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Takes a response body, and turns it into
|
|
||||||
* a two dimensional array.
|
|
||||||
*
|
|
||||||
* @param string $body
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function parse($body)
|
|
||||||
{
|
|
||||||
return str_getcsv($body);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes a two dimensional array and turns it
|
|
||||||
* into a serialized string to include as the
|
|
||||||
* body of a request
|
|
||||||
*
|
|
||||||
* @param mixed $payload
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function serialize($payload)
|
|
||||||
{
|
|
||||||
$serialized = '';
|
|
||||||
foreach ($payload as $line) {
|
|
||||||
$serialized .= '"' . implode('","', $line) . '"' . "\n";
|
|
||||||
}
|
|
||||||
return $serialized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
\Httpful\Httpful::register('text/csv', new SimpleCsvHandler());
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
require(__DIR__ . '/../bootstrap.php');
|
|
||||||
|
|
||||||
use \Httpful\Request;
|
|
||||||
|
|
||||||
// Get event details for a public event
|
|
||||||
$uri = "http://api.showclix.com/Event/8175";
|
|
||||||
$response = Request::get($uri)
|
|
||||||
->expectsType('json')
|
|
||||||
->sendIt();
|
|
||||||
|
|
||||||
// Print out the event details
|
|
||||||
echo "The event {$response->body->event} will take place on {$response->body->event_start}\n";
|
|
||||||
|
|
||||||
// Example overriding the default JSON handler with one that encodes the response as an array
|
|
||||||
\Httpful\Httpful::register(\Httpful\Mime::JSON, new \Httpful\Handlers\JsonHandler(['decode_as_array' => true]));
|
|
||||||
|
|
||||||
$response = Request::get($uri)
|
|
||||||
->expectsType('json')
|
|
||||||
->sendIt();
|
|
||||||
|
|
||||||
// Print out the event details
|
|
||||||
echo "The event {$response->body['event']} will take place on {$response->body['event_start']}\n";
|
|
|
@ -1,98 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Httpful;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bootstrap class that facilitates autoloading. A naive
|
|
||||||
* PSR-0 autoloader.
|
|
||||||
*
|
|
||||||
* @author Nate Good <me@nategood.com>
|
|
||||||
*/
|
|
||||||
class Bootstrap
|
|
||||||
{
|
|
||||||
|
|
||||||
const DIR_GLUE = DIRECTORY_SEPARATOR;
|
|
||||||
const NS_GLUE = '\\';
|
|
||||||
|
|
||||||
public static $registered = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the autoloader and any other setup needed
|
|
||||||
*/
|
|
||||||
public static function init()
|
|
||||||
{
|
|
||||||
spl_autoload_register(['\Httpful\Bootstrap', 'autoload']);
|
|
||||||
self::registerHandlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The autoload magic (PSR-0 style)
|
|
||||||
*
|
|
||||||
* @param string $classname
|
|
||||||
*/
|
|
||||||
public static function autoload($classname)
|
|
||||||
{
|
|
||||||
self::_autoload(dirname(dirname(__FILE__)), $classname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the autoloader and any other setup needed
|
|
||||||
*/
|
|
||||||
public static function pharInit()
|
|
||||||
{
|
|
||||||
spl_autoload_register(['\Httpful\Bootstrap', 'pharAutoload']);
|
|
||||||
self::registerHandlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Phar specific autoloader
|
|
||||||
*
|
|
||||||
* @param string $classname
|
|
||||||
*/
|
|
||||||
public static function pharAutoload($classname)
|
|
||||||
{
|
|
||||||
self::_autoload('phar://httpful.phar', $classname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string base
|
|
||||||
* @param string classname
|
|
||||||
*/
|
|
||||||
private static function _autoload($base, $classname)
|
|
||||||
{
|
|
||||||
$parts = explode(self::NS_GLUE, $classname);
|
|
||||||
$path = $base . self::DIR_GLUE . implode(self::DIR_GLUE, $parts) . '.php';
|
|
||||||
|
|
||||||
if (file_exists($path)) {
|
|
||||||
require_once($path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Register default mime handlers. Is idempotent.
|
|
||||||
*/
|
|
||||||
public static function registerHandlers()
|
|
||||||
{
|
|
||||||
if (self::$registered === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo check a conf file to load from that instead of
|
|
||||||
// hardcoding into the library?
|
|
||||||
$handlers = [
|
|
||||||
\Httpful\Mime::JSON => new \Httpful\Handlers\JsonHandler(),
|
|
||||||
\Httpful\Mime::XML => new \Httpful\Handlers\XmlHandler(),
|
|
||||||
\Httpful\Mime::FORM => new \Httpful\Handlers\FormHandler(),
|
|
||||||
\Httpful\Mime::CSV => new \Httpful\Handlers\CsvHandler(),
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($handlers as $mime => $handler) {
|
|
||||||
// Don't overwrite if the handler has already been registered
|
|
||||||
if (Httpful::hasParserRegistered($mime)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Httpful::register($mime, $handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$registered = true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Httpful\Exception;
|
|
||||||
|
|
||||||
class ConnectionErrorException extends \Exception
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Mime Type: text/csv
|
|
||||||
* @author Raja Kapur <rajak@twistedthrottle.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Httpful\Handlers;
|
|
||||||
|
|
||||||
class CsvHandler extends MimeHandlerAdapter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param string $body
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function parse($body)
|
|
||||||
{
|
|
||||||
if (empty($body)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$parsed = [];
|
|
||||||
$fp = fopen('data://text/plain;base64,' . base64_encode($body), 'r');
|
|
||||||
while (($r = fgetcsv($fp)) !== false) {
|
|
||||||
$parsed[] = $r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($parsed)) {
|
|
||||||
throw new \Exception("Unable to parse response as CSV");
|
|
||||||
}
|
|
||||||
return $parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $payload
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function serialize($payload)
|
|
||||||
{
|
|
||||||
$fp = fopen('php://temp/maxmemory:'. (6*1024*1024), 'r+');
|
|
||||||
$i = 0;
|
|
||||||
foreach ($payload as $fields) {
|
|
||||||
if ($i++ == 0) {
|
|
||||||
fputcsv($fp, array_keys($fields));
|
|
||||||
}
|
|
||||||
fputcsv($fp, $fields);
|
|
||||||
}
|
|
||||||
rewind($fp);
|
|
||||||
$data = stream_get_contents($fp);
|
|
||||||
fclose($fp);
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Mime Type: application/x-www-urlencoded
|
|
||||||
* @author Nathan Good <me@nategood.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Httpful\Handlers;
|
|
||||||
|
|
||||||
class FormHandler extends MimeHandlerAdapter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param string $body
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function parse($body)
|
|
||||||
{
|
|
||||||
$parsed = [];
|
|
||||||
parse_str($body, $parsed);
|
|
||||||
return $parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $payload
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function serialize($payload)
|
|
||||||
{
|
|
||||||
return http_build_query($payload, null, '&');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Mime Type: application/json
|
|
||||||
* @author Nathan Good <me@nategood.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Httpful\Handlers;
|
|
||||||
|
|
||||||
class JsonHandler extends MimeHandlerAdapter
|
|
||||||
{
|
|
||||||
private $decode_as_array = false;
|
|
||||||
|
|
||||||
public function init(array $args)
|
|
||||||
{
|
|
||||||
$this->decode_as_array = !!(array_key_exists('decode_as_array', $args) ? $args['decode_as_array'] : false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $body
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function parse($body)
|
|
||||||
{
|
|
||||||
if (empty($body)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$parsed = json_decode($body, $this->decode_as_array);
|
|
||||||
if (is_null($parsed)) {
|
|
||||||
throw new \Exception("Unable to parse response as JSON");
|
|
||||||
}
|
|
||||||
return $parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $payload
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function serialize($payload)
|
|
||||||
{
|
|
||||||
return json_encode($payload);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handlers are used to parse and serialize payloads for specific
|
|
||||||
* mime types. You can register a custom handler via the register
|
|
||||||
* method. You can also override a default parser in this way.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Httpful\Handlers;
|
|
||||||
|
|
||||||
class MimeHandlerAdapter
|
|
||||||
{
|
|
||||||
public function __construct(array $args = [])
|
|
||||||
{
|
|
||||||
$this->init($args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initial setup of
|
|
||||||
* @param array $args
|
|
||||||
*/
|
|
||||||
public function init(array $args)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $body
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function parse($body)
|
|
||||||
{
|
|
||||||
return $body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $payload
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function serialize($payload)
|
|
||||||
{
|
|
||||||
return (string) $payload;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
# Handlers
|
|
||||||
|
|
||||||
Handlers are simple classes that are used to parse response bodies and serialize request payloads. All Handlers must extend the `MimeHandlerAdapter` class and implement two methods: `serialize($payload)` and `parse($response)`. Let's build a very basic Handler to register for the `text/csv` mime type.
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
class SimpleCsvHandler extends \Httpful\Handlers\MimeHandlerAdapter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Takes a response body, and turns it into
|
|
||||||
* a two dimensional array.
|
|
||||||
*
|
|
||||||
* @param string $body
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function parse($body)
|
|
||||||
{
|
|
||||||
return str_getcsv($body);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes a two dimensional array and turns it
|
|
||||||
* into a serialized string to include as the
|
|
||||||
* body of a request
|
|
||||||
*
|
|
||||||
* @param mixed $payload
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function serialize($payload)
|
|
||||||
{
|
|
||||||
$serialized = '';
|
|
||||||
foreach ($payload as $line) {
|
|
||||||
$serialized .= '"' . implode('","', $line) . '"' . "\n";
|
|
||||||
}
|
|
||||||
return $serialized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Finally, you must register this handler for a particular mime type.
|
|
||||||
|
|
||||||
Httpful::register('text/csv', new SimpleCsvHandler());
|
|
||||||
|
|
||||||
After this registering the handler in your source code, by default, any responses with a mime type of text/csv should be parsed by this handler.
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Mime Type: text/html
|
|
||||||
* Mime Type: application/html+xml
|
|
||||||
*
|
|
||||||
* @author Nathan Good <me@nategood.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Httpful\Handlers;
|
|
||||||
|
|
||||||
class XHtmlHandler extends MimeHandlerAdapter
|
|
||||||
{
|
|
||||||
// @todo add html specific parsing
|
|
||||||
// see DomDocument::load http://docs.php.net/manual/en/domdocument.loadhtml.php
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Mime Type: application/xml
|
|
||||||
*
|
|
||||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
|
||||||
* @author Nathan Good <me@nategood.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Httpful\Handlers;
|
|
||||||
|
|
||||||
class XmlHandler extends MimeHandlerAdapter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string $namespace xml namespace to use with simple_load_string
|
|
||||||
*/
|
|
||||||
private $namespace;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int $libxml_opts see http://www.php.net/manual/en/libxml.constants.php
|
|
||||||
*/
|
|
||||||
private $libxml_opts;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $conf sets configuration options
|
|
||||||
*/
|
|
||||||
public function __construct(array $conf = [])
|
|
||||||
{
|
|
||||||
$this->namespace = isset($conf['namespace']) ? $conf['namespace'] : '';
|
|
||||||
$this->libxml_opts = isset($conf['libxml_opts']) ? $conf['libxml_opts'] : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $body
|
|
||||||
* @return mixed
|
|
||||||
* @throws Exception if unable to parse
|
|
||||||
*/
|
|
||||||
public function parse($body)
|
|
||||||
{
|
|
||||||
if (empty($body)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$parsed = simplexml_load_string($body, null, $this->libxml_opts, $this->namespace);
|
|
||||||
if ($parsed === false) {
|
|
||||||
throw new \Exception("Unable to parse response as XML");
|
|
||||||
}
|
|
||||||
return $parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $payload
|
|
||||||
* @return string
|
|
||||||
* @throws Exception if unable to serialize
|
|
||||||
*/
|
|
||||||
public function serialize($payload)
|
|
||||||
{
|
|
||||||
list($_, $dom) = $this->_future_serializeAsXml($payload);
|
|
||||||
return $dom->saveXml();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
|
||||||
*/
|
|
||||||
private function _future_serializeAsXml($value, $node = null, $dom = null)
|
|
||||||
{
|
|
||||||
if (!$dom) {
|
|
||||||
$dom = new \DOMDocument;
|
|
||||||
}
|
|
||||||
if (!$node) {
|
|
||||||
if (!is_object($value)) {
|
|
||||||
$node = $dom->createElement('response');
|
|
||||||
$dom->appendChild($node);
|
|
||||||
} else {
|
|
||||||
$node = $dom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (is_object($value)) {
|
|
||||||
$objNode = $dom->createElement(get_class($value));
|
|
||||||
$node->appendChild($objNode);
|
|
||||||
$this->_future_serializeObjectAsXml($value, $objNode, $dom);
|
|
||||||
} else if (is_array($value)) {
|
|
||||||
$arrNode = $dom->createElement('array');
|
|
||||||
$node->appendChild($arrNode);
|
|
||||||
$this->_future_serializeArrayAsXml($value, $arrNode, $dom);
|
|
||||||
} else if (is_bool($value)) {
|
|
||||||
$node->appendChild($dom->createTextNode($value?'TRUE':'FALSE'));
|
|
||||||
} else {
|
|
||||||
$node->appendChild($dom->createTextNode($value));
|
|
||||||
}
|
|
||||||
return [$node, $dom];
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
|
||||||
*/
|
|
||||||
private function _future_serializeArrayAsXml($value, &$parent, &$dom)
|
|
||||||
{
|
|
||||||
foreach ($value as $k => &$v) {
|
|
||||||
$n = $k;
|
|
||||||
if (is_numeric($k)) {
|
|
||||||
$n = "child-{$n}";
|
|
||||||
}
|
|
||||||
$el = $dom->createElement($n);
|
|
||||||
$parent->appendChild($el);
|
|
||||||
$this->_future_serializeAsXml($v, $el, $dom);
|
|
||||||
}
|
|
||||||
return [$parent, $dom];
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
|
||||||
*/
|
|
||||||
private function _future_serializeObjectAsXml($value, &$parent, &$dom)
|
|
||||||
{
|
|
||||||
$refl = new \ReflectionObject($value);
|
|
||||||
foreach ($refl->getProperties() as $pr) {
|
|
||||||
if (!$pr->isPrivate()) {
|
|
||||||
$el = $dom->createElement($pr->getName());
|
|
||||||
$parent->appendChild($el);
|
|
||||||
$this->_future_serializeAsXml($pr->getValue($value), $el, $dom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [$parent, $dom];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Httpful;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Nate Good <me@nategood.com>
|
|
||||||
*/
|
|
||||||
class Http
|
|
||||||
{
|
|
||||||
const HEAD = 'HEAD';
|
|
||||||
const GET = 'GET';
|
|
||||||
const POST = 'POST';
|
|
||||||
const PUT = 'PUT';
|
|
||||||
const DELETE = 'DELETE';
|
|
||||||
const PATCH = 'PATCH';
|
|
||||||
const OPTIONS = 'OPTIONS';
|
|
||||||
const TRACE = 'TRACE';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array of HTTP method strings
|
|
||||||
*/
|
|
||||||
public static function safeMethods()
|
|
||||||
{
|
|
||||||
return [self::HEAD, self::GET, self::OPTIONS, self::TRACE];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @param string HTTP method
|
|
||||||
*/
|
|
||||||
public static function isSafeMethod($method)
|
|
||||||
{
|
|
||||||
return in_array($method, self::safeMethods());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @param string HTTP method
|
|
||||||
*/
|
|
||||||
public static function isUnsafeMethod($method)
|
|
||||||
{
|
|
||||||
return !in_array($method, self::safeMethods());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array list of (always) idempotent HTTP methods
|
|
||||||
*/
|
|
||||||
public static function idempotentMethods()
|
|
||||||
{
|
|
||||||
// Though it is possible to be idempotent, POST
|
|
||||||
// is not guarunteed to be, and more often than
|
|
||||||
// not, it is not.
|
|
||||||
return [self::HEAD, self::GET, self::PUT, self::DELETE, self::OPTIONS, self::TRACE, self::PATCH];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @param string HTTP method
|
|
||||||
*/
|
|
||||||
public static function isIdempotent($method)
|
|
||||||
{
|
|
||||||
return in_array($method, self::safeidempotentMethodsMethods());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @param string HTTP method
|
|
||||||
*/
|
|
||||||
public static function isNotIdempotent($method)
|
|
||||||
{
|
|
||||||
return !in_array($method, self::idempotentMethods());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Technically anything *can* have a body,
|
|
||||||
* they just don't have semantic meaning. So say's Roy
|
|
||||||
* http://tech.groups.yahoo.com/group/rest-discuss/message/9962
|
|
||||||
*
|
|
||||||
* @return array of HTTP method strings
|
|
||||||
*/
|
|
||||||
public static function canHaveBody()
|
|
||||||
{
|
|
||||||
return [self::POST, self::PUT, self::PATCH, self::OPTIONS];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Httpful;
|
|
||||||
|
|
||||||
class Httpful
|
|
||||||
{
|
|
||||||
const VERSION = '0.2.4';
|
|
||||||
|
|
||||||
private static $mimeRegistrar = [];
|
|
||||||
private static $default = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $mime_type
|
|
||||||
* @param MimeHandlerAdapter $handler
|
|
||||||
*/
|
|
||||||
public static function register($mimeType, \Httpful\Handlers\MimeHandlerAdapter $handler)
|
|
||||||
{
|
|
||||||
self::$mimeRegistrar[$mimeType] = $handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $mime_type defaults to MimeHandlerAdapter
|
|
||||||
* @return MimeHandlerAdapter
|
|
||||||
*/
|
|
||||||
public static function get($mimeType = null)
|
|
||||||
{
|
|
||||||
if (isset(self::$mimeRegistrar[$mimeType])) {
|
|
||||||
return self::$mimeRegistrar[$mimeType];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty(self::$default)) {
|
|
||||||
self::$default = new \Httpful\Handlers\MimeHandlerAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does this particular Mime Type have a parser registered
|
|
||||||
* for it?
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function hasParserRegistered($mimeType)
|
|
||||||
{
|
|
||||||
return isset(self::$mimeRegistrar[$mimeType]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Httpful;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class to organize the Mime stuff a bit more
|
|
||||||
* @author Nate Good <me@nategood.com>
|
|
||||||
*/
|
|
||||||
class Mime
|
|
||||||
{
|
|
||||||
const JSON = 'application/json';
|
|
||||||
const XML = 'application/xml';
|
|
||||||
const XHTML = 'application/html+xml';
|
|
||||||
const FORM = 'application/x-www-form-urlencoded';
|
|
||||||
const PLAIN = 'text/plain';
|
|
||||||
const JS = 'text/javascript';
|
|
||||||
const HTML = 'text/html';
|
|
||||||
const YAML = 'application/x-yaml';
|
|
||||||
const CSV = 'text/csv';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map short name for a mime type
|
|
||||||
* to a full proper mime type
|
|
||||||
*/
|
|
||||||
public static $mimes = [
|
|
||||||
'json' => self::JSON,
|
|
||||||
'xml' => self::XML,
|
|
||||||
'form' => self::FORM,
|
|
||||||
'plain' => self::PLAIN,
|
|
||||||
'text' => self::PLAIN,
|
|
||||||
'html' => self::HTML,
|
|
||||||
'xhtml' => self::XHTML,
|
|
||||||
'js' => self::JS,
|
|
||||||
'javascript'=> self::JS,
|
|
||||||
'yaml' => self::YAML,
|
|
||||||
'csv' => self::CSV,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the full Mime Type name from a "short name".
|
|
||||||
* Returns the short if no mapping was found.
|
|
||||||
* @return string full mime type (e.g. application/json)
|
|
||||||
* @param string common name for mime type (e.g. json)
|
|
||||||
*/
|
|
||||||
public static function getFullMime($short_name)
|
|
||||||
{
|
|
||||||
return array_key_exists($short_name, self::$mimes) ? self::$mimes[$short_name] : $short_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @param string $short_name
|
|
||||||
*/
|
|
||||||
public static function supportsMimeType($short_name)
|
|
||||||
{
|
|
||||||
return array_key_exists($short_name, self::$mimes);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,189 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Httpful;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Models an HTTP response
|
|
||||||
*
|
|
||||||
* @author Nate Good <me@nategood.com>
|
|
||||||
*/
|
|
||||||
class Response
|
|
||||||
{
|
|
||||||
|
|
||||||
public $body,
|
|
||||||
$raw_body,
|
|
||||||
$headers,
|
|
||||||
$raw_headers,
|
|
||||||
$request,
|
|
||||||
$code = 0,
|
|
||||||
$content_type,
|
|
||||||
$parent_type,
|
|
||||||
$charset,
|
|
||||||
$is_mime_vendor_specific = false,
|
|
||||||
$is_mime_personal = false;
|
|
||||||
|
|
||||||
private $parsers;
|
|
||||||
/**
|
|
||||||
* @param string $body
|
|
||||||
* @param string $headers
|
|
||||||
* @param Request $request
|
|
||||||
*/
|
|
||||||
public function __construct($body, $headers, Request $request)
|
|
||||||
{
|
|
||||||
$this->request = $request;
|
|
||||||
$this->raw_headers = $headers;
|
|
||||||
$this->raw_body = $body;
|
|
||||||
|
|
||||||
$this->code = $this->_parseCode($headers);
|
|
||||||
$this->headers = Response\Headers::fromString($headers);
|
|
||||||
|
|
||||||
$this->_interpretHeaders();
|
|
||||||
|
|
||||||
$this->body = $this->_parse($body);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status Code Definitions
|
|
||||||
*
|
|
||||||
* Informational 1xx
|
|
||||||
* Successful 2xx
|
|
||||||
* Redirection 3xx
|
|
||||||
* Client Error 4xx
|
|
||||||
* Server Error 5xx
|
|
||||||
*
|
|
||||||
* http://pretty-rfc.herokuapp.com/RFC2616#status.codes
|
|
||||||
*
|
|
||||||
* @return bool Did we receive a 4xx or 5xx?
|
|
||||||
*/
|
|
||||||
public function hasErrors()
|
|
||||||
{
|
|
||||||
return $this->code >= 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return return bool
|
|
||||||
*/
|
|
||||||
public function hasBody()
|
|
||||||
{
|
|
||||||
return !empty($this->body);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the response into a clean data structure
|
|
||||||
* (most often an associative array) based on the expected
|
|
||||||
* Mime type.
|
|
||||||
* @return array|string|object the response parse accordingly
|
|
||||||
* @param string Http response body
|
|
||||||
*/
|
|
||||||
public function _parse($body)
|
|
||||||
{
|
|
||||||
// If the user decided to forgo the automatic
|
|
||||||
// smart parsing, short circuit.
|
|
||||||
if (!$this->request->auto_parse) {
|
|
||||||
return $body;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If provided, use custom parsing callback
|
|
||||||
if (isset($this->request->parse_callback)) {
|
|
||||||
return call_user_func($this->request->parse_callback, $body);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decide how to parse the body of the response in the following order
|
|
||||||
// 1. If provided, use the mime type specifically set as part of the `Request`
|
|
||||||
// 2. If a MimeHandler is registered for the content type, use it
|
|
||||||
// 3. If provided, use the "parent type" of the mime type from the response
|
|
||||||
// 4. Default to the content-type provided in the response
|
|
||||||
$parse_with = $this->request->expected_type;
|
|
||||||
if (empty($this->request->expected_type)) {
|
|
||||||
$parse_with = Httpful::hasParserRegistered($this->content_type)
|
|
||||||
? $this->content_type
|
|
||||||
: $this->parent_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Httpful::get($parse_with)->parse($body);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse text headers from response into
|
|
||||||
* array of key value pairs
|
|
||||||
* @return array parse headers
|
|
||||||
* @param string $headers raw headers
|
|
||||||
*/
|
|
||||||
public function _parseHeaders($headers)
|
|
||||||
{
|
|
||||||
$headers = preg_split("/(\r|\n)+/", $headers, -1, \PREG_SPLIT_NO_EMPTY);
|
|
||||||
$parse_headers = [];
|
|
||||||
for ($i = 1; $i < count($headers); $i++) {
|
|
||||||
list($key, $raw_value) = explode(':', $headers[$i], 2);
|
|
||||||
$key = trim($key);
|
|
||||||
$value = trim($raw_value);
|
|
||||||
if (array_key_exists($key, $parse_headers)) {
|
|
||||||
// See HTTP RFC Sec 4.2 Paragraph 5
|
|
||||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
|
||||||
// If a header appears more than once, it must also be able to
|
|
||||||
// be represented as a single header with a comma-separated
|
|
||||||
// list of values. We transform accordingly.
|
|
||||||
$parse_headers[$key] .= ',' . $value;
|
|
||||||
} else {
|
|
||||||
$parse_headers[$key] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $parse_headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function _parseCode($headers)
|
|
||||||
{
|
|
||||||
$parts = explode(' ', substr($headers, 0, strpos($headers, "\r\n")));
|
|
||||||
if (count($parts) < 2 || !is_numeric($parts[1])) {
|
|
||||||
throw new \Exception("Unable to parse response code from HTTP response due to malformed response");
|
|
||||||
}
|
|
||||||
return intval($parts[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* After we've parse the headers, let's clean things
|
|
||||||
* up a bit and treat some headers specially
|
|
||||||
*/
|
|
||||||
public function _interpretHeaders()
|
|
||||||
{
|
|
||||||
// Parse the Content-Type and charset
|
|
||||||
$content_type = isset($this->headers['Content-Type']) ? $this->headers['Content-Type'] : '';
|
|
||||||
$content_type = explode(';', $content_type);
|
|
||||||
|
|
||||||
$this->content_type = $content_type[0];
|
|
||||||
if (count($content_type) == 2 && strpos($content_type[1], '=') !== false) {
|
|
||||||
list($nill, $this->charset) = explode('=', $content_type[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// RFC 2616 states "text/*" Content-Types should have a default
|
|
||||||
// charset of ISO-8859-1. "application/*" and other Content-Types
|
|
||||||
// are assumed to have UTF-8 unless otherwise specified.
|
|
||||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
|
|
||||||
// http://www.w3.org/International/O-HTTP-charset.en.php
|
|
||||||
if (!isset($this->charset)) {
|
|
||||||
$this->charset = substr($this->content_type, 5) === 'text/' ? 'iso-8859-1' : 'utf-8';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is vendor type? Is personal type?
|
|
||||||
if (strpos($this->content_type, '/') !== false) {
|
|
||||||
list($type, $sub_type) = explode('/', $this->content_type);
|
|
||||||
$this->is_mime_vendor_specific = substr($sub_type, 0, 4) === 'vnd.';
|
|
||||||
$this->is_mime_personal = substr($sub_type, 0, 4) === 'prs.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent type (e.g. xml for application/vnd.github.message+xml)
|
|
||||||
$this->parent_type = $this->content_type;
|
|
||||||
if (strpos($this->content_type, '+') !== false) {
|
|
||||||
list($vendor, $this->parent_type) = explode('+', $this->content_type, 2);
|
|
||||||
$this->parent_type = Mime::getFullMime($this->parent_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
return $this->raw_body;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Httpful\Response;
|
|
||||||
|
|
||||||
final class Headers implements \ArrayAccess, \Countable
|
|
||||||
{
|
|
||||||
|
|
||||||
private $headers;
|
|
||||||
|
|
||||||
private function __construct($headers)
|
|
||||||
{
|
|
||||||
$this->headers = $headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fromString($string)
|
|
||||||
{
|
|
||||||
$lines = preg_split("/(\r|\n)+/", $string, -1, PREG_SPLIT_NO_EMPTY);
|
|
||||||
array_shift($lines); // HTTP HEADER
|
|
||||||
$headers = [];
|
|
||||||
foreach ($lines as $line) {
|
|
||||||
list($name, $value) = explode(':', $line, 2);
|
|
||||||
$headers[strtolower(trim($name))] = trim($value);
|
|
||||||
}
|
|
||||||
return new self($headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetExists($offset)
|
|
||||||
{
|
|
||||||
return isset($this->headers[strtolower($offset)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetGet($offset)
|
|
||||||
{
|
|
||||||
if (isset($this->headers[$name = strtolower($offset)])) {
|
|
||||||
return $this->headers[$name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetSet($offset, $value)
|
|
||||||
{
|
|
||||||
throw new \Exception("Headers are read-only.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetUnset($offset)
|
|
||||||
{
|
|
||||||
throw new \Exception("Headers are read-only.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function count()
|
|
||||||
{
|
|
||||||
return count($this->headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toArray()
|
|
||||||
{
|
|
||||||
return $this->headers;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,462 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Port over the original tests into a more traditional PHPUnit
|
|
||||||
* format. Still need to hook into a lightweight HTTP server to
|
|
||||||
* better test some things (e.g. obscure cURL settings). I've moved
|
|
||||||
* the old tests and node.js server to the tests/.legacy directory.
|
|
||||||
*
|
|
||||||
* @author Nate Good <me@nategood.com>
|
|
||||||
*/
|
|
||||||
namespace Httpful\Test;
|
|
||||||
|
|
||||||
require(dirname(dirname(dirname(__FILE__))) . '/bootstrap.php');
|
|
||||||
\Httpful\Bootstrap::init();
|
|
||||||
|
|
||||||
use Httpful\Httpful;
|
|
||||||
use Httpful\Request;
|
|
||||||
use Httpful\Mime;
|
|
||||||
use Httpful\Http;
|
|
||||||
use Httpful\Response;
|
|
||||||
|
|
||||||
class HttpfulTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
const TEST_SERVER = '127.0.0.1:8008';
|
|
||||||
const TEST_URL = 'http://127.0.0.1:8008';
|
|
||||||
const TEST_URL_400 = 'http://127.0.0.1:8008/400';
|
|
||||||
|
|
||||||
const SAMPLE_JSON_HEADER =
|
|
||||||
"HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/json
|
|
||||||
Connection: keep-alive
|
|
||||||
Transfer-Encoding: chunked\r\n";
|
|
||||||
const SAMPLE_JSON_RESPONSE = '{"key":"value","object":{"key":"value"},"array":[1,2,3,4]}';
|
|
||||||
const SAMPLE_CSV_HEADER =
|
|
||||||
"HTTP/1.1 200 OK
|
|
||||||
Content-Type: text/csv
|
|
||||||
Connection: keep-alive
|
|
||||||
Transfer-Encoding: chunked\r\n";
|
|
||||||
const SAMPLE_CSV_RESPONSE =
|
|
||||||
"Key1,Key2
|
|
||||||
Value1,Value2
|
|
||||||
\"40.0\",\"Forty\"";
|
|
||||||
const SAMPLE_XML_RESPONSE = '<stdClass><arrayProp><array><k1><myClass><intProp>2</intProp></myClass></k1></array></arrayProp><stringProp>a string</stringProp><boolProp>TRUE</boolProp></stdClass>';
|
|
||||||
const SAMPLE_XML_HEADER =
|
|
||||||
"HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/xml
|
|
||||||
Connection: keep-alive
|
|
||||||
Transfer-Encoding: chunked\r\n";
|
|
||||||
const SAMPLE_VENDOR_HEADER =
|
|
||||||
"HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/vnd.nategood.message+xml
|
|
||||||
Connection: keep-alive
|
|
||||||
Transfer-Encoding: chunked\r\n";
|
|
||||||
const SAMPLE_VENDOR_TYPE = "application/vnd.nategood.message+xml";
|
|
||||||
const SAMPLE_MULTI_HEADER =
|
|
||||||
"HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/json
|
|
||||||
Connection: keep-alive
|
|
||||||
Transfer-Encoding: chunked
|
|
||||||
X-My-Header:Value1
|
|
||||||
X-My-Header:Value2\r\n";
|
|
||||||
function testInit()
|
|
||||||
{
|
|
||||||
$r = Request::init();
|
|
||||||
// Did we get a 'Request' object?
|
|
||||||
$this->assertEquals('Httpful\Request', get_class($r));
|
|
||||||
}
|
|
||||||
|
|
||||||
function testMethods()
|
|
||||||
{
|
|
||||||
$valid_methods = ['get', 'post', 'delete', 'put', 'options', 'head'];
|
|
||||||
$url = 'http://example.com/';
|
|
||||||
foreach ($valid_methods as $method) {
|
|
||||||
$r = call_user_func(['Httpful\Request', $method], $url);
|
|
||||||
$this->assertEquals('Httpful\Request', get_class($r));
|
|
||||||
$this->assertEquals(strtoupper($method), $r->method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDefaults()
|
|
||||||
{
|
|
||||||
// Our current defaults are as follows
|
|
||||||
$r = Request::init();
|
|
||||||
$this->assertEquals(Http::GET, $r->method);
|
|
||||||
$this->assertFalse($r->strict_ssl);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testShortMime()
|
|
||||||
{
|
|
||||||
// Valid short ones
|
|
||||||
$this->assertEquals(Mime::JSON, Mime::getFullMime('json'));
|
|
||||||
$this->assertEquals(Mime::XML, Mime::getFullMime('xml'));
|
|
||||||
$this->assertEquals(Mime::HTML, Mime::getFullMime('html'));
|
|
||||||
$this->assertEquals(Mime::CSV, Mime::getFullMime('csv'));
|
|
||||||
|
|
||||||
// Valid long ones
|
|
||||||
$this->assertEquals(Mime::JSON, Mime::getFullMime(Mime::JSON));
|
|
||||||
$this->assertEquals(Mime::XML, Mime::getFullMime(Mime::XML));
|
|
||||||
$this->assertEquals(Mime::HTML, Mime::getFullMime(Mime::HTML));
|
|
||||||
$this->assertEquals(Mime::CSV, Mime::getFullMime(Mime::CSV));
|
|
||||||
|
|
||||||
// No false positives
|
|
||||||
$this->assertNotEquals(Mime::XML, Mime::getFullMime(Mime::HTML));
|
|
||||||
$this->assertNotEquals(Mime::JSON, Mime::getFullMime(Mime::XML));
|
|
||||||
$this->assertNotEquals(Mime::HTML, Mime::getFullMime(Mime::JSON));
|
|
||||||
$this->assertNotEquals(Mime::XML, Mime::getFullMime(Mime::CSV));
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSettingStrictSsl()
|
|
||||||
{
|
|
||||||
$r = Request::init()
|
|
||||||
->withStrictSsl();
|
|
||||||
|
|
||||||
$this->assertTrue($r->strict_ssl);
|
|
||||||
|
|
||||||
$r = Request::init()
|
|
||||||
->withoutStrictSsl();
|
|
||||||
|
|
||||||
$this->assertFalse($r->strict_ssl);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSendsAndExpectsType()
|
|
||||||
{
|
|
||||||
$r = Request::init()
|
|
||||||
->sendsAndExpectsType(Mime::JSON);
|
|
||||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
|
||||||
$this->assertEquals(Mime::JSON, $r->content_type);
|
|
||||||
|
|
||||||
$r = Request::init()
|
|
||||||
->sendsAndExpectsType('html');
|
|
||||||
$this->assertEquals(Mime::HTML, $r->expected_type);
|
|
||||||
$this->assertEquals(Mime::HTML, $r->content_type);
|
|
||||||
|
|
||||||
$r = Request::init()
|
|
||||||
->sendsAndExpectsType('form');
|
|
||||||
$this->assertEquals(Mime::FORM, $r->expected_type);
|
|
||||||
$this->assertEquals(Mime::FORM, $r->content_type);
|
|
||||||
|
|
||||||
$r = Request::init()
|
|
||||||
->sendsAndExpectsType('application/x-www-form-urlencoded');
|
|
||||||
$this->assertEquals(Mime::FORM, $r->expected_type);
|
|
||||||
$this->assertEquals(Mime::FORM, $r->content_type);
|
|
||||||
|
|
||||||
$r = Request::init()
|
|
||||||
->sendsAndExpectsType(Mime::CSV);
|
|
||||||
$this->assertEquals(Mime::CSV, $r->expected_type);
|
|
||||||
$this->assertEquals(Mime::CSV, $r->content_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testIni()
|
|
||||||
{
|
|
||||||
// Test setting defaults/templates
|
|
||||||
|
|
||||||
// Create the template
|
|
||||||
$template = Request::init()
|
|
||||||
->method(Http::POST)
|
|
||||||
->withStrictSsl()
|
|
||||||
->expectsType(Mime::HTML)
|
|
||||||
->sendsType(Mime::FORM);
|
|
||||||
|
|
||||||
Request::ini($template);
|
|
||||||
|
|
||||||
$r = Request::init();
|
|
||||||
|
|
||||||
$this->assertTrue($r->strict_ssl);
|
|
||||||
$this->assertEquals(Http::POST, $r->method);
|
|
||||||
$this->assertEquals(Mime::HTML, $r->expected_type);
|
|
||||||
$this->assertEquals(Mime::FORM, $r->content_type);
|
|
||||||
|
|
||||||
// Test the default accessor as well
|
|
||||||
$this->assertTrue(Request::d('strict_ssl'));
|
|
||||||
$this->assertEquals(Http::POST, Request::d('method'));
|
|
||||||
$this->assertEquals(Mime::HTML, Request::d('expected_type'));
|
|
||||||
$this->assertEquals(Mime::FORM, Request::d('content_type'));
|
|
||||||
|
|
||||||
Request::resetIni();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testAccept()
|
|
||||||
{
|
|
||||||
$r = Request::get('http://example.com/')
|
|
||||||
->expectsType(Mime::JSON);
|
|
||||||
|
|
||||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
|
||||||
$r->_curlPrep();
|
|
||||||
$this->assertContains('application/json', $r->raw_headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testCustomAccept()
|
|
||||||
{
|
|
||||||
$accept = 'application/api-1.0+json';
|
|
||||||
$r = Request::get('http://example.com/')
|
|
||||||
->addHeader('Accept', $accept);
|
|
||||||
|
|
||||||
$r->_curlPrep();
|
|
||||||
$this->assertContains($accept, $r->raw_headers);
|
|
||||||
$this->assertEquals($accept, $r->headers['Accept']);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testUserAgent()
|
|
||||||
{
|
|
||||||
$r = Request::get('http://example.com/')
|
|
||||||
->withUserAgent('ACME/1.2.3');
|
|
||||||
|
|
||||||
$this->assertArrayHasKey('User-Agent', $r->headers);
|
|
||||||
$r->_curlPrep();
|
|
||||||
$this->assertContains('User-Agent: ACME/1.2.3', $r->raw_headers);
|
|
||||||
$this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers);
|
|
||||||
|
|
||||||
$r = Request::get('http://example.com/')
|
|
||||||
->withUserAgent('');
|
|
||||||
|
|
||||||
$this->assertArrayHasKey('User-Agent', $r->headers);
|
|
||||||
$r->_curlPrep();
|
|
||||||
$this->assertContains('User-Agent:', $r->raw_headers);
|
|
||||||
$this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testAuthSetup()
|
|
||||||
{
|
|
||||||
$username = 'nathan';
|
|
||||||
$password = 'opensesame';
|
|
||||||
|
|
||||||
$r = Request::get('http://example.com/')
|
|
||||||
->authenticateWith($username, $password);
|
|
||||||
|
|
||||||
$this->assertEquals($username, $r->username);
|
|
||||||
$this->assertEquals($password, $r->password);
|
|
||||||
$this->assertTrue($r->hasBasicAuth());
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDigestAuthSetup()
|
|
||||||
{
|
|
||||||
$username = 'nathan';
|
|
||||||
$password = 'opensesame';
|
|
||||||
|
|
||||||
$r = Request::get('http://example.com/')
|
|
||||||
->authenticateWithDigest($username, $password);
|
|
||||||
|
|
||||||
$this->assertEquals($username, $r->username);
|
|
||||||
$this->assertEquals($password, $r->password);
|
|
||||||
$this->assertTrue($r->hasDigestAuth());
|
|
||||||
}
|
|
||||||
|
|
||||||
function testJsonResponseParse()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
|
||||||
|
|
||||||
$this->assertEquals("value", $response->body->key);
|
|
||||||
$this->assertEquals("value", $response->body->object->key);
|
|
||||||
$this->assertInternalType('array', $response->body->array);
|
|
||||||
$this->assertEquals(1, $response->body->array[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testXMLResponseParse()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::XML);
|
|
||||||
$response = new Response(self::SAMPLE_XML_RESPONSE, self::SAMPLE_XML_HEADER, $req);
|
|
||||||
$sxe = $response->body;
|
|
||||||
$this->assertEquals("object", gettype($sxe));
|
|
||||||
$this->assertEquals("SimpleXMLElement", get_class($sxe));
|
|
||||||
$bools = $sxe->xpath('/stdClass/boolProp');
|
|
||||||
list( , $bool ) = each($bools);
|
|
||||||
$this->assertEquals("TRUE", (string) $bool);
|
|
||||||
$ints = $sxe->xpath('/stdClass/arrayProp/array/k1/myClass/intProp');
|
|
||||||
list( , $int ) = each($ints);
|
|
||||||
$this->assertEquals("2", (string) $int);
|
|
||||||
$strings = $sxe->xpath('/stdClass/stringProp');
|
|
||||||
list( , $string ) = each($strings);
|
|
||||||
$this->assertEquals("a string", (string) $string);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testCsvResponseParse()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::CSV);
|
|
||||||
$response = new Response(self::SAMPLE_CSV_RESPONSE, self::SAMPLE_CSV_HEADER, $req);
|
|
||||||
|
|
||||||
$this->assertEquals("Key1", $response->body[0][0]);
|
|
||||||
$this->assertEquals("Value1", $response->body[1][0]);
|
|
||||||
$this->assertInternalType('string', $response->body[2][0]);
|
|
||||||
$this->assertEquals("40.0", $response->body[2][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testParsingContentTypeCharset()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
|
||||||
// $response = new Response(SAMPLE_JSON_RESPONSE, "", $req);
|
|
||||||
// // Check default content type of iso-8859-1
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, "HTTP/1.1 200 OK
|
|
||||||
Content-Type: text/plain; charset=utf-8\r\n", $req);
|
|
||||||
$this->assertInstanceOf('Httpful\Response\Headers', $response->headers);
|
|
||||||
$this->assertEquals($response->headers['Content-Type'], 'text/plain; charset=utf-8');
|
|
||||||
$this->assertEquals($response->content_type, 'text/plain');
|
|
||||||
$this->assertEquals($response->charset, 'utf-8');
|
|
||||||
}
|
|
||||||
|
|
||||||
function testEmptyResponseParse()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
|
||||||
$response = new Response("", self::SAMPLE_JSON_HEADER, $req);
|
|
||||||
$this->assertEquals(null, $response->body);
|
|
||||||
|
|
||||||
$reqXml = Request::init()->sendsAndExpects(Mime::XML);
|
|
||||||
$responseXml = new Response("", self::SAMPLE_XML_HEADER, $reqXml);
|
|
||||||
$this->assertEquals(null, $responseXml->body);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testNoAutoParse()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON)->withoutAutoParsing();
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
|
||||||
$this->assertInternalType('string', $response->body);
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON)->withAutoParsing();
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
|
||||||
$this->assertInternalType('object', $response->body);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testParseHeaders()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
|
||||||
$this->assertEquals('application/json', $response->headers['Content-Type']);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testRawHeaders()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
|
||||||
$this->assertContains('Content-Type: application/json', $response->raw_headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testHasErrors()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
|
||||||
$response = new Response('', "HTTP/1.1 100 Continue\r\n", $req);
|
|
||||||
$this->assertFalse($response->hasErrors());
|
|
||||||
$response = new Response('', "HTTP/1.1 200 OK\r\n", $req);
|
|
||||||
$this->assertFalse($response->hasErrors());
|
|
||||||
$response = new Response('', "HTTP/1.1 300 Multiple Choices\r\n", $req);
|
|
||||||
$this->assertFalse($response->hasErrors());
|
|
||||||
$response = new Response('', "HTTP/1.1 400 Bad Request\r\n", $req);
|
|
||||||
$this->assertTrue($response->hasErrors());
|
|
||||||
$response = new Response('', "HTTP/1.1 500 Internal Server Error\r\n", $req);
|
|
||||||
$this->assertTrue($response->hasErrors());
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_parseCode()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
|
||||||
$code = $response->_parseCode("HTTP/1.1 406 Not Acceptable\r\n");
|
|
||||||
$this->assertEquals(406, $code);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testToString()
|
|
||||||
{
|
|
||||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
|
||||||
$this->assertEquals(self::SAMPLE_JSON_RESPONSE, (string)$response);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_parseHeaders()
|
|
||||||
{
|
|
||||||
$parse_headers = Response\Headers::fromString(self::SAMPLE_JSON_HEADER);
|
|
||||||
$this->assertCount(3, $parse_headers);
|
|
||||||
$this->assertEquals('application/json', $parse_headers['Content-Type']);
|
|
||||||
$this->assertTrue(isset($parse_headers['Connection']));
|
|
||||||
}
|
|
||||||
|
|
||||||
function testMultiHeaders()
|
|
||||||
{
|
|
||||||
$req = Request::init();
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_MULTI_HEADER, $req);
|
|
||||||
$parse_headers = $response->_parseHeaders(self::SAMPLE_MULTI_HEADER);
|
|
||||||
$this->assertEquals('Value1,Value2', $parse_headers['X-My-Header']);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDetectContentType()
|
|
||||||
{
|
|
||||||
$req = Request::init();
|
|
||||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
|
||||||
$this->assertEquals('application/json', $response->headers['Content-Type']);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testMissingBodyContentType()
|
|
||||||
{
|
|
||||||
$body = 'A string';
|
|
||||||
$request = Request::post(HttpfulTest::TEST_URL, $body)->_curlPrep();
|
|
||||||
$this->assertEquals($body, $request->serialized_payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testParentType()
|
|
||||||
{
|
|
||||||
// Parent type
|
|
||||||
$request = Request::init()->sendsAndExpects(Mime::XML);
|
|
||||||
$response = new Response('<xml><name>Nathan</name></xml>', self::SAMPLE_VENDOR_HEADER, $request);
|
|
||||||
|
|
||||||
$this->assertEquals("application/xml", $response->parent_type);
|
|
||||||
$this->assertEquals(self::SAMPLE_VENDOR_TYPE, $response->content_type);
|
|
||||||
$this->assertTrue($response->is_mime_vendor_specific);
|
|
||||||
|
|
||||||
// Make sure we still parsed as if it were plain old XML
|
|
||||||
$this->assertEquals("Nathan", $response->body->name->__toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
function testMissingContentType()
|
|
||||||
{
|
|
||||||
// Parent type
|
|
||||||
$request = Request::init()->sendsAndExpects(Mime::XML);
|
|
||||||
$response = new Response(
|
|
||||||
'<xml><name>Nathan</name></xml>',
|
|
||||||
"HTTP/1.1 200 OK
|
|
||||||
Connection: keep-alive
|
|
||||||
Transfer-Encoding: chunked\r\n",
|
|
||||||
$request
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertEquals("", $response->content_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testCustomMimeRegistering()
|
|
||||||
{
|
|
||||||
// Register new mime type handler for "application/vnd.nategood.message+xml"
|
|
||||||
Httpful::register(self::SAMPLE_VENDOR_TYPE, new DemoMimeHandler());
|
|
||||||
|
|
||||||
$this->assertTrue(Httpful::hasParserRegistered(self::SAMPLE_VENDOR_TYPE));
|
|
||||||
|
|
||||||
$request = Request::init();
|
|
||||||
$response = new Response('<xml><name>Nathan</name></xml>', self::SAMPLE_VENDOR_HEADER, $request);
|
|
||||||
|
|
||||||
$this->assertEquals(self::SAMPLE_VENDOR_TYPE, $response->content_type);
|
|
||||||
$this->assertEquals('custom parse', $response->body);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testShorthandMimeDefinition()
|
|
||||||
{
|
|
||||||
$r = Request::init()->expects('json');
|
|
||||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
|
||||||
|
|
||||||
$r = Request::init()->expectsJson();
|
|
||||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testOverrideXmlHandler()
|
|
||||||
{
|
|
||||||
// Lazy test...
|
|
||||||
$prev = \Httpful\Httpful::get(\Httpful\Mime::XML);
|
|
||||||
$this->assertEquals($prev, new \Httpful\Handlers\XmlHandler());
|
|
||||||
$conf = ['namespace' => 'http://example.com'];
|
|
||||||
\Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf));
|
|
||||||
$new = \Httpful\Httpful::get(\Httpful\Mime::XML);
|
|
||||||
$this->assertNotEquals($prev, $new);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DemoMimeHandler extends \Httpful\Handlers\MimeHandlerAdapter
|
|
||||||
{
|
|
||||||
public function parse($body)
|
|
||||||
{
|
|
||||||
return 'custom parse';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
<phpunit>
|
|
||||||
<testsuite name="Httpful">
|
|
||||||
<directory>.</directory>
|
|
||||||
</testsuite>
|
|
||||||
<logging>
|
|
||||||
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
|
|
||||||
</logging>
|
|
||||||
</phpunit>
|
|
||||||
|
|
|
@ -1,519 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Note : Code is released under the GNU LGPL
|
|
||||||
*
|
|
||||||
* Please do not change the header of this file
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU
|
|
||||||
* Lesser General Public License as published by the Free Software Foundation; either version 2 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
* See the GNU Lesser General Public License for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Light PHP wrapper for the OAuth 2.0 protocol.
|
|
||||||
*
|
|
||||||
* This client is based on the OAuth2 specification draft v2.15
|
|
||||||
* http://tools.ietf.org/html/draft-ietf-oauth-v2-15
|
|
||||||
*
|
|
||||||
* @author Pierrick Charron <pierrick@webstart.fr>
|
|
||||||
* @author Anis Berejeb <anis.berejeb@gmail.com>
|
|
||||||
* @version 1.2-dev
|
|
||||||
*/
|
|
||||||
namespace OAuth2;
|
|
||||||
|
|
||||||
class Client
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Different AUTH method
|
|
||||||
*/
|
|
||||||
const AUTH_TYPE_URI = 0;
|
|
||||||
const AUTH_TYPE_AUTHORIZATION_BASIC = 1;
|
|
||||||
const AUTH_TYPE_FORM = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Different Access token type
|
|
||||||
*/
|
|
||||||
const ACCESS_TOKEN_URI = 0;
|
|
||||||
const ACCESS_TOKEN_BEARER = 1;
|
|
||||||
const ACCESS_TOKEN_OAUTH = 2;
|
|
||||||
const ACCESS_TOKEN_MAC = 3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Different Grant types
|
|
||||||
*/
|
|
||||||
const GRANT_TYPE_AUTH_CODE = 'authorization_code';
|
|
||||||
const GRANT_TYPE_PASSWORD = 'password';
|
|
||||||
const GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials';
|
|
||||||
const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTP Methods
|
|
||||||
*/
|
|
||||||
const HTTP_METHOD_GET = 'GET';
|
|
||||||
const HTTP_METHOD_POST = 'POST';
|
|
||||||
const HTTP_METHOD_PUT = 'PUT';
|
|
||||||
const HTTP_METHOD_DELETE = 'DELETE';
|
|
||||||
const HTTP_METHOD_HEAD = 'HEAD';
|
|
||||||
const HTTP_METHOD_PATCH = 'PATCH';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTP Form content types
|
|
||||||
*/
|
|
||||||
const HTTP_FORM_CONTENT_TYPE_APPLICATION = 0;
|
|
||||||
const HTTP_FORM_CONTENT_TYPE_MULTIPART = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Client ID
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $client_id = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Client Secret
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $client_secret = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Client Authentication method
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $client_auth = self::AUTH_TYPE_URI;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access Token
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $access_token = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access Token Type
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $access_token_type = self::ACCESS_TOKEN_URI;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access Token Secret
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $access_token_secret = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access Token crypt algorithm
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $access_token_algorithm = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access Token Parameter name
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $access_token_param_name = 'access_token';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The path to the certificate file to use for https connections
|
|
||||||
*
|
|
||||||
* @var string Defaults to .
|
|
||||||
*/
|
|
||||||
protected $certificate_file = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* cURL options
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $curl_options = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct
|
|
||||||
*
|
|
||||||
* @param string $client_id Client ID
|
|
||||||
* @param string $client_secret Client Secret
|
|
||||||
* @param int $client_auth (AUTH_TYPE_URI, AUTH_TYPE_AUTHORIZATION_BASIC, AUTH_TYPE_FORM)
|
|
||||||
* @param string $certificate_file Indicates if we want to use a certificate file to trust the server. Optional, defaults to null.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct($client_id, $client_secret, $client_auth = self::AUTH_TYPE_URI, $certificate_file = null)
|
|
||||||
{
|
|
||||||
if (!extension_loaded('curl')) {
|
|
||||||
throw new Exception('The PHP exention curl must be installed to use this library.', Exception::CURL_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->client_id = $client_id;
|
|
||||||
$this->client_secret = $client_secret;
|
|
||||||
$this->client_auth = $client_auth;
|
|
||||||
$this->certificate_file = $certificate_file;
|
|
||||||
if (!empty($this->certificate_file) && !is_file($this->certificate_file)) {
|
|
||||||
throw new InvalidArgumentException('The certificate file was not found', InvalidArgumentException::CERTIFICATE_NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the client Id
|
|
||||||
*
|
|
||||||
* @return string Client ID
|
|
||||||
*/
|
|
||||||
public function getClientId()
|
|
||||||
{
|
|
||||||
return $this->client_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the client Secret
|
|
||||||
*
|
|
||||||
* @return string Client Secret
|
|
||||||
*/
|
|
||||||
public function getClientSecret()
|
|
||||||
{
|
|
||||||
return $this->client_secret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getAuthenticationUrl
|
|
||||||
*
|
|
||||||
* @param string $auth_endpoint Url of the authentication endpoint
|
|
||||||
* @param string $redirect_uri Redirection URI
|
|
||||||
* @param array $extra_parameters Array of extra parameters like scope or state (Ex: array('scope' => null, 'state' => ''))
|
|
||||||
* @return string URL used for authentication
|
|
||||||
*/
|
|
||||||
public function getAuthenticationUrl($auth_endpoint, $redirect_uri, array $extra_parameters = [])
|
|
||||||
{
|
|
||||||
$parameters = array_merge([
|
|
||||||
'response_type' => 'code',
|
|
||||||
'client_id' => $this->client_id,
|
|
||||||
'redirect_uri' => $redirect_uri
|
|
||||||
], $extra_parameters);
|
|
||||||
return $auth_endpoint . '?' . http_build_query($parameters, null, '&');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getAccessToken
|
|
||||||
*
|
|
||||||
* @param string $token_endpoint Url of the token endpoint
|
|
||||||
* @param int $grant_type Grant Type ('authorization_code', 'password', 'client_credentials', 'refresh_token', or a custom code (@see GrantType Classes)
|
|
||||||
* @param array $parameters Array sent to the server (depend on which grant type you're using)
|
|
||||||
* @return array Array of parameters required by the grant_type (CF SPEC)
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function getAccessToken($token_endpoint, $grant_type, array $parameters)
|
|
||||||
{
|
|
||||||
if (!$grant_type) {
|
|
||||||
throw new InvalidArgumentException('The grant_type is mandatory.', InvalidArgumentException::INVALID_GRANT_TYPE);
|
|
||||||
}
|
|
||||||
$grantTypeClassName = $this->convertToCamelCase($grant_type);
|
|
||||||
$grantTypeClass = __NAMESPACE__ . '\\GrantType\\' . $grantTypeClassName;
|
|
||||||
if (!class_exists($grantTypeClass)) {
|
|
||||||
throw new InvalidArgumentException('Unknown grant type \'' . $grant_type . '\'', InvalidArgumentException::INVALID_GRANT_TYPE);
|
|
||||||
}
|
|
||||||
$grantTypeObject = new $grantTypeClass();
|
|
||||||
$grantTypeObject->validateParameters($parameters);
|
|
||||||
if (!defined($grantTypeClass . '::GRANT_TYPE')) {
|
|
||||||
throw new Exception('Unknown constant GRANT_TYPE for class ' . $grantTypeClassName, Exception::GRANT_TYPE_ERROR);
|
|
||||||
}
|
|
||||||
$parameters['grant_type'] = $grantTypeClass::GRANT_TYPE;
|
|
||||||
$http_headers = [];
|
|
||||||
switch ($this->client_auth) {
|
|
||||||
case self::AUTH_TYPE_URI:
|
|
||||||
case self::AUTH_TYPE_FORM:
|
|
||||||
$parameters['client_id'] = $this->client_id;
|
|
||||||
$parameters['client_secret'] = $this->client_secret;
|
|
||||||
break;
|
|
||||||
case self::AUTH_TYPE_AUTHORIZATION_BASIC:
|
|
||||||
$parameters['client_id'] = $this->client_id;
|
|
||||||
$http_headers['Authorization'] = 'Basic ' . base64_encode($this->client_id . ':' . $this->client_secret);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception('Unknown client auth type.', Exception::INVALID_CLIENT_AUTHENTICATION_TYPE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->executeRequest($token_endpoint, $parameters, self::HTTP_METHOD_POST, $http_headers, self::HTTP_FORM_CONTENT_TYPE_APPLICATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* setToken
|
|
||||||
*
|
|
||||||
* @param string $token Set the access token
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setAccessToken($token)
|
|
||||||
{
|
|
||||||
$this->access_token = $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the client authentication type
|
|
||||||
*
|
|
||||||
* @param string $client_auth (AUTH_TYPE_URI, AUTH_TYPE_AUTHORIZATION_BASIC, AUTH_TYPE_FORM)
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setClientAuthType($client_auth)
|
|
||||||
{
|
|
||||||
$this->client_auth = $client_auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an option for the curl transfer
|
|
||||||
*
|
|
||||||
* @param int $option The CURLOPT_XXX option to set
|
|
||||||
* @param mixed $value The value to be set on option
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setCurlOption($option, $value)
|
|
||||||
{
|
|
||||||
$this->curl_options[$option] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set multiple options for a cURL transfer
|
|
||||||
*
|
|
||||||
* @param array $options An array specifying which options to set and their values
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setCurlOptions($options)
|
|
||||||
{
|
|
||||||
$this->curl_options = array_merge($this->curl_options, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the access token type
|
|
||||||
*
|
|
||||||
* @param int $type Access token type (ACCESS_TOKEN_BEARER, ACCESS_TOKEN_MAC, ACCESS_TOKEN_URI)
|
|
||||||
* @param string $secret The secret key used to encrypt the MAC header
|
|
||||||
* @param string $algorithm Algorithm used to encrypt the signature
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setAccessTokenType($type, $secret = null, $algorithm = null)
|
|
||||||
{
|
|
||||||
$this->access_token_type = $type;
|
|
||||||
$this->access_token_secret = $secret;
|
|
||||||
$this->access_token_algorithm = $algorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch a protected ressource
|
|
||||||
*
|
|
||||||
* @param string $protected_ressource_url Protected resource URL
|
|
||||||
* @param array $parameters Array of parameters
|
|
||||||
* @param string $http_method HTTP Method to use (POST, PUT, GET, HEAD, DELETE)
|
|
||||||
* @param array $http_headers HTTP headers
|
|
||||||
* @param int $form_content_type HTTP form content type to use
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function fetch($protected_resource_url, $parameters = [], $http_method = self::HTTP_METHOD_GET, array $http_headers = [], $form_content_type = self::HTTP_FORM_CONTENT_TYPE_MULTIPART)
|
|
||||||
{
|
|
||||||
if ($this->access_token) {
|
|
||||||
switch ($this->access_token_type) {
|
|
||||||
case self::ACCESS_TOKEN_URI:
|
|
||||||
if (is_array($parameters)) {
|
|
||||||
$parameters[$this->access_token_param_name] = $this->access_token;
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'You need to give parameters as array if you want to give the token within the URI.',
|
|
||||||
InvalidArgumentException::REQUIRE_PARAMS_AS_ARRAY
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::ACCESS_TOKEN_BEARER:
|
|
||||||
$http_headers['Authorization'] = 'Bearer ' . $this->access_token;
|
|
||||||
break;
|
|
||||||
case self::ACCESS_TOKEN_OAUTH:
|
|
||||||
$http_headers['Authorization'] = 'OAuth ' . $this->access_token;
|
|
||||||
break;
|
|
||||||
case self::ACCESS_TOKEN_MAC:
|
|
||||||
$http_headers['Authorization'] = 'MAC ' . $this->generateMACSignature($protected_resource_url, $parameters, $http_method);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception('Unknown access token type.', Exception::INVALID_ACCESS_TOKEN_TYPE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->executeRequest($protected_resource_url, $parameters, $http_method, $http_headers, $form_content_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate the MAC signature
|
|
||||||
*
|
|
||||||
* @param string $url Called URL
|
|
||||||
* @param array $parameters Parameters
|
|
||||||
* @param string $http_method Http Method
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function generateMACSignature($url, $parameters, $http_method)
|
|
||||||
{
|
|
||||||
$timestamp = time();
|
|
||||||
$nonce = uniqid();
|
|
||||||
$parsed_url = parse_url($url);
|
|
||||||
if (!isset($parsed_url['port'])) {
|
|
||||||
$parsed_url['port'] = ($parsed_url['scheme'] == 'https') ? 443 : 80;
|
|
||||||
}
|
|
||||||
if ($http_method == self::HTTP_METHOD_GET) {
|
|
||||||
if (is_array($parameters)) {
|
|
||||||
$parsed_url['path'] .= '?' . http_build_query($parameters, null, '&');
|
|
||||||
} elseif ($parameters) {
|
|
||||||
$parsed_url['path'] .= '?' . $parameters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$signature = base64_encode(hash_hmac(
|
|
||||||
$this->access_token_algorithm,
|
|
||||||
$timestamp . "\n"
|
|
||||||
. $nonce . "\n"
|
|
||||||
. $http_method . "\n"
|
|
||||||
. $parsed_url['path'] . "\n"
|
|
||||||
. $parsed_url['host'] . "\n"
|
|
||||||
. $parsed_url['port'] . "\n\n",
|
|
||||||
$this->access_token_secret,
|
|
||||||
true
|
|
||||||
));
|
|
||||||
|
|
||||||
return 'id="' . $this->access_token . '", ts="' . $timestamp . '", nonce="' . $nonce . '", mac="' . $signature . '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a request (with curl)
|
|
||||||
*
|
|
||||||
* @param string $url URL
|
|
||||||
* @param mixed $parameters Array of parameters
|
|
||||||
* @param string $http_method HTTP Method
|
|
||||||
* @param array $http_headers HTTP Headers
|
|
||||||
* @param int $form_content_type HTTP form content type to use
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function executeRequest($url, $parameters = [], $http_method = self::HTTP_METHOD_GET, array $http_headers = null, $form_content_type = self::HTTP_FORM_CONTENT_TYPE_MULTIPART)
|
|
||||||
{
|
|
||||||
$curl_options = [
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_SSL_VERIFYPEER => true,
|
|
||||||
CURLOPT_CUSTOMREQUEST => $http_method
|
|
||||||
];
|
|
||||||
|
|
||||||
switch ($http_method) {
|
|
||||||
case self::HTTP_METHOD_POST:
|
|
||||||
$curl_options[CURLOPT_POST] = true;
|
|
||||||
/* No break */
|
|
||||||
case self::HTTP_METHOD_PUT:
|
|
||||||
case self::HTTP_METHOD_PATCH:
|
|
||||||
/**
|
|
||||||
* Passing an array to CURLOPT_POSTFIELDS will encode the data as multipart/form-data,
|
|
||||||
* while passing a URL-encoded string will encode the data as application/x-www-form-urlencoded.
|
|
||||||
* http://php.net/manual/en/function.curl-setopt.php
|
|
||||||
*/
|
|
||||||
if (is_array($parameters) && self::HTTP_FORM_CONTENT_TYPE_APPLICATION === $form_content_type) {
|
|
||||||
$parameters = http_build_query($parameters, null, '&');
|
|
||||||
}
|
|
||||||
$curl_options[CURLOPT_POSTFIELDS] = $parameters;
|
|
||||||
break;
|
|
||||||
case self::HTTP_METHOD_HEAD:
|
|
||||||
$curl_options[CURLOPT_NOBODY] = true;
|
|
||||||
/* No break */
|
|
||||||
case self::HTTP_METHOD_DELETE:
|
|
||||||
case self::HTTP_METHOD_GET:
|
|
||||||
if (is_array($parameters)) {
|
|
||||||
$url .= '?' . http_build_query($parameters, null, '&');
|
|
||||||
} elseif ($parameters) {
|
|
||||||
$url .= '?' . $parameters;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$curl_options[CURLOPT_URL] = $url;
|
|
||||||
|
|
||||||
if (is_array($http_headers)) {
|
|
||||||
$header = [];
|
|
||||||
foreach ($http_headers as $key => $parsed_urlvalue) {
|
|
||||||
$header[] = "$key: $parsed_urlvalue";
|
|
||||||
}
|
|
||||||
$curl_options[CURLOPT_HTTPHEADER] = $header;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ch = curl_init();
|
|
||||||
curl_setopt_array($ch, $curl_options);
|
|
||||||
// https handling
|
|
||||||
if (!empty($this->certificate_file)) {
|
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
|
||||||
curl_setopt($ch, CURLOPT_CAINFO, $this->certificate_file);
|
|
||||||
} else {
|
|
||||||
// bypass ssl verification
|
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
|
||||||
}
|
|
||||||
if (!empty($this->curl_options)) {
|
|
||||||
curl_setopt_array($ch, $this->curl_options);
|
|
||||||
}
|
|
||||||
$result = curl_exec($ch);
|
|
||||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
|
||||||
if ($curl_error = curl_error($ch)) {
|
|
||||||
throw new Exception($curl_error, Exception::CURL_ERROR);
|
|
||||||
} else {
|
|
||||||
$json_decode = json_decode($result, true);
|
|
||||||
}
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'result' => (null === $json_decode) ? $result : $json_decode,
|
|
||||||
'code' => $http_code,
|
|
||||||
'content_type' => $content_type
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the name of the parameter that carry the access token
|
|
||||||
*
|
|
||||||
* @param string $name Token parameter name
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setAccessTokenParamName($name)
|
|
||||||
{
|
|
||||||
$this->access_token_param_name = $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the class name to camel case
|
|
||||||
*
|
|
||||||
* @param mixed $grant_type the grant type
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function convertToCamelCase($grant_type)
|
|
||||||
{
|
|
||||||
$parts = explode('_', $grant_type);
|
|
||||||
array_walk($parts, function (&$item) {
|
|
||||||
$item = ucfirst($item);
|
|
||||||
});
|
|
||||||
return implode('', $parts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Exception extends \Exception
|
|
||||||
{
|
|
||||||
const CURL_NOT_FOUND = 0x01;
|
|
||||||
const CURL_ERROR = 0x02;
|
|
||||||
const GRANT_TYPE_ERROR = 0x03;
|
|
||||||
const INVALID_CLIENT_AUTHENTICATION_TYPE = 0x04;
|
|
||||||
const INVALID_ACCESS_TOKEN_TYPE = 0x05;
|
|
||||||
}
|
|
||||||
|
|
||||||
class InvalidArgumentException extends \InvalidArgumentException
|
|
||||||
{
|
|
||||||
const INVALID_GRANT_TYPE = 0x01;
|
|
||||||
const CERTIFICATE_NOT_FOUND = 0x02;
|
|
||||||
const REQUIRE_PARAMS_AS_ARRAY = 0x03;
|
|
||||||
const MISSING_PARAMETER = 0x04;
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace OAuth2\GrantType;
|
|
||||||
|
|
||||||
use OAuth2\InvalidArgumentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authorization code Grant Type Validator
|
|
||||||
*/
|
|
||||||
class AuthorizationCode implements IGrantType
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Defines the Grant Type
|
|
||||||
*
|
|
||||||
* @var string Defaults to 'authorization_code'.
|
|
||||||
*/
|
|
||||||
const GRANT_TYPE = 'authorization_code';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a specific Handling of the parameters
|
|
||||||
*
|
|
||||||
* @return array of Specific parameters to be sent.
|
|
||||||
* @param mixed $parameters the parameters array (passed by reference)
|
|
||||||
*/
|
|
||||||
public function validateParameters(&$parameters)
|
|
||||||
{
|
|
||||||
if (!isset($parameters['code'])) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'The \'code\' parameter must be defined for the Authorization Code grant type',
|
|
||||||
InvalidArgumentException::MISSING_PARAMETER
|
|
||||||
);
|
|
||||||
} elseif (!isset($parameters['redirect_uri'])) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'The \'redirect_uri\' parameter must be defined for the Authorization Code grant type',
|
|
||||||
InvalidArgumentException::MISSING_PARAMETER
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace OAuth2\GrantType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Client Credentials Parameters
|
|
||||||
*/
|
|
||||||
class ClientCredentials implements IGrantType
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Defines the Grant Type
|
|
||||||
*
|
|
||||||
* @var string Defaults to 'client_credentials'.
|
|
||||||
*/
|
|
||||||
const GRANT_TYPE = 'client_credentials';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a specific Handling of the parameters
|
|
||||||
*
|
|
||||||
* @return array of Specific parameters to be sent.
|
|
||||||
* @param mixed $parameters the parameters array (passed by reference)
|
|
||||||
*/
|
|
||||||
public function validateParameters(&$parameters)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace OAuth2\GrantType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specific GrantType Interface
|
|
||||||
*/
|
|
||||||
interface IGrantType
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Adds a specific Handling of the parameters
|
|
||||||
*
|
|
||||||
* @return array of Specific parameters to be sent.
|
|
||||||
* @param mixed $parameters the parameters array (passed by reference)
|
|
||||||
*/
|
|
||||||
public function validateParameters(&$parameters);
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace OAuth2\GrantType;
|
|
||||||
|
|
||||||
use OAuth2\InvalidArgumentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Password Parameters
|
|
||||||
*/
|
|
||||||
class Password implements IGrantType
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Defines the Grant Type
|
|
||||||
*
|
|
||||||
* @var string Defaults to 'password'.
|
|
||||||
*/
|
|
||||||
const GRANT_TYPE = 'password';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a specific Handling of the parameters
|
|
||||||
*
|
|
||||||
* @return array of Specific parameters to be sent.
|
|
||||||
* @param mixed $parameters the parameters array (passed by reference)
|
|
||||||
*/
|
|
||||||
public function validateParameters(&$parameters)
|
|
||||||
{
|
|
||||||
if (!isset($parameters['username'])) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'The \'username\' parameter must be defined for the Password grant type',
|
|
||||||
InvalidArgumentException::MISSING_PARAMETER
|
|
||||||
);
|
|
||||||
} elseif (!isset($parameters['password'])) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'The \'password\' parameter must be defined for the Password grant type',
|
|
||||||
InvalidArgumentException::MISSING_PARAMETER
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace OAuth2\GrantType;
|
|
||||||
|
|
||||||
use OAuth2\InvalidArgumentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh Token Parameters
|
|
||||||
*/
|
|
||||||
class RefreshToken implements IGrantType
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Defines the Grant Type
|
|
||||||
*
|
|
||||||
* @var string Defaults to 'refresh_token'.
|
|
||||||
*/
|
|
||||||
const GRANT_TYPE = 'refresh_token';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a specific Handling of the parameters
|
|
||||||
*
|
|
||||||
* @return array of Specific parameters to be sent.
|
|
||||||
* @param mixed $parameters the parameters array (passed by reference)
|
|
||||||
*/
|
|
||||||
public function validateParameters(&$parameters)
|
|
||||||
{
|
|
||||||
if (!isset($parameters['refresh_token'])) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'The \'refresh_token\' parameter must be defined for the refresh token grant type',
|
|
||||||
InvalidArgumentException::MISSING_PARAMETER
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
___________________________________
|
|
||||||
|
|
||||||
Light PHP wrapper for the OAuth 2.0
|
|
||||||
___________________________________
|
|
||||||
|
|
||||||
|
|
||||||
AUTHOR & CONTACT
|
|
||||||
================
|
|
||||||
|
|
||||||
Charron Pierrick
|
|
||||||
- pierrick@webstart.fr
|
|
||||||
|
|
||||||
Berejeb Anis
|
|
||||||
- anis.berejeb@gmail.com
|
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION & DOWNLOAD
|
|
||||||
========================
|
|
||||||
|
|
||||||
Latest version is available on github at :
|
|
||||||
- https://github.com/adoy/PHP-OAuth2
|
|
||||||
|
|
||||||
Documentation can be found on :
|
|
||||||
- https://github.com/adoy/PHP-OAuth2
|
|
||||||
|
|
||||||
|
|
||||||
LICENSE
|
|
||||||
=======
|
|
||||||
|
|
||||||
This Code is released under the GNU LGPL
|
|
||||||
|
|
||||||
Please do not change the header of the file(s).
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or modify it
|
|
||||||
under the terms of the GNU Lesser General Public License as published
|
|
||||||
by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the GNU Lesser General Public License for more details.
|
|
||||||
|
|
||||||
|
|
||||||
How can I use it ?
|
|
||||||
==================
|
|
||||||
|
|
||||||
require('client.php');
|
|
||||||
require('GrantType/IGrantType.php');
|
|
||||||
require('GrantType/AuthorizationCode.php');
|
|
||||||
|
|
||||||
const CLIENT_ID = 'your client id';
|
|
||||||
const CLIENT_SECRET = 'your client secret';
|
|
||||||
|
|
||||||
const REDIRECT_URI = 'http://url/of/this.php';
|
|
||||||
const AUTHORIZATION_ENDPOINT = 'https://graph.facebook.com/oauth/authorize';
|
|
||||||
const TOKEN_ENDPOINT = 'https://graph.facebook.com/oauth/access_token';
|
|
||||||
|
|
||||||
$client = new OAuth2\Client(CLIENT_ID, CLIENT_SECRET);
|
|
||||||
if (!isset($_GET['code']))
|
|
||||||
{
|
|
||||||
$auth_url = $client->getAuthenticationUrl(AUTHORIZATION_ENDPOINT, REDIRECT_URI);
|
|
||||||
header('Location: ' . $auth_url);
|
|
||||||
die('Redirect');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$params = array('code' => $_GET['code'], 'redirect_uri' => REDIRECT_URI);
|
|
||||||
$response = $client->getAccessToken(TOKEN_ENDPOINT, 'authorization_code', $params);
|
|
||||||
parse_str($response['result'], $info);
|
|
||||||
$client->setAccessToken($info['access_token']);
|
|
||||||
$response = $client->fetch('https://graph.facebook.com/me');
|
|
||||||
var_dump($response, $response['result']);
|
|
||||||
}
|
|
||||||
|
|
||||||
How can I add a new Grant Type ?
|
|
||||||
================================
|
|
||||||
Simply write a new class in the namespace OAuth2\GrantType. You can place the class file under GrantType.
|
|
||||||
Here is an example :
|
|
||||||
|
|
||||||
namespace OAuth2\GrantType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MyCustomGrantType Grant Type
|
|
||||||
*/
|
|
||||||
class MyCustomGrantType implements IGrantType
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Defines the Grant Type
|
|
||||||
*
|
|
||||||
* @var string Defaults to 'my_custom_grant_type'.
|
|
||||||
*/
|
|
||||||
const GRANT_TYPE = 'my_custom_grant_type';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a specific Handling of the parameters
|
|
||||||
*
|
|
||||||
* @return array of Specific parameters to be sent.
|
|
||||||
* @param mixed $parameters the parameters array (passed by reference)
|
|
||||||
*/
|
|
||||||
public function validateParameters(&$parameters)
|
|
||||||
{
|
|
||||||
if (!isset($parameters['first_mandatory_parameter']))
|
|
||||||
{
|
|
||||||
throw new \Exception('The \'first_mandatory_parameter\' parameter must be defined for the Password grant type');
|
|
||||||
}
|
|
||||||
elseif (!isset($parameters['second_mandatory_parameter']))
|
|
||||||
{
|
|
||||||
throw new \Exception('The \'seconde_mandatory_parameter\' parameter must be defined for the Password grant type');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
call the OAuth client getAccessToken with the grantType you defined in the GRANT_TYPE constant, As following :
|
|
||||||
$response = $client->getAccessToken(TOKEN_ENDPOINT, 'my_custom_grant_type', $params);
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"name": "adoy/oauth2",
|
|
||||||
"description": "Light PHP wrapper for the OAuth 2.0 protocol (based on OAuth 2.0 Authorization Protocol draft-ietf-oauth-v2-15)",
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Charron Pierrick",
|
|
||||||
"email": "pierrick@webstart.fr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Berejeb Anis",
|
|
||||||
"email": "anis.berejeb@gmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"autoload": {
|
|
||||||
"classmap": [
|
|
||||||
"../"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -643,7 +643,6 @@ class Track extends Model implements Searchable, Commentable, Favouritable
|
||||||
public function setTitleAttribute($value)
|
public function setTitleAttribute($value)
|
||||||
{
|
{
|
||||||
$this->setTitleAttributeSlug($value);
|
$this->setTitleAttributeSlug($value);
|
||||||
;
|
|
||||||
$this->updateHash();
|
$this->updateHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
"ksubileau/color-thief-php": "^1.3",
|
"ksubileau/color-thief-php": "^1.3",
|
||||||
"graham-campbell/exceptions": "^9.1",
|
"graham-campbell/exceptions": "^9.1",
|
||||||
"minishlink/web-push": "^1.0",
|
"minishlink/web-push": "^1.0",
|
||||||
"alsofronie/eloquent-uuid": "^1.0"
|
"alsofronie/eloquent-uuid": "^1.0",
|
||||||
|
"poniverse/api": "dev-rewrite"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fzaninotto/faker": "~1.4",
|
"fzaninotto/faker": "~1.4",
|
||||||
|
|
437
composer.lock
generated
437
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -168,6 +168,7 @@ return [
|
||||||
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
||||||
Cviebrock\LaravelElasticsearch\ServiceProvider::class,
|
Cviebrock\LaravelElasticsearch\ServiceProvider::class,
|
||||||
GrahamCampbell\Exceptions\ExceptionsServiceProvider::class,
|
GrahamCampbell\Exceptions\ExceptionsServiceProvider::class,
|
||||||
|
Poniverse\Lib\PoniverseServiceProvider::class
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -195,14 +196,14 @@ return [
|
||||||
'DB' => Illuminate\Support\Facades\DB::class,
|
'DB' => Illuminate\Support\Facades\DB::class,
|
||||||
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
|
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
|
||||||
'Event' => Illuminate\Support\Facades\Event::class,
|
'Event' => Illuminate\Support\Facades\Event::class,
|
||||||
// 'File' => Illuminate\Support\Facades\File::class,
|
// 'File' => Illuminate\Support\Facades\File::class,
|
||||||
'Gate' => Illuminate\Support\Facades\Gate::class,
|
'Gate' => Illuminate\Support\Facades\Gate::class,
|
||||||
'Hash' => Illuminate\Support\Facades\Hash::class,
|
'Hash' => Illuminate\Support\Facades\Hash::class,
|
||||||
'Inspiring' => Illuminate\Foundation\Inspiring::class,
|
'Inspiring' => Illuminate\Foundation\Inspiring::class,
|
||||||
'Lang' => Illuminate\Support\Facades\Lang::class,
|
'Lang' => Illuminate\Support\Facades\Lang::class,
|
||||||
'Log' => Illuminate\Support\Facades\Log::class,
|
'Log' => Illuminate\Support\Facades\Log::class,
|
||||||
'Mail' => Illuminate\Support\Facades\Mail::class,
|
'Mail' => Illuminate\Support\Facades\Mail::class,
|
||||||
'Notification' => Illuminate\Support\Facades\Notification::class,
|
// 'Notification' => Illuminate\Support\Facades\Notification::class,
|
||||||
'Password' => Illuminate\Support\Facades\Password::class,
|
'Password' => Illuminate\Support\Facades\Password::class,
|
||||||
'Queue' => Illuminate\Support\Facades\Queue::class,
|
'Queue' => Illuminate\Support\Facades\Queue::class,
|
||||||
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
'version' => 1,
|
|
||||||
'urls' => [
|
|
||||||
'api' => env('PONI_API_URL', 'https://api.poniverse.net/v1/'),
|
|
||||||
'register' => env('PONI_REGISTER_URL', 'https://poniverse.net/register?site=pony.fm'),
|
|
||||||
'auth' => env('PONI_AUTH_URL', 'https://poniverse.net/oauth/authorize'),
|
|
||||||
'token' => env('PONI_TOKEN_URL', 'https://poniverse.net/oauth/access_token')
|
|
||||||
],
|
|
||||||
'client_id' => env('PONI_CLIENT_ID'),
|
'client_id' => env('PONI_CLIENT_ID'),
|
||||||
'secret' => env('PONI_CLIENT_SECRET')
|
'secret' => env('PONI_CLIENT_SECRET')
|
||||||
];
|
];
|
||||||
|
|
|
@ -35,34 +35,36 @@ class AddDeletedAtColumnToActivities extends Migration
|
||||||
$table->softDeletes()->index();
|
$table->softDeletes()->index();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Retroactively fix activities that should be marked as deleted.
|
if ('sqlite' !== DB::getDriverName()) {
|
||||||
// Tracks
|
// Retroactively fix activities that should be marked as deleted.
|
||||||
DB::table('activities')
|
// Tracks
|
||||||
->where('resource_type', 2)
|
DB::table('activities')
|
||||||
->join('tracks', 'activities.resource_id', '=', 'tracks.id')
|
->where('resource_type', 2)
|
||||||
->whereNotNull('tracks.deleted_at')
|
->join('tracks', 'activities.resource_id', '=', 'tracks.id')
|
||||||
->update(['deleted_at' => DB::raw('tracks.deleted_at')]);
|
->whereNotNull('tracks.deleted_at')
|
||||||
|
->update(['deleted_at' => DB::raw('tracks.deleted_at')]);
|
||||||
|
|
||||||
// Albums
|
// Albums
|
||||||
DB::table('activities')
|
DB::table('activities')
|
||||||
->where('resource_type', 3)
|
->where('resource_type', 3)
|
||||||
->join('albums', 'activities.resource_id', '=', 'albums.id')
|
->join('albums', 'activities.resource_id', '=', 'albums.id')
|
||||||
->whereNotNull('albums.deleted_at')
|
->whereNotNull('albums.deleted_at')
|
||||||
->update(['deleted_at' => DB::raw('albums.deleted_at')]);
|
->update(['deleted_at' => DB::raw('albums.deleted_at')]);
|
||||||
|
|
||||||
// Playlists
|
// Playlists
|
||||||
DB::table('activities')
|
DB::table('activities')
|
||||||
->where('resource_type', 4)
|
->where('resource_type', 4)
|
||||||
->join('playlists', 'activities.resource_id', '=', 'playlists.id')
|
->join('playlists', 'activities.resource_id', '=', 'playlists.id')
|
||||||
->whereNotNull('playlists.deleted_at')
|
->whereNotNull('playlists.deleted_at')
|
||||||
->update(['deleted_at' => DB::raw('playlists.deleted_at')]);
|
->update(['deleted_at' => DB::raw('playlists.deleted_at')]);
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
DB::table('activities')
|
DB::table('activities')
|
||||||
->where('resource_type', 5)
|
->where('resource_type', 5)
|
||||||
->join('comments', 'activities.resource_id', '=', 'comments.id')
|
->join('comments', 'activities.resource_id', '=', 'comments.id')
|
||||||
->whereNotNull('comments.deleted_at')
|
->whereNotNull('comments.deleted_at')
|
||||||
->update(['deleted_at' => DB::raw('comments.deleted_at')]);
|
->update(['deleted_at' => DB::raw('comments.deleted_at')]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,7 @@ class AddVersionColumnToTracksTable extends Migration
|
||||||
public function down()
|
public function down()
|
||||||
{
|
{
|
||||||
Schema::table('tracks', function (Blueprint $table) {
|
Schema::table('tracks', function (Blueprint $table) {
|
||||||
$table->dropColumn('current_version');
|
$table->dropColumn(['current_version', 'version_upload_status']);
|
||||||
$table->dropColumn('version_upload_status');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,7 @@ class UpdateSessionsTableForLaravel52 extends Migration
|
||||||
public function down()
|
public function down()
|
||||||
{
|
{
|
||||||
Schema::table('sessions', function(Blueprint $table) {
|
Schema::table('sessions', function(Blueprint $table) {
|
||||||
$table->dropColumn('user_id');
|
$table->dropColumn(['user_id', 'ip_address', 'user_agent']);
|
||||||
$table->dropColumn('ip_address');
|
|
||||||
$table->dropColumn('user_agent');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
use Illuminate\Foundation\Testing\WithoutMiddleware;
|
use Illuminate\Foundation\Testing\WithoutMiddleware;
|
||||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Poniverse\Lib\AccessToken;
|
||||||
use Poniverse\Ponyfm\Models\User;
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
|
||||||
class ApiAuthTest extends TestCase
|
class ApiAuthTest extends TestCase
|
||||||
|
@ -35,22 +36,22 @@ class ApiAuthTest extends TestCase
|
||||||
public function testApiCreatesNewUser()
|
public function testApiCreatesNewUser()
|
||||||
{
|
{
|
||||||
$user = factory(User::class)->make();
|
$user = factory(User::class)->make();
|
||||||
$accessTokenInfo = new \Poniverse\AccessTokenInfo('nonsense-token');
|
$accessTokenInfo = new AccessToken('nonsense-token');
|
||||||
$accessTokenInfo->setIsActive(true);
|
$accessTokenInfo->setIsActive(true);
|
||||||
$accessTokenInfo->setScopes(['basic', 'ponyfm:tracks:upload']);
|
$accessTokenInfo->setScopes(['basic', 'ponyfm:tracks:upload']);
|
||||||
|
|
||||||
$poniverse = Mockery::mock('overload:Poniverse');
|
$poniverse = Mockery::mock('overload:Poniverse\Lib\OAuth2\PoniverseProvider');
|
||||||
$poniverse->shouldReceive('getUser')
|
$poniverse->shouldReceive('getResourceOwner')
|
||||||
->andReturn([
|
->andReturn(new \Poniverse\Lib\Entity\Poniverse\User([
|
||||||
|
'id' => $user->id,
|
||||||
'username' => $user->username,
|
'username' => $user->username,
|
||||||
'display_name' => $user->display_name,
|
'display_name' => $user->display_name,
|
||||||
'email' => $user->email,
|
'email' => $user->email,
|
||||||
]);
|
]));
|
||||||
|
|
||||||
$poniverse->shouldReceive('setAccessToken');
|
$poniverse->shouldReceive('setAccessToken');
|
||||||
|
|
||||||
$poniverse
|
$accessTokenService = Mockery::mock('overload:Poniverse\Lib\Service\Poniverse\AccessTokenInfo');
|
||||||
->shouldReceive('getAccessTokenInfo')
|
$accessTokenService->shouldReceive('introspect')
|
||||||
->andReturn($accessTokenInfo);
|
->andReturn($accessTokenInfo);
|
||||||
|
|
||||||
$this->dontSeeInDatabase('users', ['username' => $user->username]);
|
$this->dontSeeInDatabase('users', ['username' => $user->username]);
|
||||||
|
@ -61,23 +62,25 @@ class ApiAuthTest extends TestCase
|
||||||
public function testApiClientIdIsRecordedWhenUploadingTrack()
|
public function testApiClientIdIsRecordedWhenUploadingTrack()
|
||||||
{
|
{
|
||||||
$user = factory(User::class)->make();
|
$user = factory(User::class)->make();
|
||||||
$accessTokenInfo = new \Poniverse\AccessTokenInfo('nonsense-token');
|
|
||||||
|
$accessTokenInfo = new AccessToken('nonsense-token');
|
||||||
$accessTokenInfo->setIsActive(true);
|
$accessTokenInfo->setIsActive(true);
|
||||||
$accessTokenInfo->setClientId('Unicorns and rainbows');
|
$accessTokenInfo->setClientId('Unicorns and rainbows');
|
||||||
$accessTokenInfo->setScopes(['basic', 'ponyfm:tracks:upload']);
|
$accessTokenInfo->setScopes(['basic', 'ponyfm:tracks:upload']);
|
||||||
|
|
||||||
$poniverse = Mockery::mock('overload:Poniverse');
|
$poniverse = Mockery::mock('overload:Poniverse\Lib\OAuth2\PoniverseProvider');
|
||||||
$poniverse->shouldReceive('getUser')
|
$poniverse->shouldReceive('getResourceOwner')
|
||||||
->andReturn([
|
->andReturn(new \Poniverse\Lib\Entity\Poniverse\User([
|
||||||
|
'id' => $user->id,
|
||||||
'username' => $user->username,
|
'username' => $user->username,
|
||||||
'display_name' => $user->display_name,
|
'display_name' => $user->display_name,
|
||||||
'email' => $user->email,
|
'email' => $user->email,
|
||||||
]);
|
]));
|
||||||
|
|
||||||
$poniverse->shouldReceive('setAccessToken');
|
$poniverse->shouldReceive('setAccessToken');
|
||||||
|
|
||||||
$poniverse
|
$accessTokenService = Mockery::mock('overload:Poniverse\Lib\Service\Poniverse\AccessTokenInfo');
|
||||||
->shouldReceive('getAccessTokenInfo')
|
$accessTokenService
|
||||||
|
->shouldReceive('introspect')
|
||||||
->andReturn($accessTokenInfo);
|
->andReturn($accessTokenInfo);
|
||||||
|
|
||||||
$this->callUploadWithParameters(['access_token' => $accessTokenInfo->getToken()]);
|
$this->callUploadWithParameters(['access_token' => $accessTokenInfo->getToken()]);
|
||||||
|
|
|
@ -123,7 +123,7 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
|
||||||
Storage::disk('local')->makeDirectory('testing-datastore/tmp');
|
Storage::disk('local')->makeDirectory('testing-datastore/tmp');
|
||||||
Storage::disk('local')->copy("test-files/${filename}", "testing-datastore/tmp/${filename}");
|
Storage::disk('local')->copy("test-files/${filename}", "testing-datastore/tmp/${filename}");
|
||||||
|
|
||||||
return new \Symfony\Component\HttpFoundation\File\UploadedFile(storage_path("app/testing-datastore/tmp/${filename}"), $filename, null, null, null, true);
|
return new \Illuminate\Http\UploadedFile(storage_path("app/testing-datastore/tmp/${filename}"), $filename, null, null, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue