<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>yoast-seo Archives - Backup Copilot</title>
	<atom:link href="https://backupcopilotplugin.com/blog/tag/yoast-seo/feed/" rel="self" type="application/rss+xml" />
	<link>https://backupcopilotplugin.com/blog/tag/yoast-seo/</link>
	<description>WordPress Backups Done Right</description>
	<lastBuildDate>Mon, 24 Nov 2025 11:17:04 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://storage.googleapis.com/backupcopilotplugin/2025/11/favicon-alt-150x150.png</url>
	<title>yoast-seo Archives - Backup Copilot</title>
	<link>https://backupcopilotplugin.com/blog/tag/yoast-seo/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Building Custom Cloud Storage Providers for Backup Copilot Pro</title>
		<link>https://backupcopilotplugin.com/blog/building-custom-cloud-storage-providers-for-backup-copilot-pro/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Fri, 05 Dec 2025 09:00:00 +0000</pubDate>
				<category><![CDATA[Developer Resources]]></category>
		<category><![CDATA[plugin-test]]></category>
		<category><![CDATA[rest api]]></category>
		<category><![CDATA[yoast-seo]]></category>
		<guid isPermaLink="false">https://backupcopilotplugin.com/?p=269</guid>

					<description><![CDATA[<p>Backup Copilot Pro supports Dropbox, Google Drive, and Amazon S3 out of the box.</p>
<p>The post <a href="https://backupcopilotplugin.com/blog/building-custom-cloud-storage-providers-for-backup-copilot-pro/">Building Custom Cloud Storage Providers for Backup Copilot Pro</a> appeared first on <a href="https://backupcopilotplugin.com">Backup Copilot</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>Backup Copilot Pro supports Dropbox, Google Drive, and Amazon S3 out of the box. But what if you need Wasabi, Backblaze B2, DigitalOcean Spaces, or your own custom storage backend? This developer guide shows you how to build custom cloud storage providers by extending Backup Copilot Pro’s provider architecture with complete code examples and implementation details.</p>
<h2 id="understanding-the-cloud-provider-architecture">Understanding the Cloud Provider Architecture</h2>
<p>Backup Copilot Pro uses an abstract cloud provider interface that all storage backends implement. This architecture enables consistent behavior across different cloud services while allowing provider-specific implementations.</p>
<p><strong>Base Provider Class</strong>: All providers extend <code>BKPC_Cloud_Provider</code> abstract class defining required methods:</p>
<div class="sourceCode" id="cb1">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="kw">abstract</span> <span class="kw">class</span> BKPC_Cloud_Provider {</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a>    <span class="kw">abstract</span> <span class="kw">public</span> <span class="kw">function</span> authenticate<span class="ot">(</span><span class="kw">$credentials</span><span class="ot">);</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a>    <span class="kw">abstract</span> <span class="kw">public</span> <span class="kw">function</span> upload<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$options</span><span class="ot">);</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a>    <span class="kw">abstract</span> <span class="kw">public</span> <span class="kw">function</span> download<span class="ot">(</span><span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$local_file</span><span class="ot">);</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a>    <span class="kw">abstract</span> <span class="kw">public</span> <span class="kw">function</span> delete<span class="ot">(</span><span class="kw">$remote_path</span><span class="ot">);</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a>    <span class="kw">abstract</span> <span class="kw">public</span> <span class="kw">function</span> list_files<span class="ot">(</span><span class="kw">$remote_dir</span><span class="ot">);</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a>    <span class="kw">abstract</span> <span class="kw">public</span> <span class="kw">function</span> get_storage_info<span class="ot">();</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Your custom provider implements these methods using your chosen storage service’s API.</p>
<h2 id="required-methods-overview">Required Methods Overview</h2>
<p>Each method serves specific purpose in backup workflow:</p>
<p><strong>authenticate($credentials)</strong>: Validates API credentials, OAuth tokens, or connection parameters. Returns true on success, throws exception on failure.</p>
<p><strong>upload($local_file, $remote_path, $options)</strong>: Uploads backup file to cloud storage. Handles chunked uploads for large files, progress callbacks, and resumable sessions.</p>
<p><strong>download($remote_path, $local_file)</strong>: Downloads backup from cloud to local server for restoration. Must handle partial downloads and resume capability.</p>
<p><strong>delete($remote_path)</strong>: Removes backup file from cloud storage. Called during retention policy enforcement.</p>
<p><strong>list_files($remote_dir)</strong>: Returns array of files in remote directory with metadata (size, modified date, path). Used for backup inventory.</p>
<p><strong>get_storage_info()</strong>: Returns current storage quota usage and available space. Displayed in plugin dashboard.</p>
<h2 id="building-an-s3-compatible-provider">Building an S3-Compatible Provider</h2>
<p>S3-compatible storage (Wasabi, Backblaze B2, DigitalOcean Spaces) shares common API. Here’s complete implementation:</p>
<div class="sourceCode" id="cb2">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">class</span> BKPC_Cloud_Provider_S3_Compatible <span class="kw">extends</span> BKPC_Cloud_Provider {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a>    <span class="kw">private</span> <span class="kw">$endpoint</span><span class="ot">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>    <span class="kw">private</span> <span class="kw">$bucket</span><span class="ot">;</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a>    <span class="kw">private</span> <span class="kw">$access_key</span><span class="ot">;</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a>    <span class="kw">private</span> <span class="kw">$secret_key</span><span class="ot">;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a>    <span class="kw">private</span> <span class="kw">$client</span><span class="ot">;</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> <span class="fu">__construct</span><span class="ot">()</span> {</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;endpoint = get_option<span class="ot">(</span><span class="st">&#39;bkpc_s3_endpoint&#39;</span><span class="ot">);</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;bucket = get_option<span class="ot">(</span><span class="st">&#39;bkpc_s3_bucket&#39;</span><span class="ot">);</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;access_key = get_option<span class="ot">(</span><span class="st">&#39;bkpc_s3_access_key&#39;</span><span class="ot">);</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;secret_key = <span class="kw">$this</span>-&gt;decrypt<span class="ot">(</span>get_option<span class="ot">(</span><span class="st">&#39;bkpc_s3_secret_key&#39;</span><span class="ot">));</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a>    }</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true"></a></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> authenticate<span class="ot">(</span><span class="kw">$credentials</span><span class="ot">)</span> {</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true"></a>        <span class="kw">require_once</span> <span class="st">&#39;aws-sdk/aws-autoloader.php&#39;</span><span class="ot">;</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true"></a></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true"></a>            <span class="kw">$this</span>-&gt;client = <span class="kw">new</span> Aws\<span class="kw">S3</span>\S3Client<span class="ot">([</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true"></a>                <span class="st">&#39;version&#39;</span> =&gt; <span class="st">&#39;latest&#39;</span><span class="ot">,</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true"></a>                <span class="st">&#39;region&#39;</span> =&gt; <span class="st">&#39;us-east-1&#39;</span><span class="ot">,</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true"></a>                <span class="st">&#39;endpoint&#39;</span> =&gt; <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;endpoint&#39;</span><span class="ot">],</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true"></a>                <span class="st">&#39;credentials&#39;</span> =&gt; <span class="ot">[</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true"></a>                    <span class="st">&#39;key&#39;</span> =&gt; <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;access_key&#39;</span><span class="ot">],</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true"></a>                    <span class="st">&#39;secret&#39;</span> =&gt; <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;secret_key&#39;</span><span class="ot">]</span></span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true"></a>                <span class="ot">],</span></span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true"></a>                <span class="st">&#39;use_path_style_endpoint&#39;</span> =&gt; <span class="kw">true</span></span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true"></a>            <span class="ot">]);</span></span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true"></a></span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true"></a>            <span class="co">// Test connection by listing buckets</span></span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true"></a>            <span class="kw">$this</span>-&gt;client-&gt;headBucket<span class="ot">([</span><span class="st">&#39;Bucket&#39;</span> =&gt; <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;bucket&#39;</span><span class="ot">]]);</span></span>
<span id="cb2-33"><a href="#cb2-33" aria-hidden="true"></a></span>
<span id="cb2-34"><a href="#cb2-34" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">true</span><span class="ot">;</span></span>
<span id="cb2-35"><a href="#cb2-35" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb2-36"><a href="#cb2-36" aria-hidden="true"></a>            <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;S3 Authentication failed: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">());</span></span>
<span id="cb2-37"><a href="#cb2-37" aria-hidden="true"></a>        }</span>
<span id="cb2-38"><a href="#cb2-38" aria-hidden="true"></a>    }</span>
<span id="cb2-39"><a href="#cb2-39" aria-hidden="true"></a></span>
<span id="cb2-40"><a href="#cb2-40" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> upload<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$options</span> = <span class="ot">[])</span> {</span>
<span id="cb2-41"><a href="#cb2-41" aria-hidden="true"></a>        <span class="kw">$file_size</span> = <span class="fu">filesize</span><span class="ot">(</span><span class="kw">$local_file</span><span class="ot">);</span></span>
<span id="cb2-42"><a href="#cb2-42" aria-hidden="true"></a></span>
<span id="cb2-43"><a href="#cb2-43" aria-hidden="true"></a>        <span class="co">// Use multipart upload for files &gt; 100MB</span></span>
<span id="cb2-44"><a href="#cb2-44" aria-hidden="true"></a>        <span class="kw">if</span> <span class="ot">(</span><span class="kw">$file_size</span> &gt; <span class="dv">100</span> * <span class="dv">1024</span> * <span class="dv">1024</span><span class="ot">)</span> {</span>
<span id="cb2-45"><a href="#cb2-45" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">$this</span>-&gt;multipart_upload<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$options</span><span class="ot">);</span></span>
<span id="cb2-46"><a href="#cb2-46" aria-hidden="true"></a>        }</span>
<span id="cb2-47"><a href="#cb2-47" aria-hidden="true"></a></span>
<span id="cb2-48"><a href="#cb2-48" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb2-49"><a href="#cb2-49" aria-hidden="true"></a>            <span class="kw">$result</span> = <span class="kw">$this</span>-&gt;client-&gt;putObject<span class="ot">([</span></span>
<span id="cb2-50"><a href="#cb2-50" aria-hidden="true"></a>                <span class="st">&#39;Bucket&#39;</span> =&gt; <span class="kw">$this</span>-&gt;bucket<span class="ot">,</span></span>
<span id="cb2-51"><a href="#cb2-51" aria-hidden="true"></a>                <span class="st">&#39;Key&#39;</span> =&gt; <span class="kw">$remote_path</span><span class="ot">,</span></span>
<span id="cb2-52"><a href="#cb2-52" aria-hidden="true"></a>                <span class="st">&#39;SourceFile&#39;</span> =&gt; <span class="kw">$local_file</span><span class="ot">,</span></span>
<span id="cb2-53"><a href="#cb2-53" aria-hidden="true"></a>                <span class="st">&#39;ServerSideEncryption&#39;</span> =&gt; <span class="st">&#39;AES256&#39;</span></span>
<span id="cb2-54"><a href="#cb2-54" aria-hidden="true"></a>            <span class="ot">]);</span></span>
<span id="cb2-55"><a href="#cb2-55" aria-hidden="true"></a></span>
<span id="cb2-56"><a href="#cb2-56" aria-hidden="true"></a>            <span class="kw">if</span> <span class="ot">(</span><span class="kw">isset</span><span class="ot">(</span><span class="kw">$options</span><span class="ot">[</span><span class="st">&#39;progress_callback&#39;</span><span class="ot">]))</span> {</span>
<span id="cb2-57"><a href="#cb2-57" aria-hidden="true"></a>                <span class="fu">call_user_func</span><span class="ot">(</span><span class="kw">$options</span><span class="ot">[</span><span class="st">&#39;progress_callback&#39;</span><span class="ot">],</span> <span class="kw">$file_size</span><span class="ot">,</span> <span class="kw">$file_size</span><span class="ot">);</span></span>
<span id="cb2-58"><a href="#cb2-58" aria-hidden="true"></a>            }</span>
<span id="cb2-59"><a href="#cb2-59" aria-hidden="true"></a></span>
<span id="cb2-60"><a href="#cb2-60" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">$result</span><span class="ot">[</span><span class="st">&#39;ObjectURL&#39;</span><span class="ot">];</span></span>
<span id="cb2-61"><a href="#cb2-61" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb2-62"><a href="#cb2-62" aria-hidden="true"></a>            <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;Upload failed: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">());</span></span>
<span id="cb2-63"><a href="#cb2-63" aria-hidden="true"></a>        }</span>
<span id="cb2-64"><a href="#cb2-64" aria-hidden="true"></a>    }</span>
<span id="cb2-65"><a href="#cb2-65" aria-hidden="true"></a></span>
<span id="cb2-66"><a href="#cb2-66" aria-hidden="true"></a>    <span class="kw">private</span> <span class="kw">function</span> multipart_upload<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$options</span><span class="ot">)</span> {</span>
<span id="cb2-67"><a href="#cb2-67" aria-hidden="true"></a>        <span class="kw">$uploader</span> = <span class="kw">new</span> Aws\<span class="kw">S3</span>\MultipartUploader<span class="ot">(</span><span class="kw">$this</span>-&gt;client<span class="ot">,</span> <span class="kw">$local_file</span><span class="ot">,</span> <span class="ot">[</span></span>
<span id="cb2-68"><a href="#cb2-68" aria-hidden="true"></a>            <span class="st">&#39;bucket&#39;</span> =&gt; <span class="kw">$this</span>-&gt;bucket<span class="ot">,</span></span>
<span id="cb2-69"><a href="#cb2-69" aria-hidden="true"></a>            <span class="st">&#39;key&#39;</span> =&gt; <span class="kw">$remote_path</span><span class="ot">,</span></span>
<span id="cb2-70"><a href="#cb2-70" aria-hidden="true"></a>            <span class="st">&#39;before_initiate&#39;</span> =&gt; <span class="kw">function</span> <span class="ot">()</span> <span class="kw">use</span> <span class="ot">(</span><span class="kw">$options</span><span class="ot">)</span> {</span>
<span id="cb2-71"><a href="#cb2-71" aria-hidden="true"></a>                <span class="co">// Store upload ID for resume capability</span></span>
<span id="cb2-72"><a href="#cb2-72" aria-hidden="true"></a>            }<span class="ot">,</span></span>
<span id="cb2-73"><a href="#cb2-73" aria-hidden="true"></a>            <span class="st">&#39;before_upload&#39;</span> =&gt; <span class="kw">function</span> <span class="ot">(</span><span class="kw">$command</span><span class="ot">)</span> <span class="kw">use</span> <span class="ot">(</span><span class="kw">$options</span><span class="ot">)</span> {</span>
<span id="cb2-74"><a href="#cb2-74" aria-hidden="true"></a>                <span class="kw">if</span> <span class="ot">(</span><span class="kw">isset</span><span class="ot">(</span><span class="kw">$options</span><span class="ot">[</span><span class="st">&#39;progress_callback&#39;</span><span class="ot">]))</span> {</span>
<span id="cb2-75"><a href="#cb2-75" aria-hidden="true"></a>                    <span class="co">// Update progress</span></span>
<span id="cb2-76"><a href="#cb2-76" aria-hidden="true"></a>                }</span>
<span id="cb2-77"><a href="#cb2-77" aria-hidden="true"></a>            }</span>
<span id="cb2-78"><a href="#cb2-78" aria-hidden="true"></a>        <span class="ot">]);</span></span>
<span id="cb2-79"><a href="#cb2-79" aria-hidden="true"></a></span>
<span id="cb2-80"><a href="#cb2-80" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb2-81"><a href="#cb2-81" aria-hidden="true"></a>            <span class="kw">$result</span> = <span class="kw">$uploader</span>-&gt;upload<span class="ot">();</span></span>
<span id="cb2-82"><a href="#cb2-82" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">$result</span><span class="ot">[</span><span class="st">&#39;ObjectURL&#39;</span><span class="ot">];</span></span>
<span id="cb2-83"><a href="#cb2-83" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span>Aws\<span class="kw">S3</span>\<span class="kw">Exception</span>\S3MultipartUploadException <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb2-84"><a href="#cb2-84" aria-hidden="true"></a>            <span class="co">// Resume failed upload</span></span>
<span id="cb2-85"><a href="#cb2-85" aria-hidden="true"></a>            <span class="kw">$uploader</span> = <span class="kw">new</span> Aws\<span class="kw">S3</span>\MultipartUploader<span class="ot">(</span><span class="kw">$this</span>-&gt;client<span class="ot">,</span> <span class="kw">$local_file</span><span class="ot">,</span> <span class="ot">[</span></span>
<span id="cb2-86"><a href="#cb2-86" aria-hidden="true"></a>                <span class="st">&#39;bucket&#39;</span> =&gt; <span class="kw">$this</span>-&gt;bucket<span class="ot">,</span></span>
<span id="cb2-87"><a href="#cb2-87" aria-hidden="true"></a>                <span class="st">&#39;key&#39;</span> =&gt; <span class="kw">$remote_path</span><span class="ot">,</span></span>
<span id="cb2-88"><a href="#cb2-88" aria-hidden="true"></a>                <span class="st">&#39;state&#39;</span> =&gt; <span class="kw">$e</span>-&gt;getState<span class="ot">()</span></span>
<span id="cb2-89"><a href="#cb2-89" aria-hidden="true"></a>            <span class="ot">]);</span></span>
<span id="cb2-90"><a href="#cb2-90" aria-hidden="true"></a>            <span class="kw">$result</span> = <span class="kw">$uploader</span>-&gt;upload<span class="ot">();</span></span>
<span id="cb2-91"><a href="#cb2-91" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">$result</span><span class="ot">[</span><span class="st">&#39;ObjectURL&#39;</span><span class="ot">];</span></span>
<span id="cb2-92"><a href="#cb2-92" aria-hidden="true"></a>        }</span>
<span id="cb2-93"><a href="#cb2-93" aria-hidden="true"></a>    }</span>
<span id="cb2-94"><a href="#cb2-94" aria-hidden="true"></a></span>
<span id="cb2-95"><a href="#cb2-95" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> download<span class="ot">(</span><span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$local_file</span><span class="ot">)</span> {</span>
<span id="cb2-96"><a href="#cb2-96" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb2-97"><a href="#cb2-97" aria-hidden="true"></a>            <span class="kw">$this</span>-&gt;client-&gt;getObject<span class="ot">([</span></span>
<span id="cb2-98"><a href="#cb2-98" aria-hidden="true"></a>                <span class="st">&#39;Bucket&#39;</span> =&gt; <span class="kw">$this</span>-&gt;bucket<span class="ot">,</span></span>
<span id="cb2-99"><a href="#cb2-99" aria-hidden="true"></a>                <span class="st">&#39;Key&#39;</span> =&gt; <span class="kw">$remote_path</span><span class="ot">,</span></span>
<span id="cb2-100"><a href="#cb2-100" aria-hidden="true"></a>                <span class="st">&#39;SaveAs&#39;</span> =&gt; <span class="kw">$local_file</span></span>
<span id="cb2-101"><a href="#cb2-101" aria-hidden="true"></a>            <span class="ot">]);</span></span>
<span id="cb2-102"><a href="#cb2-102" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">true</span><span class="ot">;</span></span>
<span id="cb2-103"><a href="#cb2-103" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb2-104"><a href="#cb2-104" aria-hidden="true"></a>            <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;Download failed: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">());</span></span>
<span id="cb2-105"><a href="#cb2-105" aria-hidden="true"></a>        }</span>
<span id="cb2-106"><a href="#cb2-106" aria-hidden="true"></a>    }</span>
<span id="cb2-107"><a href="#cb2-107" aria-hidden="true"></a></span>
<span id="cb2-108"><a href="#cb2-108" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> delete<span class="ot">(</span><span class="kw">$remote_path</span><span class="ot">)</span> {</span>
<span id="cb2-109"><a href="#cb2-109" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb2-110"><a href="#cb2-110" aria-hidden="true"></a>            <span class="kw">$this</span>-&gt;client-&gt;deleteObject<span class="ot">([</span></span>
<span id="cb2-111"><a href="#cb2-111" aria-hidden="true"></a>                <span class="st">&#39;Bucket&#39;</span> =&gt; <span class="kw">$this</span>-&gt;bucket<span class="ot">,</span></span>
<span id="cb2-112"><a href="#cb2-112" aria-hidden="true"></a>                <span class="st">&#39;Key&#39;</span> =&gt; <span class="kw">$remote_path</span></span>
<span id="cb2-113"><a href="#cb2-113" aria-hidden="true"></a>            <span class="ot">]);</span></span>
<span id="cb2-114"><a href="#cb2-114" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">true</span><span class="ot">;</span></span>
<span id="cb2-115"><a href="#cb2-115" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb2-116"><a href="#cb2-116" aria-hidden="true"></a>            <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;Delete failed: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">());</span></span>
<span id="cb2-117"><a href="#cb2-117" aria-hidden="true"></a>        }</span>
<span id="cb2-118"><a href="#cb2-118" aria-hidden="true"></a>    }</span>
<span id="cb2-119"><a href="#cb2-119" aria-hidden="true"></a></span>
<span id="cb2-120"><a href="#cb2-120" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> list_files<span class="ot">(</span><span class="kw">$remote_dir</span><span class="ot">)</span> {</span>
<span id="cb2-121"><a href="#cb2-121" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb2-122"><a href="#cb2-122" aria-hidden="true"></a>            <span class="kw">$results</span> = <span class="kw">$this</span>-&gt;client-&gt;listObjects<span class="ot">([</span></span>
<span id="cb2-123"><a href="#cb2-123" aria-hidden="true"></a>                <span class="st">&#39;Bucket&#39;</span> =&gt; <span class="kw">$this</span>-&gt;bucket<span class="ot">,</span></span>
<span id="cb2-124"><a href="#cb2-124" aria-hidden="true"></a>                <span class="st">&#39;Prefix&#39;</span> =&gt; <span class="kw">$remote_dir</span></span>
<span id="cb2-125"><a href="#cb2-125" aria-hidden="true"></a>            <span class="ot">]);</span></span>
<span id="cb2-126"><a href="#cb2-126" aria-hidden="true"></a></span>
<span id="cb2-127"><a href="#cb2-127" aria-hidden="true"></a>            <span class="kw">$files</span> = <span class="ot">[];</span></span>
<span id="cb2-128"><a href="#cb2-128" aria-hidden="true"></a>            <span class="kw">foreach</span> <span class="ot">(</span><span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;Contents&#39;</span><span class="ot">]</span> <span class="kw">as</span> <span class="kw">$object</span><span class="ot">)</span> {</span>
<span id="cb2-129"><a href="#cb2-129" aria-hidden="true"></a>                <span class="kw">$files</span><span class="ot">[]</span> = <span class="ot">[</span></span>
<span id="cb2-130"><a href="#cb2-130" aria-hidden="true"></a>                    <span class="st">&#39;path&#39;</span> =&gt; <span class="kw">$object</span><span class="ot">[</span><span class="st">&#39;Key&#39;</span><span class="ot">],</span></span>
<span id="cb2-131"><a href="#cb2-131" aria-hidden="true"></a>                    <span class="st">&#39;size&#39;</span> =&gt; <span class="kw">$object</span><span class="ot">[</span><span class="st">&#39;Size&#39;</span><span class="ot">],</span></span>
<span id="cb2-132"><a href="#cb2-132" aria-hidden="true"></a>                    <span class="st">&#39;modified&#39;</span> =&gt; <span class="kw">$object</span><span class="ot">[</span><span class="st">&#39;LastModified&#39;</span><span class="ot">]</span></span>
<span id="cb2-133"><a href="#cb2-133" aria-hidden="true"></a>                <span class="ot">];</span></span>
<span id="cb2-134"><a href="#cb2-134" aria-hidden="true"></a>            }</span>
<span id="cb2-135"><a href="#cb2-135" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">$files</span><span class="ot">;</span></span>
<span id="cb2-136"><a href="#cb2-136" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb2-137"><a href="#cb2-137" aria-hidden="true"></a>            <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;List files failed: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">());</span></span>
<span id="cb2-138"><a href="#cb2-138" aria-hidden="true"></a>        }</span>
<span id="cb2-139"><a href="#cb2-139" aria-hidden="true"></a>    }</span>
<span id="cb2-140"><a href="#cb2-140" aria-hidden="true"></a></span>
<span id="cb2-141"><a href="#cb2-141" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> get_storage_info<span class="ot">()</span> {</span>
<span id="cb2-142"><a href="#cb2-142" aria-hidden="true"></a>        <span class="co">// S3 doesn&#39;t provide quota info via API</span></span>
<span id="cb2-143"><a href="#cb2-143" aria-hidden="true"></a>        <span class="kw">return</span> <span class="ot">[</span></span>
<span id="cb2-144"><a href="#cb2-144" aria-hidden="true"></a>            <span class="st">&#39;used&#39;</span> =&gt; <span class="dv">0</span><span class="ot">,</span></span>
<span id="cb2-145"><a href="#cb2-145" aria-hidden="true"></a>            <span class="st">&#39;total&#39;</span> =&gt; <span class="dv">-1</span><span class="ot">,</span> <span class="co">// Unlimited</span></span>
<span id="cb2-146"><a href="#cb2-146" aria-hidden="true"></a>            <span class="st">&#39;available&#39;</span> =&gt; <span class="dv">-1</span></span>
<span id="cb2-147"><a href="#cb2-147" aria-hidden="true"></a>        <span class="ot">];</span></span>
<span id="cb2-148"><a href="#cb2-148" aria-hidden="true"></a>    }</span>
<span id="cb2-149"><a href="#cb2-149" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="implementing-ftpsftp-provider">Implementing FTP/SFTP Provider</h2>
<p>For legacy systems or custom infrastructure, FTP/SFTP support may be needed:</p>
<div class="sourceCode" id="cb3">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw">class</span> BKPC_Cloud_Provider_SFTP <span class="kw">extends</span> BKPC_Cloud_Provider {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a>    <span class="kw">private</span> <span class="kw">$connection</span><span class="ot">;</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a>    <span class="kw">private</span> <span class="kw">$sftp</span><span class="ot">;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> authenticate<span class="ot">(</span><span class="kw">$credentials</span><span class="ot">)</span> {</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a>        <span class="kw">if</span> <span class="ot">(</span>!<span class="fu">function_exists</span><span class="ot">(</span><span class="st">&#39;ssh2_connect&#39;</span><span class="ot">))</span> {</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a>            <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;PHP SSH2 extension required&#39;</span><span class="ot">);</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a>        }</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;connection = <span class="fu">ssh2_connect</span><span class="ot">(</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true"></a>            <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;host&#39;</span><span class="ot">],</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true"></a>            <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;port&#39;</span><span class="ot">]</span> <span class="ot">??</span> <span class="dv">22</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true"></a>        <span class="ot">);</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true"></a></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true"></a>        <span class="kw">if</span> <span class="ot">(</span>!<span class="kw">$this</span>-&gt;connection<span class="ot">)</span> {</span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true"></a>            <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;Cannot connect to SFTP server&#39;</span><span class="ot">);</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true"></a>        }</span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true"></a></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true"></a>        <span class="kw">if</span> <span class="ot">(</span><span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;auth_type&#39;</span><span class="ot">]</span> === <span class="st">&#39;password&#39;</span><span class="ot">)</span> {</span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true"></a>            <span class="kw">$auth</span> = <span class="fu">ssh2_auth_password</span><span class="ot">(</span></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true"></a>                <span class="kw">$this</span>-&gt;connection<span class="ot">,</span></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true"></a>                <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;username&#39;</span><span class="ot">],</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true"></a>                <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;password&#39;</span><span class="ot">]</span></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true"></a>            <span class="ot">);</span></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true"></a>        } <span class="kw">else</span> {</span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true"></a>            <span class="kw">$auth</span> = <span class="fu">ssh2_auth_pubkey_file</span><span class="ot">(</span></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true"></a>                <span class="kw">$this</span>-&gt;connection<span class="ot">,</span></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true"></a>                <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;username&#39;</span><span class="ot">],</span></span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true"></a>                <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;public_key&#39;</span><span class="ot">],</span></span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true"></a>                <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;private_key&#39;</span><span class="ot">],</span></span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true"></a>                <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;passphrase&#39;</span><span class="ot">]</span></span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true"></a>            <span class="ot">);</span></span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true"></a>        }</span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true"></a></span>
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true"></a>        <span class="kw">if</span> <span class="ot">(</span>!<span class="kw">$auth</span><span class="ot">)</span> {</span>
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true"></a>            <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;SFTP authentication failed&#39;</span><span class="ot">);</span></span>
<span id="cb3-38"><a href="#cb3-38" aria-hidden="true"></a>        }</span>
<span id="cb3-39"><a href="#cb3-39" aria-hidden="true"></a></span>
<span id="cb3-40"><a href="#cb3-40" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;sftp = <span class="fu">ssh2_sftp</span><span class="ot">(</span><span class="kw">$this</span>-&gt;connection<span class="ot">);</span></span>
<span id="cb3-41"><a href="#cb3-41" aria-hidden="true"></a>        <span class="kw">return</span> <span class="kw">true</span><span class="ot">;</span></span>
<span id="cb3-42"><a href="#cb3-42" aria-hidden="true"></a>    }</span>
<span id="cb3-43"><a href="#cb3-43" aria-hidden="true"></a></span>
<span id="cb3-44"><a href="#cb3-44" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> upload<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$options</span> = <span class="ot">[])</span> {</span>
<span id="cb3-45"><a href="#cb3-45" aria-hidden="true"></a>        <span class="kw">$remote_stream</span> = <span class="fu">fopen</span><span class="ot">(</span><span class="st">&quot;ssh2.sftp://</span><span class="kw">{$this-&gt;sftp}{$remote_path}</span><span class="st">&quot;</span><span class="ot">,</span> <span class="st">&#39;w&#39;</span><span class="ot">);</span></span>
<span id="cb3-46"><a href="#cb3-46" aria-hidden="true"></a>        <span class="kw">$local_stream</span> = <span class="fu">fopen</span><span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="st">&#39;r&#39;</span><span class="ot">);</span></span>
<span id="cb3-47"><a href="#cb3-47" aria-hidden="true"></a></span>
<span id="cb3-48"><a href="#cb3-48" aria-hidden="true"></a>        <span class="kw">$file_size</span> = <span class="fu">filesize</span><span class="ot">(</span><span class="kw">$local_file</span><span class="ot">);</span></span>
<span id="cb3-49"><a href="#cb3-49" aria-hidden="true"></a>        <span class="kw">$uploaded</span> = <span class="dv">0</span><span class="ot">;</span></span>
<span id="cb3-50"><a href="#cb3-50" aria-hidden="true"></a></span>
<span id="cb3-51"><a href="#cb3-51" aria-hidden="true"></a>        <span class="kw">while</span> <span class="ot">(</span>!<span class="fu">feof</span><span class="ot">(</span><span class="kw">$local_stream</span><span class="ot">))</span> {</span>
<span id="cb3-52"><a href="#cb3-52" aria-hidden="true"></a>            <span class="kw">$buffer</span> = <span class="fu">fread</span><span class="ot">(</span><span class="kw">$local_stream</span><span class="ot">,</span> <span class="dv">8192</span><span class="ot">);</span></span>
<span id="cb3-53"><a href="#cb3-53" aria-hidden="true"></a>            <span class="fu">fwrite</span><span class="ot">(</span><span class="kw">$remote_stream</span><span class="ot">,</span> <span class="kw">$buffer</span><span class="ot">);</span></span>
<span id="cb3-54"><a href="#cb3-54" aria-hidden="true"></a>            <span class="kw">$uploaded</span> += <span class="fu">strlen</span><span class="ot">(</span><span class="kw">$buffer</span><span class="ot">);</span></span>
<span id="cb3-55"><a href="#cb3-55" aria-hidden="true"></a></span>
<span id="cb3-56"><a href="#cb3-56" aria-hidden="true"></a>            <span class="kw">if</span> <span class="ot">(</span><span class="kw">isset</span><span class="ot">(</span><span class="kw">$options</span><span class="ot">[</span><span class="st">&#39;progress_callback&#39;</span><span class="ot">]))</span> {</span>
<span id="cb3-57"><a href="#cb3-57" aria-hidden="true"></a>                <span class="fu">call_user_func</span><span class="ot">(</span><span class="kw">$options</span><span class="ot">[</span><span class="st">&#39;progress_callback&#39;</span><span class="ot">],</span> <span class="kw">$uploaded</span><span class="ot">,</span> <span class="kw">$file_size</span><span class="ot">);</span></span>
<span id="cb3-58"><a href="#cb3-58" aria-hidden="true"></a>            }</span>
<span id="cb3-59"><a href="#cb3-59" aria-hidden="true"></a>        }</span>
<span id="cb3-60"><a href="#cb3-60" aria-hidden="true"></a></span>
<span id="cb3-61"><a href="#cb3-61" aria-hidden="true"></a>        <span class="fu">fclose</span><span class="ot">(</span><span class="kw">$local_stream</span><span class="ot">);</span></span>
<span id="cb3-62"><a href="#cb3-62" aria-hidden="true"></a>        <span class="fu">fclose</span><span class="ot">(</span><span class="kw">$remote_stream</span><span class="ot">);</span></span>
<span id="cb3-63"><a href="#cb3-63" aria-hidden="true"></a></span>
<span id="cb3-64"><a href="#cb3-64" aria-hidden="true"></a>        <span class="kw">return</span> <span class="st">&quot;sftp://</span><span class="kw">{$credentials[&#39;host&#39;]}{$remote_path}</span><span class="st">&quot;</span><span class="ot">;</span></span>
<span id="cb3-65"><a href="#cb3-65" aria-hidden="true"></a>    }</span>
<span id="cb3-66"><a href="#cb3-66" aria-hidden="true"></a></span>
<span id="cb3-67"><a href="#cb3-67" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> download<span class="ot">(</span><span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$local_file</span><span class="ot">)</span> {</span>
<span id="cb3-68"><a href="#cb3-68" aria-hidden="true"></a>        <span class="kw">return</span> <span class="fu">ssh2_scp_recv</span><span class="ot">(</span><span class="kw">$this</span>-&gt;connection<span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$local_file</span><span class="ot">);</span></span>
<span id="cb3-69"><a href="#cb3-69" aria-hidden="true"></a>    }</span>
<span id="cb3-70"><a href="#cb3-70" aria-hidden="true"></a></span>
<span id="cb3-71"><a href="#cb3-71" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> delete<span class="ot">(</span><span class="kw">$remote_path</span><span class="ot">)</span> {</span>
<span id="cb3-72"><a href="#cb3-72" aria-hidden="true"></a>        <span class="kw">return</span> <span class="fu">ssh2_sftp_unlink</span><span class="ot">(</span><span class="kw">$this</span>-&gt;sftp<span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">);</span></span>
<span id="cb3-73"><a href="#cb3-73" aria-hidden="true"></a>    }</span>
<span id="cb3-74"><a href="#cb3-74" aria-hidden="true"></a></span>
<span id="cb3-75"><a href="#cb3-75" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> list_files<span class="ot">(</span><span class="kw">$remote_dir</span><span class="ot">)</span> {</span>
<span id="cb3-76"><a href="#cb3-76" aria-hidden="true"></a>        <span class="kw">$handle</span> = <span class="fu">opendir</span><span class="ot">(</span><span class="st">&quot;ssh2.sftp://</span><span class="kw">{$this-&gt;sftp}{$remote_dir}</span><span class="st">&quot;</span><span class="ot">);</span></span>
<span id="cb3-77"><a href="#cb3-77" aria-hidden="true"></a>        <span class="kw">$files</span> = <span class="ot">[];</span></span>
<span id="cb3-78"><a href="#cb3-78" aria-hidden="true"></a></span>
<span id="cb3-79"><a href="#cb3-79" aria-hidden="true"></a>        <span class="kw">while</span> <span class="ot">(</span><span class="kw">false</span> !== <span class="ot">(</span><span class="kw">$file</span> = <span class="fu">readdir</span><span class="ot">(</span><span class="kw">$handle</span><span class="ot">)))</span> {</span>
<span id="cb3-80"><a href="#cb3-80" aria-hidden="true"></a>            <span class="kw">if</span> <span class="ot">(</span><span class="kw">$file</span> !== <span class="st">&#39;.&#39;</span> &amp;&amp; <span class="kw">$file</span> !== <span class="st">&#39;..&#39;</span><span class="ot">)</span> {</span>
<span id="cb3-81"><a href="#cb3-81" aria-hidden="true"></a>                <span class="kw">$path</span> = <span class="kw">$remote_dir</span> . <span class="st">&#39;/&#39;</span> . <span class="kw">$file</span><span class="ot">;</span></span>
<span id="cb3-82"><a href="#cb3-82" aria-hidden="true"></a>                <span class="kw">$stat</span> = <span class="fu">ssh2_sftp_stat</span><span class="ot">(</span><span class="kw">$this</span>-&gt;sftp<span class="ot">,</span> <span class="kw">$path</span><span class="ot">);</span></span>
<span id="cb3-83"><a href="#cb3-83" aria-hidden="true"></a>                <span class="kw">$files</span><span class="ot">[]</span> = <span class="ot">[</span></span>
<span id="cb3-84"><a href="#cb3-84" aria-hidden="true"></a>                    <span class="st">&#39;path&#39;</span> =&gt; <span class="kw">$path</span><span class="ot">,</span></span>
<span id="cb3-85"><a href="#cb3-85" aria-hidden="true"></a>                    <span class="st">&#39;size&#39;</span> =&gt; <span class="kw">$stat</span><span class="ot">[</span><span class="st">&#39;size&#39;</span><span class="ot">],</span></span>
<span id="cb3-86"><a href="#cb3-86" aria-hidden="true"></a>                    <span class="st">&#39;modified&#39;</span> =&gt; <span class="kw">$stat</span><span class="ot">[</span><span class="st">&#39;mtime&#39;</span><span class="ot">]</span></span>
<span id="cb3-87"><a href="#cb3-87" aria-hidden="true"></a>                <span class="ot">];</span></span>
<span id="cb3-88"><a href="#cb3-88" aria-hidden="true"></a>            }</span>
<span id="cb3-89"><a href="#cb3-89" aria-hidden="true"></a>        }</span>
<span id="cb3-90"><a href="#cb3-90" aria-hidden="true"></a></span>
<span id="cb3-91"><a href="#cb3-91" aria-hidden="true"></a>        <span class="fu">closedir</span><span class="ot">(</span><span class="kw">$handle</span><span class="ot">);</span></span>
<span id="cb3-92"><a href="#cb3-92" aria-hidden="true"></a>        <span class="kw">return</span> <span class="kw">$files</span><span class="ot">;</span></span>
<span id="cb3-93"><a href="#cb3-93" aria-hidden="true"></a>    }</span>
<span id="cb3-94"><a href="#cb3-94" aria-hidden="true"></a></span>
<span id="cb3-95"><a href="#cb3-95" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> get_storage_info<span class="ot">()</span> {</span>
<span id="cb3-96"><a href="#cb3-96" aria-hidden="true"></a>        <span class="co">// Execute df command via SSH</span></span>
<span id="cb3-97"><a href="#cb3-97" aria-hidden="true"></a>        <span class="kw">$stream</span> = <span class="fu">ssh2_exec</span><span class="ot">(</span><span class="kw">$this</span>-&gt;connection<span class="ot">,</span> <span class="st">&#39;df -k .&#39;</span><span class="ot">);</span></span>
<span id="cb3-98"><a href="#cb3-98" aria-hidden="true"></a>        <span class="fu">stream_set_blocking</span><span class="ot">(</span><span class="kw">$stream</span><span class="ot">,</span> <span class="kw">true</span><span class="ot">);</span></span>
<span id="cb3-99"><a href="#cb3-99" aria-hidden="true"></a>        <span class="kw">$output</span> = <span class="fu">stream_get_contents</span><span class="ot">(</span><span class="kw">$stream</span><span class="ot">);</span></span>
<span id="cb3-100"><a href="#cb3-100" aria-hidden="true"></a></span>
<span id="cb3-101"><a href="#cb3-101" aria-hidden="true"></a>        <span class="co">// Parse df output to get storage info</span></span>
<span id="cb3-102"><a href="#cb3-102" aria-hidden="true"></a>        <span class="co">// Implementation depends on server OS</span></span>
<span id="cb3-103"><a href="#cb3-103" aria-hidden="true"></a></span>
<span id="cb3-104"><a href="#cb3-104" aria-hidden="true"></a>        <span class="kw">return</span> <span class="ot">[</span><span class="st">&#39;used&#39;</span> =&gt; <span class="dv">0</span><span class="ot">,</span> <span class="st">&#39;total&#39;</span> =&gt; <span class="dv">0</span><span class="ot">,</span> <span class="st">&#39;available&#39;</span> =&gt; <span class="dv">0</span><span class="ot">];</span></span>
<span id="cb3-105"><a href="#cb3-105" aria-hidden="true"></a>    }</span>
<span id="cb3-106"><a href="#cb3-106" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="registering-custom-providers">Registering Custom Providers</h2>
<p>Make your provider available in Backup Copilot Pro:</p>
<div class="sourceCode" id="cb4">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a>add_filter<span class="ot">(</span><span class="st">&#39;bkpc_cloud_providers&#39;</span><span class="ot">,</span> <span class="st">&#39;register_custom_providers&#39;</span><span class="ot">);</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a><span class="kw">function</span> register_custom_providers<span class="ot">(</span><span class="kw">$providers</span><span class="ot">)</span> {</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a>    <span class="kw">$providers</span><span class="ot">[</span><span class="st">&#39;s3_compatible&#39;</span><span class="ot">]</span> = <span class="ot">[</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a>        <span class="st">&#39;name&#39;</span> =&gt; <span class="st">&#39;S3-Compatible Storage&#39;</span><span class="ot">,</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a>        <span class="st">&#39;class&#39;</span> =&gt; <span class="st">&#39;BKPC_Cloud_Provider_S3_Compatible&#39;</span><span class="ot">,</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a>        <span class="st">&#39;icon&#39;</span> =&gt; <span class="st">&#39;dashicons-cloud&#39;</span><span class="ot">,</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a>        <span class="st">&#39;settings_fields&#39;</span> =&gt; <span class="ot">[</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true"></a>            <span class="st">&#39;endpoint&#39;</span> =&gt; <span class="ot">[</span><span class="st">&#39;type&#39;</span> =&gt; <span class="st">&#39;text&#39;</span><span class="ot">,</span> <span class="st">&#39;label&#39;</span> =&gt; <span class="st">&#39;Endpoint URL&#39;</span><span class="ot">],</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true"></a>            <span class="st">&#39;bucket&#39;</span> =&gt; <span class="ot">[</span><span class="st">&#39;type&#39;</span> =&gt; <span class="st">&#39;text&#39;</span><span class="ot">,</span> <span class="st">&#39;label&#39;</span> =&gt; <span class="st">&#39;Bucket Name&#39;</span><span class="ot">],</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true"></a>            <span class="st">&#39;access_key&#39;</span> =&gt; <span class="ot">[</span><span class="st">&#39;type&#39;</span> =&gt; <span class="st">&#39;text&#39;</span><span class="ot">,</span> <span class="st">&#39;label&#39;</span> =&gt; <span class="st">&#39;Access Key&#39;</span><span class="ot">],</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true"></a>            <span class="st">&#39;secret_key&#39;</span> =&gt; <span class="ot">[</span><span class="st">&#39;type&#39;</span> =&gt; <span class="st">&#39;password&#39;</span><span class="ot">,</span> <span class="st">&#39;label&#39;</span> =&gt; <span class="st">&#39;Secret Key&#39;</span><span class="ot">]</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true"></a>        <span class="ot">]</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true"></a>    <span class="ot">];</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true"></a></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true"></a>    <span class="kw">$providers</span><span class="ot">[</span><span class="st">&#39;sftp&#39;</span><span class="ot">]</span> = <span class="ot">[</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true"></a>        <span class="st">&#39;name&#39;</span> =&gt; <span class="st">&#39;SFTP&#39;</span><span class="ot">,</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true"></a>        <span class="st">&#39;class&#39;</span> =&gt; <span class="st">&#39;BKPC_Cloud_Provider_SFTP&#39;</span><span class="ot">,</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true"></a>        <span class="st">&#39;icon&#39;</span> =&gt; <span class="st">&#39;dashicons-admin-site&#39;</span><span class="ot">,</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true"></a>        <span class="st">&#39;settings_fields&#39;</span> =&gt; <span class="ot">[</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true"></a>            <span class="st">&#39;host&#39;</span> =&gt; <span class="ot">[</span><span class="st">&#39;type&#39;</span> =&gt; <span class="st">&#39;text&#39;</span><span class="ot">,</span> <span class="st">&#39;label&#39;</span> =&gt; <span class="st">&#39;Host&#39;</span><span class="ot">],</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true"></a>            <span class="st">&#39;port&#39;</span> =&gt; <span class="ot">[</span><span class="st">&#39;type&#39;</span> =&gt; <span class="st">&#39;number&#39;</span><span class="ot">,</span> <span class="st">&#39;label&#39;</span> =&gt; <span class="st">&#39;Port&#39;</span><span class="ot">,</span> <span class="st">&#39;default&#39;</span> =&gt; <span class="dv">22</span><span class="ot">],</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true"></a>            <span class="st">&#39;username&#39;</span> =&gt; <span class="ot">[</span><span class="st">&#39;type&#39;</span> =&gt; <span class="st">&#39;text&#39;</span><span class="ot">,</span> <span class="st">&#39;label&#39;</span> =&gt; <span class="st">&#39;Username&#39;</span><span class="ot">],</span></span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true"></a>            <span class="st">&#39;password&#39;</span> =&gt; <span class="ot">[</span><span class="st">&#39;type&#39;</span> =&gt; <span class="st">&#39;password&#39;</span><span class="ot">,</span> <span class="st">&#39;label&#39;</span> =&gt; <span class="st">&#39;Password&#39;</span><span class="ot">]</span></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true"></a>        <span class="ot">]</span></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true"></a>    <span class="ot">];</span></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true"></a></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$providers</span><span class="ot">;</span></span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="implementing-oauth-2.0-authentication">Implementing OAuth 2.0 Authentication</h2>
<p>For providers requiring OAuth (Google Drive, Dropbox):</p>
<div class="sourceCode" id="cb5">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="kw">public</span> <span class="kw">function</span> authenticate<span class="ot">(</span><span class="kw">$credentials</span><span class="ot">)</span> {</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a>    <span class="co">// Step 1: Redirect to OAuth consent screen</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>!<span class="kw">isset</span><span class="ot">(</span><span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;code&#39;</span><span class="ot">]))</span> {</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a>        <span class="kw">$auth_url</span> = <span class="st">&#39;https://provider.com/oauth/authorize?&#39;</span> . <span class="fu">http_build_query</span><span class="ot">([</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a>            <span class="st">&#39;client_id&#39;</span> =&gt; <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;client_id&#39;</span><span class="ot">],</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a>            <span class="st">&#39;redirect_uri&#39;</span> =&gt; admin_url<span class="ot">(</span><span class="st">&#39;admin.php?page=bkpc-oauth-callback&#39;</span><span class="ot">),</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a>            <span class="st">&#39;response_type&#39;</span> =&gt; <span class="st">&#39;code&#39;</span><span class="ot">,</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a>            <span class="st">&#39;scope&#39;</span> =&gt; <span class="st">&#39;file.read file.write&#39;</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a>        <span class="ot">]);</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a>        wp_redirect<span class="ot">(</span><span class="kw">$auth_url</span><span class="ot">);</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a>        <span class="kw">exit</span><span class="ot">;</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true"></a>    }</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true"></a></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true"></a>    <span class="co">// Step 2: Exchange code for access token</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true"></a>    <span class="kw">$response</span> = wp_remote_post<span class="ot">(</span><span class="st">&#39;https://provider.com/oauth/token&#39;</span><span class="ot">,</span> <span class="ot">[</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true"></a>        <span class="st">&#39;body&#39;</span> =&gt; <span class="ot">[</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true"></a>            <span class="st">&#39;code&#39;</span> =&gt; <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;code&#39;</span><span class="ot">],</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true"></a>            <span class="st">&#39;client_id&#39;</span> =&gt; <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;client_id&#39;</span><span class="ot">],</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true"></a>            <span class="st">&#39;client_secret&#39;</span> =&gt; <span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;client_secret&#39;</span><span class="ot">],</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true"></a>            <span class="st">&#39;redirect_uri&#39;</span> =&gt; admin_url<span class="ot">(</span><span class="st">&#39;admin.php?page=bkpc-oauth-callback&#39;</span><span class="ot">),</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true"></a>            <span class="st">&#39;grant_type&#39;</span> =&gt; <span class="st">&#39;authorization_code&#39;</span></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true"></a>        <span class="ot">]</span></span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true"></a>    <span class="ot">]);</span></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true"></a></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true"></a>    <span class="kw">$body</span> = <span class="fu">json_decode</span><span class="ot">(</span>wp_remote_retrieve_body<span class="ot">(</span><span class="kw">$response</span><span class="ot">),</span> <span class="kw">true</span><span class="ot">);</span></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true"></a></span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true"></a>    <span class="co">// Store tokens securely</span></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true"></a>    update_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_access_token&#39;</span><span class="ot">,</span> <span class="kw">$this</span>-&gt;encrypt<span class="ot">(</span><span class="kw">$body</span><span class="ot">[</span><span class="st">&#39;access_token&#39;</span><span class="ot">]));</span></span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true"></a>    update_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_refresh_token&#39;</span><span class="ot">,</span> <span class="kw">$this</span>-&gt;encrypt<span class="ot">(</span><span class="kw">$body</span><span class="ot">[</span><span class="st">&#39;refresh_token&#39;</span><span class="ot">]));</span></span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true"></a>    update_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_token_expires&#39;</span><span class="ot">,</span> <span class="fu">time</span><span class="ot">()</span> + <span class="kw">$body</span><span class="ot">[</span><span class="st">&#39;expires_in&#39;</span><span class="ot">]);</span></span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true"></a></span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">true</span><span class="ot">;</span></span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true"></a>}</span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true"></a></span>
<span id="cb5-35"><a href="#cb5-35" aria-hidden="true"></a><span class="kw">private</span> <span class="kw">function</span> get_access_token<span class="ot">()</span> {</span>
<span id="cb5-36"><a href="#cb5-36" aria-hidden="true"></a>    <span class="kw">$expires</span> = get_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_token_expires&#39;</span><span class="ot">);</span></span>
<span id="cb5-37"><a href="#cb5-37" aria-hidden="true"></a></span>
<span id="cb5-38"><a href="#cb5-38" aria-hidden="true"></a>    <span class="co">// Refresh token if expired</span></span>
<span id="cb5-39"><a href="#cb5-39" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span><span class="fu">time</span><span class="ot">()</span> &gt;= <span class="kw">$expires</span><span class="ot">)</span> {</span>
<span id="cb5-40"><a href="#cb5-40" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;refresh_access_token<span class="ot">();</span></span>
<span id="cb5-41"><a href="#cb5-41" aria-hidden="true"></a>    }</span>
<span id="cb5-42"><a href="#cb5-42" aria-hidden="true"></a></span>
<span id="cb5-43"><a href="#cb5-43" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$this</span>-&gt;decrypt<span class="ot">(</span>get_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_access_token&#39;</span><span class="ot">));</span></span>
<span id="cb5-44"><a href="#cb5-44" aria-hidden="true"></a>}</span>
<span id="cb5-45"><a href="#cb5-45" aria-hidden="true"></a></span>
<span id="cb5-46"><a href="#cb5-46" aria-hidden="true"></a><span class="kw">private</span> <span class="kw">function</span> refresh_access_token<span class="ot">()</span> {</span>
<span id="cb5-47"><a href="#cb5-47" aria-hidden="true"></a>    <span class="kw">$refresh_token</span> = <span class="kw">$this</span>-&gt;decrypt<span class="ot">(</span>get_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_refresh_token&#39;</span><span class="ot">));</span></span>
<span id="cb5-48"><a href="#cb5-48" aria-hidden="true"></a></span>
<span id="cb5-49"><a href="#cb5-49" aria-hidden="true"></a>    <span class="kw">$response</span> = wp_remote_post<span class="ot">(</span><span class="st">&#39;https://provider.com/oauth/token&#39;</span><span class="ot">,</span> <span class="ot">[</span></span>
<span id="cb5-50"><a href="#cb5-50" aria-hidden="true"></a>        <span class="st">&#39;body&#39;</span> =&gt; <span class="ot">[</span></span>
<span id="cb5-51"><a href="#cb5-51" aria-hidden="true"></a>            <span class="st">&#39;refresh_token&#39;</span> =&gt; <span class="kw">$refresh_token</span><span class="ot">,</span></span>
<span id="cb5-52"><a href="#cb5-52" aria-hidden="true"></a>            <span class="st">&#39;client_id&#39;</span> =&gt; get_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_client_id&#39;</span><span class="ot">),</span></span>
<span id="cb5-53"><a href="#cb5-53" aria-hidden="true"></a>            <span class="st">&#39;client_secret&#39;</span> =&gt; <span class="kw">$this</span>-&gt;decrypt<span class="ot">(</span>get_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_client_secret&#39;</span><span class="ot">)),</span></span>
<span id="cb5-54"><a href="#cb5-54" aria-hidden="true"></a>            <span class="st">&#39;grant_type&#39;</span> =&gt; <span class="st">&#39;refresh_token&#39;</span></span>
<span id="cb5-55"><a href="#cb5-55" aria-hidden="true"></a>        <span class="ot">]</span></span>
<span id="cb5-56"><a href="#cb5-56" aria-hidden="true"></a>    <span class="ot">]);</span></span>
<span id="cb5-57"><a href="#cb5-57" aria-hidden="true"></a></span>
<span id="cb5-58"><a href="#cb5-58" aria-hidden="true"></a>    <span class="kw">$body</span> = <span class="fu">json_decode</span><span class="ot">(</span>wp_remote_retrieve_body<span class="ot">(</span><span class="kw">$response</span><span class="ot">),</span> <span class="kw">true</span><span class="ot">);</span></span>
<span id="cb5-59"><a href="#cb5-59" aria-hidden="true"></a>    update_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_access_token&#39;</span><span class="ot">,</span> <span class="kw">$this</span>-&gt;encrypt<span class="ot">(</span><span class="kw">$body</span><span class="ot">[</span><span class="st">&#39;access_token&#39;</span><span class="ot">]));</span></span>
<span id="cb5-60"><a href="#cb5-60" aria-hidden="true"></a>    update_option<span class="ot">(</span><span class="st">&#39;bkpc_provider_token_expires&#39;</span><span class="ot">,</span> <span class="fu">time</span><span class="ot">()</span> + <span class="kw">$body</span><span class="ot">[</span><span class="st">&#39;expires_in&#39;</span><span class="ot">]);</span></span>
<span id="cb5-61"><a href="#cb5-61" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="error-handling-and-retry-logic">Error Handling and Retry Logic</h2>
<p>Robust providers handle transient failures:</p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="kw">public</span> <span class="kw">function</span> upload_with_retry<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$options</span> = <span class="ot">[])</span> {</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>    <span class="kw">$max_retries</span> = <span class="dv">3</span><span class="ot">;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>    <span class="kw">$retry_delay</span> = <span class="dv">5</span><span class="ot">;</span> <span class="co">// seconds</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a>    <span class="kw">for</span> <span class="ot">(</span><span class="kw">$attempt</span> = <span class="dv">1</span><span class="ot">;</span> <span class="kw">$attempt</span> &lt;= <span class="kw">$max_retries</span><span class="ot">;</span> <span class="kw">$attempt</span>++<span class="ot">)</span> {</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">$this</span>-&gt;upload<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$options</span><span class="ot">);</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a>            <span class="kw">if</span> <span class="ot">(</span><span class="kw">$attempt</span> === <span class="kw">$max_retries</span><span class="ot">)</span> {</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a>                <span class="kw">throw</span> <span class="kw">$e</span><span class="ot">;</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true"></a>            }</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true"></a></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true"></a>            <span class="co">// Log retry attempt</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true"></a>            <span class="fu">error_log</span><span class="ot">(</span><span class="st">&quot;Upload attempt </span><span class="kw">{$attempt}</span><span class="st"> failed: {</span><span class="kw">$e</span><span class="st">-&gt;getMessage()}. Retrying in </span><span class="kw">{$retry_delay}</span><span class="st">s&quot;</span><span class="ot">);</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true"></a></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true"></a>            <span class="fu">sleep</span><span class="ot">(</span><span class="kw">$retry_delay</span><span class="ot">);</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true"></a>            <span class="kw">$retry_delay</span> *= <span class="dv">2</span><span class="ot">;</span> <span class="co">// Exponential backoff</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true"></a>        }</span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true"></a>    }</span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="security-best-practices">Security Best Practices</h2>
<p>Protect credentials and user data:</p>
<p><strong>Encrypt Sensitive Data</strong>:</p>
<div class="sourceCode" id="cb7">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="kw">private</span> <span class="kw">function</span> encrypt<span class="ot">(</span><span class="kw">$value</span><span class="ot">)</span> {</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>!<span class="fu">defined</span><span class="ot">(</span><span class="st">&#39;BKPC_ENCRYPTION_KEY&#39;</span><span class="ot">))</span> {</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a>        <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;Encryption key not defined&#39;</span><span class="ot">);</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a>    }</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a>    <span class="kw">return</span> <span class="fu">openssl_encrypt</span><span class="ot">(</span><span class="kw">$value</span><span class="ot">,</span> <span class="st">&#39;AES-256-CBC&#39;</span><span class="ot">,</span> <span class="kw">BKPC_ENCRYPTION_KEY</span><span class="ot">,</span> <span class="dv">0</span><span class="ot">,</span> <span class="fu">substr</span><span class="ot">(</span><span class="kw">BKPC_ENCRYPTION_KEY</span><span class="ot">,</span> <span class="dv">0</span><span class="ot">,</span> <span class="dv">16</span><span class="ot">));</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a>}</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a><span class="kw">private</span> <span class="kw">function</span> decrypt<span class="ot">(</span><span class="kw">$value</span><span class="ot">)</span> {</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>!<span class="fu">defined</span><span class="ot">(</span><span class="st">&#39;BKPC_ENCRYPTION_KEY&#39;</span><span class="ot">))</span> {</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a>        <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;Encryption key not defined&#39;</span><span class="ot">);</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a>    }</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a>    <span class="kw">return</span> <span class="fu">openssl_decrypt</span><span class="ot">(</span><span class="kw">$value</span><span class="ot">,</span> <span class="st">&#39;AES-256-CBC&#39;</span><span class="ot">,</span> <span class="kw">BKPC_ENCRYPTION_KEY</span><span class="ot">,</span> <span class="dv">0</span><span class="ot">,</span> <span class="fu">substr</span><span class="ot">(</span><span class="kw">BKPC_ENCRYPTION_KEY</span><span class="ot">,</span> <span class="dv">0</span><span class="ot">,</span> <span class="dv">16</span><span class="ot">));</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a>}</span></code></pre>
</div>
<p><strong>Validate Inputs</strong>:</p>
<div class="sourceCode" id="cb8">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="kw">public</span> <span class="kw">function</span> authenticate<span class="ot">(</span><span class="kw">$credentials</span><span class="ot">)</span> {</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>    <span class="kw">$required</span> = <span class="ot">[</span><span class="st">&#39;endpoint&#39;</span><span class="ot">,</span> <span class="st">&#39;bucket&#39;</span><span class="ot">,</span> <span class="st">&#39;access_key&#39;</span><span class="ot">,</span> <span class="st">&#39;secret_key&#39;</span><span class="ot">];</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a>    <span class="kw">foreach</span> <span class="ot">(</span><span class="kw">$required</span> <span class="kw">as</span> <span class="kw">$field</span><span class="ot">)</span> {</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>        <span class="kw">if</span> <span class="ot">(</span><span class="kw">empty</span><span class="ot">(</span><span class="kw">$credentials</span><span class="ot">[</span><span class="kw">$field</span><span class="ot">]))</span> {</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>            <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&quot;Missing required field: </span><span class="kw">{$field}</span><span class="st">&quot;</span><span class="ot">);</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a>        }</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a>    }</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a>    <span class="co">// Validate endpoint is proper URL</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>!<span class="fu">filter_var</span><span class="ot">(</span><span class="kw">$credentials</span><span class="ot">[</span><span class="st">&#39;endpoint&#39;</span><span class="ot">],</span> <span class="kw">FILTER_VALIDATE_URL</span><span class="ot">))</span> {</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a>        <span class="kw">throw</span> <span class="kw">new</span> <span class="kw">Exception</span><span class="ot">(</span><span class="st">&#39;Invalid endpoint URL&#39;</span><span class="ot">);</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a>    }</span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true"></a></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true"></a>    <span class="co">// Continue authentication...</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="testing-custom-providers">Testing Custom Providers</h2>
<p>Thorough testing ensures reliability:</p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="kw">class</span> BKPC_Cloud_Provider_Tests {</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">static</span> <span class="kw">function</span> test_provider<span class="ot">(</span><span class="kw">$provider_class</span><span class="ot">,</span> <span class="kw">$credentials</span><span class="ot">)</span> {</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a>        <span class="kw">$provider</span> = <span class="kw">new</span> <span class="kw">$provider_class</span><span class="ot">();</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a>        <span class="kw">$results</span> = <span class="ot">[];</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a>        <span class="co">// Test authentication</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a>            <span class="kw">$provider</span>-&gt;authenticate<span class="ot">(</span><span class="kw">$credentials</span><span class="ot">);</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;auth&#39;</span><span class="ot">]</span> = <span class="st">&#39;PASS&#39;</span><span class="ot">;</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;auth&#39;</span><span class="ot">]</span> = <span class="st">&#39;FAIL: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">();</span></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">$results</span><span class="ot">;</span> <span class="co">// Can&#39;t continue without auth</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true"></a>        }</span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true"></a></span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true"></a>        <span class="co">// Test upload</span></span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true"></a>        <span class="kw">$test_file</span> = <span class="fu">tempnam</span><span class="ot">(</span><span class="fu">sys_get_temp_dir</span><span class="ot">(),</span> <span class="st">&#39;bkpc_test_&#39;</span><span class="ot">);</span></span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true"></a>        <span class="fu">file_put_contents</span><span class="ot">(</span><span class="kw">$test_file</span><span class="ot">,</span> <span class="st">&#39;Test backup content&#39;</span><span class="ot">);</span></span>
<span id="cb9-19"><a href="#cb9-19" aria-hidden="true"></a></span>
<span id="cb9-20"><a href="#cb9-20" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb9-21"><a href="#cb9-21" aria-hidden="true"></a>            <span class="kw">$provider</span>-&gt;upload<span class="ot">(</span><span class="kw">$test_file</span><span class="ot">,</span> <span class="st">&#39;/test-backup.txt&#39;</span><span class="ot">);</span></span>
<span id="cb9-22"><a href="#cb9-22" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;upload&#39;</span><span class="ot">]</span> = <span class="st">&#39;PASS&#39;</span><span class="ot">;</span></span>
<span id="cb9-23"><a href="#cb9-23" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb9-24"><a href="#cb9-24" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;upload&#39;</span><span class="ot">]</span> = <span class="st">&#39;FAIL: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">();</span></span>
<span id="cb9-25"><a href="#cb9-25" aria-hidden="true"></a>        }</span>
<span id="cb9-26"><a href="#cb9-26" aria-hidden="true"></a></span>
<span id="cb9-27"><a href="#cb9-27" aria-hidden="true"></a>        <span class="co">// Test list files</span></span>
<span id="cb9-28"><a href="#cb9-28" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb9-29"><a href="#cb9-29" aria-hidden="true"></a>            <span class="kw">$files</span> = <span class="kw">$provider</span>-&gt;list_files<span class="ot">(</span><span class="st">&#39;/&#39;</span><span class="ot">);</span></span>
<span id="cb9-30"><a href="#cb9-30" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;list&#39;</span><span class="ot">]</span> = <span class="fu">count</span><span class="ot">(</span><span class="kw">$files</span><span class="ot">)</span> &gt; <span class="dv">0</span> <span class="ot">?</span> <span class="st">&#39;PASS&#39;</span> <span class="ot">:</span> <span class="st">&#39;FAIL: No files found&#39;</span><span class="ot">;</span></span>
<span id="cb9-31"><a href="#cb9-31" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb9-32"><a href="#cb9-32" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;list&#39;</span><span class="ot">]</span> = <span class="st">&#39;FAIL: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">();</span></span>
<span id="cb9-33"><a href="#cb9-33" aria-hidden="true"></a>        }</span>
<span id="cb9-34"><a href="#cb9-34" aria-hidden="true"></a></span>
<span id="cb9-35"><a href="#cb9-35" aria-hidden="true"></a>        <span class="co">// Test download</span></span>
<span id="cb9-36"><a href="#cb9-36" aria-hidden="true"></a>        <span class="kw">$download_file</span> = <span class="fu">tempnam</span><span class="ot">(</span><span class="fu">sys_get_temp_dir</span><span class="ot">(),</span> <span class="st">&#39;bkpc_download_&#39;</span><span class="ot">);</span></span>
<span id="cb9-37"><a href="#cb9-37" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb9-38"><a href="#cb9-38" aria-hidden="true"></a>            <span class="kw">$provider</span>-&gt;download<span class="ot">(</span><span class="st">&#39;/test-backup.txt&#39;</span><span class="ot">,</span> <span class="kw">$download_file</span><span class="ot">);</span></span>
<span id="cb9-39"><a href="#cb9-39" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;download&#39;</span><span class="ot">]</span> = <span class="fu">file_get_contents</span><span class="ot">(</span><span class="kw">$download_file</span><span class="ot">)</span> === <span class="st">&#39;Test backup content&#39;</span> <span class="ot">?</span> <span class="st">&#39;PASS&#39;</span> <span class="ot">:</span> <span class="st">&#39;FAIL: Content mismatch&#39;</span><span class="ot">;</span></span>
<span id="cb9-40"><a href="#cb9-40" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb9-41"><a href="#cb9-41" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;download&#39;</span><span class="ot">]</span> = <span class="st">&#39;FAIL: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">();</span></span>
<span id="cb9-42"><a href="#cb9-42" aria-hidden="true"></a>        }</span>
<span id="cb9-43"><a href="#cb9-43" aria-hidden="true"></a></span>
<span id="cb9-44"><a href="#cb9-44" aria-hidden="true"></a>        <span class="co">// Test delete</span></span>
<span id="cb9-45"><a href="#cb9-45" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb9-46"><a href="#cb9-46" aria-hidden="true"></a>            <span class="kw">$provider</span>-&gt;delete<span class="ot">(</span><span class="st">&#39;/test-backup.txt&#39;</span><span class="ot">);</span></span>
<span id="cb9-47"><a href="#cb9-47" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;delete&#39;</span><span class="ot">]</span> = <span class="st">&#39;PASS&#39;</span><span class="ot">;</span></span>
<span id="cb9-48"><a href="#cb9-48" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span><span class="kw">Exception</span> <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb9-49"><a href="#cb9-49" aria-hidden="true"></a>            <span class="kw">$results</span><span class="ot">[</span><span class="st">&#39;delete&#39;</span><span class="ot">]</span> = <span class="st">&#39;FAIL: &#39;</span> . <span class="kw">$e</span>-&gt;getMessage<span class="ot">();</span></span>
<span id="cb9-50"><a href="#cb9-50" aria-hidden="true"></a>        }</span>
<span id="cb9-51"><a href="#cb9-51" aria-hidden="true"></a></span>
<span id="cb9-52"><a href="#cb9-52" aria-hidden="true"></a>        <span class="co">// Cleanup</span></span>
<span id="cb9-53"><a href="#cb9-53" aria-hidden="true"></a>        <span class="fu">unlink</span><span class="ot">(</span><span class="kw">$test_file</span><span class="ot">);</span></span>
<span id="cb9-54"><a href="#cb9-54" aria-hidden="true"></a>        <span class="fu">unlink</span><span class="ot">(</span><span class="kw">$download_file</span><span class="ot">);</span></span>
<span id="cb9-55"><a href="#cb9-55" aria-hidden="true"></a></span>
<span id="cb9-56"><a href="#cb9-56" aria-hidden="true"></a>        <span class="kw">return</span> <span class="kw">$results</span><span class="ot">;</span></span>
<span id="cb9-57"><a href="#cb9-57" aria-hidden="true"></a>    }</span>
<span id="cb9-58"><a href="#cb9-58" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="performance-optimization">Performance Optimization</h2>
<p>Optimize for large backups:</p>
<p><strong>Chunked Uploads</strong>: Upload large files in chunks (5-10MB) to handle network interruptions and show progress.</p>
<p><strong>Concurrent Connections</strong>: Use multiple connections for faster transfers when API supports it.</p>
<p><strong>Compression</strong>: Compress before upload to reduce bandwidth:</p>
<div class="sourceCode" id="cb10">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="kw">public</span> <span class="kw">function</span> upload<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$options</span> = <span class="ot">[])</span> {</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a>    <span class="co">// Compress if not already compressed</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>!<span class="fu">preg_match</span><span class="ot">(</span><span class="st">&#39;/\.(zip|gz|bz2)$/i&#39;</span><span class="ot">,</span> <span class="kw">$local_file</span><span class="ot">))</span> {</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>        <span class="kw">$compressed</span> = <span class="kw">$local_file</span> . <span class="st">&#39;.gz&#39;</span><span class="ot">;</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;compress_file<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$compressed</span><span class="ot">);</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a>        <span class="kw">$local_file</span> = <span class="kw">$compressed</span><span class="ot">;</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a>        <span class="kw">$remote_path</span> .= <span class="st">&#39;.gz&#39;</span><span class="ot">;</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a>    }</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a>    <span class="co">// Upload compressed file</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$this</span>-&gt;do_upload<span class="ot">(</span><span class="kw">$local_file</span><span class="ot">,</span> <span class="kw">$remote_path</span><span class="ot">,</span> <span class="kw">$options</span><span class="ot">);</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="publishing-your-provider">Publishing Your Provider</h2>
<p>Share with community:</p>
<ol type="1">
<li><strong>Package as WordPress Plugin</strong>: Create standalone plugin users can install</li>
<li><strong>Documentation</strong>: Provide setup instructions and API credentials guidance</li>
<li><strong>WordPress.org Repository</strong>: Submit to official repository for distribution</li>
<li><strong>GitHub</strong>: Open source on GitHub for community contributions</li>
<li><strong>Support</strong>: Provide support channels for users</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>Custom cloud storage providers extend Backup Copilot Pro to any storage backend. Whether integrating S3-compatible services, legacy FTP/SFTP systems, or proprietary storage APIs, the provider architecture enables consistent functionality while accommodating provider-specific implementations.</p>
<p>Key implementation requirements: authentication, upload/download with chunking and progress tracking, error handling with retries, security through credential encryption, and thorough testing. Follow these patterns and your custom provider will integrate seamlessly with Backup Copilot Pro’s backup workflows.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://docs.aws.amazon.com/s3/">AWS S3 API Documentation</a></li>
<li><a href="https://www.backblaze.com/b2/docs/s3_compatible_api.html">S3-Compatible Storage Guide</a></li>
<li><a href="https://oauth.net/2/">OAuth 2.0 Specification</a></li>
<li><a href="https://www.php.net/manual/en/book.ftp.php">PHP FTP Functions</a></li>
<li><a href="https://developer.wordpress.org/plugins/">WordPress Plugin Development</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Need a custom storage solution? <a href="https://backupcopilot.com/contact">Contact our team</a> for custom development services or <a href="https://backupcopilotplugin.com/#pricing">get Pro</a> to build your own provider using our SDK!</p>
<p>The post <a href="https://backupcopilotplugin.com/blog/building-custom-cloud-storage-providers-for-backup-copilot-pro/">Building Custom Cloud Storage Providers for Backup Copilot Pro</a> appeared first on <a href="https://backupcopilotplugin.com">Backup Copilot</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
