1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-03 09:49:28 +02:00

Bug fixes

This commit is contained in:
DanieL 2022-11-18 14:44:58 -03:00
parent bf29c081fa
commit 7e90293d00
6061 changed files with 655309 additions and 131371 deletions

1
.gitignore vendored
View file

@ -82,3 +82,4 @@ objects/ezyang/
/plugin/TestOnly/
view/videoComments_bkp.php
/.compose/
test.php

View file

@ -1,3 +1,4 @@
{
"git.ignoreLimitWarning": true
"git.ignoreLimitWarning": true,
"editor.fontSize": 18
}

View file

@ -43,6 +43,8 @@
"norkunas/onesignal-php-api": "^2.7",
"stripe/stripe-php": "^9.1",
"symfony/translation": "^5.3",
"amphp/amp": "^2.6"
"amphp/amp": "^2.6",
"scssphp/scssphp": "^1.11",
"vimeo/vimeo-api": "^3.0"
}
}

1484
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4437,6 +4437,23 @@ function _mysql_connect($persistent = false) {
}
}
function _mysql_commit() {
global $global;
if (_mysql_is_open()) {
try{
/**
*
* @var array $global
* @var object $global['mysqli']
*/
@$global['mysqli']->commit();
}catch(Exception $exc){
}
//$global['mysqli'] = false;
}
}
function _mysql_close() {
global $global, $mysql_connect_was_closed;
if (_mysql_is_open()) {

View file

@ -98,7 +98,7 @@ class PlayList extends ObjectYPT {
* @param type $publicOnly
* @param type $userId if not present check session
* @param type $isVideoIdPresent pass the ID of the video checking
* @return boolean
* @return array
*/
public static function getAllFromUser($userId, $publicOnly = true, $status = false, $playlists_id = 0, $try = 0, $includeSeries = false) {
global $global, $config, $refreshCacheFromPlaylist;
@ -203,7 +203,8 @@ class PlayList extends ObjectYPT {
array_unshift($rows, $watch_later);
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
$rows = array();
}
return $rows;
}
@ -267,7 +268,8 @@ class PlayList extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
$rows = array();
}
return $rows;
}
@ -378,7 +380,8 @@ class PlayList extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
$rows = array();
}
$getVideosIDFromPlaylistLight[$playlists_id] = $rows;
return $rows;
@ -445,7 +448,8 @@ class PlayList extends ObjectYPT {
$cache = self::setCache($cacheName, $rows);
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
$rows = array();
}
} else {
$rows = object_to_array($rows);
@ -732,6 +736,9 @@ class PlayList extends ObjectYPT {
return $this->users_id;
}
/**
* @return string
*/
public function getStatus() {
return $this->status;
}
@ -811,7 +818,8 @@ class PlayList extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
$rows = array();
}
return $rows;
}

View file

@ -163,7 +163,7 @@ class Subscribe extends ObjectYPT{
* return all subscribers that has subscribe to an user channel
* @global type $global
* @param type $user_id
* @return boolean
* @return array
*/
public static function getAllSubscribes($user_id = "", $status = "a", $verifiedOnly = false)
{
@ -217,11 +217,10 @@ class Subscribe extends ObjectYPT{
$subscribe[] = $row;
}
//$subscribe = $res->fetch_all(MYSQLI_ASSOC);
} else {
$subscribe = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
ObjectYPT::setCache($cacheName, $subscribe);
} else {
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
} else {
$subscribe = object_to_array($subscribe);
}
@ -290,9 +289,6 @@ class Subscribe extends ObjectYPT{
$subscribe[] = $row;
}
//$subscribe = $res->fetch_all(MYSQLI_ASSOC);
} else {
$subscribe = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $subscribe;
}

View file

