Skip to content

Instantly share code, notes, and snippets.

@DaisukeDaisuke
Last active February 12, 2022 01:35
Show Gist options
  • Select an option

  • Save DaisukeDaisuke/21520a4b1216597cb1647fd84b11a163 to your computer and use it in GitHub Desktop.

Select an option

Save DaisukeDaisuke/21520a4b1216597cb1647fd84b11a163 to your computer and use it in GitHub Desktop.
<?php
include_once __DIR__."/http.php";
use GithubAPI\API\http;
class github{
protected static bool $buffer = true;
public static $commitmessage = null;
public const EMAIL = "17798680+DaisukeDaisuke@users.noreply.github.com";
public const NAME = "DaisukeDaisuke";
public static $pushBuffer;
public static $cache;
public static $lock = [];
public $data;
public $pointer = -1;
public $position;
public $content = null;
public $userName;
public $repository;
public $path;
public $rawPath;
public $changed = false;
public function stream_open($basepath, $mode, $options, &$opened_path) {
list($userName,$repository,$path) = self::getDatas($basepath);
//var_dump("stream_open", $basepath,$userName,$repository,$path);
$this->userName = $userName;
$this->repository = $repository;
$this->path = $path;
$this->rawPath = $basepath;
$this->data = self::getTrees($userName,$repository);
if(!isset($this->data["tree"])){
var_dump($this->data);//For debug
throw new RuntimeException("(".$basepath.") failed to open stream: The response from Github is incorrect. (User or Repository Not Found)");
}
//throw new RuntimeException("(".$basepath.") failed to open stream: No such file or directory");
//$this->content = "";//書き込みモード...
return true;
}
public function onDownloadContent(){
if(isset(self::$cache["file"][$this->userName."/".$this->repository."/".$this->path])){
$this->content = self::$cache["file"][$this->userName."/".$this->repository."/".$this->path];
return true;
}
foreach($this->data["tree"] as $data){
if($data["path"] === $this->path){
$response = http::getGithub(str_replace("https://api.github.com", "", $data["url"]));
if(!isset($response["content"])){
var_dump($response);//For debug
throw new RuntimeException("(".$this->rawPath.") failed to open stream: The response from Github is incorrect. (".$data["url"].")");
}
self::$cache["file"][$this->userName."/".$this->repository."/".$this->path] = base64_decode($response["content"]);
$this->content = self::$cache["file"][$this->userName."/".$this->repository."/".$this->path];
return true;
}
}
return false;
}
public static function destroyCache($name){
unset(self::$cache[$name]);
}
public function stream_close(){
var_dump("stream_close");
if($this->changed){
self::$pushBuffer[$this->userName][$this->repository][$this->path] = $this->content;
if(self::$buffer){
return;
}
$sha = null;
foreach($this->data["tree"] as $data){
if($data["path"] === $this->path){
$sha = $data["sha"];
break;
}
}
//$data = http::getGithub("/users/".$this->userName);
if($this->content === ""){
$this->unlink($this->rawPath);
return;
}
if(isset($sha)){
$return = http::getGithub("/repos/".$this->userName."/".$this->repository."/contents/".$this->path,[
"message" => self::getCommitmessage() ?? "test update file",
"content" => base64_encode($this->content),
"sha" => $sha,
"author" => [
"name" => self::NAME,
"email" => self::EMAIL,
],
"committer" => [
"name" => self::NAME,
"email" => self::EMAIL,
],
],"PUT");
}else{
$return = http::getGithub("/repos/".$this->userName."/".$this->repository."/contents/".$this->path,[
"message" => self::getCommitmessage() ?? "test create file",
"content" => base64_encode($this->content),
"author" => [
"name" => self::NAME,
"email" => self::EMAIL,
],
"committer" => [
"name" => self::NAME,
"email" => self::EMAIL,
],
],"PUT");
}
if($this->content !== ""&&!isset($return["content"]["sha"])){
var_dump($return);//For debug
throw new RuntimeException("(".$this->rawPath.") failed to push and close stream: The response from Github is incorrect.");
}
$trees = self::getTrees($this->userName,$this->repository)["tree"];
$column = array_column($trees, "path");
$search = array_search($this->path, $column);
//create sub tree cache
$dir = explode("/", $this->path);
unset($dir[array_key_last($dir)]);
$dir_tmp = "";
foreach($dir as $path1){
$dir_tmp .= $path1;
$search1 = array_search($dir_tmp, $column);
if($search1 === false){
$trees[] = [
"path" => $dir_tmp,
"mode" => "040000",
"type" => "tree"
];//"sha" => ""
}
$dir_tmp .= "/";
}
//update tree & file cache
if($search !== false){
$trees[$search]["sha"] = $return["content"]["sha"];
}else{
$trees[] = [
"path" => $this->path,
"mode" => "100644",
"type"=> "blob",
"sha" => $return["content"]["sha"],
];
}
self::$cache["tree"][$this->userName."/".$this->repository]["tree"] = $trees;
self::$cache["file"][$this->userName."/".$this->repository."/".$this->path] = $this->content;
}
}
function stream_read(int $count){
if($this->content === null){
if(!$this->onDownloadContent()){
throw new RuntimeException("(".$this->rawPath.") failed to read stream: No such file or directory");
};
}
$ret = substr($this->content, $this->position, $count);
$this->position += strlen($ret);
return $ret;
}
public static function uploadFiles(array $datas, $userName, $repository): array{
$output = [];
//var_dump($datas);
foreach($datas as $key => $data){
if($data !== ""){
$output[] = [
"path" => $key,
"mode" => "100644",
"type"=> "blob",
"sha" => http::getGithub("/repos/".$userName."/".$repository."/git/blobs",[
"content" => base64_encode($data),
"encoding" => "base64",
])["sha"],
];
}else{
$output[] = [
"path" => $key,
"mode" => "100644",
"type"=> "blob",
"sha" => null,
];
}
}
return $output;
}
/*
public static function push($userName,$repository){//to github
var_dump("push");
//return false;
if(!isset(self::$pushBuffer[$userName][$repository])){
return false;
}
echo "get master...\n";
$master = http::getGithub("/repos/".$userName."/".$repository."/git/refs/heads/master");
if(!isset($master["object"]["url"])) exit("プログラムは異常終了しました");
echo "get Commit...\n";
$Commit = http::getGithub($master["object"]["url"]);
//var_dump($Commit);
echo "file upload...\n";
$blobs = self::uploadFiles(self::$pushBuffer[$userName][$repository],$userName, $repository);
echo "create trees...\n";
$tree = http::getGithub("/repos/".$userName."/".$repository."/git/trees",[
"base_tree" => $Commit["tree"]["sha"],
"tree" => $blobs,
]);
echo "create commit...\n";
$Newcommit = http::getGithub("/repos/".$userName."/".$repository."/git/commits",[
"message" => "Test Commit",
"author" => $Commit["author"],
"parents" => [
$master["object"]["sha"],
],
"tree" => $tree["sha"],
]);
echo "master updete...";
http::getGithub("/repos/".$userName."/".$repository."/git/refs/heads/master",[
"sha" => $Newcommit["sha"],
"force" => false
],"PATCH");
return true;
}*/
public static function push(string $userName,string $repository,string $commitMessage){//to github
var_dump("push");
//return false;
if(!isset(self::$pushBuffer[$userName][$repository])){
return false;
}
echo "get trees...\n";
$trees = self::getTrees($userName,$repository)["tree"];
echo "get master...\n";
$master = http::getGithub("/repos/".$userName."/".$repository."/git/refs/heads/master");
if(!isset($master["object"]["url"])) exit("プログラムは異常終了しました");
echo "get Commit...\n";
$Commit = http::getGithub($master["object"]["url"]);
$Commit["author"]["date"] = str_replace('+00:00', 'Z', gmdate('c'));
echo "file upload...\n";
$blobs = self::uploadFiles(self::$pushBuffer[$userName][$repository],$userName, $repository);//array
echo "create trees...\n";
$tree = http::getGithub("/repos/".$userName."/".$repository."/git/trees",[
"base_tree" => $Commit["tree"]["sha"],
"tree" => $blobs,
]);
echo "create commit...\n";
$Newcommit = http::getGithub("/repos/".$userName."/".$repository."/git/commits",[
"message" => $commitMessage,
"author" => $Commit["author"],
"parents" => [
$master["object"]["sha"],
],
"tree" => $tree["sha"],
]);
echo "master updete...";
http::getGithub("/repos/".$userName."/".$repository."/git/refs/heads/master",[
"sha" => $Newcommit["sha"],
"force" => false
],"PATCH");
echo "update cache...\n";
$column = array_column($trees, "path");
$count = 0;
foreach(self::$pushBuffer[$userName][$repository] as $path => $contents){
$search = array_search($path, $column);
//create sub tree cache
$dir = explode("/", $path);
unset($dir[array_key_last($dir)]);
$dir_tmp = "";
foreach($dir as $path1){
$dir_tmp .= $path1;
$search1 = array_search($dir_tmp, $column);
if($search1 === false){
$trees[] = [
"path" => $dir_tmp,
"mode" => "040000",
"type" => "tree"
];//"sha" => ""
$column[] = $dir_tmp;
}
$dir_tmp .= "/";
}
//update tree & file cache
if($contents === ""){
unset($trees[$search]); //delete file
unset($column[$search]);
}else if($search !== false){
$trees[$search]["sha"] = $blobs[$count]["sha"];
}else{
$trees[] = $blobs[$count];
$column[] = $path;
}
if($contents === ""){
unset(self::$cache["file"][$userName."/".$repository."/".$path]);
}else{
self::$cache["file"][$userName."/".$repository."/".$path] = $contents;
}
//Delete the empty directory cache.
$pos = strripos($path,"/");
if($pos !== false){
$item = 0;
$file = null;
foreach($column as $key => $data){
//var_dump([$key, $data."/", $path, $pos, $count]);
if(!strncmp($data."/", $path, $pos)){
$file = $key;
++$item;
//var_dump(["!!".$file,$item]);
}
}
if($item === 1){
unset($trees[$file]);
var_dump("unset");
}
}
++$count;
}
$trees = array_values($trees);
self::$cache["tree"][$userName."/".$repository]["tree"] = $trees;
self::$pushBuffer[$userName][$repository] = [];
//var_dump(array_column($trees, "path"));
return true;
}
public function stream_write(string $data): int{
var_dump("stream_write");
$this->changed = true;
//var_dump($data);
if($this->path === ""||is_dir($this->rawPath)){
throw new RuntimeException("(".$this->rawPath.") failed to write stream: cannot write to the directory.");
}
if($this->position === 0){
$this->content = "";
}
$this->content .= $data;
$this->position += strlen($data);
return strlen($data);
}
public function stream_stat(){
var_dump("stream_stat");
var_dump($this->rawPath);
return $this->url_stat($this->rawPath, 0);
//return [];
}
function stream_eof(){
if($this->content === null){
if(!$this->onDownloadContent()){
throw new RuntimeException("(".$this->rawPath.") failed to read stream: No such file or directory");
}
}
return $this->position >= strlen($this->content);
}
function stream_tell(){
return $this->position;
}
public function stream_seek($offset, $whence){
if($this->content === null){
if(!$this->onDownloadContent()){
throw new RuntimeException("(".$this->rawPath.") failed to read stream: No such file or directory");
}
}
switch($whence){
case SEEK_SET:
if($offset < strlen($this->content) && $offset >= 0){
$this->position = $offset;
return true;
}
return false;
break;
case SEEK_CUR:
if($offset >= 0){
$this->position += $offset;
return true;
}
return false;
break;
case SEEK_END:
if(strlen($this->content) + $offset >= 0){
$this->position = strlen($this->content) + $offset;
return true;
}
return false;
break;
default:
return false;
}
}
public static function getDatas($basepath){
$url = parse_url($basepath);
if(!isset($url["host"])){
throw new RuntimeException("Username cannot be empty. (".$basepath.") Was specified.");
return false;
}
//$path = $url["host"] . ($url["path"] ?? "");
$userName = $url["host"];
$path = $url["path"];
$pos = strpos(substr($url["path"],1),"/");
$repository = substr($url["path"],1, $pos);
$path = substr($url["path"],$pos+2);
if($repository === ""){
throw new RuntimeException("The repository name cannot be empty. (".$basepath.") Was specified.");
return false;
}
$path = (substr($path, -1) === "/") ? substr($path, 0, -1) : $path;
return [$userName,$repository,$path];
}
public function dir_opendir($basepath, $options){
var_dump("dir_opendir, ".$basepath, $options);
list($userName,$repository,$path) = self::getDatas($basepath);
$this->path = $path;
$this->pointer = -1;
$this->data = self::getTrees($userName,$repository);
if(!isset($this->data["tree"])){
var_dump($this->data);//For debug
throw new RuntimeException("(".$basepath.") failed to open stream: The response from Github is incorrect. (User or Repository Not Found)");
}
return true;
}
public static function getTrees(String $owner,String $repository,$branch = "master"): array{
if(!isset(self::$cache["tree"][$owner."/".$repository])){
self::$cache["tree"][$owner."/".$repository] = http::getGithub("/repos/".$owner."/".$repository."/git/trees/".$branch."?recursive=true");
}
return self::$cache["tree"][$owner."/".$repository];
}
public function dir_readdir(){
var_dump("dir_readdir");
while(true){
++$this->pointer;
if(!isset($this->data["tree"][$this->pointer])){
return false;
}
if($this->path !== ""){
if(strncmp($this->data["tree"][$this->pointer]["path"], $this->path."/", strlen($this->path."/"))){
continue;
}
}else{
if(strncmp($this->data["tree"][$this->pointer]["path"], $this->path, strlen($this->path))){
continue;
}
}
$str = $this->data["tree"][$this->pointer]["path"];
if($this->path !== ""){
$str = str_replace($this->path."/", "", $str);
}
if(strpos($str, "/") !== false){
continue;
}
if($this->data["tree"][$this->pointer]["type"] === "blob"){
return $str;
}
if($this->data["tree"][$this->pointer]["type"] === "tree"){
if($this->path === $this->data["tree"][$this->pointer]["path"]){
continue;
}
return $str;
}
}
return false;//
}
public function dir_closedir(){
var_dump("dir_closedir");
return true;
}
public function dir_rewinddir(){
var_dump("dir_rewinddir");//ファイルリストを先頭に...
$this->pointer = -1;
return true;
}
public function url_stat(string $basepath , int $flags): array{
var_dump("url_stat");
list($userName,$repository,$path) = self::getDatas($basepath);
if(isset(self::$cache["url_stat"][$userName."/".$repository."/".$path])){
return self::$cache["url_stat"][$userName."/".$repository."/".$path];
}
$this->path = $path;
$this->data = self::getTrees($userName,$repository);
if(!isset($this->data["tree"])){
var_dump($this->data);//For debug
trigger_error("url_stat falled for ".$basepath.": The response from Github is incorrect. (User or Repository Not Found)", E_USER_ERROR);
}
if($this->path === ""){//Repository root directory
self::$cache["url_stat"][$userName."/".$repository."/".$path] = [
0 => 0,
1 => 0,
2 => 0040000,//mode
3 => 1,
4 => 0,
5 => 0,
6 => 0,
7 => 0,
8 => 0,
9 => 0,
10 => 0,
11 => 4096,
12 => 0,
'dev' => 0,
'ino' => 0,
'mode' => 0040000,//...?
'nlink' => 1,
'uid' => 0,
'gid' => 0,
'rdev' => 0,
'size' => 0,
'atime' => 0,
'mtime' => 0,
'ctime' => 0,
'blksize' => 4096,
'blocks' => 0,
];
return self::$cache["url_stat"][$userName."/".$repository."/".$path];
}
foreach($this->data["tree"] as $data){
if($data["path"] === $path){
//var_dump($data["mode"]);
$mode = 0;
switch($data["type"]){
case "blob":
$mode = 0100000;
break;
case "tree":
$mode = 0040000;
break;
}
self::$cache["url_stat"][$userName."/".$repository."/".$path] = [
0 => 0,
1 => 0,
2 => $mode,//mode
3 => 1,
4 => 0,
5 => 0,
6 => 0,
7 => ($data["size"] ?? 0),
8 => 0,
9 => 0,
10 => 0,
11 => 4096,
12 => ceil((($data["size"] ?? 0) / 512)),
'dev' => 0,
'ino' => 0,
'mode' => $mode,//...?
'nlink' => 1,
'uid' => 0,
'gid' => 0,
'rdev' => 0,
'size' => ($data["size"] ?? 0),
'atime' => 0,
'mtime' => 0,
'ctime' => 0,
'blksize' => 4096,
'blocks' => ceil((($data["size"] ?? 0) / 512)),
];
return self::$cache["url_stat"][$userName."/".$repository."/".$path];
}
}
return [];
//trigger_error("url_stat falled for ".$basepath.": No such file or directory", E_USER_ERROR);
}
public function stream_flush(){
var_dump("stream_flush");
return true;
}
public function stream_lock($operation){
var_dump("stream_lock");
if($operation === LOCK_UN){
unset(self::$lock[$this->rawPath]);
return true;
}
if(self::isLooked($this->rawPath)){
//if($this->getLook($this->rawPath) & LOCK_NB){
return false;//
//}
//while($this->getLook($this->rawPath)) sleep(1);
}
self::$lock[$this->rawPath] = $operation;
return true;
}
public static function getLook($rawPath): int{
return self::$lock[$rawPath] ?? 0;
}
public static function isLooked($rawPath): bool{
return self::readLock($rawPath)&&self::writeLock($rawPath);//...?
}
public static function readLock($rawPath): bool{
return self::getLook($rawPath) & LOCK_SH;
}
public static function writeLock($rawPath): bool{
return self::getLook($rawPath) & LOCK_EX;
}
public function unlink($basepath){
list($userName,$repository,$path) = self::getDatas($basepath);
$trees = self::getTrees($userName,$repository);
if(!isset($trees["tree"])){
var_dump($this->data);//For debug
throw new RuntimeException("unlink(".$basepath."): The response from Github is incorrect. (User or Repository Not Found)");
}
$sha = null;
foreach($trees["tree"] as $data){
if($data["path"] === $path){
$sha = $data["sha"];
break;
}
}
if($sha === null){
throw new RuntimeException("unlink(".$basepath."): No such file or directory");//Warning:
}
if(self::$buffer){
self::$pushBuffer[$userName][$repository][$path] = "";
return;
}
http::getGithub("/repos/".$userName."/".$repository."/contents/".$path,[
"message" => "test delete file",
"sha" => $sha,
"author" => [
"name" => self::NAME,
"email" => self::EMAIL,
],
"committer" => [
"name" => self::NAME,
"email" => self::EMAIL,
],
],"DELETE");
echo "update cache...\n";
$column = array_column($trees["tree"], "path");
$search = array_search($path, $column);
unset($trees["tree"][$search]);
unset($column[$search]);
$trees["tree"] = array_values($trees["tree"]);
unset(self::$cache["file"][$userName."/".$repository."/".$path]);
self::$cache["tree"][$userName."/".$repository] = $trees;
$pos = strripos($path,"/");
if($pos === false){
return;
}
$count = 0;
$file = null;
foreach($column as $key => $data){
if(!strncmp($data."/", $path, $pos)){
$file = $key;
++$count;
}
}
var_dump($count);
if($count === 1){
unset($trees["tree"][$file]);//
$trees["tree"] = array_values($trees["tree"]);
self::$cache["tree"][$userName."/".$repository] = $trees;
}
}
/*public static function getDirectoryCount($basepath): array{
list($userName,$repository,$path) = self::getDatas($basepath);
$trees = self::getTrees($userName,$repository);
$pos = strripos($path,"/");
$count = 0;
$file = null;
foreach($column as $data){
if($pos === false){
if(strpos($data,"/") === false){
$file = $data;
++$count;
}
}else if(!strncmp($data, $path, $pos){
$file = $data;
++$count;
}
}
return $output;
}*/
public function stream_set_option(int $option , int $arg1 , int $arg2){
/*
var_dump("!!!!!!!!!!!!!!!!!!!!!!!!!!!",$option, $arg1, $arg2);
switch($option){
case STREAM_OPTION_BLOCKING:
var_dump("STREAM_OPTION_BLOCKING", $arg1, $arg2);
break;
case STREAM_OPTION_READ_TIMEOUT:
var_dump("STREAM_OPTION_READ_TIMEOUT", $arg1, $arg2);
break;
case STREAM_OPTION_WRITE_BUFFER:
var_dump("STREAM_OPTION_WRITE_BUFFER", $arg1, $arg2);
break;
case STREAM_OPTION_READ_BUFFER://2,stream_set_read_buffer
switch($arg1){
case STREAM_BUFFER_NONE://stream_set_read_buffer($fp,0);
break;
case STREAM_BUFFER_FULL://stream_set_read_buffer($fp,1~);
break;
}
break;
}*/
return false;
}
public static function isBuffer() : bool{
return self::$buffer;
}
public static function setBuffer(bool $buffer) : void{
self::$buffer = $buffer;
}
public static function getCommitmessage(){
return self::$commitmessage;
}
public static function setCommitmessage($commitmessage) : void{
self::$commitmessage = $commitmessage;
}
}
stream_wrapper_register('github', 'github');
<?php
namespace GithubAPI\API;
//https://gist.github.com/DaisukeDaisuke/0d934cde7cf72b91be5c2f6ff366eef4
class http{
public static function getGithub($url,$data = false,$request = false){
if(strpos($url, "/") !== false){
$url = str_replace("https://api.github.com", "", $url);
}
echo "\n";
if($request !== false){
var_dump($request.": ".$url);
}else if($data !== false){
var_dump("POST: ".$url);
}else{
var_dump("GET: ".$url);
}
//return false;
$curl = curl_init("https://api.github.com".$url);
//curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0");
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, FALSE); // オレオレ証明書対策
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl,CURLOPT_FOLLOWLOCATION, TRUE);// Locationヘッダを追跡
curl_setopt($curl, CURLOPT_HTTPHEADER, [
'Authorization: token ?????',
]);
if($request !== false) curl_setopt($curl,CURLOPT_CUSTOMREQUEST,$request);
if($data !== false){
curl_setopt($curl,CURLOPT_POST, TRUE);
curl_setopt($curl, CURLOPT_POSTFIELDS,json_encode($data));
}
curl_setopt($curl,CURLOPT_USERAGENT, "USER_AGENT");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$return = curl_exec($curl);
//var_dump($return);
$errno = curl_errno($curl);
$error = curl_error($curl);
if($errno !== CURLE_OK){
throw new \RuntimeException($error, $errno);
}
//var_dump("StatusCode: ".curl_getinfo($curl, CURLINFO_RESPONSE_CODE));
curl_close($curl);
$test = json_decode($return,true);
//var_dump($data);
//var_dump($test);
sleep(1);
return $test;
}
}
@DaisukeDaisuke
Copy link
Author

DaisukeDaisuke commented Mar 31, 2021

//「allow_url_include」無効な環境下にて、インターネットよりinclude可能...?
include "github://DaisukeDaisuke/testrepos0001/php/echo.txt";
file_put_contents("github://DaisukeDaisuke/testrepos0001/php/echo.txt");
github::push("DaisukeDaisuke","testrepos0001"); //「github::BUTTER = true」(出力バッファ有効)の場合のみ
var_dump(file_get_contents("github://DaisukeDaisuke/testrepos0001/test3.txt"));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment