php/googleClientLib/googledrive.php
<?php
/* Google Drive Interface
docs + links
api: https://developers.google.com/drive/api/guides/about-sdk
file fields: https://developers.google.com/drive/api/reference/rest/v3/files
guide https://developers.google.com/drive/api/guides/manage-uploads examples in php http etc.
----------- installation
download latest release, select zip with appropriate release and expand
https://github.com/googleapis/google-api-php-client/releases
expand and remove in vendor/google/apiclient-services/src everything except Drive and Drive.php
https://github.com/googleapis/google-api-php-client-services/releases
*/
require_once 'env.php';
require_once 'google-api-small/vendor/autoload.php';
class GoogleDrive {
/*
docs: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/WebDAV/index.html
*/
public $cloudId, $user, $meta;
private $srv, $fiFi = 'name, id, mimeType, modifiedTime, owners(emailAddress), size, parents';
public function __construct($gc, $id, $ty, $cliN, $us) {
$this->gc = $gc;
$this->srv = new Google\Service\Drive($gc);
$this->cloudId = $id;
$this->user = $us;
$this->accessTk = $gc->getAccessToken();
$this->hAuth = "Authorization: Bearer " . $this->accessTk['access_token'];
dbg1("auth $this->hAuth, accessToken", $this->accessTk);
$r = $this->get('root');
if ($r[5] !== $this->user)
err("mismatch GoogleDrive(user=$this->user) but root user $r[5]:", $r);
}
function meta($fun, $dir) {
$now = new DateTime();
return $this->meta =[
['timestamp' , 'cloudId' , 'fun', 'dir', 'user' , 'timeZone']
, [toLocalTst($now), $this->cloudId, $fun , $dir , $this->user , $now->format('e P')]
, ['filename', 'id', 'type', 'path', 'lastmod', 'owner', 'size', 'parent', 'level']
];
}
public function getHTTP($gid, $gp=false) {
# "Authorization: Bearer " . $this->accessTk['access_token']ACCESS_TOKEN"
$ctx = stream_context_create(['http' => ['header' => $this->hAuth
#. "\nAccept: application/json" # . ' realm="https://www.googleapis.com/auth/documents.readonly"' # . $this->accessTk['scope'] . '"'
# , 'method' => 'PUT'
]]);
$cnt = @file_get_contents("https://www.googleapis.com/drive/v3/files/$gid?fields=" . rawurlencode($this->fiFi) , false, $ctx);
if(is_null($cnt) or $http_response_header[0] !== 'HTTP/1.1 200 OK') {
if ($http_response_header[0] === 'HTTP/1.1 404 Not Found')
return null;
err(__METHOD__ . "get($gid)", error_get_last(), ", response", $http_response_header);
};
return json_decode($cnt);
$g = $f = $this->srv->files->get($gid, ['fields' => $this->fiFi]);
if (! ($id = $g->getParents()[0] ?? false)) {
$p = './';
} elseif ($gp) {
$p = $g->getName() . substr('/', 0, 'application/vnd.google-apps.folder' === $f->getMimeType());
while(true) {
$g = $this->srv->files->get($id, ['fields' => 'name, parents']);
if ( ! ($id = $g->getParents()[0] ?? false))
break;
$p = $g->getName() . "/$p";
}
}
return [$f->getName(), $f->getiD(), $f->getMimeType(), $p ?? ''
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0] ?? null, 0];
}
public function get($gid, $gp=false) {
$g = $f = $this->srv->files->get($gid, ['fields' => $this->fiFi]);
if (! ($id = $g->getParents()[0] ?? false)) {
$p = './';
} elseif ($gp) {
$p = $g->getName() . substr('/', 0, 'application/vnd.google-apps.folder' === $f->getMimeType());
while(true) {
$g = $this->srv->files->get($id, ['fields' => 'name, parents']);
if ( ! ($id = $g->getParents()[0] ?? false))
break;
$p = $g->getName() . "/$p";
}
}
dbg1("drive client token", $this->gc->getAccessToken());
return [$f->getName(), $f->getiD(), $f->getMimeType(), $p ?? ''
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0] ?? null, 0];
}
public function ffSim($q, $hn=null) {
$opt = [ 'pageSize' => 100
, 'fields' => "nextPageToken, files($this->fiFi)"
, 'q' => 'not trashed'. ($q ? " and ($q)" : '')
, 'orderBy' => 'name'
];
$this->ffFi = $this->ffFo = $this->ffSz = 0;
if ($hn)
return ffOpt($opt, $hn);
$res = [];
$this->ffOpt($opt, function ($n) use(&$res) {$res[] = $n; });
return $res;
}
public function ffOpt($opt, $hn, $pPa = '') {
out("ffOpt", $opt);
do {
$results = $this->srv->files->listFiles($opt);
foreach ($files=$results->getFiles() as $f) {
$fo = 'application/vnd.google-apps.folder' === $ty = $f->getMimeType();
if ($fo) {
$this->ffFo++;
} else {
$this->ffFi++;
$this->ffSz += $f->getSize();
}
$p = "$pPa{$f->getName()}" . substr('/', 0, $fo);
$hn([$f->getName(), $f->getiD(), $f->getMimeType(), $p
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0]]);
}
} while ($opt['pageToken'] = $results->getNextPageToken());
}
public function ff($dirId, $hn, $hMeta=null, $whr=null) {
$dirId or $dirId='root';
$dirNd = $this->get($dirId, true);
$this->meta('ff', $dirNd[3]);
if ($hMeta)
$hMeta($this->meta);
$ix = 0;
$this->ffHn = $hn;
$this->ffOpt = [ 'pageSize' => 100
, 'fields' => "nextPageToken, files($this->fiFi)"
, 'q' => ' in parents and not trashed' . ($whr ? " and ($whr)" : '')
, 'orderBy' => 'name'
];
$this->ffFi = $this->ffFo = $this->ffSz = 0;
$dirNd[3] = '';
($this->ffHn)($dirNd);
$this->ff1(1, $dirId, '');
}
function ff1($lv, $pId, $pPa) {
$opt = $this->ffOpt;
$opt['q'] = "\"$pId\" $opt[q]";
do {
$results = $this->srv->files->listFiles($opt);
foreach ($files=$results->getFiles() as $f) {
$fo = 'application/vnd.google-apps.folder' === $ty = $f->getMimeType();
if ($fo) {
$this->ffFo++;
} else {
$this->ffFi++;
$this->ffSz += $f->getSize();
}
$p = "$pPa{$f->getName()}" . substr('/', 0, $fo);
($this->ffHn)([$f->getName(), $f->getiD(), $ty, $p
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0], $lv]);
if ($fo)
$this->ff1($lv+1, $f->getiD(), $p);
}
} while ($opt['pageToken'] = $results->getNextPageToken());
}
public function create($pa, $fn, $ty, $cnt) {
$fileMetadata = new Google\Service\Drive\DriveFile(['name' => $fn, 'parents' => [$pa]]);
$file = $this->srv->files->create($fileMetadata
, [ 'data' => $cnt . "\n" . toLocalTst('now') . "\n und so weiter"
, 'mimeType' => $ty
, 'uploadType' => 'multipart'
, 'fields' => 'id'
, 'keepRevisionForever' => true
]);
out("File ID: $file->id");
return $file->id;
}
public function uploadversion($gid, $cnt) {
$fileMetadata = new Google\Service\Drive\DriveFile();
$f = $this->srv->files->update($gid, $fileMetadata
, [ 'data' => $cnt . "\n" . toLocalTst('now') . "\n und so weiter immer spö"
# , 'mimeType' => $ty
, 'uploadType' => 'multipart'
, 'fields' => $this->fiFi
, 'keepRevisionForever' => true
]);
out('uploadVers file ID:', [$f->getName(), $f->getiD(), $f->getMimeType(), $p ?? ''
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0] ?? null]);
return $f->id;
}
public function upload($fn, $ty, $cnt) {
$ff = $this->ffSim("\"root\" in parents and name =\"$fn\"" );
if (count($ff) === 0)
$this->create('root', $fn, $ty, $cnt);
elseif (count($ff) === 1)
$this->uploadversion($ff[0][1], $cnt);
else
err("upload $fn has " . count($ff) . " nodes", $ff);
}
public function mv($fP, $tP) {
/* move file/directory $fr to new path/name $to
*/
$fr =implode('/', array_map('rawurlencode', is_array($fP) ? $fP : explode('/', $fP)));
$to =implode('/', array_map('rawurlencode', is_array($tP) ? $tP :explode('/', $tP)));
$ctx = stream_context_create(['http' => [
'header' => "$this->hAuth\nDestination: $this->fUri$to\nOverwrite: F"
, 'method' => 'MOVE'
]]);
$r = file_get_contents("$this->fUri$fr", 0, $ctx);
if ($http_response_header[0] !== 'HTTP/1.1 201 Created')
err("mv $fr to $to failed: http_response_header", $http_response_header);
}
public function putFi($fn, $cnt, $ty='text/csv') {
// upload to host $hst a file named $fn and contents $cnt, if the file already existst, create new version of the same id
$ctx = stream_context_create(['http' => [
'header' => "Content-Type: $ty\n$this->hAuth"
, 'method' => 'PUT'
, 'content' => $cnt
]]);
$r = file_get_contents("$this->fUri$fn", 0, $ctx);
echo "putFi sent " .strlen($cnt) ." chars to $this->cloudId $this->host $fn, response $http_response_header[0]\n";
if ($http_response_header[0] !== "HTTP/1.1 201 Created" and $http_response_header[0] !== "HTTP/1.1 204 No Content")
err("putFi $fn failed, response:>n ", $http_response_header);
}
public function byId($id, $dir='') {
$ctx = stream_context_create($c = ['http' => [
'header' => "Content-Type: text/xml\n$this->hAuth"
, 'method' => 'SEARCH'
, 'content' => <<<data
<?xml version="1.0" encoding="UTF-8"?>
<d:searchrequest xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:basicsearch>
<d:select>
<d:prop>
<oc:fileid/>
<d:displayname/>
<d:getcontenttype/>
<d:getetag/>
<oc:size/>
</d:prop>
</d:select>
<d:from>
<d:scope>
<d:href>/files/$this->rootDir/</d:href>
<d:depth>infinity</d:depth>
</d:scope>
</d:from>
<d:where>
<d:eq>
<d:prop>
<oc:fileid/>
</d:prop>
<d:literal>$id</d:literal>
</d:eq>
</d:where>
</d:basicsearch>
</d:searchrequest>
data ]]);
$r = file_get_contents("$this->sUri", 0, $ctx);
echo "search $this->sUri response $http_response_header[0]\n";
var_dump($http_response_header);
return $r;
}
public function rename($r, $f2v, $hi) {
/* check all resources in xmmResult $r, if they need to be renamed
$f2v: callback: rename last level of path
$hi($nx, $n, $fP, $fM, $tP): callback if last level should be rename:
$nx=count, $n array for resource, $fP from path old, $fM from path for mv, $tp to path for mv
*/
$nx = -1;
$fP = $tP = [];
$hR = function ($n) use (&$nx, &$fP, &$tP, $f2v, $hi) {
$nx++;
$aS = $n[0];
if (($fo = $n[2] === 'd:collection') !== str_ends_with($aS, '/'))
err("path $aS mismatches type $n[2]");
if ($aS === './')
return;
$aP = explode('/', $fo ? substr($aS, 0, -1) : $aS);
$aV = $f2v($aL = $aP[$aY = count($aP) - 1]);
if ($aL === $aV)
return;
$y = min($aY, count($fP));
for ($x=0; $x < $y and $aP[$x] === $fP[$x]; $x++) ; # index of first difference
out("a) $x $aY fP", $fP, ", tP", $tP);
for ( ; $x < $aY ; $x++) # copy new levels
$fP[$x] = $tP[$x] = $aP[$x];
out("b) $x $aY fP", $fP, ", tP", $tP);
while (array_key_last($fP) > $aY)
array_pop($fP);
while (array_key_last($tP) > $aY)
array_pop($tP);
$fM = $tP;
$fP[$aY] = $fM[$aY] = $aL;
$tP[$aY] = $aV;
$hi($nx, $n, $fP, $fM, $tP);
};
$this->ffRsrc($r, $hR);
echo "$nx nodes in $this->cloudId\n";
}
} # end class GoogleDrive