@ -2117,6 +2117,9 @@ if (typeof gtag !== \"function\") {
return $userId;
}
/**
* @return string
*/
public function getRecoverPass()
{
return $this->recoverPass;
@ -2496,6 +2499,7 @@ if (typeof gtag !== \"function\") {
_error_log("sendVerificationLink: Email already sent, we will wait 30 min {$users_id}");
return true;
}
try {
$config = new Configuration();
$code = urlencode(static::createVerificationCode($users_id));
//Create a new PHPMailer instance
@ -2505,9 +2509,11 @@ if (typeof gtag !== \"function\") {
}
$contactEmail = $config->getContactEmail();
$webSiteTitle = $config->getWebSiteTitle();
/**
* @var string $email
*/
$email = '';
$email = $user->getEmail();
try {
$mail = new \PHPMailer\PHPMailer\PHPMailer();
setSiteSendMessage($mail);
//$mail->SMTPDebug = 4;

View file

@ -68,6 +68,11 @@ class UserGroups{
$values = [$this->group_name];
}
if (sqlDAL::writeSql($sql, $formats, $values)) {
/**
*
* @var array $global
* @var object $global['mysqli']
*/
if (empty($this->id)) {
$id = $global['mysqli']->insert_id;
} else {
@ -131,7 +136,7 @@ class UserGroups{
//$category = $res->fetch_all(MYSQLI_ASSOC);
} else {
$arr = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $arr;
}
@ -152,7 +157,7 @@ class UserGroups{
//$category = $res->fetch_all(MYSQLI_ASSOC);
} else {
$arr = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $arr;
}
@ -310,7 +315,7 @@ class UserGroups{
}
} else {
$arr = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
$__getUserGroups[$users_id] = $arr;
return $arr;
@ -366,7 +371,6 @@ class UserGroups{
}
$sql = "INSERT INTO videos_group_view ( videos_id, users_groups_id) VALUES (?,?)";
$value = intval($value);
$response = sqlDAL::writeSql($sql, "ii", [$videos_id,$users_groups_id]);
if ($response) {
@ -443,7 +447,7 @@ class UserGroups{
}
} else {
$arr = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $arr;
}
@ -469,7 +473,7 @@ class UserGroups{
}
} else {
$arr = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $arr;
}

View file

@ -540,7 +540,7 @@ if (!class_exists('Video')) {
* @var array $global
* @var object $global['mysqli']
*/
_error_log('Video::save Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error . " $sql");
_error_log('Video::save Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
return false;
}
@ -1193,12 +1193,12 @@ if (!class_exists('Video')) {
* @param type $showOnlyLoggedUserVideos you may pass an user ID to filter results
* @param type $ignoreGroup
* @param type $videosArrayId an array with videos to return (for filter only)
* @return boolean
* @return array
*/
public static function getAllVideos($status = "viewable", $showOnlyLoggedUserVideos = false, $ignoreGroup = false, $videosArrayId = [], $getStatistcs = false, $showUnlisted = false, $activeUsersOnly = true, $suggestedOnly = false, $is_serie = null, $type = '') {
global $global, $config, $advancedCustom, $advancedCustomUser;
if ($config->currentVersionLowerThen('11.7')) {
return false;
return array();
}
/**
*
@ -1435,6 +1435,11 @@ if (!class_exists('Video')) {
// for the cache on the database fast insert
TimeLogEnd($timeLogName, __LINE__, 0.2);
/**
*
* @var array $global
* @var object $global['mysqli']
*/
$global['mysqli']->begin_transaction();
foreach ($fullData as $row) {
if (is_null($row['likes'])) {
@ -1634,11 +1639,11 @@ if (!class_exists('Video')) {
}
public static function updateFilesizeFromFilename($filename) {
$value = Video::getVideoFromFileNameLight($filename);
$video = Video::getVideoFromFileNameLight($filename);
if ($video['type'] !== 'video' && $video['type'] !== 'audio') {
return false;
}
return self::updateFilesize($value['id']);
return self::updateFilesize($video['id']);
}
public static function updateFilesize($videos_id) {
@ -1963,6 +1968,7 @@ if (!class_exists('Video')) {
}
static function getSearchFieldsNames() {
global $advancedCustomUser;
$searchFieldsNames = self::$searchFieldsNames;
if ($advancedCustomUser->videosSearchAlsoSearchesOnChannelName) {
$searchFieldsNames[] = 'u.channelName';
@ -2158,7 +2164,7 @@ if (!class_exists('Video')) {
$resp = sqlDAL::writeSql($sql, "i", [$this->id]);
if ($resp == false) {
_error_log('Error (delete on video) : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//_error_log('Error (delete on video) : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
return false;
} else {
$this->removeVideoFiles();
@ -2482,7 +2488,9 @@ if (!class_exists('Video')) {
public function getFilename() {
return $this->filename;
}
/**
* return string
*/
public function getStatus() {
return $this->status;
}
@ -2500,52 +2508,6 @@ if (!class_exists('Video')) {
$this->videoDownloadedLink = $videoDownloadedLink;
}
public static function isLandscape($pathFileName) {
global $config;
// get movie duration HOURS:MM:SS.MICROSECONDS
if (!file_exists($pathFileName)) {
echo '{"status":"error", "msg":"isLandscape ERROR, File (' . $pathFileName . ') Not Found"}';
return true;
}
eval('$cmd="' . $config->getExiftool() . '";');
$resp = true; // is landscape by default
exec($cmd . ' 2>&1', $output, $return_val);
if ($return_val !== 0) {
$resp = true;
} else {
$w = 1;
$h = 0;
$rotation = 0;
foreach ($output as $value) {
preg_match("/Image Size.*:[^0-9]*([0-9]+x[0-9]+)/i", $value, $match);
if (!empty($match)) {
$parts = explode("x", $match[1]);
$w = $parts[0];
$h = $parts[1];
}
preg_match("/Rotation.*:[^0-9]*([0-9]+)/i", $value, $match);
if (!empty($match)) {
$rotation = $match[1];
}
}
if ($rotation == 0) {
if ($w > $h) {
$resp = true;
} else {
$resp = false;
}
} else {
if ($w < $h) {
$resp = true;
} else {
$resp = false;
}
}
}
//var_dump($cmd, $w, $h, $rotation, $resp);exit;
return $resp;
}
public function userCanManageVideo() {
global $advancedCustomUser;
if (Permissions::canAdminVideos()) {
@ -2744,6 +2706,9 @@ if (!class_exists('Video')) {
if (empty($type) || $type === "status") {
$objTag = new stdClass();
$objTag->label = __("Status");
/**
* @var string $status
*/
$status = $video->getStatus();
$objTag->text = __(Video::$statusDesc[$status]);
switch ($status) {
@ -3022,7 +2987,7 @@ if (!class_exists('Video')) {
}
} else {
$videos = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return false;
}
@ -3680,6 +3645,11 @@ if (!class_exists('Video')) {
$search = array_merge($search, $global['langs_codes_values_withdot']);
}
/**
*
* @var array $global
* @var array $global['avideo_resolutions']
*/
foreach ($global['avideo_resolutions'] as $value) {
$search[] = "_{$value}";
@ -3888,6 +3858,11 @@ if (!class_exists('Video')) {
public static function getHigestResolutionVideoMP4Source($filename, $includeS3 = false) {
global $global;
$types = ['', '_HD', '_SD', '_Low'];
/**
*
* @var array $global
* @var array $global['avideo_resolutions']
*/
$resolutions = $global['avideo_resolutions'];
rsort($resolutions);
foreach ($resolutions as $value) {
@ -3987,6 +3962,11 @@ if (!class_exists('Video')) {
$types = ['', '_Low', '_SD', '_HD'];
/**
*
* @var array $global
* @var array $global['avideo_resolutions']
*/
foreach ($global['avideo_resolutions'] as $value) {
$types[] = "_{$value}";
}
@ -4362,7 +4342,7 @@ if (!class_exists('Video')) {
}
} else {
$videos = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return false;
}
@ -4380,7 +4360,7 @@ if (!class_exists('Video')) {
}
} else {
$videos = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return false;
}
@ -5122,6 +5102,11 @@ if (!class_exists('Video')) {
public static function getChangeVideoStatusButton($videos_id) {
$video = new Video('', '', $videos_id);
/**
*
* @var string $status
*/
$status = $video->getStatus();
$activeBtn = '<button onclick="changeVideoStatus(' . $videos_id . ', \'u\');" style="color: #090" type="button" '
@ -5413,7 +5398,7 @@ if (!class_exists('Video')) {
}
$galleryDropDownMenu = Gallery::getVideoDropdownMenu($videos_id);
$galleryVideoButtons = '';
$galleryVideoButtons .= '
<!-- getVideoImagewithHoverAnimationFromVideosId --><div class="galleryVideoButtons '.getCSSAnimationClassAndStyle('animate__flipInY', uniqid(), 0).'">
<button onclick="addVideoToPlayList(' . $videos_id . ', false, ' . $watchLaterId . ');return false;" class="btn btn-dark btn-xs watchLaterBtnAdded watchLaterBtnAdded' . $videos_id . '" data-toggle="tooltip" data-placement="left" title=' . printJSString("Added On Watch Later", true) . ' style="color: #4285f4;' . $watchLaterBtnAddedStyle . '" ><i class="fas fa-check"></i></button>
@ -5698,7 +5683,7 @@ if (!class_exists('Video')) {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $rows;
}

View file

@ -64,6 +64,11 @@ class VideoStatistic extends ObjectYPT {
. "(now(),?," . $userId . ",?,{$lastVideoTime},now(),now(),'" . session_id() . "')";
$insert_row = sqlDAL::writeSql($sql, "si", [getRealIpAddr(), $videos_id]);
//if($videos_id==4){_error_log($sql);}
/**
*
* @var array $global
* @var object $global['mysqli']
*/
if (!empty($global['mysqli']->insert_id)) {
return $global['mysqli']->insert_id;
} else {
@ -513,7 +518,6 @@ class VideoStatistic extends ObjectYPT {
if (!empty($result2)) {
return intval($result2['total']);
}
ObjectYPT::setCache($cacheName, $result);
return 0;
}
@ -601,7 +605,8 @@ class VideoStatistic extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
$rows = array();
}
return $rows;
}
@ -665,7 +670,7 @@ class VideoStatistic extends ObjectYPT {
$rows = array();
if ($res != false) {
$totalViews = 0;
$totalWatchingTIme = 0;
$totalWatchingTime = 0;
foreach ($fullData as $row) {
$sql = "SELECT count(s.videos_id) total_views, sum(seconds_watching_video) as seconds_watching_video FROM " . static::getTableName() . " s WHERE 1=1 ";
@ -704,8 +709,6 @@ class VideoStatistic extends ObjectYPT {
$row2['seconds_watching_video_human2'] = seconds2human($row2['seconds_watching_video']);
$row2['totalViews'] = $totalViews;
$row2['totalWatchingTime'] = $totalWatchingTime;
$row2['totalWatchingTimeHuman'] = $totalWatchingTimeHuman;
$row2['totalWatchingTimeHuman2'] = $totalWatchingTimeHuman2;
$rows[] = array_merge($row, $row2);
}

View file

@ -118,7 +118,7 @@ class AD_Server extends PluginAbstract {
_error_log("AD_Server:addVideoIdIntoCampaignId videos_id NOT found {$videos_id}");
}
} else {
_error_log("AD_Server:addVideoIdIntoCampaignId autoAddNewVideosInCampaignId NOT found " . json_encode($obj->autoAddNewVideosInCampaignId));
_error_log("AD_Server:addVideoIdIntoCampaignId autoAddNewVideosInCampaignId NOT found ");
}
} else {
_error_log("AD_Server:addVideoIdIntoCampaignId is disabled");

View file

@ -109,7 +109,7 @@ In the Reset keys tab, press the Reset button, update the consumer key and secre
if(AVideoPlugin::isEnabledByName('BitLy')){
$url = BitLy::getLink($videos_id);
}else{
$url = Video::getLinkToVideo($videos_id, "", false, "permalink", $get);
$url = Video::getLinkToVideo($videos_id, "", false, "permalink");
}
_error_log("AutoPostOnSocialMedia::postVideo($videos_id) $url");
return self::post($url);

View file

@ -51,20 +51,20 @@ class BlockonomicsYPT extends PluginAbstract {
$currency = $objWallet->currency;
//return here if total is empty
if (empty($total_cost)) {
echo $json_response = json_encode(array("error" => "Total Is Empty"));
return;
echo json_encode(array("error" => "Total Is Empty"));
return false;
}
if (!User::isLogged()) {
echo $json_response = json_encode(array("error" => "Must be logged in"));
return;
echo json_encode(array("error" => "Must be logged in"));
return false;
}
//Generate new address for this invoice
$new_address = $this->getNewAddress($obj->APIKey);
if (empty($new_address)) {
_error_log('Blockonomics ERROR 1: ' . $contents . ' ' . json_last_error_msg(), AVideoLog::$ERROR);
_error_log('Blockonomics ERROR 1: ' . json_last_error_msg(), AVideoLog::$ERROR);
return false;
}

View file

@ -407,12 +407,6 @@ class CustomizeUser extends PluginAbstract {
public function onUserSignup($users_id) {
global $global;
$obj = $this->getDataObject();
/**
* No need to send verification email here
if ($obj->sendVerificationMailAutomatic) {
url_get_contents("{$global['webSiteRootURL']}objects/userVerifyEmail.php?users_id=$users_id");
}
*/
}
public function getWatchActionButton($videos_id) {

View file

@ -112,7 +112,7 @@ class Users_affiliations extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $rows;
}
@ -142,7 +142,7 @@ class Users_affiliations extends ObjectYPT {
}
public function save() {
global $global;
if (empty($this->id) && !empty($this->users_id_company) && !empty($this->users_id_affiliate)) {
$row = self::getAll($this->users_id_company, $this->users_id_affiliate);
//var_dump($row);

View file

@ -226,6 +226,7 @@ function createGallerySection($videos, $crc = "", $get = array(), $ignoreAds = f
}
function getLabelTags($video) {
global $global;
$obj = AVideoPlugin::getObjectData("Gallery");
if (empty($_GET['catName']) && !empty($obj->showCategoryTag)) {
$iconClass = 'fas fa-folder';

View file

@ -380,6 +380,19 @@ class Live extends PluginAbstract {
$_getLiveApplicationModelArray_counter = 0;
}
$users_id = '';
$title = '';
$link = '';
$imgJPG = '';
$imgGIF = '';
$type = '';
$LiveUsersLabelLive = '';
$uid = '';
$callback = '';
$startsOnDate = '';
$class = '';
$description = '';
$expectedValues = array(
'users_id',
'title',
@ -1380,16 +1393,6 @@ Click <a href=\"{link}\">here</a> to join our live.";
return $o->stats;
}
public function getChat($uuid) {
global $global;
//check if LiveChat Plugin is available
$filename = $global['systemRootPath'] . 'plugin/LiveChat/LiveChat.php';
if (file_exists($filename)) {
require_once $filename;
LiveChat::includeChatPanel($uuid);
}
}
public function getStatsObject($live_servers_id = 0, $force_recreate = false, $tries = 0) {
global $global;
@ -2709,7 +2712,9 @@ Click <a href=\"{link}\">here</a> to join our live.";
public static function getPrerollPosterImage($users_id = 0, $live_servers_id = 0, $live_schedule_id = 0) {
return self::getPosterImage($users_id, $live_servers_id, $live_schedule_id, self::$posterType_preroll);
}
/**
* @return object
*/
public static function getPrerollPosterImageTimes($users_id = 0, $live_servers_id = 0, $live_schedule_id = 0) {
global $global;
$path = self::getPrerollPosterImage($users_id, $live_servers_id, $live_schedule_id);
@ -2899,10 +2904,6 @@ Click <a href=\"{link}\">here</a> to join our live.";
}
}
public static function getPosterFromKey($key, $live_servers_id, $live_index = '') {
$key = self::getLatestKeyFromUser($users_id);
}
public static function getOfflineImage($includeURL = true) {
global $global;
$img = "plugin/Live/view/Offline.jpg";
@ -3281,8 +3282,7 @@ Click <a href=\"{link}\">here</a> to join our live.";
}
//$videos = $res->fetch_all(MYSQLI_ASSOC);
} else {
$videos = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $videos;
}
@ -3531,7 +3531,6 @@ Click <a href=\"{link}\">here</a> to join our live.";
'live_servers_id' => $live_servers_id,
'live_index' => $live_index,
'playlists_id_live' => $playlists_id_live,
'playlists_id_live' => $keyWithIndex,
'history' => false,
'isLive' => false,
'isFinished' => false,
@ -3737,7 +3736,8 @@ Click <a href=\"{link}\">here</a> to join our live.";
public function getWatchActionButton($videos_id): string {
$isLive = isLive();
if (!empty($isLive['live_schedule'])) {
return '<button class="btn btn-default no-outline" onclick="avideoModalIframeSmall(webSiteRootURL+\'plugin/Live/remindMe.php?live_schedule_id=' . $isLive['live_schedule'] . '\');"><i class="fas fa-bell"></i> ' . __('Remind Me') . '</button>';
return '<button class="btn btn-default no-outline" onclick="avideoModalIframeSmall(webSiteRootURL+\'plugin/Live/remindMe.php?live_schedule_id=' .
$isLive['live_schedule'] . '\');"><i class="fas fa-bell"></i> ' . __('Remind Me') . '</button>';
}
return '';
}
@ -3882,7 +3882,9 @@ class LiveStreamObject {
$this->key = $parts['cleanKey'];
$this->live_index = preg_replace('/[^0-9a-z]/i', '', $this->live_index);
}
/**
* @return string
*/
public function getKey() {
return $this->key;
}

View file

@ -48,7 +48,9 @@ class LiveTransmitionHistory extends ObjectYPT {
public function getDescription() {
return $this->description;
}
/**
* @return string
*/
public function getKey() {
return $this->key;
}
@ -61,6 +63,10 @@ class LiveTransmitionHistory extends ObjectYPT {
return $this->modified;
}
/**
*
* @return int
*/
public function getUsers_id() {
return $this->users_id;
}
@ -154,7 +160,11 @@ class LiveTransmitionHistory extends ObjectYPT {
function setTotal_viewers($total_viewers): void {
$this->total_viewers = intval($total_viewers);
}
/**
*
* @param int $liveTransmitionHistory_id
* @return object
*/
public static function getApplicationObject($liveTransmitionHistory_id) {
global $global;
$_playlists_id_live = @$_REQUEST['playlists_id_live'];
@ -237,7 +247,9 @@ class LiveTransmitionHistory extends ObjectYPT {
public static function getStatsAndAddApplication($liveTransmitionHistory_id) {
$stats = getStatsNotifications();
$lth = new LiveTransmitionHistory($liveTransmitionHistory_id);
/**
* @var string $key
*/
$key = $lth->getKey();
if (!empty($stats['applications'])) {
foreach ($stats['applications'] as $value) {
@ -263,6 +275,9 @@ class LiveTransmitionHistory extends ObjectYPT {
}
}
}
/**
* @var object $application
*/
$application = self::getApplicationObject($liveTransmitionHistory_id);
if ($application->isPrivate) {
$stats['hidden_applications'][] = $application;
@ -355,7 +370,7 @@ class LiveTransmitionHistory extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
////die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $rows;
}
@ -430,7 +445,7 @@ class LiveTransmitionHistory extends ObjectYPT {
$sql = "UPDATE " . static::getTableName() . " SET finished = now() WHERE id = {$live_transmitions_history_id} ";
$insert_row = sqlDAL::writeSql($sql);
$global['mysqli']->commit();
_mysql_commit();
Live::unfinishAllFromStats();
return $insert_row;
@ -447,7 +462,7 @@ class LiveTransmitionHistory extends ObjectYPT {
$sql = "UPDATE " . static::getTableName() . " SET modified = now() WHERE id = {$live_transmitions_history_id} ";
$insert_row = sqlDAL::writeSql($sql);
$global['mysqli']->commit();
_mysql_commit();
Live::unfinishAllFromStats();
return $insert_row;
@ -462,7 +477,7 @@ class LiveTransmitionHistory extends ObjectYPT {
$sql = "UPDATE " . static::getTableName() . " SET finished = NULL WHERE id = {$live_transmitions_history_id} ";
$insert_row = sqlDAL::writeSql($sql);
$global['mysqli']->commit();
_mysql_commit();
return $insert_row;
}
@ -558,7 +573,7 @@ class LiveTransmitionHistory extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $rows;
}
@ -610,7 +625,7 @@ class LiveTransmitionHistory extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $rows;
}
@ -682,7 +697,7 @@ class LiveTransmitionHistory extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $rows;
}
@ -690,7 +705,7 @@ class LiveTransmitionHistory extends ObjectYPT {
public function save() {
global $global;
$global['mysqli']->commit();
_mysql_commit();
/*
$activeLive = self::getActiveLiveFromUser($this->users_id, $this->live_servers_id, $this->key);
if(!empty($activeLive)){
@ -734,7 +749,7 @@ class LiveTransmitionHistory extends ObjectYPT {
$id = parent::save();
_error_log("LiveTransmitionHistory::save: id=$id ($this->users_id, $this->live_servers_id, $this->key) ". json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)));
$global['mysqli']->commit();
_mysql_commit();
return $id;
}

View file

@ -263,9 +263,6 @@ $global['doNotLoadPlayer'] = 1;
</div>
<?php
$p->getChat($trasnmition['key']);
?>
<?php
include $global['systemRootPath'] . 'view/include/footer.php';
?>
<script>

View file

@ -67,10 +67,6 @@ $poster = Live::getPosterImage($livet['users_id'], $_REQUEST['live_servers_id'],
padding-right: 0 !important;
padding-left: 0 !important;
}
.liveChat .messages{
-webkit-transition: all 1s ease; /* Safari */
transition: all 1s ease;
}
#embedVideo-content .embed-responsive{
max-height: 98vh;
}
@ -117,20 +113,7 @@ $poster = Live::getPosterImage($livet['users_id'], $_REQUEST['live_servers_id'],
echo getAdsLeaderBoardFooter();
?>
</div>
<div class="col-md-3 col-sm-3 col-xs-3" style="margin: 0; padding: 0;">
<?php
$p->getChat($uuid);
?>
</div>
</div>
<script>
$(function () {
$('.liveChat .messages').css({"height": ($(window).height() - 128) + "px"});
window.addEventListener('resize', function () {
$('.liveChat .messages').css({"height": ($(window).height() - 128) + "px"});
})
});
</script>
<?php
include $global['systemRootPath'] . 'view/include/video.min.js.php';
?>

View file

@ -92,6 +92,12 @@ class LiveLinks extends PluginAbstract {
$sql .= " ORDER BY start_date ";
//echo $sql;//exit;
/**
*
* @var array $global
* @var object $global['mysqli']
*/
$res = $global['mysqli']->query($sql);
$rows = array();
if ($res) {
@ -211,7 +217,11 @@ class LiveLinks extends PluginAbstract {
return $js . $css;
}
/**
* @param int $id
* @param boolean $embed
* @return string
*/
public static function getLinkToLiveFromId($id, $embed = false) {
return self::getLink($id, $embed);
}
@ -390,8 +400,7 @@ class LiveLinks extends PluginAbstract {
}
//$videos = $res->fetch_all(MYSQLI_ASSOC);
} else {
$videos = false;
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $videos;
}
@ -472,11 +481,11 @@ class LiveLinks extends PluginAbstract {
$ll = new LiveLinks($id);
$posters = array();
//var_dump($posters);exit;
$category = Category::getCategory($lt['categories_id']);
$category = Category::getCategory($ll['categories_id']);
$MediaMetadata = new stdClass();
$MediaMetadata->title = $lt['title'];
$MediaMetadata->artist = User::getNameIdentificationById($lt['users_id']);
$MediaMetadata->title = $ll['title'];
$MediaMetadata->artist = User::getNameIdentificationById($ll['users_id']);
$MediaMetadata->album = $category['name'];
$MediaMetadata->artwork = array();
foreach ($posters as $key => $value) {

View file

@ -139,7 +139,7 @@ class LiveLinksTable extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $rows;
}
@ -227,7 +227,7 @@ class LiveLinksTable extends ObjectYPT {
$rows[] = $row;
}
} else {
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
//die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $rows;
}

View file

@ -196,6 +196,10 @@ if (isHTMLEmpty($sideAd)) {
</div>
<?php
if (empty($advancedCustom->disableShareAndPlaylist) && empty($advancedCustom->disableShareOnly)) {
/**
* @var string $link
* @var string $linkEmbed
*/
$link = LiveLinks::getLinkToLiveFromId($_GET['link']);
$linkEmbed = LiveLinks::getLinkToLiveFromId($_GET['link'], true);
getShareMenu($t['title'], $link, $link, $linkEmbed, $img, "row");
@ -224,12 +228,6 @@ if (isHTMLEmpty($sideAd)) {
<?php
include $global['systemRootPath'] . 'view/include/footer.php';
?>
<?php
if (!empty($p)) {
$p->getChat($uuid);
}
?>
</body>
</html>

View file

@ -262,12 +262,12 @@ class PayPalYPT extends PluginAbstract {
} catch (PayPal\Exception\PayPalConnectionException $ex) {
_error_log("PayPal Error createBillingPlan 1: " . $ex->getData());
} catch (Exception $ex) {
_error_log("PayPal Error createBillingPlan 2: " . $ex->getData());
_error_log("PayPal Error createBillingPlan 2: " . $ex->getMessage());
}
} catch (PayPal\Exception\PayPalConnectionException $ex) {
_error_log("PayPal Error createBillingPlan 3: " . $ex->getData());
} catch (Exception $ex) {
_error_log("PayPal Error createBillingPlan 4: " . $ex->getData());
_error_log("PayPal Error createBillingPlan 4: " . $ex->getMessage());
}
return false;
}
@ -351,7 +351,7 @@ class PayPalYPT extends PluginAbstract {
} catch (PayPal\Exception\PayPalConnectionException $ex) {
_error_log("PayPal Error createBillingPlan 5: startDate: {$startDate} " . $ex->getData());
} catch (Exception $ex) {
_error_log("PayPal Error createBillingPlan 6: startDate: {$startDate} " . $ex->getData());
_error_log("PayPal Error createBillingPlan 6: startDate: {$startDate} " . $ex->getMessage());
}
return false;
}
@ -402,7 +402,7 @@ class PayPalYPT extends PluginAbstract {
} catch (PayPal\Exception\PayPalConnectionException $ex) {
_error_log("setUpSubscriptionV2: PayPal Error createBillingPlan 5: startDate: {$startDate} " . $ex->getData());
} catch (Exception $ex) {
_error_log("setUpSubscriptionV2: PayPal Error createBillingPlan 6: startDate: {$startDate} " . $ex->getData());
_error_log("setUpSubscriptionV2: PayPal Error createBillingPlan 6: startDate: {$startDate} " . $ex->getMessage());
}
return false;
}
@ -495,12 +495,12 @@ class PayPalYPT extends PluginAbstract {
} catch (PayPal\Exception\PayPalConnectionException $ex) {
_error_log("createBillingPlanV2: PayPal Error createBillingPlan 1: " . $ex->getData());
} catch (Exception $ex) {
_error_log("createBillingPlanV2: PayPal Error createBillingPlan 2: " . $ex->getData());
_error_log("createBillingPlanV2: PayPal Error createBillingPlan 2: " . $ex->getMessage());
}
} catch (PayPal\Exception\PayPalConnectionException $ex) {
_error_log("createBillingPlanV2: PayPal Error createBillingPlan 3: " . $ex->getData());
} catch (Exception $ex) {
_error_log("createBillingPlanV2: PayPal Error createBillingPlan 4: " . $ex->getData());
_error_log("createBillingPlanV2: PayPal Error createBillingPlan 4: " . $ex->getMessage());
}
return false;
}
@ -639,7 +639,7 @@ class PayPalYPT extends PluginAbstract {
return Plan::get($createdPlan->getId(), $apiContext);
} catch (Exception $ex) {
_error_log("PayPal Error updateBillingPlan: " . $ex->getData());
_error_log("PayPal Error updateBillingPlan: " . $ex->getMessage());
}
return false;
}
@ -662,11 +662,7 @@ class PayPalYPT extends PluginAbstract {
$get_magic_quotes_exists = true;
}
foreach ($myPost as $key => $value) {
if ($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
$value = urlencode(stripslashes($value));
} else {
$value = urlencode($value);
}
$req .= "&$key=$value";
}
@ -803,7 +799,7 @@ class PayPalYPT extends PluginAbstract {
}
if (empty($receiver_email)) {
$obj->msg = "PayPal::Payout The user {$users_id_to_be_paid} does not have a paypal receiver email";
$obj->msg = "PayPal::Payout The user does not have a paypal receiver email";
_error_log($obj->msg);
return $obj;
}
@ -840,7 +836,7 @@ class PayPalYPT extends PluginAbstract {
$obj->error = false;
}
return $obj;
} catch (HttpException $e) {
} catch (\PayPalHttp\HttpException $e) {
$msg = '';
//Parse failure response
$msg .= $e->getMessage() . PHP_EOL;
@ -859,7 +855,7 @@ class PayPalYPT extends PluginAbstract {
$request = new PaypalPayoutsSDK\Payouts\PayoutsGetRequest($payout_batch_id);
$client = PayPalClient::client();
return $client->execute($request);
} catch (HttpException $e) {
} catch (\PayPalHttp\HttpException $e) {
$msg = '';
//Parse failure response
$msg .= $e->getMessage() . PHP_EOL;
@ -867,6 +863,7 @@ class PayPalYPT extends PluginAbstract {
$msg .= $error->message . PHP_EOL;
$msg .= $error->name . PHP_EOL;
$msg .= $error->debug_id . PHP_EOL;
$obj = new stdClass();
$obj->msg = 'PayPal::PayoutInfo ' . $msg;
_error_log($obj->msg);
}
@ -915,7 +912,7 @@ class PayPalYPT extends PluginAbstract {
}
return self::createWebhook();
} catch (Exception $ex) {
_error_log("List all webhooks " . $ex->getMessage() . jaon_encode($webhookId));
_error_log("List all webhooks " . $ex->getMessage());
return false;
}
return false;
@ -976,7 +973,7 @@ class PayPalYPT extends PluginAbstract {
$webhook->delete($apiContext);
}
} catch (Exception $ex) {
_error_log("Deleted all Webhooks " . $ex->getMessage() . jaon_encode($output));
_error_log("Deleted all Webhooks " . $ex->getMessage());
return false;
}
return true;

View file

@ -308,7 +308,7 @@ class PlayerSkins extends PluginAbstract {
$js = "<!-- playerSkin -->";
$obj = $this->getDataObject();
if (!empty($_GET['videoName']) || !empty($_GET['u']) || !empty($_GET['evideo']) || !empty($_GET['playlists_id'])) {
if (empty($obj->showLoopButton) && empty($playerSkinsObj->contextMenuLoop)) {
if (empty($obj->showLoopButton) && empty($obj->contextMenuLoop)) {
$js .= "<script>setPlayerLoop(false);</script>";
}
if ($obj->showLogoOnEmbed && isEmbed() || $obj->showLogo) {
@ -730,7 +730,7 @@ class PlayerSkins extends PluginAbstract {
$bt = debug_backtrace();
$file = str_replace($global['systemRootPath'], '', $bt[0]['file']);
$onPlayerReady = '';
$onPlayerReady .= " /* {$file} */
player.markers({markerStyle: {
'width': '{$width}px',

View file

@ -23,7 +23,7 @@
</p>
<dl>
<dt>
<img src = "<? php echo $ global ['webSiteRootURL'];?> plugin / PlayerSkins / allowAutoplay / firefox.png" title = "">
<img src="<?php echo getURL('plugin/PlayerSkins/allowAutoplay/firefox.png');?>" title="Firefox">
</dt>
</dl>
</small>

View file

@ -304,6 +304,9 @@ function createEPG($channel) {
*
*/
$minutes = getDurationInMinutes($program['start'], $program['stop']);
/**
* @var int $timeLineElementSize
*/
$left = ($minuteSize * $minutesSinceZeroTime) + $timeLineElementSize;
$width = ($minuteSize * $minutes);
$pclass = '';
@ -345,6 +348,11 @@ $positionNow = ($minuteSize * $minutesSince0Time) + $timeLineElementSize;
$bgColors = array('#222222', '#333333', '#444444', '#555555');
_ob_start();
//var_dump($minuteSize, $minutes,$positionNow);exit;
$animateJson = '{scrollLeft: $(\'#positionNow\').position().left - '.($timeLineElementSize + 50);
if (!empty($videos_id)) {
$animateJson .= ', scrollTop: ($(\'#video_'.$videos_id.'\').offset().top) - 100';
}
$animateJson .= '}';
?><!DOCTYPE html>
<html>
<head>
@ -576,14 +584,7 @@ _ob_start();
}
function goToPositionNow() {
$('html, body').animate({
scrollLeft: ($('#positionNow').position().left -<?php echo $timeLineElementSize + 50; ?>),
<?php
if (!empty($videos_id)) {
echo "scrollTop: (($(\"#video_{$videos_id}\").offset().top) - 100)";
}
?>
}, 1000);
$('html, body').animate(<?php echo $animateJson; ?>, 1000);
}
</script>
</body>

View file

@ -230,6 +230,9 @@ abstract class PluginAbstract {
return false;
}
eval("\$array = {$name}::getDataObject{$type}();");
/**
* @var array $array
*/
return in_array($parameter_name, $array);
}

View file

@ -91,6 +91,7 @@ class SlackBot extends PluginAbstract
if ($slackChannel != "") {
//Send the message to the user as a slack bot if the slack channel was returned for the users email
$paylod = new stdClass();
$paylod->text = $username . " just uploaded a video\nVideo Name: " . $videoName . "\nVideo Link: " . $videoLink . "\nVideo Duration: " . $videoDuration;
$paylod->channel = $slackChannel;
$message = json_encode($paylod);

View file

@ -3,7 +3,7 @@
require_once $global['systemRootPath'] . 'plugin/Plugin.abstract.php';
require_once $global['systemRootPath'] . 'plugin/VimeoAPI/Objects/VimeoUploads.php';
require_once $global['systemRootPath'] . 'plugin/VimeoAPI/vimeo-api/autoload.php';
require_once $global['systemRootPath'] . 'vendor/vimeo/vimeo-api/autoload.php';
use Vimeo\Vimeo;
use Vimeo\Exceptions\VimeoUploadException;
@ -48,7 +48,7 @@ class VimeoAPI extends PluginAbstract {
public function afterNewVideo($videos_id) {
$vimeoObj = $this->getDataObject();
if ($obj->automaticallyUploadToVimeo) {
if ($vimeoObj->automaticallyUploadToVimeo) {
$this->upload($videos_id);
}
}
@ -148,7 +148,7 @@ class VimeoAPI extends PluginAbstract {
// We may have had an error. We can't resolve it here necessarily, so report it to the user.
$object->msg = 'Error uploading (' . $video->getTitle() . ') ' . $e->getMessage();
_error_log('Vimeo::upload ' . $file_name . ' ' . $object->msg);
} catch (VimeoRequestException $e) {
} catch (Exception $e) {
$object->msg = 'Error uploading (' . $video->getTitle() . ') ' . $e->getMessage();
_error_log('Vimeo::upload ' . $file_name . ' ' . $object->msg);
}

View file

@ -1,47 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaME0xCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIg
U2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83
nf36QYSvx6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bd
KpPDkC55gIDvEwRqFDu1m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f
/ld0Uzs1gN2ujkSYs58O09rg1/RrKatEp0tYhG2SS4HD2nOLEpdIkARFdRrdNzGX
kujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJTvOX6+guqw9ypzAO+sf0
/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8C
AQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY
aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6
Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1
oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RD
QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHVLyjnjUY4tCzh
xtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB
CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl
5TlPHoOlblyYoiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA
8MXW5dRNJ2Srm8c+cftIl7gzbckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC
2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8sjX7tN8Cp1Tm5gr8ZDOo0rwAhaPit
c+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopYJeS4d60tbvVS3bR0
j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw
MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn
TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5
BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H
4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y
7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB
o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm
8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF
BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr
EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt
tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886
UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----

View file

@ -0,0 +1,14 @@
<?php
$finder = PhpCsFixer\Finder::create()->in(['src', 'tests']);
return (new PhpCsFixer\Config())
->setRules([
'@PSR12' => true,
'not_operator_with_space' => true,
'single_quote' => true,
'binary_operator_spaces' => ['operators' => ['=' => 'align_single_space']],
'native_function_invocation' => ['include' => ['@compiler_optimized']],
])
->setRiskyAllowed(true)
->setFinder($finder);

21
vendor/ankitpokhrel/tus-php/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Ankit Pokhrel
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.

508
vendor/ankitpokhrel/tus-php/README.md vendored Normal file
View file

@ -0,0 +1,508 @@
<h1 align="center">TusPHP</h1>
<p align="center">
<a href="https://packagist.org/packages/ankitpokhrel/tus-php">
<img alt="PHP Version" src="https://img.shields.io/badge/php-7.2.5%2B-brightgreen.svg?style=flat-square" />
</a>
<a href="https://github.com/ankitpokhrel/tus-php/actions/workflows/ci.yml?query=branch%3Amain+is%3Acompleted">
<img alt="Build Status" src="https://img.shields.io/github/workflow/status/ankitpokhrel/tus-php/CI?style=flat-square" />
</a>
<a href="https://scrutinizer-ci.com/g/ankitpokhrel/tus-php">
<img alt="Code Coverage" src="https://img.shields.io/scrutinizer/coverage/g/ankitpokhrel/tus-php.svg?style=flat-square" />
</a>
<a href="https://scrutinizer-ci.com/g/ankitpokhrel/tus-php">
<img alt="Scrutinizer Code Quality" src="https://img.shields.io/scrutinizer/g/ankitpokhrel/tus-php.svg?style=flat-square" />
</a>
<a href="https://packagist.org/packages/ankitpokhrel/tus-php">
<img alt="Downloads" src="https://img.shields.io/packagist/dm/ankitpokhrel/tus-php.svg?style=flat-square" />
</a>
<a href="https://github.com/ankitpokhrel/tus-php/blob/main/LICENSE">
<img alt="Software License" src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square" />
</a>
</p>
<p align="center">
<i align="center">Resumable file upload in PHP using <a href="https://tus.io">tus resumable upload protocol v1.0.0</a></i>
</p>
<p align="center">
<img alt="TusPHP Demo" src="https://github.com/ankitpokhrel/tus-php/blob/main/example/demo.gif" /><br/><br/>
<a href="https://medium.com/@ankitpokhrel/resumable-file-upload-in-php-handle-large-file-uploads-in-an-elegant-way-e6c6dfdeaedb">Medium Article</a>&nbsp;&nbsp;<a href="https://github.com/ankitpokhrel/tus-php/wiki/Laravel-&-Lumen-Integration">Laravel & Lumen Integration</a>&nbsp;&nbsp;<a href="https://github.com/ankitpokhrel/tus-php/wiki/Symfony-Integration">Symfony Integration</a>&nbsp;&nbsp;<a href="https://github.com/ankitpokhrel/tus-php/wiki/CakePHP-Integration">CakePHP Integration</a>&nbsp;&nbsp;<a href="https://github.com/ankitpokhrel/tus-php/wiki/WordPress-Integration">WordPress Integration</a>
</p>
<p align="center">
<a href="https://opencollective.com/tus-php#backers" target="_blank" align="center"><img src="https://opencollective.com/tus-php/backers.svg"></a>
</p>
**tus** is a HTTP based protocol for resumable file uploads. Resumable means you can carry on where you left off without
re-uploading whole data again in case of any interruptions. An interruption may happen willingly if the user wants
to pause, or by accident in case of a network issue or server outage.
### Table of Contents
* [Installation](#installation)
* [Usage](#usage)
* [Server](#server)
* [Nginx](#nginx)
* [Apache](#apache)
* [Client](#client)
* [Third Party Client Libraries](#third-party-client-libraries)
* [Cloud Providers](#cloud-providers)
* [Extension support](#extension-support)
* [Expiration](#expiration)
* [Concatenation](#concatenation)
* [Events](#events)
* [Responding to an Event](#responding-to-an-event)
* [Middleware](#middleware)
* [Creating a Middleware](#creating-a-middleware)
* [Adding a Middleware](#adding-a-middleware)
* [Skipping a Middleware](#skipping-a-middleware)
* [Setting up a dev environment and/or running examples locally](#setting-up-a-dev-environment-andor-running-examples-locally)
* [Docker](#docker)
* [Contributing](#contributing)
* [Questions about this project?](#questions-about-this-project)
* [Supporters](#supporters)
### Installation
Pull the package via composer.
```shell
$ composer require ankitpokhrel/tus-php
// Use v1 for php7.1, Symfony 3 or 4.
$ composer require ankitpokhrel/tus-php:^1.2
```
### Usage
| ![Basic Tus Architecture](https://cdn-images-1.medium.com/max/2000/1*N4JhqeXJgWA1Z7pc6_5T_A.png "Basic Tus Architecture") |
|:--:|
| Basic Tus Architecture |
#### Server
This is how a simple server looks like.
```php
// server.php
// Either redis, file or apcu. Leave empty for file based cache.
$server = new \TusPhp\Tus\Server('redis');
$response = $server->serve();
$response->send();
exit(0); // Exit from current PHP process.
```
> :bangbang: File based cache is not recommended for production use.
You need to rewrite your server to respond to a specific endpoint. For example:
###### Nginx
```nginx
# nginx.conf
location /files {
try_files $uri $uri/ /server.php?$query_string;
}
```
A new config option [fastcgi_request_buffering](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_request_buffering) is available since nginx 1.7.11.
When buffering is enabled, the entire request body is read from the client before sending the request to a FastCGI server. Disabling this option might help with timeouts during the upload.
Furthermore, it helps if youre running out of disc space on the tmp partition of your system.
If you do not turn off `fastcgi_request_buffering` and you use `fastcgi`, you will not be able to resume uploads because nginx will not give the request back to PHP until the entire file is uploaded.
```nginx
location ~ \.php$ {
# ...
fastcgi_request_buffering off; # Disable request buffering
# ...
}
```
A sample nginx configuration can be found [here](docker/server/configs/default.conf).
###### Apache
```apache
# .htaccess
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^files/?(.*)?$ /server.php?$1 [QSA,L]
```
Default max upload size is 0 which means there is no restriction. You can set max upload size as described below.
```php
$server->setMaxUploadSize(100000000); // 100 MB in bytes
```
Default redis and file configuration for server and client can be found inside `config/server.php` and `config/client.php` respectively.
To override default config you can simply copy the file to your preferred location and update the parameters. You then need to set the config before doing anything else.
```php
\TusPhp\Config::set('<path to your config>');
$server = new \TusPhp\Tus\Server('redis');
```
Alternately, you can set `REDIS_HOST`, `REDIS_PORT` and `REDIS_DB` env in your server to override redis settings for both server and client.
#### Client
The client can be used for creating, resuming and/or deleting uploads.
```php
$client = new \TusPhp\Tus\Client($baseUrl);
// Key is mandatory.
$key = 'your unique key';
$client->setKey($key)->file('/path/to/file', 'filename.ext');
// Create and upload a chunk of 1MB
$bytesUploaded = $client->upload(1000000);
// Resume, $bytesUploaded = 2MB
$bytesUploaded = $client->upload(1000000);
// To upload whole file, skip length param
$client->file('/path/to/file', 'filename.ext')->upload();
```
To check if the file was partially uploaded before, you can use `getOffset` method. It returns false if the upload
isn't there or invalid, returns total bytes uploaded otherwise.
```php
$offset = $client->getOffset(); // 2000000 bytes or 2MB
```
Delete partial upload from the cache.
```php
$client->delete($key);
```
By default, the client uses `/files` as an API path. You can change it with `setApiPath` method.
```php
$client->setApiPath('/api');
```
By default, the server will use `sha256` algorithm to verify the integrity of the upload. If you want to use a different hash algorithm, you can do so by
using `setChecksumAlgorithm` method. To get the list of supported hash algorithms, you can send `OPTIONS` request to the server.
```php
$client->setChecksumAlgorithm('crc32');
```
#### Third Party Client Libraries
##### [Uppy](https://uppy.io/)
Uppy is a sleek, modular file uploader plugin developed by same folks behind tus protocol.
You can use uppy to seamlessly integrate official [tus-js-client](https://github.com/tus/tus-js-client) with tus-php server.
Check out more details in [uppy docs](https://uppy.io/docs/tus/).
```js
uppy.use(Tus, {
endpoint: 'https://tus-server.yoursite.com/files/', // use your tus endpoint here
resume: true,
autoRetry: true,
retryDelays: [0, 1000, 3000, 5000]
})
```
##### [Tus-JS-Client](https://github.com/tus/tus-js-client)
Tus-php server is compatible with the official [tus-js-client](https://github.com/tus/tus-js-client) Javascript library.
```js
var upload = new tus.Upload(file, {
endpoint: "/tus",
retryDelays: [0, 3000, 5000, 10000, 20000],
metadata: {
name: file.name,
type: file.type
}
})
upload.start()
```
#### Cloud Providers
Many cloud providers implement PHP [streamWrapper](https://www.php.net/manual/en/class.streamwrapper.php) interface that enables us to store and retrieve data from these providers using built-in PHP functions. Since tus-php relies on PHP's built-in filesystem functions, we can easily use it to upload files to the providers like [Amazon S3](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-stream-wrapper.html) if their API supports writing in append binary mode. An example implementation to upload files directly to S3 bucket is as follows:
```php
// server.php
// composer require aws/aws-sdk-php
use Aws\S3\S3Client;
use TusPhp\Tus\Server;
use Aws\Credentials\Credentials;
$awsAccessKey = 'AWS_ACCESS_KEY'; // YOUR AWS ACCESS KEY
$awsSecretKey = 'AWS_SECRET_KEY'; // YOUR AWS SECRET KEY
$awsRegion = 'eu-west-1'; // YOUR AWS BUCKET REGION
$basePath = 's3://your-bucket-name';
$s3Client = new S3Client([
'version' => 'latest',
'region' => $awsRegion,
'credentials' => new Credentials($awsAccessKey, $awsSecretKey)
]);
$s3Client->registerStreamWrapper();
$server = new Server('file');
$server->setUploadDir($basePath);
$response = $server->serve();
$response->send();
exit(0);
```
### Extension Support
- [x] The Creation extension is mostly implemented and is used for creating the upload. Deferring the upload's length is not possible at the moment.
- [x] The Termination extension is implemented which is used to terminate completed and unfinished uploads allowing the Server to free up used resources.
- [x] The Checksum extension is implemented, the server will use `sha256` algorithm by default to verify the upload.
- [x] The Expiration extension is implemented, details below.
- [x] This Concatenation extension is implemented except that the server is not capable of handling unfinished concatenation.
#### Expiration
The Server is capable of removing expired but unfinished uploads. You can use the following command manually or in a
cron job to remove them. Note that this command checks your cache storage to find expired uploads. So, make sure
to run it before the cache is expired, else it will not find all files that needs to be cleared.
```shell
$ ./vendor/bin/tus tus:expired --help
Usage:
tus:expired [<cache-adapter>] [options]
Arguments:
cache-adapter Cache adapter to use: redis, file or apcu [default: "file"]
Options:
-c, --config=CONFIG File to get config parameters from.
eg:
$ ./vendor/bin/tus tus:expired redis
Cleaning server resources
=========================
1. Deleted 1535888128_35094.jpg from /var/www/uploads
```
You can use`--config` option to override default redis or file configuration.
```shell
$ ./vendor/bin/tus tus:expired redis --config=<path to your config file>
```
#### Concatenation
The Server is capable of concatenating multiple uploads into a single one enabling Clients to perform parallel uploads and to upload non-contiguous chunks.
```php
// Actual file key
$uploadKey = uniqid();
$client->setKey($uploadKey)->file('/path/to/file', 'chunk_a.ext');
// Upload 10000 bytes starting from 1000 bytes
$bytesUploaded = $client->seek(1000)->upload(10000);
$chunkAkey = $client->getKey();
// Upload 1000 bytes starting from 0 bytes
$bytesUploaded = $client->setFileName('chunk_b.ext')->seek(0)->upload(1000);
$chunkBkey = $client->getKey();
// Upload remaining bytes starting from 11000 bytes (10000 + 1000)
$bytesUploaded = $client->setFileName('chunk_c.ext')->seek(11000)->upload();
$chunkCkey = $client->getKey();
// Concatenate partial uploads
$client->setFileName('actual_file.ext')->concat($uploadKey, $chunkBkey, $chunkAkey, $chunkCkey);
```
Additionally, the server will verify checksum against the merged file to make sure that the file is not corrupt.
### Events
Often times, you may want to perform some operation after the upload is complete or created. For example, you may want to crop images after upload or transcode a file and email it to your user.
You can utilize tus events for these operations. Following events are dispatched by server during different point of execution.
| Event Name | Dispatched |
-------------|------------|
| `tus-server.upload.created` | after the upload is created during `POST` request. |
| `tus-server.upload.progress` | after a chunk is uploaded during `PATCH` request. |
| `tus-server.upload.complete` | after the upload is complete and checksum verification is done. |
| `tus-server.upload.merged` | after all partial uploads are merged during concatenation request. |
#### Responding to an Event
To listen to an event, you can simply attach a listener to the event name. An `TusEvent` instance is created and passed to all of the listeners.
```php
$server->event()->addListener('tus-server.upload.complete', function (\TusPhp\Events\TusEvent $event) {
$fileMeta = $event->getFile()->details();
$request = $event->getRequest();
$response = $event->getResponse();
// ...
});
```
or, you can also bind some method of a custom class.
```php
/**
* Listener can be method from any normal class.
*/
class SomeClass
{
public function postUploadOperation(\TusPhp\Events\TusEvent $event)
{
// ...
}
}
$listener = new SomeClass();
$server->event()->addListener('tus-server.upload.complete', [$listener, 'postUploadOperation']);
```
### Middleware
You can manipulate request and response of a server using a middleware. Middleware can be used to run a piece of code before a server calls the actual handle method.
You can use middleware to authenticate a request, handle CORS, whitelist/blacklist an IP etc.
#### Creating a Middleware
In order to create a middleware, you need to implement `TusMiddleware` interface. The handle method provides request and response object for you to manipulate.
```php
<?php
namespace Your\Namespace;
use TusPhp\Request;
use TusPhp\Response;
use TusPhp\Middleware\TusMiddleware;
class Authenticated implements TusMiddleware
{
// ...
/**
* {@inheritDoc}
*/
public function handle(Request $request, Response $response)
{
// Check if user is authenticated
if (! $this->user->isLoggedIn()) {
throw new UnauthorizedHttpException('User not authenticated');
}
$request->getRequest()->headers->set('Authorization', 'Bearer ' . $this->user->token());
}
// ...
}
```
#### Adding a Middleware
To add a middleware, get middleware object from server and simply pass middleware classes.
```php
$server->middleware()->add(Authenticated::class, AnotherMiddleware::class);
```
Or, you can also pass middleware class objects.
```php
$authenticated = new Your\Namespace\Authenticated(new User());
$server->middleware()->add($authenticated);
```
#### Skipping a Middleware
If you wish to skip or ignore any middleware, you can do so by using the `skip` method.
```php
$server->middleware()->skip(Cors::class, AnotherMiddleware::class);
```
### Setting up a dev environment and/or running examples locally
An ajax based example for this implementation can be found in `examples/` folder. You can build and run it using docker as described below.
#### Docker
Make sure that [docker](https://docs.docker.com/engine/installation/) and [docker-compose](https://docs.docker.com/compose/install/)
are installed in your system. Then, run docker script from project root.
```shell
# PHP7
$ make dev
# PHP8
$ make dev8
# or, without make
# PHP7
$ bin/docker.sh
# PHP8
$ PHP_VERSION=8 bin/docker.sh
```
Now, the client can be accessed at http://0.0.0.0:8080 and the server can be accessed at http://0.0.0.0:8081. The default API endpoint is set to`/files`
and uploaded files can be found inside `uploads` folder. All docker configs can be found in `docker/` folder.
If you want a fresh start then you can use the following commands. It will delete and recreate all containers, images, and uploads folder.
```shell
# PHP7
$ make dev-fresh
# PHP8
$ make dev8-fresh
# or, without make
# PHP7
$ bin/clean.sh && bin/docker.sh
# PHP8
$ bin/clean.sh && PHP_VERSION=8 bin/docker.sh
```
We also have some utility scripts that will ease your local development experience. See [Makefile](Makefile) for a list of all available commands.
If you are not using [make](https://www.gnu.org/software/make/manual/make.html#Overview), then you can use shell scripts available [here](bin).
### Contributing
1. Install [PHPUnit](https://phpunit.de/) and [composer](https://getcomposer.org/) if you haven't already.
2. Install dependencies
```shell
$ make vendor
# or
$ composer install
```
3. Run tests with phpunit
```shell
$ make test
# or
$ composer test
# or
$ ./vendor/bin/phpunit
```
4. Validate changes against [PSR2 Coding Standards](http://www.php-fig.org/psr/psr-2/)
```shell
# fix lint issues
$ make lint
# dry run
$ make lint-dry
```
You can use `xdebug enable` and `xdebug disable` to enable and disable [Xdebug](https://xdebug.org/) inside the container.
### Questions about this project?
Please feel free to report any bug found. Pull requests, issues, and project recommendations are more than welcome!

15
vendor/ankitpokhrel/tus-php/SECURITY.md vendored Normal file
View file

@ -0,0 +1,15 @@
# Security Policy
## Supported Versions
All security updates are available for following versions.
| Version | Supported |
| --------------- | ------------------ |
| 2.x.x | :white_check_mark: |
| 1.x.x | :white_check_mark: |
| 0.x.x | :x: |
## Reporting a Vulnerability
Please report any security issues directly to hello [at] ankit.pl

17
vendor/ankitpokhrel/tus-php/bin/tus vendored Normal file
View file

@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
if (file_exists($autoloader = __DIR__ . '/../../../autoload.php')) {
require_once $autoloader;
} else {
require_once __DIR__ . '/../vendor/autoload.php';
}
use TusPhp\Commands\ExpirationCommand;
use Symfony\Component\Console\Application;
$app = new Application();
$app->add(new ExpirationCommand());
$app->run();

View file

@ -0,0 +1,56 @@
{
"name": "ankitpokhrel/tus-php",
"description": "A pure PHP server and client for the tus resumable upload protocol v1.0.0",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Ankit Pokhrel",
"email": "hello@ankit.pl"
}
],
"require": {
"php": "^7.2.5 || ^8.0",
"ext-json": "*",
"guzzlehttp/guzzle": "^6.3 || ^7.0",
"nesbot/carbon": "^1.26.3 || ^2.0",
"predis/predis": "^1.1 || ^2.0",
"ramsey/uuid": "^3.7 || ^4.0",
"symfony/console": "^5.0 || ^6.0",
"symfony/event-dispatcher": "^5.0 || ^6.0",
"symfony/http-foundation": "^5.0.7 || ^6.0",
"symfony/mime": "^5.0.9 || ^6.0"
},
"require-dev": {
"ext-pcntl": "*",
"friendsofphp/php-cs-fixer": "^3.0",
"mockery/mockery": "^1.3.4 || ^1.4.2",
"phpunit/phpunit": "^8.5.19 || ^9.4"
},
"autoload": {
"psr-4": {
"TusPhp\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"TusPhp\\Test\\": "tests/"
}
},
"config": {
"optimize-autoloader": true,
"sort-packages": true
},
"scripts": {
"test": "xdebug disable && vendor/bin/phpunit",
"test-coverage": "xdebug enable && vendor/bin/phpunit --coverage-html ./coverage"
},
"bin": [
"bin/tus"
],
"extra": {
"branch-alias": {
"dev-main": "2.3-dev"
}
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace TusPhp\Cache;
abstract class AbstractCache implements Cacheable
{
/** @var int TTL in secs (default 1 day) */
protected $ttl = 86400;
/** @var string Prefix for cache keys */
protected $prefix = 'tus:';
/**
* Set time to live.
*
* @param int $secs
*
* @return self
*/
public function setTtl(int $secs): self
{
$this->ttl = $secs;
return $this;
}
/**
* {@inheritDoc}
*/
public function getTtl(): int
{
return $this->ttl;
}
/**
* Set cache prefix.
*
* @param string $prefix
*
* @return Cacheable
*/
public function setPrefix(string $prefix): Cacheable
{
$this->prefix = $prefix;
return $this;
}
/**
* Get cache prefix.
*
* @return string
*/
public function getPrefix(): string
{
return $this->prefix;
}
/**
* Delete all keys.
*
* @param array $keys
*
* @return bool
*/
public function deleteAll(array $keys): bool
{
if (empty($keys)) {
return false;
}
$status = true;
foreach ($keys as $key) {
$status = $status && $this->delete($key);
}
return $status;
}
}

View file

@ -0,0 +1,81 @@
<?php
namespace TusPhp\Cache;
use APCUIterator;
use Carbon\Carbon;
class ApcuStore extends AbstractCache
{
/**
* {@inheritDoc}
*/
public function get(string $key, bool $withExpired = false)
{
$contents = apcu_fetch($this->getActualCacheKey($key));
if ( ! $contents) {
return null;
}
if ($withExpired) {
return $contents ?: null;
}
$isExpired = Carbon::parse($contents['expires_at'])->lt(Carbon::now());
return $isExpired ? null : $contents;
}
/**
* {@inheritDoc}
*/
public function set(string $key, $value)
{
$contents = $this->get($key) ?? [];
if (\is_array($value)) {
$contents = $value + $contents;
} else {
$contents[] = $value;
}
return apcu_store($this->getActualCacheKey($key), $contents, $this->getTtl());
}
/**
* {@inheritDoc}
*/
public function delete(string $key): bool
{
return true === apcu_delete($this->getActualCacheKey($key));
}
/**
* {@inheritDoc}
*/
public function keys(): array
{
$iterator = new APCUIterator('/^' . preg_quote($this->getPrefix()) . '.*$/', APC_ITER_KEY);
return array_column(iterator_to_array($iterator, false), 'key');
}
/**
* Get actual cache key with prefix.
*
* @param string $key
*
* @return string
*/
protected function getActualCacheKey(string $key): string
{
$prefix = $this->getPrefix();
if (false === strpos($key, $prefix)) {
$key = $prefix . $key;
}
return $key;
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace TusPhp\Cache;
class CacheFactory
{
/**
* Make cache.
*
* @param string $type
*
* @static
*
* @return Cacheable
*/
public static function make(string $type = 'file'): Cacheable
{
switch ($type) {
case 'redis':
return new RedisStore();
case 'apcu':
return new ApcuStore();
}
return new FileStore();
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace TusPhp\Cache;
interface Cacheable
{
/** @see https://tools.ietf.org/html/rfc7231#section-7.1.1.1 */
public const RFC_7231 = 'D, d M Y H:i:s \G\M\T';
/**
* Get data associated with the key.
*
* @param string $key
* @param bool $withExpired
*
* @return mixed
*/
public function get(string $key, bool $withExpired = false);
/**
* Set data to the given key.
*
* @param string $key
* @param mixed $value
*
* @return mixed
*/
public function set(string $key, $value);
/**
* Delete data associated with the key.
*
* @param string $key
*
* @return bool
*/
public function delete(string $key): bool;
/**
* Delete all data associated with the keys.
*
* @param array $keys
*
* @return bool
*/
public function deleteAll(array $keys): bool;
/**
* Get time to live.
*
* @return int
*/
public function getTtl(): int;
/**
* Get cache keys.
*
* @return array
*/
public function keys(): array;
/**
* Set cache prefix.
*
* @param string $prefix
*
* @return self
*/
public function setPrefix(string $prefix): self;
/**
* Get cache prefix.
*
* @return string
*/
public function getPrefix(): string;
}

View file

@ -0,0 +1,306 @@
<?php
namespace TusPhp\Cache;
use TusPhp\File;
use Carbon\Carbon;
use TusPhp\Config;
class FileStore extends AbstractCache
{
/** @var int */
public const LOCK_NONE = 0;
/** @var string */
protected $cacheDir;
/** @var string */
protected $cacheFile;
/**
* FileStore constructor.
*
* @param string|null $cacheDir
* @param string|null $cacheFile
*/
public function __construct(string $cacheDir = null, string $cacheFile = null)
{
$cacheDir = $cacheDir ?? Config::get('file.dir');
$cacheFile = $cacheFile ?? Config::get('file.name');
$this->setCacheDir($cacheDir);
$this->setCacheFile($cacheFile);
}
/**
* Set cache dir.
*
* @param string $path
*
* @return self
*/
public function setCacheDir(string $path): self
{
$this->cacheDir = $path;
return $this;
}
/**
* Get cache dir.
*
* @return string
*/
public function getCacheDir(): string
{
return $this->cacheDir;
}
/**
* Set cache file.
*
* @param string $file
*
* @return self
*/
public function setCacheFile(string $file): self
{
$this->cacheFile = $file;
return $this;
}
/**
* Get cache file.
*
* @return string
*/
public function getCacheFile(): string
{
return $this->cacheDir . $this->cacheFile;
}
/**
* Create cache dir if not exists.
*
* @return void
*/
protected function createCacheDir()
{
if ( ! file_exists($this->cacheDir)) {
mkdir($this->cacheDir);
}
}
/**
* Create a cache file.
*
* @return void
*/
protected function createCacheFile()
{
$this->createCacheDir();
$cacheFilePath = $this->getCacheFile();
if ( ! file_exists($cacheFilePath)) {
touch($cacheFilePath);
}
}
/**
* {@inheritDoc}
*/
public function get(string $key, bool $withExpired = false)
{
$key = $this->getActualCacheKey($key);
$contents = $this->getCacheContents();
if (empty($contents[$key])) {
return null;
}
if ($withExpired) {
return $contents[$key];
}
return $this->isValid($key) ? $contents[$key] : null;
}
/**
* @param string $path
* @param int $type
* @param callable|null $cb
*
* @return mixed
*/
protected function lock(string $path, int $type = LOCK_SH, callable $cb = null, $fopenType = FILE::READ_BINARY)
{
$out = false;
$handle = @fopen($path, $fopenType);
if (false === $handle) {
return $out;
}
try {
if (flock($handle, $type)) {
clearstatcache(true, $path);
$out = $cb($handle);
}
} finally {
flock($handle, LOCK_UN);
fclose($handle);
}
return $out;
}
/**
* Get contents of a file with shared access.
*
* @param string $path
*
* @return string
*/
public function sharedGet(string $path): string
{
return $this->lock($path, LOCK_SH, function ($handle) use ($path) {
$fstat = fstat($handle);
$size = $fstat ? $fstat['size'] : 1;
$contents = fread($handle, $size ?: 1);
if (false === $contents) {
return '';
}
return $contents;
});
}
/**
* Write the contents of a file with exclusive lock.
*
* @param string $path
* @param string $contents
* @param int $lock
*
* @return int|false
*/
public function put(string $path, string $contents, int $lock = LOCK_EX)
{
return file_put_contents($path, $contents, $lock);
}
/**
* {@inheritDoc}
*/
public function set(string $key, $value)
{
$cacheKey = $this->getActualCacheKey($key);
$cacheFile = $this->getCacheFile();
if ( ! file_exists($cacheFile) || ! $this->isValid($cacheKey)) {
$this->createCacheFile();
}
return $this->lock($cacheFile, LOCK_EX, function ($handle) use ($cacheKey, $cacheFile, $value) {
$size = fstat($handle)['size'];
$contents = fread($handle, $size ?: 1) ?? '';
$contents = json_decode($contents, true) ?? [];
if ( ! empty($contents[$cacheKey]) && \is_array($value)) {
$contents[$cacheKey] = $value + $contents[$cacheKey];
} else {
$contents[$cacheKey] = $value;
}
ftruncate($handle, 0);
return fwrite($handle, json_encode($contents));
}, FILE::APPEND_WRITE);
}
/**
* {@inheritDoc}
*/
public function delete(string $key): bool
{
$cacheKey = $this->getActualCacheKey($key);
$contents = $this->getCacheContents();
if (isset($contents[$cacheKey])) {
unset($contents[$cacheKey]);
return false !== $this->put($this->getCacheFile(), json_encode($contents));
}
return false;
}
/**
* {@inheritDoc}
*/
public function keys(): array
{
$contents = $this->getCacheContents();
if (\is_array($contents)) {
return array_keys($contents);
}
return [];
}
/**
* Check if cache is still valid.
*
* @param string $key
*
* @return bool
*/
public function isValid(string $key): bool
{
$key = $this->getActualCacheKey($key);
$meta = $this->getCacheContents()[$key] ?? [];
if (empty($meta['expires_at'])) {
return false;
}
return Carbon::now() < Carbon::createFromFormat(self::RFC_7231, $meta['expires_at']);
}
/**
* Get cache contents.
*
* @return array|bool
*/
public function getCacheContents()
{
$cacheFile = $this->getCacheFile();
if ( ! file_exists($cacheFile)) {
return false;
}
return json_decode($this->sharedGet($cacheFile), true) ?? [];
}
/**
* Get actual cache key with prefix.
*
* @param string $key
*
* @return string
*/
public function getActualCacheKey(string $key): string
{
$prefix = $this->getPrefix();
if (false === strpos($key, $prefix)) {
$key = $prefix . $key;
}
return $key;
}
}

View file

@ -0,0 +1,104 @@
<?php
namespace TusPhp\Cache;
use Carbon\Carbon;
use TusPhp\Config;
use Predis\Client as RedisClient;
class RedisStore extends AbstractCache
{
/** @var RedisClient */
protected $redis;
/**
* RedisStore constructor.
*
* @param array $options
*/
public function __construct(array $options = [])
{
$options = empty($options) ? Config::get('redis') : $options;
$this->redis = new RedisClient($options);
}
/**
* Get redis.
*
* @return RedisClient
*/
public function getRedis(): RedisClient
{
return $this->redis;
}
/**
* {@inheritDoc}
*/
public function get(string $key, bool $withExpired = false)
{
$prefix = $this->getPrefix();
if (false === strpos($key, $prefix)) {
$key = $prefix . $key;
}
$contents = $this->redis->get($key);
if (null !== $contents) {
$contents = json_decode($contents, true);
}
if ($withExpired) {
return $contents;
}
if ( ! $contents) {
return null;
}
$isExpired = Carbon::parse($contents['expires_at'])->lt(Carbon::now());
return $isExpired ? null : $contents;
}
/**
* {@inheritDoc}
*/
public function set(string $key, $value)
{
$contents = $this->get($key) ?? [];
if (\is_array($value)) {
$contents = $value + $contents;
} else {
$contents[] = $value;
}
$status = $this->redis->set($this->getPrefix() . $key, json_encode($contents));
return 'OK' === $status->getPayload();
}
/**
* {@inheritDoc}
*/
public function delete(string $key): bool
{
$prefix = $this->getPrefix();
if (false === strpos($key, $prefix)) {
$key = $prefix . $key;
}
return $this->redis->del([$key]) > 0;
}
/**
* {@inheritDoc}
*/
public function keys(): array
{
return $this->redis->keys($this->getPrefix() . '*');
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace TusPhp\Commands;
use TusPhp\Config;
use TusPhp\Cache\CacheFactory;
use TusPhp\Tus\Server as TusServer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ExpirationCommand extends Command
{
/** @var TusServer */
protected $server;
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('tus:expired')
->setDescription('Remove expired uploads.')
->setHelp('Deletes all expired uploads to free server resources. Values can be redis, file or apcu. Defaults to file.')
->addArgument(
'cache-adapter',
InputArgument::OPTIONAL,
'Cache adapter to use: redis, file or apcu',
'file'
)
->addOption(
'config',
'c',
InputArgument::OPTIONAL,
'File to get config parameters from.'
);
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln([
'<info>Cleaning server resources</info>',
'<info>=========================</info>',
'',
]);
$config = $input->getOption('config');
if ( ! empty($config)) {
Config::set($config);
}
$cacheAdapter = $input->getArgument('cache-adapter') ?? 'file';
$this->server = new TusServer(CacheFactory::make($cacheAdapter));
$deleted = $this->server->handleExpiration();
if (empty($deleted)) {
$output->writeln('<comment>Nothing to delete.</comment>');
} else {
foreach ($deleted as $key => $item) {
$output->writeln('<comment>' . ($key + 1) . ". Deleted {$item['name']} from " . \dirname($item['file_path']) . '</comment>');
}
}
$output->writeln('');
return 0;
}
}

View file

@ -0,0 +1,82 @@
<?php
namespace TusPhp;
class Config
{
/** @const string */
private const DEFAULT_CONFIG_PATH = __DIR__ . '/Config/server.php';
/** @var array */
protected static $config = [];
/**
* Get path to the base cache directory.
*
* @return string
*/
public static function getCacheHome(): string
{
if (($cacheDir = getenv('TUS_CACHE_HOME')) !== false) {
return $cacheDir;
}
// See https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
if (($cacheDir = getenv('XDG_CACHE_HOME')) !== false) {
return $cacheDir;
}
return \dirname(__DIR__);
}
/**
* Load default application configs.
*
* @param string|array $config
* @param bool $force
*
* @return void
*/
public static function set($config = null, bool $force = false)
{
if ( ! $force && ! empty(self::$config)) {
return;
}
if (\is_array($config)) {
self::$config = $config;
} else {
self::$config = require $config ?? self::DEFAULT_CONFIG_PATH;
}
}
/**
* Get config.
*
* @param string|null $key Key to extract.
*
* @return mixed
*/
public static function get(string $key = null)
{
self::set();
if (empty($key)) {
return self::$config;
}
$keys = explode('.', $key);
$value = self::$config;
foreach ($keys as $k) {
if ( ! isset($value[$k])) {
return null;
}
$value = $value[$k];
}
return $value;
}
}

View file

@ -0,0 +1,21 @@
<?php
return [
/**
* Redis connection parameters.
*/
'redis' => [
'host' => getenv('REDIS_HOST') !== false ? getenv('REDIS_HOST') : '127.0.0.1',
'port' => getenv('REDIS_PORT') !== false ? getenv('REDIS_PORT') : '6379',
'database' => getenv('REDIS_DB') !== false ? getenv('REDIS_DB') : 0,
],
/**
* File cache configs.
*/
'file' => [
'dir' => \TusPhp\Config::getCacheHome() . DIRECTORY_SEPARATOR . '.cache' . DIRECTORY_SEPARATOR,
'name' => 'tus_php.client.cache',
],
];

View file

@ -0,0 +1,21 @@
<?php
return [
/**
* Redis connection parameters.
*/
'redis' => [
'host' => getenv('REDIS_HOST') !== false ? getenv('REDIS_HOST') : '127.0.0.1',
'port' => getenv('REDIS_PORT') !== false ? getenv('REDIS_PORT') : '6379',
'database' => getenv('REDIS_DB') !== false ? getenv('REDIS_DB') : 0,
],
/**
* File cache configs.
*/
'file' => [
'dir' => \TusPhp\Config::getCacheHome() . DIRECTORY_SEPARATOR . '.cache' . DIRECTORY_SEPARATOR,
'name' => 'tus_php.server.cache',
],
];

View file

@ -0,0 +1,50 @@
<?php
namespace TusPhp\Events;
use TusPhp\File;
use TusPhp\Request;
use TusPhp\Response;
use Symfony\Contracts\EventDispatcher\Event;
class TusEvent extends Event
{
/** @var File */
protected $file;
/** @var Request */
protected $request;
/** @var Response */
protected $response;
/**
* Get file.
*
* @return File
*/
public function getFile(): File
{
return $this->file;
}
/**
* Get request.
*
* @return Request
*/
public function getRequest(): Request
{
return $this->request;
}
/**
* Get response.
*
* @return Response
*/
public function getResponse(): Response
{
return $this->response;
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace TusPhp\Events;
use TusPhp\File;
use TusPhp\Request;
use TusPhp\Response;
class UploadComplete extends TusEvent
{
/** @var string */
public const NAME = 'tus-server.upload.complete';
/**
* UploadCompleteEvent constructor.
*
* @param File $file
* @param Request $request
* @param Response $response
*/
public function __construct(File $file, Request $request, Response $response)
{
$this->file = $file;
$this->request = $request;
$this->response = $response;
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace TusPhp\Events;
use TusPhp\File;
use TusPhp\Request;
use TusPhp\Response;
class UploadCreated extends TusEvent
{
/** @var string */
public const NAME = 'tus-server.upload.created';
/**
* UploadCreatedEvent constructor.
*
* @param File $file
* @param Request $request
* @param Response $response
*/
public function __construct(File $file, Request $request, Response $response)
{
$this->file = $file;
$this->request = $request;
$this->response = $response;
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace TusPhp\Events;
use TusPhp\File;
use TusPhp\Request;
use TusPhp\Response;
class UploadMerged extends TusEvent
{
/** @var string */
public const NAME = 'tus-server.upload.merged';
/**
* UploadMergedEvent constructor.
*
* @param File $file
* @param Request $request
* @param Response $response
*/
public function __construct(File $file, Request $request, Response $response)
{
$this->file = $file;
$this->request = $request;
$this->response = $response;
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace TusPhp\Events;
use TusPhp\File;
use TusPhp\Request;
use TusPhp\Response;
class UploadProgress extends TusEvent
{
/** @var string */
public const NAME = 'tus-server.upload.progress';
/**
* UploadProgressEvent constructor.
*
* @param File $file
* @param Request $request
* @param Response $response
*/
public function __construct(File $file, Request $request, Response $response)
{
$this->file = $file;
$this->request = $request;
$this->response = $response;
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace TusPhp\Exception;
class ConnectionException extends \Exception
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace TusPhp\Exception;
class FileException extends \RuntimeException
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace TusPhp\Exception;
class OutOfRangeException extends \OutOfRangeException
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace TusPhp\Exception;
class TusException extends \Exception
{
}

568
vendor/ankitpokhrel/tus-php/src/File.php vendored Normal file
View file

@ -0,0 +1,568 @@
<?php
namespace TusPhp;
use Carbon\Carbon;
use TusPhp\Cache\Cacheable;
use TusPhp\Exception\FileException;
use TusPhp\Exception\ConnectionException;
use TusPhp\Exception\OutOfRangeException;
class File
{
/** @const Max chunk size */
public const CHUNK_SIZE = 8192; // 8 kilobytes.
/** @const Input stream */
public const INPUT_STREAM = 'php://input';
/** @const Read binary mode */
public const READ_BINARY = 'rb';
/** @const Append binary mode */
public const APPEND_BINARY = 'ab';
/** @const Read and write mode */
public const APPEND_WRITE = 'a+';
/** @var string */
protected $key;
/** @var string */
protected $checksum;
/** @var string */
protected $name;
/** @var Cacheable */
protected $cache;
/** @var int */
protected $offset;
/** @var string */
protected $location;
/** @var string */
protected $filePath;
/** @var int */
protected $fileSize;
/** @var string[] */
private $uploadMetadata = [];
/**
* File constructor.
*
* @param string|null $name
* @param Cacheable|null $cache
*/
public function __construct(string $name = null, Cacheable $cache = null)
{
$this->name = $name;
$this->cache = $cache;
}
/**
* Set file meta.
*
* @param int $offset
* @param int $fileSize
* @param string $filePath
* @param string|null $location
*
* @return File
*/
public function setMeta(int $offset, int $fileSize, string $filePath, string $location = null): self
{
$this->offset = $offset;
$this->fileSize = $fileSize;
$this->filePath = $filePath;
$this->location = $location;
return $this;
}
/**
* Set name.
*
* @param string $name
*
* @return File
*/
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* Get name.
*
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* Set file size.
*
* @param int $size
*
* @return File
*/
public function setFileSize(int $size): self
{
$this->fileSize = $size;
return $this;
}
/**
* Get file size.
*
* @return int
*/
public function getFileSize(): int
{
return $this->fileSize;
}
/**
* Set key.
*
* @param string $key
*
* @return File
*/
public function setKey(string $key): self
{
$this->key = $key;
return $this;
}
/**
* Get key.
*
* @return string
*/
public function getKey(): string
{
return $this->key;
}
/**
* Set checksum.
*
* @param string $checksum
*
* @return File
*/
public function setChecksum(string $checksum): self
{
$this->checksum = $checksum;
return $this;
}
/**
* Get checksum.
*
* @return string
*/
public function getChecksum(): string
{
return $this->checksum;
}
/**
* Set offset.
*
* @param int $offset
*
* @return File
*/
public function setOffset(int $offset): self
{
$this->offset = $offset;
return $this;
}
/**
* Get offset.
*
* @return int
*/
public function getOffset(): int
{
return $this->offset;
}
/**
* Set location.
*
* @param string $location
*
* @return File
*/
public function setLocation(string $location): self
{
$this->location = $location;
return $this;
}
/**
* Get location.
*
* @return string
*/
public function getLocation(): string
{
return $this->location;
}
/**
* Set absolute file location.
*
* @param string $path
*
* @return File
*/
public function setFilePath(string $path): self
{
$this->filePath = $path;
return $this;
}
/**
* Get absolute location.
*
* @return string
*/
public function getFilePath(): string
{
return $this->filePath;
}
/**
* @param string[] $metadata
*
* @return File
*/
public function setUploadMetadata(array $metadata): self
{
$this->uploadMetadata = $metadata;
return $this;
}
/**
* Get input stream.
*
* @return string
*/
public function getInputStream(): string
{
return self::INPUT_STREAM;
}
/**
* Get file meta.
*
* @return array
*/
public function details(): array
{
$now = Carbon::now();
return [
'name' => $this->name,
'size' => $this->fileSize,
'offset' => $this->offset,
'checksum' => $this->checksum,
'location' => $this->location,
'file_path' => $this->filePath,
'metadata' => $this->uploadMetadata,
'created_at' => $now->format($this->cache::RFC_7231),
'expires_at' => $now->addSeconds($this->cache->getTtl())->format($this->cache::RFC_7231),
];
}
/**
* Upload file to server.
*
* @param int $totalBytes
*
* @throws ConnectionException
*
* @return int
*/
public function upload(int $totalBytes): int
{
if ($this->offset === $totalBytes) {
return $this->offset;
}
$input = $this->open($this->getInputStream(), self::READ_BINARY);
$output = $this->open($this->getFilePath(), self::APPEND_BINARY);
$key = $this->getKey();
try {
$this->seek($output, $this->offset);
while ( ! feof($input)) {
if (CONNECTION_NORMAL !== connection_status()) {
throw new ConnectionException('Connection aborted by user.');
}
$data = $this->read($input, self::CHUNK_SIZE);
$bytes = $this->write($output, $data, self::CHUNK_SIZE);
$this->offset += $bytes;
$this->cache->set($key, ['offset' => $this->offset]);
if ($this->offset > $totalBytes) {
throw new OutOfRangeException('The uploaded file is corrupt.');
}
if ($this->offset === $totalBytes) {
break;
}
}
} finally {
$this->close($input);
$this->close($output);
}
return $this->offset;
}
/**
* Open file in given mode.
*
* @param string $filePath
* @param string $mode
*
* @throws FileException
*
* @return resource
*/
public function open(string $filePath, string $mode)
{
$this->exists($filePath, $mode);
$ptr = @fopen($filePath, $mode);
if (false === $ptr) {
throw new FileException("Unable to open $filePath.");
}
return $ptr;
}
/**
* Check if file to read exists.
*
* @param string $filePath
* @param string $mode
*
* @throws FileException
*
* @return bool
*/
public function exists(string $filePath, string $mode = self::READ_BINARY): bool
{
if (self::INPUT_STREAM === $filePath) {
return true;
}
if (self::READ_BINARY === $mode && ! file_exists($filePath)) {
throw new FileException('File not found.');
}
return true;
}
/**
* Move file pointer to given offset.
*
* @param resource $handle
* @param int $offset
* @param int $whence
*
* @throws FileException
*
* @return int
*/
public function seek($handle, int $offset, int $whence = SEEK_SET): int
{
$position = fseek($handle, $offset, $whence);
if (-1 === $position) {
throw new FileException('Cannot move pointer to desired position.');
}
return $position;
}
/**
* Read data from file.
*
* @param resource $handle
* @param int $chunkSize
*
* @throws FileException
*
* @return string
*/
public function read($handle, int $chunkSize): string
{
$data = fread($handle, $chunkSize);
if (false === $data) {
throw new FileException('Cannot read file.');
}
return $data;
}
/**
* Write data to file.
*
* @param resource $handle
* @param string $data
* @param int|null $length
*
* @throws FileException
*
* @return int
*/
public function write($handle, string $data, $length = null): int
{
$bytesWritten = \is_int($length) ? fwrite($handle, $data, $length) : fwrite($handle, $data);
if (false === $bytesWritten) {
throw new FileException('Cannot write to a file.');
}
return $bytesWritten;
}
/**
* Merge 2 or more files.
*
* @param array $files File data with meta info.
*
* @return int
*/
public function merge(array $files): int
{
$destination = $this->getFilePath();
$firstFile = array_shift($files);
// First partial file can directly be copied.
$this->copy($firstFile['file_path'], $destination);
$this->offset = $firstFile['offset'];
$this->fileSize = filesize($firstFile['file_path']);
$handle = $this->open($destination, self::APPEND_BINARY);
foreach ($files as $file) {
if ( ! file_exists($file['file_path'])) {
throw new FileException('File to be merged not found.');
}
$this->fileSize += $this->write($handle, file_get_contents($file['file_path']));
$this->offset += $file['offset'];
}
$this->close($handle);
return $this->fileSize;
}
/**
* Copy file from source to destination.
*
* @param string $source
* @param string $destination
*
* @return bool
*/
public function copy(string $source, string $destination): bool
{
$status = @copy($source, $destination);
if (false === $status) {
throw new FileException(sprintf('Cannot copy source (%s) to destination (%s).', $source, $destination));
}
return $status;
}
/**
* Delete file and/or folder.
*
* @param array $files
* @param bool $folder
*
* @return bool
*/
public function delete(array $files, bool $folder = false): bool
{
$status = $this->deleteFiles($files);
if ($status && $folder) {
return rmdir(\dirname(current($files)));
}
return $status;
}
/**
* Delete multiple files.
*
* @param array $files
*
* @return bool
*/
public function deleteFiles(array $files): bool
{
if (empty($files)) {
return false;
}
$status = true;
foreach ($files as $file) {
if (file_exists($file)) {
$status = $status && unlink($file);
}
}
return $status;
}
/**
* Close file.
*
* @param $handle
*
* @return bool
*/
public function close($handle): bool
{
return fclose($handle);
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace TusPhp\Middleware;
use TusPhp\Request;
use TusPhp\Response;
class Cors implements TusMiddleware
{
/** @const int 24 hours access control max age header */
private const HEADER_ACCESS_CONTROL_MAX_AGE = 86400;
/**
* {@inheritDoc}
*/
public function handle(Request $request, Response $response)
{
$response->setHeaders([
'Access-Control-Allow-Origin' => $request->header('Origin'),
'Access-Control-Allow-Methods' => implode(',', $request->allowedHttpVerbs()),
'Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Content-Type, Content-Length, Upload-Key, Upload-Checksum, Upload-Length, Upload-Offset, Tus-Version, Tus-Resumable, Upload-Metadata',
'Access-Control-Expose-Headers' => 'Upload-Key, Upload-Checksum, Upload-Length, Upload-Offset, Upload-Metadata, Tus-Version, Tus-Resumable, Tus-Extension, Location',
'Access-Control-Max-Age' => self::HEADER_ACCESS_CONTROL_MAX_AGE,
]);
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace TusPhp\Middleware;
use TusPhp\Request;
use TusPhp\Response;
use TusPhp\Tus\Server;
class GlobalHeaders implements TusMiddleware
{
/**
* {@inheritDoc}
*/
public function handle(Request $request, Response $response)
{
$headers = [
'X-Content-Type-Options' => 'nosniff',
'Tus-Resumable' => Server::TUS_PROTOCOL_VERSION,
];
$response->setHeaders($headers);
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace TusPhp\Middleware;
class Middleware
{
/** @var array */
protected $globalMiddleware = [];
/**
* Middleware constructor.
*/
public function __construct()
{
$this->globalMiddleware = [
GlobalHeaders::class => new GlobalHeaders(),
Cors::class => new Cors(),
];
}
/**
* Get registered middleware.
*
* @return array
*/
public function list(): array
{
return $this->globalMiddleware;
}
/**
* Set middleware.
*
* @param array $middleware
*
* @return Middleware
*/
public function add(...$middleware): self
{
foreach ($middleware as $m) {
if ($m instanceof TusMiddleware) {
$this->globalMiddleware[\get_class($m)] = $m;
} elseif (\is_string($m)) {
$this->globalMiddleware[$m] = new $m();
}
}
return $this;
}
/**
* Skip middleware.
*
* @param array $middleware
*
* @return Middleware
*/
public function skip(...$middleware): self
{
foreach ($middleware as $m) {
unset($this->globalMiddleware[$m]);
}
return $this;
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace TusPhp\Middleware;
use TusPhp\Request;
use TusPhp\Response;
interface TusMiddleware
{
/**
* Handle request/response.
*
* @param Request $request
* @param Response $response
*
* @return mixed
*/
public function handle(Request $request, Response $response);
}

View file

@ -0,0 +1,248 @@
<?php
namespace TusPhp;
use TusPhp\Tus\Server;
use Symfony\Component\HttpFoundation\Request as HttpRequest;
class Request
{
/** @var HttpRequest */
protected $request;
/**
* Request constructor.
*/
public function __construct()
{
if (null === $this->request) {
$this->request = HttpRequest::createFromGlobals();
}
}
/**
* Get http method from current request.
*
* @return string
*/
public function method(): string
{
return $this->request->getMethod();
}
/**
* Get the current path info for the request.
*
* @return string
*/
public function path(): string
{
return $this->request->getPathInfo();
}
/**
* Get upload key from url.
*
* @return string
*/
public function key(): string
{
return basename($this->path());
}
/**
* Supported http requests.
*
* @return array
*/
public function allowedHttpVerbs(): array
{
return [
HttpRequest::METHOD_GET,
HttpRequest::METHOD_POST,
HttpRequest::METHOD_PATCH,
HttpRequest::METHOD_DELETE,
HttpRequest::METHOD_HEAD,
HttpRequest::METHOD_OPTIONS,
];
}
/**
* Retrieve a header from the request.
*
* @param string $key
* @param string|string[]|null $default
*
* @return string|null
*/
public function header(string $key, $default = null): ?string
{
return $this->request->headers->get($key, $default);
}
/**
* Get the root URL for the request.
*
* @return string
*/
public function url(): string
{
return rtrim($this->request->getUriForPath('/'), '/');
}
/**
* Extract metadata from header.
*
* @param string $key
* @param string $value
*
* @return array
*/
public function extractFromHeader(string $key, string $value): array
{
$meta = $this->header($key);
if (false !== strpos($meta, $value)) {
$meta = trim(str_replace($value, '', $meta));
return explode(' ', $meta) ?? [];
}
return [];
}
/**
* Extract base64 encoded filename from header.
*
* @return string
*/
public function extractFileName(): string
{
$name = $this->extractMeta('name') ?: $this->extractMeta('filename');
if ( ! $this->isValidFilename($name)) {
return '';
}
return $name;
}
/**
* Extracts the metadata from the request header.
*
* @param string $requestedKey
*
* @return string
*/
public function extractMeta(string $requestedKey): string
{
$uploadMetaData = $this->request->headers->get('Upload-Metadata');
if (empty($uploadMetaData)) {
return '';
}
$uploadMetaDataChunks = explode(',', $uploadMetaData);
foreach ($uploadMetaDataChunks as $chunk) {
$pieces = explode(' ', trim($chunk));
$key = $pieces[0];
$value = $pieces[1] ?? '';
if ($key === $requestedKey) {
return base64_decode($value);
}
}
return '';
}
/**
* Extracts all meta data from the request header.
*
* @return string[]
*/
public function extractAllMeta(): array
{
$uploadMetaData = $this->request->headers->get('Upload-Metadata');
if (empty($uploadMetaData)) {
return [];
}
$uploadMetaDataChunks = explode(',', $uploadMetaData);
$result = [];
foreach ($uploadMetaDataChunks as $chunk) {
$pieces = explode(' ', trim($chunk));
$key = $pieces[0];
$value = $pieces[1] ?? '';
$result[$key] = base64_decode($value);
}
return $result;
}
/**
* Extract partials from header.
*
* @return array
*/
public function extractPartials(): array
{
return $this->extractFromHeader('Upload-Concat', Server::UPLOAD_TYPE_FINAL . ';');
}
/**
* Check if this is a partial upload request.
*
* @return bool
*/
public function isPartial(): bool
{
return Server::UPLOAD_TYPE_PARTIAL === $this->header('Upload-Concat');
}
/**
* Check if this is a final concatenation request.
*
* @return bool
*/
public function isFinal(): bool
{
return null !== ($header = $this->header('Upload-Concat')) && false !== strpos($header, Server::UPLOAD_TYPE_FINAL . ';');
}
/**
* Get request.
*
* @return HttpRequest
*/
public function getRequest(): HttpRequest
{
return $this->request;
}
/**
* Validate file name.
*
* @param string $filename
*
* @return bool
*/
protected function isValidFilename(string $filename): bool
{
$forbidden = ['../', '"', "'", '&', '/', '\\', '?', '#', ':'];
foreach ($forbidden as $char) {
if (false !== strpos($filename, $char)) {
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,134 @@
<?php
namespace TusPhp;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request as HttpRequest;
use Symfony\Component\HttpFoundation\Response as HttpResponse;
class Response
{
/** @var HttpResponse */
protected $response;
/** @var bool */
protected $createOnly = true;
/** @var array */
protected $headers = [];
/**
* Set create only.
*
* @param bool $state
*
* @return self
*/
public function createOnly(bool $state): self
{
$this->createOnly = $state;
return $this;
}
/**
* Set headers.
*
* @param array $headers
*
* @return Response
*/
public function setHeaders(array $headers): self
{
$this->headers += $headers;
return $this;
}
/**
* Replace headers.
*
* @param array $headers
*
* @return Response
*/
public function replaceHeaders(array $headers): self
{
$this->headers = $headers;
return $this;
}
/**
* Get global headers.
*
* @return array
*/
public function getHeaders(): array
{
return $this->headers;
}
/**
* Get create only.
*
* @return bool
*/
public function getCreateOnly(): bool
{
return $this->createOnly;
}
/**
* Create and send a response.
*
* @param mixed $content Response data.
* @param int $status Http status code.
* @param array $headers Headers.
*
* @return HttpResponse
*/
public function send($content, int $status = HttpResponse::HTTP_OK, array $headers = []): HttpResponse
{
$headers = array_merge($this->headers, $headers);
if (\is_array($content)) {
$content = json_encode($content);
}
$response = new HttpResponse($content, $status, $headers);
return $this->createOnly ? $response : $response->send();
}
/**
* Create a new file download response.
*
* @param \SplFileInfo|string $file
* @param string|null $name
* @param array $headers
* @param string|null $disposition
*
* @return BinaryFileResponse
*/
public function download(
$file,
string $name = null,
array $headers = [],
string $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT
): BinaryFileResponse {
$response = new BinaryFileResponse($file, HttpResponse::HTTP_OK, $headers, true, $disposition);
$response->prepare(HttpRequest::createFromGlobals());
if ($name !== null) {
$response = $response->setContentDisposition(
$disposition,
$name
);
}
return $this->createOnly ? $response : $response->send();
}
}

View file

@ -0,0 +1,124 @@
<?php
namespace TusPhp\Tus;
use TusPhp\Cache\Cacheable;
use TusPhp\Cache\CacheFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
abstract class AbstractTus
{
/** @const string Tus protocol version. */
public const TUS_PROTOCOL_VERSION = '1.0.0';
/** @const string Upload type partial. */
public const UPLOAD_TYPE_PARTIAL = 'partial';
/** @const string Upload type final. */
public const UPLOAD_TYPE_FINAL = 'final';
/** @const string Name separator for partial upload. */
protected const PARTIAL_UPLOAD_NAME_SEPARATOR = '_';
/** @const string Upload type normal. */
protected const UPLOAD_TYPE_NORMAL = 'normal';
/** @const string Header Content Type */
protected const HEADER_CONTENT_TYPE = 'application/offset+octet-stream';
/** @var Cacheable */
protected $cache;
/** @var string */
protected $apiPath = '/files';
/** @var EventDispatcherInterface */
protected $dispatcher;
/**
* Set cache.
*
* @param mixed $cache
*
* @throws \ReflectionException
*
* @return self
*/
public function setCache($cache): self
{
if (\is_string($cache)) {
$this->cache = CacheFactory::make($cache);
} elseif ($cache instanceof Cacheable) {
$this->cache = $cache;
}
$prefix = 'tus:' . strtolower((new \ReflectionClass(static::class))->getShortName()) . ':';
$this->cache->setPrefix($prefix);
return $this;
}
/**
* Get cache.
*
* @return Cacheable
*/
public function getCache(): Cacheable
{
return $this->cache;
}
/**
* Set API path.
*
* @param string $path
*
* @return self
*/
public function setApiPath(string $path): self
{
$this->apiPath = $path;
return $this;
}
/**
* Get API path.
*
* @return string
*/
public function getApiPath(): string
{
return $this->apiPath;
}
/**
* Set and get event dispatcher.
*
* @return EventDispatcherInterface
*/
public function event(): EventDispatcherInterface
{
if ( ! $this->dispatcher) {
$this->dispatcher = new EventDispatcher();
}
return $this->dispatcher;
}
/**
* Set event dispatcher.
*
* @param EventDispatcherInterface $dispatcher
*
* @return self
*/
public function setDispatcher(EventDispatcherInterface $dispatcher): self
{
$this->dispatcher = $dispatcher;
return $this;
}
}

View file

@ -0,0 +1,704 @@
<?php
namespace TusPhp\Tus;
use TusPhp\File;
use Carbon\Carbon;
use TusPhp\Config;
use Ramsey\Uuid\Uuid;
use TusPhp\Exception\TusException;
use TusPhp\Exception\FileException;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\ClientException;
use TusPhp\Exception\ConnectionException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ConnectException;
use Symfony\Component\HttpFoundation\Response as HttpResponse;
class Client extends AbstractTus
{
/** @var GuzzleClient */
protected $client;
/** @var string */
protected $filePath;
/** @var int */
protected $fileSize = 0;
/** @var string */
protected $fileName;
/** @var string */
protected $key;
/** @var string */
protected $url;
/** @var string */
protected $checksum;
/** @var int */
protected $partialOffset = -1;
/** @var bool */
protected $partial = false;
/** @var string */
protected $checksumAlgorithm = 'sha256';
/** @var array */
protected $metadata = [];
/** @var array */
protected $headers = [];
/**
* Client constructor.
*
* @param string $baseUri
* @param array $options
*
* @throws \ReflectionException
*/
public function __construct(string $baseUri, array $options = [])
{
$this->headers = $options['headers'] ?? [];
$options['headers'] = [
'Tus-Resumable' => self::TUS_PROTOCOL_VERSION,
] + ($this->headers);
$this->client = new GuzzleClient(
['base_uri' => $baseUri] + $options
);
Config::set(__DIR__ . '/../Config/client.php');
$this->setCache('file');
}
/**
* Set file properties.
*
* @param string $file File path.
* @param string|null $name File name.
*
* @return Client
*/
public function file(string $file, string $name = null): self
{
$this->filePath = $file;
if ( ! file_exists($file) || ! is_readable($file)) {
throw new FileException('Cannot read file: ' . $file);
}
$this->fileName = $name ?? basename($this->filePath);
$this->fileSize = filesize($file);
$this->addMetadata('filename', $this->fileName);
return $this;
}
/**
* Get file path.
*
* @return string|null
*/
public function getFilePath(): ?string
{
return $this->filePath;
}
/**
* Set file name.
*
* @param string $name
*
* @return Client
*/
public function setFileName(string $name): self
{
$this->addMetadata('filename', $this->fileName = $name);
return $this;
}
/**
* Get file name.
*
* @return string|null
*/
public function getFileName(): ?string
{
return $this->fileName;
}
/**
* Get file size.
*
* @return int
*/
public function getFileSize(): int
{
return $this->fileSize;
}
/**
* Get guzzle client.
*
* @return GuzzleClient
*/
public function getClient(): GuzzleClient
{
return $this->client;
}
/**
* Set checksum.
*
* @param string $checksum
*
* @return Client
*/
public function setChecksum(string $checksum): self
{
$this->checksum = $checksum;
return $this;
}
/**
* Get checksum.
*
* @return string
*/
public function getChecksum(): string
{
if (empty($this->checksum)) {
$this->setChecksum(hash_file($this->getChecksumAlgorithm(), $this->getFilePath()));
}
return $this->checksum;
}
/**
* Add metadata.
*
* @param string $key
* @param string $value
*
* @return Client
*/
public function addMetadata(string $key, string $value): self
{
$this->metadata[$key] = base64_encode($value);
return $this;
}
/**
* Remove metadata.
*
* @param string $key
*
* @return Client
*/
public function removeMetadata(string $key): self
{
unset($this->metadata[$key]);
return $this;
}
/**
* Set metadata.
*
* @param array $items
*
* @return Client
*/
public function setMetadata(array $items): self
{
$items = array_map('base64_encode', $items);
$this->metadata = $items;
return $this;
}
/**
* Get metadata.
*
* @return array
*/
public function getMetadata(): array
{
return $this->metadata;
}
/**
* Get metadata for Upload-Metadata header.
*
* @return string
*/
protected function getUploadMetadataHeader(): string
{
$metadata = [];
foreach ($this->getMetadata() as $key => $value) {
$metadata[] = "{$key} {$value}";
}
return implode(',', $metadata);
}
/**
* Set key.
*
* @param string $key
*
* @return Client
*/
public function setKey(string $key): self
{
$this->key = $key;
return $this;
}
/**
* Get key.
*
* @return string
*/
public function getKey(): string
{
return $this->key;
}
/**
* Get url.
*
* @return string|null
*/
public function getUrl(): ?string
{
$this->url = $this->getCache()->get($this->getKey())['location'] ?? null;
if ( ! $this->url) {
throw new FileException('File not found.');
}
return $this->url;
}
/**
* Set checksum algorithm.
*
* @param string $algorithm
*
* @return Client
*/
public function setChecksumAlgorithm(string $algorithm): self
{
$this->checksumAlgorithm = $algorithm;
return $this;
}
/**
* Get checksum algorithm.
*
* @return string
*/
public function getChecksumAlgorithm(): string
{
return $this->checksumAlgorithm;
}
/**
* Check if current upload is expired.
*
* @return bool
*/
public function isExpired(): bool
{
$expiresAt = $this->getCache()->get($this->getKey())['expires_at'] ?? null;
return empty($expiresAt) || Carbon::parse($expiresAt)->lt(Carbon::now());
}
/**
* Check if this is a partial upload request.
*
* @return bool
*/
public function isPartial(): bool
{
return $this->partial;
}
/**
* Get partial offset.
*
* @return int
*/
public function getPartialOffset(): int
{
return $this->partialOffset;
}
/**
* Set offset and force this to be a partial upload request.
*
* @param int $offset
*
* @return self
*/
public function seek(int $offset): self
{
$this->partialOffset = $offset;
$this->partial();
return $this;
}
/**
* Upload file.
*
* @param int $bytes Bytes to upload
*
* @throws TusException
* @throws GuzzleException
* @throws ConnectionException
*
* @return int
*/
public function upload(int $bytes = -1): int
{
$bytes = $bytes < 0 ? $this->getFileSize() : $bytes;
$offset = $this->partialOffset < 0 ? 0 : $this->partialOffset;
try {
// Check if this upload exists with HEAD request.
$offset = $this->sendHeadRequest();
} catch (FileException | ClientException $e) {
// Create a new upload.
$this->url = $this->create($this->getKey());
} catch (ConnectException $e) {
throw new ConnectionException("Couldn't connect to server.");
}
// Verify that upload is not yet expired.
if ($this->isExpired()) {
throw new TusException('Upload expired.');
}
// Now, resume upload with PATCH request.
return $this->sendPatchRequest($bytes, $offset);
}
/**
* Returns offset if file is partially uploaded.
*
* @throws GuzzleException
*
* @return bool|int
*/
public function getOffset()
{
try {
$offset = $this->sendHeadRequest();
} catch (FileException | ClientException $e) {
return false;
}
return $offset;
}
/**
* Create resource with POST request.
*
* @param string $key
*
* @throws FileException
* @throws GuzzleException
*
* @return string
*/
public function create(string $key): string
{
return $this->createWithUpload($key, 0)['location'];
}
/**
* Create resource with POST request and upload data using the creation-with-upload extension.
*
* @see https://tus.io/protocols/resumable-upload.html#creation-with-upload
*
* @param string $key
* @param int $bytes -1 => all data; 0 => no data
*
* @throws GuzzleException
*
* @return array [
* 'location' => string,
* 'offset' => int
* ]
*/
public function createWithUpload(string $key, int $bytes = -1): array
{
$bytes = $bytes < 0 ? $this->fileSize : $bytes;
$headers = $this->headers + [
'Upload-Length' => $this->fileSize,
'Upload-Key' => $key,
'Upload-Checksum' => $this->getUploadChecksumHeader(),
'Upload-Metadata' => $this->getUploadMetadataHeader(),
];
$data = '';
if ($bytes > 0) {
$data = $this->getData(0, $bytes);
$headers += [
'Content-Type' => self::HEADER_CONTENT_TYPE,
'Content-Length' => \strlen($data),
];
}
if ($this->isPartial()) {
$headers += ['Upload-Concat' => 'partial'];
}
try {
$response = $this->getClient()->post($this->apiPath, [
'body' => $data,
'headers' => $headers,
]);
} catch (ClientException $e) {
$response = $e->getResponse();
}
$statusCode = $response->getStatusCode();
if (HttpResponse::HTTP_CREATED !== $statusCode) {
throw new FileException('Unable to create resource.');
}
$uploadOffset = $bytes > 0 ? current($response->getHeader('upload-offset')) : 0;
$uploadLocation = current($response->getHeader('location'));
$this->getCache()->set($this->getKey(), [
'location' => $uploadLocation,
'expires_at' => Carbon::now()->addSeconds($this->getCache()->getTtl())->format($this->getCache()::RFC_7231),
]);
return [
'location' => $uploadLocation,
'offset' => $uploadOffset,
];
}
/**
* Concatenate 2 or more partial uploads.
*
* @param string $key
* @param mixed $partials
*
* @throws GuzzleException
*
* @return string
*/
public function concat(string $key, ...$partials): string
{
$response = $this->getClient()->post($this->apiPath, [
'headers' => $this->headers + [
'Upload-Length' => $this->fileSize,
'Upload-Key' => $key,
'Upload-Checksum' => $this->getUploadChecksumHeader(),
'Upload-Metadata' => $this->getUploadMetadataHeader(),
'Upload-Concat' => self::UPLOAD_TYPE_FINAL . ';' . implode(' ', $partials),
],
]);
$data = json_decode($response->getBody(), true);
$checksum = $data['data']['checksum'] ?? null;
$statusCode = $response->getStatusCode();
if (HttpResponse::HTTP_CREATED !== $statusCode || ! $checksum) {
throw new FileException('Unable to create resource.');
}
return $checksum;
}
/**
* Send DELETE request.
*
* @throws FileException
* @throws GuzzleException
*
* @return void
*/
public function delete()
{
try {
$this->getClient()->delete($this->getUrl());
} catch (ClientException $e) {
$statusCode = $e->getResponse()->getStatusCode();
if (HttpResponse::HTTP_NOT_FOUND === $statusCode || HttpResponse::HTTP_GONE === $statusCode) {
throw new FileException('File not found.');
}
}
}
/**
* Set as partial request.
*
* @param bool $state
*
* @return void
*/
protected function partial(bool $state = true)
{
$this->partial = $state;
if ( ! $this->partial) {
return;
}
$key = $this->getKey();
if (false !== strpos($key, self::PARTIAL_UPLOAD_NAME_SEPARATOR)) {
[$key, /* $partialKey */] = explode(self::PARTIAL_UPLOAD_NAME_SEPARATOR, $key);
}
$this->key = $key . self::PARTIAL_UPLOAD_NAME_SEPARATOR . Uuid::uuid4()->toString();
}
/**
* Send HEAD request.
*
* @throws FileException
* @throws GuzzleException
*
* @return int
*/
protected function sendHeadRequest(): int
{
$response = $this->getClient()->head($this->getUrl());
$statusCode = $response->getStatusCode();
if (HttpResponse::HTTP_OK !== $statusCode) {
throw new FileException('File not found.');
}
return (int) current($response->getHeader('upload-offset'));
}
/**
* Send PATCH request.
*
* @param int $bytes
* @param int $offset
*
* @throws TusException
* @throws FileException
* @throws GuzzleException
* @throws ConnectionException
*
* @return int
*/
protected function sendPatchRequest(int $bytes, int $offset): int
{
$data = $this->getData($offset, $bytes);
$headers = $this->headers + [
'Content-Type' => self::HEADER_CONTENT_TYPE,
'Content-Length' => \strlen($data),
'Upload-Checksum' => $this->getUploadChecksumHeader(),
];
if ($this->isPartial()) {
$headers += ['Upload-Concat' => self::UPLOAD_TYPE_PARTIAL];
} else {
$headers += ['Upload-Offset' => $offset];
}
try {
$response = $this->getClient()->patch($this->getUrl(), [
'body' => $data,
'headers' => $headers,
]);
return (int) current($response->getHeader('upload-offset'));
} catch (ClientException $e) {
throw $this->handleClientException($e);
} catch (ConnectException $e) {
throw new ConnectionException("Couldn't connect to server.");
}
}
/**
* Handle client exception during patch request.
*
* @param ClientException $e
*
* @return \Exception
*/
protected function handleClientException(ClientException $e)
{
$response = $e->getResponse();
$statusCode = $response !== null ? $response->getStatusCode() : HttpResponse::HTTP_INTERNAL_SERVER_ERROR;
if (HttpResponse::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE === $statusCode) {
return new FileException('The uploaded file is corrupt.');
}
if (HttpResponse::HTTP_CONTINUE === $statusCode) {
return new ConnectionException('Connection aborted by user.');
}
if (HttpResponse::HTTP_UNSUPPORTED_MEDIA_TYPE === $statusCode) {
return new TusException('Unsupported media types.');
}
return new TusException((string) $response->getBody(), $statusCode);
}
/**
* Get X bytes of data from file.
*
* @param int $offset
* @param int $bytes
*
* @return string
*/
protected function getData(int $offset, int $bytes): string
{
$file = new File();
$handle = $file->open($this->getFilePath(), $file::READ_BINARY);
$file->seek($handle, $offset);
$data = $file->read($handle, $bytes);
$file->close($handle);
return $data;
}
/**
* Get upload checksum header.
*
* @return string
*/
protected function getUploadChecksumHeader(): string
{
return $this->getChecksumAlgorithm() . ' ' . base64_encode($this->getChecksum());
}
}

View file

@ -0,0 +1,837 @@
<?php
namespace TusPhp\Tus;
use TusPhp\File;
use Carbon\Carbon;
use TusPhp\Request;
use TusPhp\Response;
use Ramsey\Uuid\Uuid;
use TusPhp\Cache\Cacheable;
use TusPhp\Events\UploadMerged;
use TusPhp\Events\UploadCreated;
use TusPhp\Events\UploadComplete;
use TusPhp\Events\UploadProgress;
use TusPhp\Middleware\Middleware;
use TusPhp\Exception\FileException;
use TusPhp\Exception\ConnectionException;
use TusPhp\Exception\OutOfRangeException;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request as HttpRequest;
use Symfony\Component\HttpFoundation\Response as HttpResponse;
class Server extends AbstractTus
{
/** @const string Tus Creation Extension */
public const TUS_EXTENSION_CREATION = 'creation';
/** @const string Tus Termination Extension */
public const TUS_EXTENSION_TERMINATION = 'termination';
/** @const string Tus Checksum Extension */
public const TUS_EXTENSION_CHECKSUM = 'checksum';
/** @const string Tus Expiration Extension */
public const TUS_EXTENSION_EXPIRATION = 'expiration';
/** @const string Tus Concatenation Extension */
public const TUS_EXTENSION_CONCATENATION = 'concatenation';
/** @const array All supported tus extensions */
public const TUS_EXTENSIONS = [
self::TUS_EXTENSION_CREATION,
self::TUS_EXTENSION_TERMINATION,
self::TUS_EXTENSION_CHECKSUM,
self::TUS_EXTENSION_EXPIRATION,
self::TUS_EXTENSION_CONCATENATION,
];
/** @const int 460 Checksum Mismatch */
private const HTTP_CHECKSUM_MISMATCH = 460;
/** @const string Default checksum algorithm */
private const DEFAULT_CHECKSUM_ALGORITHM = 'sha256';
/** @var Request */
protected $request;
/** @var Response */
protected $response;
/** @var string */
protected $uploadDir;
/** @var string */
protected $uploadKey;
/** @var Middleware */
protected $middleware;
/**
* @var int Max upload size in bytes
* Default 0, no restriction.
*/
protected $maxUploadSize = 0;
/**
* TusServer constructor.
*
* @param Cacheable|string $cacheAdapter
*
* @throws \ReflectionException
*/
public function __construct($cacheAdapter = 'file')
{
$this->request = new Request();
$this->response = new Response();
$this->middleware = new Middleware();
$this->uploadDir = \dirname(__DIR__, 2) . '/' . 'uploads';
$this->setCache($cacheAdapter);
}
/**
* Set upload dir.
*
* @param string $path
*
* @return Server
*/
public function setUploadDir(string $path): self
{
$this->uploadDir = $path;
return $this;
}
/**
* Get upload dir.
*
* @return string
*/
public function getUploadDir(): string
{
return $this->uploadDir;
}
/**
* Get request.
*
* @return Request
*/
public function getRequest(): Request
{
return $this->request;
}
/**
* Get request.
*
* @return Response
*/
public function getResponse(): Response
{
return $this->response;
}
/**
* Get file checksum.
*
* @param string $filePath
*
* @return string
*/
public function getServerChecksum(string $filePath): string
{
return hash_file($this->getChecksumAlgorithm(), $filePath);
}
/**
* Get checksum algorithm.
*
* @return string|null
*/
public function getChecksumAlgorithm(): ?string
{
$checksumHeader = $this->getRequest()->header('Upload-Checksum');
if (empty($checksumHeader)) {
return self::DEFAULT_CHECKSUM_ALGORITHM;
}
[$checksumAlgorithm, /* $checksum */] = explode(' ', $checksumHeader);
return $checksumAlgorithm;
}
/**
* Set upload key.
*
* @param string $key
*
* @return Server
*/
public function setUploadKey(string $key): self
{
$this->uploadKey = $key;
return $this;
}
/**
* Get upload key from header.
*
* @return string|HttpResponse
*/
public function getUploadKey()
{
if ( ! empty($this->uploadKey)) {
return $this->uploadKey;
}
$key = $this->getRequest()->header('Upload-Key') ?? Uuid::uuid4()->toString();
if (empty($key)) {
return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
}
$this->uploadKey = $key;
return $this->uploadKey;
}
/**
* Set middleware.
*
* @param Middleware $middleware
*
* @return self
*/
public function setMiddleware(Middleware $middleware): self
{
$this->middleware = $middleware;
return $this;
}
/**
* Get middleware.
*
* @return Middleware
*/
public function middleware(): Middleware
{
return $this->middleware;
}
/**
* Set max upload size in bytes.
*
* @param int $uploadSize
*
* @return Server
*/
public function setMaxUploadSize(int $uploadSize): self
{
$this->maxUploadSize = $uploadSize;
return $this;
}
/**
* Get max upload size.
*
* @return int
*/
public function getMaxUploadSize(): int
{
return $this->maxUploadSize;
}
/**
* Handle all HTTP request.
*
* @return HttpResponse|BinaryFileResponse
*/
public function serve()
{
$this->applyMiddleware();
$requestMethod = $this->getRequest()->method();
if ( ! \in_array($requestMethod, $this->getRequest()->allowedHttpVerbs(), true)) {
return $this->response->send(null, HttpResponse::HTTP_METHOD_NOT_ALLOWED);
}
$clientVersion = $this->getRequest()->header('Tus-Resumable');
if (HttpRequest::METHOD_OPTIONS !== $requestMethod && $clientVersion && self::TUS_PROTOCOL_VERSION !== $clientVersion) {
return $this->response->send(null, HttpResponse::HTTP_PRECONDITION_FAILED, [
'Tus-Version' => self::TUS_PROTOCOL_VERSION,
]);
}
$method = 'handle' . ucfirst(strtolower($requestMethod));
return $this->{$method}();
}
/**
* Apply middleware.
*
* @return void
*/
protected function applyMiddleware()
{
$middleware = $this->middleware()->list();
foreach ($middleware as $m) {
$m->handle($this->getRequest(), $this->getResponse());
}
}
/**
* Handle OPTIONS request.
*
* @return HttpResponse
*/
protected function handleOptions(): HttpResponse
{
$headers = [
'Allow' => implode(',', $this->request->allowedHttpVerbs()),
'Tus-Version' => self::TUS_PROTOCOL_VERSION,
'Tus-Extension' => implode(',', self::TUS_EXTENSIONS),
'Tus-Checksum-Algorithm' => $this->getSupportedHashAlgorithms(),
];
$maxUploadSize = $this->getMaxUploadSize();
if ($maxUploadSize > 0) {
$headers['Tus-Max-Size'] = $maxUploadSize;
}
return $this->response->send(null, HttpResponse::HTTP_OK, $headers);
}
/**
* Handle HEAD request.
*
* @return HttpResponse
*/
protected function handleHead(): HttpResponse
{
$key = $this->request->key();
if ( ! $fileMeta = $this->cache->get($key)) {
return $this->response->send(null, HttpResponse::HTTP_NOT_FOUND);
}
$offset = $fileMeta['offset'] ?? false;
if (false === $offset) {
return $this->response->send(null, HttpResponse::HTTP_GONE);
}
return $this->response->send(null, HttpResponse::HTTP_OK, $this->getHeadersForHeadRequest($fileMeta));
}
/**
* Handle POST request.
*
* @return HttpResponse
*/
protected function handlePost(): HttpResponse
{
$fileName = $this->getRequest()->extractFileName();
$uploadType = self::UPLOAD_TYPE_NORMAL;
if (empty($fileName)) {
return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
}
if ( ! $this->verifyUploadSize()) {
return $this->response->send(null, HttpResponse::HTTP_REQUEST_ENTITY_TOO_LARGE);
}
$uploadKey = $this->getUploadKey();
$filePath = $this->uploadDir . '/' . $fileName;
if ($this->getRequest()->isFinal()) {
return $this->handleConcatenation($fileName, $filePath);
}
if ($this->getRequest()->isPartial()) {
$filePath = $this->getPathForPartialUpload($uploadKey) . $fileName;
$uploadType = self::UPLOAD_TYPE_PARTIAL;
}
$checksum = $this->getClientChecksum();
$location = $this->getRequest()->url() . $this->getApiPath() . '/' . $uploadKey;
$file = $this->buildFile([
'name' => $fileName,
'offset' => 0,
'size' => $this->getRequest()->header('Upload-Length'),
'file_path' => $filePath,
'location' => $location,
])->setKey($uploadKey)->setChecksum($checksum)->setUploadMetadata($this->getRequest()->extractAllMeta());
$this->cache->set($uploadKey, $file->details() + ['upload_type' => $uploadType]);
$headers = [
'Location' => $location,
'Upload-Expires' => $this->cache->get($uploadKey)['expires_at'],
];
$this->event()->dispatch(
new UploadCreated($file, $this->getRequest(), $this->getResponse()->setHeaders($headers)),
UploadCreated::NAME
);
return $this->response->send(null, HttpResponse::HTTP_CREATED, $headers);
}
/**
* Handle file concatenation.
*
* @param string $fileName
* @param string $filePath
*
* @return HttpResponse
*/
protected function handleConcatenation(string $fileName, string $filePath): HttpResponse
{
$partials = $this->getRequest()->extractPartials();
$uploadKey = $this->getUploadKey();
$files = $this->getPartialsMeta($partials);
$filePaths = array_column($files, 'file_path');
$location = $this->getRequest()->url() . $this->getApiPath() . '/' . $uploadKey;
$file = $this->buildFile([
'name' => $fileName,
'offset' => 0,
'size' => 0,
'file_path' => $filePath,
'location' => $location,
])->setFilePath($filePath)->setKey($uploadKey)->setUploadMetadata($this->getRequest()->extractAllMeta());
$file->setOffset($file->merge($files));
// Verify checksum.
$checksum = $this->getServerChecksum($filePath);
if ($checksum !== $this->getClientChecksum()) {
return $this->response->send(null, self::HTTP_CHECKSUM_MISMATCH);
}
$file->setChecksum($checksum);
$this->cache->set($uploadKey, $file->details() + ['upload_type' => self::UPLOAD_TYPE_FINAL]);
// Cleanup.
if ($file->delete($filePaths, true)) {
$this->cache->deleteAll($partials);
}
$this->event()->dispatch(
new UploadMerged($file, $this->getRequest(), $this->getResponse()),
UploadMerged::NAME
);
return $this->response->send(
['data' => ['checksum' => $checksum]],
HttpResponse::HTTP_CREATED,
[
'Location' => $location,
]
);
}
/**
* Handle PATCH request.
*
* @return HttpResponse
*/
protected function handlePatch(): HttpResponse
{
$uploadKey = $this->request->key();
if ( ! $meta = $this->cache->get($uploadKey)) {
return $this->response->send(null, HttpResponse::HTTP_GONE);
}
$status = $this->verifyPatchRequest($meta);
if (HttpResponse::HTTP_OK !== $status) {
return $this->response->send(null, $status);
}
$file = $this->buildFile($meta)->setUploadMetadata($meta['metadata'] ?? []);
$checksum = $meta['checksum'];
try {
$fileSize = $file->getFileSize();
$offset = $file->setKey($uploadKey)->setChecksum($checksum)->upload($fileSize);
// If upload is done, verify checksum.
if ($offset === $fileSize) {
if ( ! $this->verifyChecksum($checksum, $meta['file_path'])) {
return $this->response->send(null, self::HTTP_CHECKSUM_MISMATCH);
}
$this->event()->dispatch(
new UploadComplete($file, $this->getRequest(), $this->getResponse()),
UploadComplete::NAME
);
} else {
$this->event()->dispatch(
new UploadProgress($file, $this->getRequest(), $this->getResponse()),
UploadProgress::NAME
);
}
} catch (FileException $e) {
return $this->response->send($e->getMessage(), HttpResponse::HTTP_UNPROCESSABLE_ENTITY);
} catch (OutOfRangeException $e) {
return $this->response->send(null, HttpResponse::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE);
} catch (ConnectionException $e) {
return $this->response->send(null, HttpResponse::HTTP_CONTINUE);
}
if ( ! $meta = $this->cache->get($uploadKey)) {
return $this->response->send(null, HttpResponse::HTTP_GONE);
}
return $this->response->send(null, HttpResponse::HTTP_NO_CONTENT, [
'Content-Type' => self::HEADER_CONTENT_TYPE,
'Upload-Expires' => $meta['expires_at'],
'Upload-Offset' => $offset,
]);
}
/**
* Verify PATCH request.
*
* @param array $meta
*
* @return int
*/
protected function verifyPatchRequest(array $meta): int
{
if (self::UPLOAD_TYPE_FINAL === $meta['upload_type']) {
return HttpResponse::HTTP_FORBIDDEN;
}
$uploadOffset = $this->request->header('upload-offset');
if ($uploadOffset && $uploadOffset !== (string) $meta['offset']) {
return HttpResponse::HTTP_CONFLICT;
}
$contentType = $this->request->header('Content-Type');
if ($contentType !== self::HEADER_CONTENT_TYPE) {
return HTTPRESPONSE::HTTP_UNSUPPORTED_MEDIA_TYPE;
}
return HttpResponse::HTTP_OK;
}
/**
* Handle GET request.
*
* As per RFC7231, we need to treat HEAD and GET as an identical request.
* All major PHP frameworks follows the same and silently transforms each
* HEAD requests to GET.
*
* @return BinaryFileResponse|HttpResponse
*/
protected function handleGet()
{
// We will treat '/files/<key>/get' as a download request.
if ('get' === $this->request->key()) {
return $this->handleDownload();
}
return $this->handleHead();
}
/**
* Handle Download request.
*
* @return BinaryFileResponse|HttpResponse
*/
protected function handleDownload()
{
$path = explode('/', str_replace('/get', '', $this->request->path()));
$key = end($path);
if ( ! $fileMeta = $this->cache->get($key)) {
return $this->response->send('404 upload not found.', HttpResponse::HTTP_NOT_FOUND);
}
$resource = $fileMeta['file_path'] ?? null;
$fileName = $fileMeta['name'] ?? null;
if ( ! $resource || ! file_exists($resource)) {
return $this->response->send('404 upload not found.', HttpResponse::HTTP_NOT_FOUND);
}
return $this->response->download($resource, $fileName);
}
/**
* Handle DELETE request.
*
* @return HttpResponse
*/
protected function handleDelete(): HttpResponse
{
$key = $this->request->key();
$fileMeta = $this->cache->get($key);
$resource = $fileMeta['file_path'] ?? null;
if ( ! $resource) {
return $this->response->send(null, HttpResponse::HTTP_NOT_FOUND);
}
$isDeleted = $this->cache->delete($key);
if ( ! $isDeleted || ! file_exists($resource)) {
return $this->response->send(null, HttpResponse::HTTP_GONE);
}
unlink($resource);
return $this->response->send(null, HttpResponse::HTTP_NO_CONTENT, [
'Tus-Extension' => self::TUS_EXTENSION_TERMINATION,
]);
}
/**
* Get required headers for head request.
*
* @param array $fileMeta
*
* @return array
*/
protected function getHeadersForHeadRequest(array $fileMeta): array
{
$headers = [
'Upload-Length' => (int) $fileMeta['size'],
'Upload-Offset' => (int) $fileMeta['offset'],
'Cache-Control' => 'no-store',
];
if (self::UPLOAD_TYPE_FINAL === $fileMeta['upload_type'] && $fileMeta['size'] !== $fileMeta['offset']) {
unset($headers['Upload-Offset']);
}
if (self::UPLOAD_TYPE_NORMAL !== $fileMeta['upload_type']) {
$headers += ['Upload-Concat' => $fileMeta['upload_type']];
}
return $headers;
}
/**
* Build file object.
*
* @param array $meta
*
* @return File
*/
protected function buildFile(array $meta): File
{
$file = new File($meta['name'], $this->cache);
if (\array_key_exists('offset', $meta)) {
$file->setMeta($meta['offset'], $meta['size'], $meta['file_path'], $meta['location']);
}
return $file;
}
/**
* Get list of supported hash algorithms.
*
* @return string
*/
protected function getSupportedHashAlgorithms(): string
{
$supportedAlgorithms = hash_algos();
$algorithms = [];
foreach ($supportedAlgorithms as $hashAlgo) {
if (false !== strpos($hashAlgo, ',')) {
$algorithms[] = "'{$hashAlgo}'";
} else {
$algorithms[] = $hashAlgo;
}
}
return implode(',', $algorithms);
}
/**
* Verify and get upload checksum from header.
*
* @return string|HttpResponse
*/
protected function getClientChecksum()
{
$checksumHeader = $this->getRequest()->header('Upload-Checksum');
if (empty($checksumHeader)) {
return '';
}
[$checksumAlgorithm, $checksum] = explode(' ', $checksumHeader);
$checksum = base64_decode($checksum);
if (false === $checksum || ! \in_array($checksumAlgorithm, hash_algos(), true)) {
return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
}
return $checksum;
}
/**
* Get expired but incomplete uploads.
*
* @param array|null $contents
*
* @return bool
*/
protected function isExpired($contents): bool
{
if (empty($contents)) {
return true;
}
$isExpired = empty($contents['expires_at']) || Carbon::parse($contents['expires_at'])->lt(Carbon::now());
if ($isExpired && $contents['offset'] !== $contents['size']) {
return true;
}
return false;
}
/**
* Get path for partial upload.
*
* @param string $key
*
* @return string
*/
protected function getPathForPartialUpload(string $key): string
{
[$actualKey, /* $partialUploadKey */] = explode(self::PARTIAL_UPLOAD_NAME_SEPARATOR, $key);
$path = $this->uploadDir . '/' . $actualKey . '/';
if ( ! file_exists($path)) {
mkdir($path);
}
return $path;
}
/**
* Get metadata of partials.
*
* @param array $partials
*
* @return array
*/
protected function getPartialsMeta(array $partials): array
{
$files = [];
foreach ($partials as $partial) {
$fileMeta = $this->getCache()->get($partial);
$files[] = $fileMeta;
}
return $files;
}
/**
* Delete expired resources.
*
* @return array
*/
public function handleExpiration(): array
{
$deleted = [];
$cacheKeys = $this->cache->keys();
foreach ($cacheKeys as $key) {
$fileMeta = $this->cache->get($key, true);
if ( ! $this->isExpired($fileMeta)) {
continue;
}
if ( ! $this->cache->delete($key)) {
continue;
}
if (is_writable($fileMeta['file_path'])) {
unlink($fileMeta['file_path']);
}
$deleted[] = $fileMeta;
}
return $deleted;
}
/**
* Verify max upload size.
*
* @return bool
*/
protected function verifyUploadSize(): bool
{
$maxUploadSize = $this->getMaxUploadSize();
if ($maxUploadSize > 0 && $this->getRequest()->header('Upload-Length') > $maxUploadSize) {
return false;
}
return true;
}
/**
* Verify checksum if available.
*
* @param string $checksum
* @param string $filePath
*
* @return bool
*/
protected function verifyChecksum(string $checksum, string $filePath): bool
{
// Skip if checksum is empty.
if (empty($checksum)) {
return true;
}
return $checksum === $this->getServerChecksum($filePath);
}
/**
* No other methods are allowed.
*
* @param string $method
* @param array $params
*
* @return HttpResponse
*/
public function __call(string $method, array $params)
{
return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
}
}

View file

@ -32,7 +32,7 @@
"ext-dom": "*",
"ext-pcntl": "*",
"ext-sockets": "*",
"phpunit/phpunit": "^4.8.35 || ^5.6.3",
"phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5",
"behat/behat": "~3.0",
"doctrine/cache": "~1.4",
"aws/aws-php-sns-message-validator": "~1.0",
@ -41,7 +41,9 @@
"psr/cache": "^1.0",
"psr/simple-cache": "^1.0",
"paragonie/random_compat": ">= 2",
"sebastian/comparator": "^1.2.3"
"sebastian/comparator": "^1.2.3 || ^4.0",
"yoast/phpunit-polyfills": "^1.0",
"dms/phpunit-arraysubset-asserts": "^0.4.0"
},
"suggest": {
"ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",

View file

@ -7,30 +7,46 @@ use Aws\AwsClient;
* This client is used to interact with the **AWS Amplify UI Builder** service.
* @method \Aws\Result createComponent(array $args = [])
* @method \GuzzleHttp\Promise\Promise createComponentAsync(array $args = [])
* @method \Aws\Result createForm(array $args = [])
* @method \GuzzleHttp\Promise\Promise createFormAsync(array $args = [])
* @method \Aws\Result createTheme(array $args = [])
* @method \GuzzleHttp\Promise\Promise createThemeAsync(array $args = [])
* @method \Aws\Result deleteComponent(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteComponentAsync(array $args = [])
* @method \Aws\Result deleteForm(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteFormAsync(array $args = [])
* @method \Aws\Result deleteTheme(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteThemeAsync(array $args = [])
* @method \Aws\Result exchangeCodeForToken(array $args = [])
* @method \GuzzleHttp\Promise\Promise exchangeCodeForTokenAsync(array $args = [])
* @method \Aws\Result exportComponents(array $args = [])
* @method \GuzzleHttp\Promise\Promise exportComponentsAsync(array $args = [])
* @method \Aws\Result exportForms(array $args = [])
* @method \GuzzleHttp\Promise\Promise exportFormsAsync(array $args = [])
* @method \Aws\Result exportThemes(array $args = [])
* @method \GuzzleHttp\Promise\Promise exportThemesAsync(array $args = [])
* @method \Aws\Result getComponent(array $args = [])
* @method \GuzzleHttp\Promise\Promise getComponentAsync(array $args = [])
* @method \Aws\Result getForm(array $args = [])
* @method \GuzzleHttp\Promise\Promise getFormAsync(array $args = [])
* @method \Aws\Result getMetadata(array $args = [])
* @method \GuzzleHttp\Promise\Promise getMetadataAsync(array $args = [])
* @method \Aws\Result getTheme(array $args = [])
* @method \GuzzleHttp\Promise\Promise getThemeAsync(array $args = [])
* @method \Aws\Result listComponents(array $args = [])
* @method \GuzzleHttp\Promise\Promise listComponentsAsync(array $args = [])
* @method \Aws\Result listForms(array $args = [])
* @method \GuzzleHttp\Promise\Promise listFormsAsync(array $args = [])
* @method \Aws\Result listThemes(array $args = [])
* @method \GuzzleHttp\Promise\Promise listThemesAsync(array $args = [])
* @method \Aws\Result putMetadataFlag(array $args = [])
* @method \GuzzleHttp\Promise\Promise putMetadataFlagAsync(array $args = [])
* @method \Aws\Result refreshToken(array $args = [])
* @method \GuzzleHttp\Promise\Promise refreshTokenAsync(array $args = [])
* @method \Aws\Result updateComponent(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateComponentAsync(array $args = [])
* @method \Aws\Result updateForm(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateFormAsync(array $args = [])
* @method \Aws\Result updateTheme(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateThemeAsync(array $args = [])
*/

View file

@ -15,12 +15,26 @@ trait JsonParserTrait
private function genericHandler(ResponseInterface $response)
{
$code = (string) $response->getStatusCode();
if ($this->api
&& $this->api->getMetadata('awsQueryCompatible')
&& $response->getHeaderLine('x-amzn-query-error')
) {
$queryError = $response->getHeaderLine('x-amzn-query-error');
$parts = explode(';', $queryError);
if (isset($parts) && count($parts) == 2 && $parts[0] && $parts[1]) {
$error_code = $parts[0];
$error_type = $parts[1];
}
}
if (!isset($error_type)) {
$error_type = $code[0] == '4' ? 'client' : 'server';
}
return [
'request_id' => (string) $response->getHeaderLine('x-amzn-requestid'),
'code' => null,
'code' => isset($error_code) ? $error_code : null,
'message' => null,
'type' => $code[0] == '4' ? 'client' : 'server',
'type' => $error_type,
'parsed' => $this->parseJson($response->getBody(), $response)
];
}

View file

@ -33,8 +33,10 @@ class JsonRpcErrorParser extends AbstractErrorParser
}
if (isset($data['parsed']['__type'])) {
if (!isset($data['code'])) {
$parts = explode('#', $data['parsed']['__type']);
$data['code'] = isset($parts[1]) ? $parts[1] : $parts[0];
}
$data['message'] = isset($data['parsed']['message'])
? $data['parsed']['message']
: null;

View file

@ -160,7 +160,7 @@ abstract class AbstractRestParser extends AbstractParser
// Check if the headers are prefixed by a location name
$result[$name] = [];
$prefix = $shape['locationName'];
$prefixLen = strlen($prefix);
$prefixLen = $prefix !== null ? strlen($prefix) : 0;
foreach ($response->getHeaders() as $k => $values) {
if (!$prefixLen) {

View file

@ -28,8 +28,17 @@ class JsonBody
*/
public static function getContentType(Service $service)
{
if ($service->getMetadata('protocol') === 'rest-json') {
return 'application/json';
}
$jsonVersion = $service->getMetadata('jsonVersion');
if (empty($jsonVersion)) {
throw new \InvalidArgumentException('invalid json');
} else {
return 'application/x-amz-json-'
. number_format($service->getMetadata('jsonVersion'), 1);
. @number_format($service->getMetadata('jsonVersion'), 1);
}
}
/**

View file

@ -27,7 +27,7 @@ class RestJsonSerializer extends RestSerializer
JsonBody $jsonFormatter = null
) {
parent::__construct($api, $endpoint);
$this->contentType = 'application/json';
$this->contentType = JsonBody::getContentType($api);
$this->jsonFormatter = $jsonFormatter ?: new JsonBody($api);
}

View file

@ -216,6 +216,7 @@ class Validator
return;
}
$value = isset($value) ? $value : '';
$this->validateRange($shape, strlen($value), "string length");
if ($this->constraints['pattern']) {

View file

@ -17,6 +17,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createServiceAsync(array $args = [])
* @method \Aws\Result createVpcConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise createVpcConnectorAsync(array $args = [])
* @method \Aws\Result createVpcIngressConnection(array $args = [])
* @method \GuzzleHttp\Promise\Promise createVpcIngressConnectionAsync(array $args = [])
* @method \Aws\Result deleteAutoScalingConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAutoScalingConfigurationAsync(array $args = [])
* @method \Aws\Result deleteConnection(array $args = [])
@ -27,6 +29,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteServiceAsync(array $args = [])
* @method \Aws\Result deleteVpcConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteVpcConnectorAsync(array $args = [])
* @method \Aws\Result deleteVpcIngressConnection(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteVpcIngressConnectionAsync(array $args = [])
* @method \Aws\Result describeAutoScalingConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeAutoScalingConfigurationAsync(array $args = [])
* @method \Aws\Result describeCustomDomains(array $args = [])
@ -37,6 +41,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise describeServiceAsync(array $args = [])
* @method \Aws\Result describeVpcConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeVpcConnectorAsync(array $args = [])
* @method \Aws\Result describeVpcIngressConnection(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeVpcIngressConnectionAsync(array $args = [])
* @method \Aws\Result disassociateCustomDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociateCustomDomainAsync(array $args = [])
* @method \Aws\Result listAutoScalingConfigurations(array $args = [])
@ -53,6 +59,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result listVpcConnectors(array $args = [])
* @method \GuzzleHttp\Promise\Promise listVpcConnectorsAsync(array $args = [])
* @method \Aws\Result listVpcIngressConnections(array $args = [])
* @method \GuzzleHttp\Promise\Promise listVpcIngressConnectionsAsync(array $args = [])
* @method \Aws\Result pauseService(array $args = [])
* @method \GuzzleHttp\Promise\Promise pauseServiceAsync(array $args = [])
* @method \Aws\Result resumeService(array $args = [])
@ -65,5 +73,7 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateService(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateServiceAsync(array $args = [])
* @method \Aws\Result updateVpcIngressConnection(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateVpcIngressConnectionAsync(array $args = [])
*/
class AppRunnerClient extends AwsClient {}

View file

@ -23,7 +23,7 @@ class ArnParser
*/
public static function isArn($string)
{
return strpos($string, 'arn:') === 0;
return $string !== null && strpos($string, 'arn:') === 0;
}
/**

View file

@ -17,6 +17,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise disassociateGatewayFromServerAsync(array $args = [])
* @method \Aws\Result getGateway(array $args = [])
* @method \GuzzleHttp\Promise\Promise getGatewayAsync(array $args = [])
* @method \Aws\Result getVirtualMachine(array $args = [])
* @method \GuzzleHttp\Promise\Promise getVirtualMachineAsync(array $args = [])
* @method \Aws\Result importHypervisorConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise importHypervisorConfigurationAsync(array $args = [])
* @method \Aws\Result listGateways(array $args = [])

View file

@ -39,6 +39,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listBillingGroupCostReportsAsync(array $args = [])
* @method \Aws\Result listBillingGroups(array $args = [])
* @method \GuzzleHttp\Promise\Promise listBillingGroupsAsync(array $args = [])
* @method \Aws\Result listCustomLineItemVersions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listCustomLineItemVersionsAsync(array $args = [])
* @method \Aws\Result listCustomLineItems(array $args = [])
* @method \GuzzleHttp\Promise\Promise listCustomLineItemsAsync(array $args = [])
* @method \Aws\Result listPricingPlans(array $args = [])

View file

@ -795,16 +795,13 @@ class ClientResolver
public static function _apply_user_agent($inputUserAgent, array &$args, HandlerList $list)
{
//Add SDK version
$xAmzUserAgent = ['aws-sdk-php/' . Sdk::VERSION];
$userAgent = ['aws-sdk-php/' . Sdk::VERSION];
//If on HHVM add the HHVM version
if (defined('HHVM_VERSION')) {
$xAmzUserAgent []= 'HHVM/' . HHVM_VERSION;
$userAgent []= 'HHVM/' . HHVM_VERSION;
}
//Set up the updated user agent
$legacyUserAgent = $xAmzUserAgent;
//Add OS version
$disabledFunctions = explode(',', ini_get('disable_functions'));
if (function_exists('php_uname')
@ -812,16 +809,16 @@ class ClientResolver
) {
$osName = "OS/" . php_uname('s') . '/' . php_uname('r');
if (!empty($osName)) {
$legacyUserAgent []= $osName;
$userAgent []= $osName;
}
}
//Add the language version
$legacyUserAgent []= 'lang/php/' . phpversion();
$userAgent []= 'lang/php/' . phpversion();
//Add exec environment if present
if ($executionEnvironment = getenv('AWS_EXECUTION_ENV')) {
$legacyUserAgent []= $executionEnvironment;
$userAgent []= $executionEnvironment;
}
//Add the input to the end
@ -830,32 +827,28 @@ class ClientResolver
$inputUserAgent = [$inputUserAgent];
}
$inputUserAgent = array_map('strval', $inputUserAgent);
$legacyUserAgent = array_merge($legacyUserAgent, $inputUserAgent);
$xAmzUserAgent = array_merge($xAmzUserAgent, $inputUserAgent);
$userAgent = array_merge($userAgent, $inputUserAgent);
}
$args['ua_append'] = $legacyUserAgent;
$args['ua_append'] = $userAgent;
$list->appendBuild(static function (callable $handler) use (
$xAmzUserAgent,
$legacyUserAgent
) {
$list->appendBuild(static function (callable $handler) use ($userAgent) {
return function (
CommandInterface $command,
RequestInterface $request
) use ($handler, $legacyUserAgent, $xAmzUserAgent) {
) use ($handler, $userAgent) {
return $handler(
$command,
$request->withHeader(
'X-Amz-User-Agent',
implode(' ', array_merge(
$xAmzUserAgent,
$userAgent,
$request->getHeader('X-Amz-User-Agent')
))
)->withHeader(
'User-Agent',
implode(' ', array_merge(
$legacyUserAgent,
$userAgent,
$request->getHeader('User-Agent')
))
)

View file

@ -34,11 +34,15 @@ class Signer
if (!$this->pkHandle = openssl_pkey_get_private($privateKey, $passphrase)) {
if (!file_exists($privateKey)) {
throw new \InvalidArgumentException("PK file not found: $privateKey");
} else {
}
$this->pkHandle = openssl_pkey_get_private("file://$privateKey", $passphrase);
if (!$this->pkHandle) {
throw new \InvalidArgumentException(openssl_error_string());
$errorMessages = [];
while(($newMessage = openssl_error_string()) !== false){
$errorMessages[] = $newMessage;
}
throw new \InvalidArgumentException(implode("\n",$errorMessages));
}
}
}

View file

@ -18,6 +18,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteEventDataStoreAsync(array $args = [])
* @method \Aws\Result deleteTrail(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteTrailAsync(array $args = [])
* @method \Aws\Result deregisterOrganizationDelegatedAdmin(array $args = [])
* @method \GuzzleHttp\Promise\Promise deregisterOrganizationDelegatedAdminAsync(array $args = [])
* @method \Aws\Result describeQuery(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeQueryAsync(array $args = [])
* @method \Aws\Result describeTrails(array $args = [])
@ -28,6 +30,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getEventDataStoreAsync(array $args = [])
* @method \Aws\Result getEventSelectors(array $args = [])
* @method \GuzzleHttp\Promise\Promise getEventSelectorsAsync(array $args = [])
* @method \Aws\Result getImport(array $args = [])
* @method \GuzzleHttp\Promise\Promise getImportAsync(array $args = [])
* @method \Aws\Result getInsightSelectors(array $args = [])
* @method \GuzzleHttp\Promise\Promise getInsightSelectorsAsync(array $args = [])
* @method \Aws\Result getQueryResults(array $args = [])
@ -40,6 +44,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listChannelsAsync(array $args = [])
* @method \Aws\Result listEventDataStores(array $args = [])
* @method \GuzzleHttp\Promise\Promise listEventDataStoresAsync(array $args = [])
* @method \Aws\Result listImportFailures(array $args = [])
* @method \GuzzleHttp\Promise\Promise listImportFailuresAsync(array $args = [])
* @method \Aws\Result listImports(array $args = [])
* @method \GuzzleHttp\Promise\Promise listImportsAsync(array $args = [])
* @method \Aws\Result listPublicKeys(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPublicKeysAsync(array $args = [])
* @method \Aws\Result listQueries(array $args = [])
@ -54,14 +62,20 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise putEventSelectorsAsync(array $args = [])
* @method \Aws\Result putInsightSelectors(array $args = [])
* @method \GuzzleHttp\Promise\Promise putInsightSelectorsAsync(array $args = [])
* @method \Aws\Result registerOrganizationDelegatedAdmin(array $args = [])
* @method \GuzzleHttp\Promise\Promise registerOrganizationDelegatedAdminAsync(array $args = [])
* @method \Aws\Result removeTags(array $args = [])
* @method \GuzzleHttp\Promise\Promise removeTagsAsync(array $args = [])
* @method \Aws\Result restoreEventDataStore(array $args = [])
* @method \GuzzleHttp\Promise\Promise restoreEventDataStoreAsync(array $args = [])
* @method \Aws\Result startImport(array $args = [])
* @method \GuzzleHttp\Promise\Promise startImportAsync(array $args = [])
* @method \Aws\Result startLogging(array $args = [])
* @method \GuzzleHttp\Promise\Promise startLoggingAsync(array $args = [])
* @method \Aws\Result startQuery(array $args = [])
* @method \GuzzleHttp\Promise\Promise startQueryAsync(array $args = [])
* @method \Aws\Result stopImport(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopImportAsync(array $args = [])
* @method \Aws\Result stopLogging(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopLoggingAsync(array $args = [])
* @method \Aws\Result updateEventDataStore(array $args = [])

View file

@ -62,6 +62,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getLogRecordAsync(array $args = [])
* @method \Aws\Result getQueryResults(array $args = [])
* @method \GuzzleHttp\Promise\Promise getQueryResultsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result listTagsLogGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsLogGroupAsync(array $args = [])
* @method \Aws\Result putDestination(array $args = [])
@ -86,9 +88,13 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise stopQueryAsync(array $args = [])
* @method \Aws\Result tagLogGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagLogGroupAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result testMetricFilter(array $args = [])
* @method \GuzzleHttp\Promise\Promise testMetricFilterAsync(array $args = [])
* @method \Aws\Result untagLogGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagLogGroupAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
*/
class CloudWatchLogsClient extends AwsClient {}

View file

@ -5,25 +5,39 @@ use Aws\AwsClient;
/**
* This client is used to interact with the **CloudWatch RUM** service.
* @method \Aws\Result batchCreateRumMetricDefinitions(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchCreateRumMetricDefinitionsAsync(array $args = [])
* @method \Aws\Result batchDeleteRumMetricDefinitions(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchDeleteRumMetricDefinitionsAsync(array $args = [])
* @method \Aws\Result batchGetRumMetricDefinitions(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetRumMetricDefinitionsAsync(array $args = [])
* @method \Aws\Result createAppMonitor(array $args = [])
* @method \GuzzleHttp\Promise\Promise createAppMonitorAsync(array $args = [])
* @method \Aws\Result deleteAppMonitor(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAppMonitorAsync(array $args = [])
* @method \Aws\Result deleteRumMetricsDestination(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteRumMetricsDestinationAsync(array $args = [])
* @method \Aws\Result getAppMonitor(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAppMonitorAsync(array $args = [])
* @method \Aws\Result getAppMonitorData(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAppMonitorDataAsync(array $args = [])
* @method \Aws\Result listAppMonitors(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAppMonitorsAsync(array $args = [])
* @method \Aws\Result listRumMetricsDestinations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listRumMetricsDestinationsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result putRumEvents(array $args = [])
* @method \GuzzleHttp\Promise\Promise putRumEventsAsync(array $args = [])
* @method \Aws\Result putRumMetricsDestination(array $args = [])
* @method \GuzzleHttp\Promise\Promise putRumMetricsDestinationAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateAppMonitor(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateAppMonitorAsync(array $args = [])
* @method \Aws\Result updateRumMetricDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateRumMetricDefinitionAsync(array $args = [])
*/
class CloudWatchRUMClient extends AwsClient {}

View file

@ -15,6 +15,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise batchDetectSentimentAsync(array $args = [])
* @method \Aws\Result batchDetectSyntax(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchDetectSyntaxAsync(array $args = [])
* @method \Aws\Result batchDetectTargetedSentiment(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchDetectTargetedSentimentAsync(array $args = [])
* @method \Aws\Result classifyDocument(array $args = [])
* @method \GuzzleHttp\Promise\Promise classifyDocumentAsync(array $args = [])
* @method \Aws\Result containsPiiEntities(array $args = [])
@ -71,6 +73,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise detectSentimentAsync(array $args = [])
* @method \Aws\Result detectSyntax(array $args = [])
* @method \GuzzleHttp\Promise\Promise detectSyntaxAsync(array $args = [])
* @method \Aws\Result detectTargetedSentiment(array $args = [])
* @method \GuzzleHttp\Promise\Promise detectTargetedSentimentAsync(array $args = [])
* @method \Aws\Result importModel(array $args = [])
* @method \GuzzleHttp\Promise\Promise importModelAsync(array $args = [])
* @method \Aws\Result listDocumentClassificationJobs(array $args = [])

View file

@ -49,6 +49,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createSecurityProfileAsync(array $args = [])
* @method \Aws\Result createTaskTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise createTaskTemplateAsync(array $args = [])
* @method \Aws\Result createTrafficDistributionGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise createTrafficDistributionGroupAsync(array $args = [])
* @method \Aws\Result createUseCase(array $args = [])
* @method \GuzzleHttp\Promise\Promise createUseCaseAsync(array $args = [])
* @method \Aws\Result createUser(array $args = [])
@ -73,6 +75,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteSecurityProfileAsync(array $args = [])
* @method \Aws\Result deleteTaskTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteTaskTemplateAsync(array $args = [])
* @method \Aws\Result deleteTrafficDistributionGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteTrafficDistributionGroupAsync(array $args = [])
* @method \Aws\Result deleteUseCase(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteUseCaseAsync(array $args = [])
* @method \Aws\Result deleteUser(array $args = [])
@ -107,6 +111,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise describeRoutingProfileAsync(array $args = [])
* @method \Aws\Result describeSecurityProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeSecurityProfileAsync(array $args = [])
* @method \Aws\Result describeTrafficDistributionGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeTrafficDistributionGroupAsync(array $args = [])
* @method \Aws\Result describeUser(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeUserAsync(array $args = [])
* @method \Aws\Result describeUserHierarchyGroup(array $args = [])
@ -133,6 +139,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise disassociateRoutingProfileQueuesAsync(array $args = [])
* @method \Aws\Result disassociateSecurityKey(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociateSecurityKeyAsync(array $args = [])
* @method \Aws\Result dismissUserContact(array $args = [])
* @method \GuzzleHttp\Promise\Promise dismissUserContactAsync(array $args = [])
* @method \Aws\Result getContactAttributes(array $args = [])
* @method \GuzzleHttp\Promise\Promise getContactAttributesAsync(array $args = [])
* @method \Aws\Result getCurrentMetricData(array $args = [])
@ -145,6 +153,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getMetricDataAsync(array $args = [])
* @method \Aws\Result getTaskTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise getTaskTemplateAsync(array $args = [])
* @method \Aws\Result getTrafficDistribution(array $args = [])
* @method \GuzzleHttp\Promise\Promise getTrafficDistributionAsync(array $args = [])
* @method \Aws\Result listAgentStatuses(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAgentStatusesAsync(array $args = [])
* @method \Aws\Result listApprovedOrigins(array $args = [])
@ -199,6 +209,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result listTaskTemplates(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTaskTemplatesAsync(array $args = [])
* @method \Aws\Result listTrafficDistributionGroups(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTrafficDistributionGroupsAsync(array $args = [])
* @method \Aws\Result listUseCases(array $args = [])
* @method \GuzzleHttp\Promise\Promise listUseCasesAsync(array $args = [])
* @method \Aws\Result listUserHierarchyGroups(array $args = [])
@ -209,6 +221,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise putUserStatusAsync(array $args = [])
* @method \Aws\Result releasePhoneNumber(array $args = [])
* @method \GuzzleHttp\Promise\Promise releasePhoneNumberAsync(array $args = [])
* @method \Aws\Result replicateInstance(array $args = [])
* @method \GuzzleHttp\Promise\Promise replicateInstanceAsync(array $args = [])
* @method \Aws\Result resumeContactRecording(array $args = [])
* @method \GuzzleHttp\Promise\Promise resumeContactRecordingAsync(array $args = [])
* @method \Aws\Result searchAvailablePhoneNumbers(array $args = [])
@ -299,6 +313,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateSecurityProfileAsync(array $args = [])
* @method \Aws\Result updateTaskTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateTaskTemplateAsync(array $args = [])
* @method \Aws\Result updateTrafficDistribution(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateTrafficDistributionAsync(array $args = [])
* @method \Aws\Result updateUserHierarchy(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateUserHierarchyAsync(array $args = [])
* @method \Aws\Result updateUserHierarchyGroupName(array $args = [])

View file

@ -0,0 +1,67 @@
<?php
namespace Aws\ConnectCases;
use Aws\AwsClient;
/**
* This client is used to interact with the **Amazon Connect Cases** service.
* @method \Aws\Result batchGetField(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetFieldAsync(array $args = [])
* @method \Aws\Result batchPutFieldOptions(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchPutFieldOptionsAsync(array $args = [])
* @method \Aws\Result createCase(array $args = [])
* @method \GuzzleHttp\Promise\Promise createCaseAsync(array $args = [])
* @method \Aws\Result createDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDomainAsync(array $args = [])
* @method \Aws\Result createField(array $args = [])
* @method \GuzzleHttp\Promise\Promise createFieldAsync(array $args = [])
* @method \Aws\Result createLayout(array $args = [])
* @method \GuzzleHttp\Promise\Promise createLayoutAsync(array $args = [])
* @method \Aws\Result createRelatedItem(array $args = [])
* @method \GuzzleHttp\Promise\Promise createRelatedItemAsync(array $args = [])
* @method \Aws\Result createTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise createTemplateAsync(array $args = [])
* @method \Aws\Result getCase(array $args = [])
* @method \GuzzleHttp\Promise\Promise getCaseAsync(array $args = [])
* @method \Aws\Result getCaseEventConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise getCaseEventConfigurationAsync(array $args = [])
* @method \Aws\Result getDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDomainAsync(array $args = [])
* @method \Aws\Result getLayout(array $args = [])
* @method \GuzzleHttp\Promise\Promise getLayoutAsync(array $args = [])
* @method \Aws\Result getTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise getTemplateAsync(array $args = [])
* @method \Aws\Result listCasesForContact(array $args = [])
* @method \GuzzleHttp\Promise\Promise listCasesForContactAsync(array $args = [])
* @method \Aws\Result listDomains(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDomainsAsync(array $args = [])
* @method \Aws\Result listFieldOptions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listFieldOptionsAsync(array $args = [])
* @method \Aws\Result listFields(array $args = [])
* @method \GuzzleHttp\Promise\Promise listFieldsAsync(array $args = [])
* @method \Aws\Result listLayouts(array $args = [])
* @method \GuzzleHttp\Promise\Promise listLayoutsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result listTemplates(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTemplatesAsync(array $args = [])
* @method \Aws\Result putCaseEventConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise putCaseEventConfigurationAsync(array $args = [])
* @method \Aws\Result searchCases(array $args = [])
* @method \GuzzleHttp\Promise\Promise searchCasesAsync(array $args = [])
* @method \Aws\Result searchRelatedItems(array $args = [])
* @method \GuzzleHttp\Promise\Promise searchRelatedItemsAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateCase(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateCaseAsync(array $args = [])
* @method \Aws\Result updateField(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateFieldAsync(array $args = [])
* @method \Aws\Result updateLayout(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateLayoutAsync(array $args = [])
* @method \Aws\Result updateTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateTemplateAsync(array $args = [])
*/
class ConnectCasesClient extends AwsClient {}

View file

@ -0,0 +1,9 @@
<?php
namespace Aws\ConnectCases\Exception;
use Aws\Exception\AwsException;
/**
* Represents an error interacting with the **Amazon Connect Cases** service.
*/
class ConnectCasesException extends AwsException {}

View file

@ -49,8 +49,6 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result notifyRecommendationsReceived(array $args = [])
* @method \GuzzleHttp\Promise\Promise notifyRecommendationsReceivedAsync(array $args = [])
* @method \Aws\Result putFeedback(array $args = [])
* @method \GuzzleHttp\Promise\Promise putFeedbackAsync(array $args = [])
* @method \Aws\Result queryAssistant(array $args = [])
* @method \GuzzleHttp\Promise\Promise queryAssistantAsync(array $args = [])
* @method \Aws\Result removeKnowledgeBaseTemplateUri(array $args = [])

View file

@ -72,6 +72,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise describeSnapshotsAsync(array $args = [])
* @method \Aws\Result describeTrusts(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeTrustsAsync(array $args = [])
* @method \Aws\Result describeUpdateDirectory(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeUpdateDirectoryAsync(array $args = [])
* @method \Aws\Result disableClientAuthentication(array $args = [])
* @method \GuzzleHttp\Promise\Promise disableClientAuthenticationAsync(array $args = [])
* @method \Aws\Result disableLDAPS(array $args = [])
@ -126,6 +128,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise unshareDirectoryAsync(array $args = [])
* @method \Aws\Result updateConditionalForwarder(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateConditionalForwarderAsync(array $args = [])
* @method \Aws\Result updateDirectorySetup(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateDirectorySetupAsync(array $args = [])
* @method \Aws\Result updateNumberOfDomainControllers(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateNumberOfDomainControllersAsync(array $args = [])
* @method \Aws\Result updateRadius(array $args = [])

View file

@ -7,22 +7,30 @@ use Aws\AwsClient;
* This client is used to interact with the **Amazon EMR Containers** service.
* @method \Aws\Result cancelJobRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise cancelJobRunAsync(array $args = [])
* @method \Aws\Result createJobTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise createJobTemplateAsync(array $args = [])
* @method \Aws\Result createManagedEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise createManagedEndpointAsync(array $args = [])
* @method \Aws\Result createVirtualCluster(array $args = [])
* @method \GuzzleHttp\Promise\Promise createVirtualClusterAsync(array $args = [])
* @method \Aws\Result deleteJobTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteJobTemplateAsync(array $args = [])
* @method \Aws\Result deleteManagedEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteManagedEndpointAsync(array $args = [])
* @method \Aws\Result deleteVirtualCluster(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteVirtualClusterAsync(array $args = [])
* @method \Aws\Result describeJobRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeJobRunAsync(array $args = [])
* @method \Aws\Result describeJobTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeJobTemplateAsync(array $args = [])
* @method \Aws\Result describeManagedEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeManagedEndpointAsync(array $args = [])
* @method \Aws\Result describeVirtualCluster(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeVirtualClusterAsync(array $args = [])
* @method \Aws\Result listJobRuns(array $args = [])
* @method \GuzzleHttp\Promise\Promise listJobRunsAsync(array $args = [])
* @method \Aws\Result listJobTemplates(array $args = [])
* @method \GuzzleHttp\Promise\Promise listJobTemplatesAsync(array $args = [])
* @method \Aws\Result listManagedEndpoints(array $args = [])
* @method \GuzzleHttp\Promise\Promise listManagedEndpointsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])

View file

@ -13,6 +13,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteApplicationAsync(array $args = [])
* @method \Aws\Result getApplication(array $args = [])
* @method \GuzzleHttp\Promise\Promise getApplicationAsync(array $args = [])
* @method \Aws\Result getDashboardForJobRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDashboardForJobRunAsync(array $args = [])
* @method \Aws\Result getJobRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise getJobRunAsync(array $args = [])
* @method \Aws\Result listApplications(array $args = [])

View file

@ -434,6 +434,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise acceptReservedInstancesExchangeQuoteAsync(array $args = []) (supported in versions 2016-09-15, 2016-11-15)
* @method \Aws\Result getReservedInstancesExchangeQuote(array $args = []) (supported in versions 2016-09-15, 2016-11-15)
* @method \GuzzleHttp\Promise\Promise getReservedInstancesExchangeQuoteAsync(array $args = []) (supported in versions 2016-09-15, 2016-11-15)
* @method \Aws\Result acceptAddressTransfer(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise acceptAddressTransferAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result acceptTransitGatewayMulticastDomainAssociations(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise acceptTransitGatewayMulticastDomainAssociationsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result acceptTransitGatewayPeeringAttachment(array $args = []) (supported in versions 2016-11-15)
@ -476,6 +478,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise cancelCapacityReservationAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result cancelCapacityReservationFleets(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise cancelCapacityReservationFleetsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result cancelImageLaunchPermission(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise cancelImageLaunchPermissionAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result copyFpgaImage(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise copyFpgaImageAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createCapacityReservation(array $args = []) (supported in versions 2016-11-15)
@ -488,6 +492,10 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise createClientVpnEndpointAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createClientVpnRoute(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createClientVpnRouteAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createCoipCidr(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createCoipCidrAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createCoipPool(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createCoipPoolAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createDefaultSubnet(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createDefaultSubnetAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createDefaultVpc(array $args = []) (supported in versions 2016-11-15)
@ -512,6 +520,10 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise createLaunchTemplateVersionAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createLocalGatewayRoute(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createLocalGatewayRouteAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createLocalGatewayRouteTable(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createLocalGatewayRouteTableAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createLocalGatewayRouteTableVirtualInterfaceGroupAssociation(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createLocalGatewayRouteTableVirtualInterfaceGroupAssociationAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createLocalGatewayRouteTableVpcAssociation(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createLocalGatewayRouteTableVpcAssociationAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createManagedPrefixList(array $args = []) (supported in versions 2016-11-15)
@ -574,6 +586,10 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise deleteClientVpnEndpointAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteClientVpnRoute(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteClientVpnRouteAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteCoipCidr(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteCoipCidrAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteCoipPool(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteCoipPoolAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteEgressOnlyInternetGateway(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteEgressOnlyInternetGatewayAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteFleets(array $args = []) (supported in versions 2016-11-15)
@ -594,6 +610,10 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise deleteLaunchTemplateVersionsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteLocalGatewayRoute(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteLocalGatewayRouteAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteLocalGatewayRouteTable(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteLocalGatewayRouteTableAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteLocalGatewayRouteTableVirtualInterfaceGroupAssociation(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteLocalGatewayRouteTableVirtualInterfaceGroupAssociationAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteLocalGatewayRouteTableVpcAssociation(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteLocalGatewayRouteTableVpcAssociationAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteManagedPrefixList(array $args = []) (supported in versions 2016-11-15)
@ -660,6 +680,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise deregisterTransitGatewayMulticastGroupMembersAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deregisterTransitGatewayMulticastGroupSources(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deregisterTransitGatewayMulticastGroupSourcesAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeAddressTransfers(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeAddressTransfersAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeAddressesAttribute(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeAddressesAttributeAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeAggregateIdFormat(array $args = []) (supported in versions 2016-11-15)
@ -802,6 +824,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise describeVpcEndpointServiceConfigurationsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeVpcEndpointServicePermissions(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeVpcEndpointServicePermissionsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result disableAddressTransfer(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise disableAddressTransferAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result disableEbsEncryptionByDefault(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise disableEbsEncryptionByDefaultAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result disableFastLaunch(array $args = []) (supported in versions 2016-11-15)
@ -836,6 +860,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise disassociateTrunkInterfaceAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result disassociateVpcCidrBlock(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise disassociateVpcCidrBlockAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result enableAddressTransfer(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise enableAddressTransferAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result enableEbsEncryptionByDefault(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise enableEbsEncryptionByDefaultAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result enableFastLaunch(array $args = []) (supported in versions 2016-11-15)
@ -968,6 +994,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise modifyIpamScopeAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyLaunchTemplate(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise modifyLaunchTemplateAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyLocalGatewayRoute(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise modifyLocalGatewayRouteAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyManagedPrefixList(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise modifyManagedPrefixListAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyPrivateDnsNameOptions(array $args = []) (supported in versions 2016-11-15)

View file

@ -48,6 +48,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise discoverPollEndpointAsync(array $args = [])
* @method \Aws\Result executeCommand(array $args = [])
* @method \GuzzleHttp\Promise\Promise executeCommandAsync(array $args = [])
* @method \Aws\Result getTaskProtection(array $args = [])
* @method \GuzzleHttp\Promise\Promise getTaskProtectionAsync(array $args = [])
* @method \Aws\Result listAccountSettings(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAccountSettingsAsync(array $args = [])
* @method \Aws\Result listAttributes(array $args = [])
@ -108,6 +110,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateServiceAsync(array $args = [])
* @method \Aws\Result updateServicePrimaryTaskSet(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateServicePrimaryTaskSetAsync(array $args = [])
* @method \Aws\Result updateTaskProtection(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateTaskProtectionAsync(array $args = [])
* @method \Aws\Result updateTaskSet(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateTaskSetAsync(array $args = [])
*/

View file

@ -12,6 +12,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise addTagsAsync(array $args = [])
* @method \Aws\Result associatePackage(array $args = [])
* @method \GuzzleHttp\Promise\Promise associatePackageAsync(array $args = [])
* @method \Aws\Result authorizeVpcEndpointAccess(array $args = [])
* @method \GuzzleHttp\Promise\Promise authorizeVpcEndpointAccessAsync(array $args = [])
* @method \Aws\Result cancelElasticsearchServiceSoftwareUpdate(array $args = [])
* @method \GuzzleHttp\Promise\Promise cancelElasticsearchServiceSoftwareUpdateAsync(array $args = [])
* @method \Aws\Result createElasticsearchDomain(array $args = [])
@ -20,6 +22,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createOutboundCrossClusterSearchConnectionAsync(array $args = [])
* @method \Aws\Result createPackage(array $args = [])
* @method \GuzzleHttp\Promise\Promise createPackageAsync(array $args = [])
* @method \Aws\Result createVpcEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise createVpcEndpointAsync(array $args = [])
* @method \Aws\Result deleteElasticsearchDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteElasticsearchDomainAsync(array $args = [])
* @method \Aws\Result deleteElasticsearchServiceRole(array $args = [])
@ -30,6 +34,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteOutboundCrossClusterSearchConnectionAsync(array $args = [])
* @method \Aws\Result deletePackage(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePackageAsync(array $args = [])
* @method \Aws\Result deleteVpcEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteVpcEndpointAsync(array $args = [])
* @method \Aws\Result describeDomainAutoTunes(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeDomainAutoTunesAsync(array $args = [])
* @method \Aws\Result describeDomainChangeProgress(array $args = [])
@ -52,6 +58,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise describeReservedElasticsearchInstanceOfferingsAsync(array $args = [])
* @method \Aws\Result describeReservedElasticsearchInstances(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeReservedElasticsearchInstancesAsync(array $args = [])
* @method \Aws\Result describeVpcEndpoints(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeVpcEndpointsAsync(array $args = [])
* @method \Aws\Result dissociatePackage(array $args = [])
* @method \GuzzleHttp\Promise\Promise dissociatePackageAsync(array $args = [])
* @method \Aws\Result getCompatibleElasticsearchVersions(array $args = [])
@ -74,18 +82,28 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listPackagesForDomainAsync(array $args = [])
* @method \Aws\Result listTags(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsAsync(array $args = [])
* @method \Aws\Result listVpcEndpointAccess(array $args = [])
* @method \GuzzleHttp\Promise\Promise listVpcEndpointAccessAsync(array $args = [])
* @method \Aws\Result listVpcEndpoints(array $args = [])
* @method \GuzzleHttp\Promise\Promise listVpcEndpointsAsync(array $args = [])
* @method \Aws\Result listVpcEndpointsForDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise listVpcEndpointsForDomainAsync(array $args = [])
* @method \Aws\Result purchaseReservedElasticsearchInstanceOffering(array $args = [])
* @method \GuzzleHttp\Promise\Promise purchaseReservedElasticsearchInstanceOfferingAsync(array $args = [])
* @method \Aws\Result rejectInboundCrossClusterSearchConnection(array $args = [])
* @method \GuzzleHttp\Promise\Promise rejectInboundCrossClusterSearchConnectionAsync(array $args = [])
* @method \Aws\Result removeTags(array $args = [])
* @method \GuzzleHttp\Promise\Promise removeTagsAsync(array $args = [])
* @method \Aws\Result revokeVpcEndpointAccess(array $args = [])
* @method \GuzzleHttp\Promise\Promise revokeVpcEndpointAccessAsync(array $args = [])
* @method \Aws\Result startElasticsearchServiceSoftwareUpdate(array $args = [])
* @method \GuzzleHttp\Promise\Promise startElasticsearchServiceSoftwareUpdateAsync(array $args = [])
* @method \Aws\Result updateElasticsearchDomainConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateElasticsearchDomainConfigAsync(array $args = [])
* @method \Aws\Result updatePackage(array $args = [])
* @method \GuzzleHttp\Promise\Promise updatePackageAsync(array $args = [])
* @method \Aws\Result updateVpcEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateVpcEndpointAsync(array $args = [])
* @method \Aws\Result upgradeElasticsearchDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise upgradeElasticsearchDomainAsync(array $args = [])
*/

View file

@ -9,6 +9,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise associateAdminAccountAsync(array $args = [])
* @method \Aws\Result associateThirdPartyFirewall(array $args = [])
* @method \GuzzleHttp\Promise\Promise associateThirdPartyFirewallAsync(array $args = [])
* @method \Aws\Result batchAssociateResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchAssociateResourceAsync(array $args = [])
* @method \Aws\Result batchDisassociateResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchDisassociateResourceAsync(array $args = [])
* @method \Aws\Result deleteAppsList(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAppsListAsync(array $args = [])
* @method \Aws\Result deleteNotificationChannel(array $args = [])
@ -17,6 +21,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deletePolicyAsync(array $args = [])
* @method \Aws\Result deleteProtocolsList(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteProtocolsListAsync(array $args = [])
* @method \Aws\Result deleteResourceSet(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteResourceSetAsync(array $args = [])
* @method \Aws\Result disassociateAdminAccount(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociateAdminAccountAsync(array $args = [])
* @method \Aws\Result disassociateThirdPartyFirewall(array $args = [])
@ -35,6 +41,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getProtectionStatusAsync(array $args = [])
* @method \Aws\Result getProtocolsList(array $args = [])
* @method \GuzzleHttp\Promise\Promise getProtocolsListAsync(array $args = [])
* @method \Aws\Result getResourceSet(array $args = [])
* @method \GuzzleHttp\Promise\Promise getResourceSetAsync(array $args = [])
* @method \Aws\Result getThirdPartyFirewallAssociationStatus(array $args = [])
* @method \GuzzleHttp\Promise\Promise getThirdPartyFirewallAssociationStatusAsync(array $args = [])
* @method \Aws\Result getViolationDetails(array $args = [])
@ -43,12 +51,18 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listAppsListsAsync(array $args = [])
* @method \Aws\Result listComplianceStatus(array $args = [])
* @method \GuzzleHttp\Promise\Promise listComplianceStatusAsync(array $args = [])
* @method \Aws\Result listDiscoveredResources(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDiscoveredResourcesAsync(array $args = [])
* @method \Aws\Result listMemberAccounts(array $args = [])
* @method \GuzzleHttp\Promise\Promise listMemberAccountsAsync(array $args = [])
* @method \Aws\Result listPolicies(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPoliciesAsync(array $args = [])
* @method \Aws\Result listProtocolsLists(array $args = [])
* @method \GuzzleHttp\Promise\Promise listProtocolsListsAsync(array $args = [])
* @method \Aws\Result listResourceSetResources(array $args = [])
* @method \GuzzleHttp\Promise\Promise listResourceSetResourcesAsync(array $args = [])
* @method \Aws\Result listResourceSets(array $args = [])
* @method \GuzzleHttp\Promise\Promise listResourceSetsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result listThirdPartyFirewallFirewallPolicies(array $args = [])
@ -61,6 +75,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise putPolicyAsync(array $args = [])
* @method \Aws\Result putProtocolsList(array $args = [])
* @method \GuzzleHttp\Promise\Promise putProtocolsListAsync(array $args = [])
* @method \Aws\Result putResourceSet(array $args = [])
* @method \GuzzleHttp\Promise\Promise putResourceSetAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])

View file

@ -17,6 +17,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createDataRepositoryAssociationAsync(array $args = [])
* @method \Aws\Result createDataRepositoryTask(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDataRepositoryTaskAsync(array $args = [])
* @method \Aws\Result createFileCache(array $args = [])
* @method \GuzzleHttp\Promise\Promise createFileCacheAsync(array $args = [])
* @method \Aws\Result createFileSystem(array $args = [])
* @method \GuzzleHttp\Promise\Promise createFileSystemAsync(array $args = [])
* @method \Aws\Result createFileSystemFromBackup(array $args = [])
@ -33,6 +35,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteBackupAsync(array $args = [])
* @method \Aws\Result deleteDataRepositoryAssociation(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDataRepositoryAssociationAsync(array $args = [])
* @method \Aws\Result deleteFileCache(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteFileCacheAsync(array $args = [])
* @method \Aws\Result deleteFileSystem(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteFileSystemAsync(array $args = [])
* @method \Aws\Result deleteSnapshot(array $args = [])
@ -47,6 +51,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise describeDataRepositoryAssociationsAsync(array $args = [])
* @method \Aws\Result describeDataRepositoryTasks(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeDataRepositoryTasksAsync(array $args = [])
* @method \Aws\Result describeFileCaches(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeFileCachesAsync(array $args = [])
* @method \Aws\Result describeFileSystemAliases(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeFileSystemAliasesAsync(array $args = [])
* @method \Aws\Result describeFileSystems(array $args = [])
@ -71,6 +77,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateDataRepositoryAssociation(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateDataRepositoryAssociationAsync(array $args = [])
* @method \Aws\Result updateFileCache(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateFileCacheAsync(array $args = [])
* @method \Aws\Result updateFileSystem(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateFileSystemAsync(array $args = [])
* @method \Aws\Result updateSnapshot(array $args = [])

View file

@ -7,6 +7,8 @@ use Aws\AwsClient;
* This client is used to interact with the **AWS Global Accelerator** service.
* @method \Aws\Result addCustomRoutingEndpoints(array $args = [])
* @method \GuzzleHttp\Promise\Promise addCustomRoutingEndpointsAsync(array $args = [])
* @method \Aws\Result addEndpoints(array $args = [])
* @method \GuzzleHttp\Promise\Promise addEndpointsAsync(array $args = [])
* @method \Aws\Result advertiseByoipCidr(array $args = [])
* @method \GuzzleHttp\Promise\Promise advertiseByoipCidrAsync(array $args = [])
* @method \Aws\Result allowCustomRoutingTraffic(array $args = [])
@ -79,6 +81,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise provisionByoipCidrAsync(array $args = [])
* @method \Aws\Result removeCustomRoutingEndpoints(array $args = [])
* @method \GuzzleHttp\Promise\Promise removeCustomRoutingEndpointsAsync(array $args = [])
* @method \Aws\Result removeEndpoints(array $args = [])
* @method \GuzzleHttp\Promise\Promise removeEndpointsAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])

View file

@ -357,6 +357,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateDevEndpointAsync(array $args = [])
* @method \Aws\Result updateJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateJobAsync(array $args = [])
* @method \Aws\Result updateJobFromSourceControl(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateJobFromSourceControlAsync(array $args = [])
* @method \Aws\Result updateMLTransform(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateMLTransformAsync(array $args = [])
* @method \Aws\Result updatePartition(array $args = [])
@ -365,6 +367,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateRegistryAsync(array $args = [])
* @method \Aws\Result updateSchema(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateSchemaAsync(array $args = [])
* @method \Aws\Result updateSourceControlFromJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateSourceControlFromJobAsync(array $args = [])
* @method \Aws\Result updateTable(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateTableAsync(array $args = [])
* @method \Aws\Result updateTrigger(array $args = [])

View file

@ -11,16 +11,22 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createConfigAsync(array $args = [])
* @method \Aws\Result createDataflowEndpointGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDataflowEndpointGroupAsync(array $args = [])
* @method \Aws\Result createEphemeris(array $args = [])
* @method \GuzzleHttp\Promise\Promise createEphemerisAsync(array $args = [])
* @method \Aws\Result createMissionProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise createMissionProfileAsync(array $args = [])
* @method \Aws\Result deleteConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteConfigAsync(array $args = [])
* @method \Aws\Result deleteDataflowEndpointGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDataflowEndpointGroupAsync(array $args = [])
* @method \Aws\Result deleteEphemeris(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteEphemerisAsync(array $args = [])
* @method \Aws\Result deleteMissionProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteMissionProfileAsync(array $args = [])
* @method \Aws\Result describeContact(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeContactAsync(array $args = [])
* @method \Aws\Result describeEphemeris(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeEphemerisAsync(array $args = [])
* @method \Aws\Result getMissionProfileConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise getMissionProfileConfigAsync(array $args = [])
* @method \Aws\Result getDataflowEndpointGroup(array $args = [])
@ -37,6 +43,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listContactsAsync(array $args = [])
* @method \Aws\Result listDataflowEndpointGroups(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDataflowEndpointGroupsAsync(array $args = [])
* @method \Aws\Result listEphemerides(array $args = [])
* @method \GuzzleHttp\Promise\Promise listEphemeridesAsync(array $args = [])
* @method \Aws\Result listGroundStations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listGroundStationsAsync(array $args = [])
* @method \Aws\Result listMissionProfiles(array $args = [])
@ -53,6 +61,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateConfigAsync(array $args = [])
* @method \Aws\Result updateEphemeris(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateEphemerisAsync(array $args = [])
* @method \Aws\Result updateMissionProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateMissionProfileAsync(array $args = [])
*/

Some files were not shown because too many files have changed in this diff Show more