From f1166757ba3cc0af744344f0010b1e7ed5383619 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jordi=20Gonzalez=20Mu=C3=B1oz?= <jordigonzm@gmail.com>
Date: Wed, 20 Jul 2022 18:20:12 +0200
Subject: [PATCH] librclone: add PHP bindings and test program

---
 librclone/README.md      |  8 ++++++
 librclone/php/rclone.php | 53 ++++++++++++++++++++++++++++++++++++++
 librclone/php/test.php   | 55 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 116 insertions(+)
 create mode 100644 librclone/php/rclone.php
 create mode 100644 librclone/php/test.php

diff --git a/librclone/README.md b/librclone/README.md
index 77d3a86c5..45d522828 100644
--- a/librclone/README.md
+++ b/librclone/README.md
@@ -217,6 +217,14 @@ This needs expanding and submitting to pypi...
 
 Rust bindings are available in the `librclone` crate: https://crates.io/crates/librclone
 
+## PHP
+
+The `php` subdirectory contains how to use the C library librclone in php through foreign 
+function interface (FFI).
+
+Useful docs:
+- [PHP / FFI](https://www.php.net/manual/en/book.ffi.php)
+
 ## TODO
 
 - Async jobs must currently be cancelled manually at the moment - RcloneFinalize doesn't do it.
diff --git a/librclone/php/rclone.php b/librclone/php/rclone.php
new file mode 100644
index 000000000..485e27dc5
--- /dev/null
+++ b/librclone/php/rclone.php
@@ -0,0 +1,53 @@
+<?php
+/*
+PHP interface to librclone.so, using FFI ( Foreign Function Interface )
+
+Create an rclone object
+
+$rc = new Rclone( __DIR__ . '/librclone.so' );
+
+Then call rpc calls on it
+
+    $rc->rpc( "config/listremotes", "{}" );
+
+When finished, close it
+
+    $rc->close();
+*/
+
+class Rclone {
+
+    protected $rclone;
+    private $out;
+
+    public function __construct( $libshared )
+    {
+        $this->rclone = \FFI::cdef("
+        struct RcloneRPCResult {
+            char* Output;
+            int	Status;
+        };        
+        extern void RcloneInitialize();
+        extern void RcloneFinalize();
+        extern struct RcloneRPCResult RcloneRPC(char* method, char* input);
+        extern void RcloneFreeString(char* str);
+        ", $libshared);
+        $this->rclone->RcloneInitialize();
+    }
+
+    public function rpc( $method, $input ): array
+    {
+        $this->out = $this->rclone->RcloneRPC( $method, $input );
+        $response = [
+            'output' => \FFI::string( $this->out->Output ),
+            'status' => $this->out->Status
+        ];
+        $this->rclone->RcloneFreeString( $this->out->Output );
+        return $response;
+    }
+
+    public function close( ): void
+    {
+        $this->rclone->RcloneFinalize();
+    }
+}
diff --git a/librclone/php/test.php b/librclone/php/test.php
new file mode 100644
index 000000000..0de0f94ad
--- /dev/null
+++ b/librclone/php/test.php
@@ -0,0 +1,55 @@
+<?php
+/*
+Test program for librclone
+*/
+
+include_once ( "rclone.php" );
+
+const REMOTE    = 'gdrive:/';
+const FOLDER    = "rcloneTest";
+const FILE      = "testFile.txt";
+
+$rc = new Rclone( __DIR__ . '/librclone.so' );
+
+$response = $rc->rpc( "config/listremotes", "{}" );
+print_r( $response );
+
+$response = $rc->rpc("operations/mkdir",
+    json_encode( [
+        'fs' => REMOTE,
+        'remote'=> FOLDER
+    ]));
+print_r( $response );
+
+$response = $rc->rpc("operations/list",
+    json_encode( [
+        'fs' => REMOTE,
+        'remote'=> ''
+    ]));
+print_r( $response );
+
+file_put_contents("./" . FILE, "Success!!!");
+$response = $rc->rpc("operations/copyfile",
+    json_encode( [
+        'srcFs' => getcwd(),
+        'srcRemote'=> FILE,
+        'dstFs' => REMOTE . FOLDER,
+        'dstRemote' => FILE
+    ]));
+print_r( $response );
+
+$response = $rc->rpc("operations/list",
+    json_encode( [
+        'fs' => REMOTE . FOLDER,
+        'remote'=> ''
+    ]));
+print_r( $response );
+if ( $response['output'] ) {
+    $array = @json_decode( $response['output'], true );
+    if ( $response['status'] == 200 && $array['list'] ?? 0 ) {
+        $valid = $array['list'][0]['Name'] == FILE ? "SUCCESS" : "FAIL";
+        print_r("The test seems: " . $valid . "\n");
+    }
+}
+
+$rc->close();