PluginProbe
Media Cloud Sync / 1.4.2
Media Cloud Sync v1.4.2
1.4.2 1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 All 36 releases
media-cloud-sync / includes / base / stream-wrappers / stream-wrapper.php

stream-wrapper.php in Media Cloud Sync 1.4.2, at includes/base/stream-wrappers/stream-wrapper.php

104 lines 4.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Dudlewebs\WPMCS;
3
4 defined('ABSPATH') || exit;
5
6 /**
7 * Last-resort, read-only fallback for wpmcs_get_attached_file — every
8 * specific integration (Compatibility, EWWW, Meta Slider, all priority 10)
9 * runs first and wins if it matches; this only engages when nothing else
10 * has already resolved a real local path.
11 *
12 * @since 1.4.3
13 */
14 class StreamWrapper {
15 private static $instance = null;
16 const PROTOCOL = 'wpmcssw';
17 private $registered = false;
18 private $bucket_name = null;
19 private $failed_bucket = null;
20
21 public function __construct() {
22 add_filter('wpmcs_get_attached_file', [$this, 'stream_wrapper_fallback'], 999, 4);
23 }
24
25 public function stream_wrapper_fallback($url, $file, $attachment_id, $item) {
26 // Skip the filtered wp_get_upload_dir() call when $url is clearly still a cloud URL, not a local path.
27 $looks_like_url = stripos((string) $url, 'http') === 0;
28 if (!$looks_like_url) {
29 $upload_basedir = trailingslashit(wp_get_upload_dir()['basedir']);
30 if (strpos((string) $url, $upload_basedir) === 0 && file_exists($url)) {
31 return $url;
32 }
33 }
34 if (empty($item) || empty($item['key'])) {
35 return $url;
36 }
37 if ($this->register()) {
38 // $url always resolves to the main file's URL regardless of which WP core hook fired; $file doesn't, so use it to detect an original-image request.
39 $key = $item['key'];
40 if (!empty($item['original_source_path']) && !empty($item['original_key'])
41 && basename((string) $file) === basename($item['original_source_path'])) {
42 $key = $item['original_key'];
43 }
44 return self::PROTOCOL . '://' . $this->bucket_name . '/' . $key;
45 }
46 // No disk-restore fallback here on purpose — that's what a dedicated integration is for.
47 return $url;
48 }
49
50 private function register() {
51 $bucket_name = Utils::get_credentials('bucketConfig')['bucket_name'] ?? null;
52 $service = Utils::get_credentials('service');
53 if ($this->registered && $this->bucket_name === $bucket_name) {
54 return true;
55 }
56 // Bucket-keyed failure memoization avoids re-attempting/re-logging on every call of a persistently broken setup.
57 if ($this->failed_bucket === $bucket_name) {
58 return false;
59 }
60 if (empty($bucket_name)) {
61 return false;
62 }
63 $client = Service::instance()->get_active_client();
64 if (empty($client)) {
65 $this->failed_bucket = $bucket_name;
66 return false;
67 }
68
69 try {
70 // Unregister explicitly first — the two vendor register() methods aren't consistently idempotent.
71 if (in_array(self::PROTOCOL, stream_get_wrappers(), true)) {
72 stream_wrapper_unregister(self::PROTOCOL);
73 }
74 // Branches on the credential value, not instanceof, to avoid force-loading the GCS SDK on non-GCS sites.
75 if ($service === 'gcloud') {
76 ReadOnlyGcsStreamWrapper::register($client, self::PROTOCOL);
77 } else {
78 ReadOnlyS3StreamWrapper::register($client, self::PROTOCOL);
79 }
80 } catch (\Throwable $e) {
81 // Must never propagate — this runs inside a filter WP core fires broadly.
82 Logger::instance()->add_log('stream_wrapper_register_failed', 0, 'media_library', ['message' => $e->getMessage()]);
83 $this->registered = false;
84 $this->failed_bucket = $bucket_name;
85 return false;
86 }
87
88 // Default context ensures unaware callers get seekable reads (needed by e.g. getimagesize()).
89 stream_context_set_default([self::PROTOCOL => ['seekable' => true]]);
90
91 $this->registered = true;
92 $this->bucket_name = $bucket_name;
93 $this->failed_bucket = null;
94 return true;
95 }
96
97 public static function instance() {
98 if (is_null(self::$instance)) {
99 self::$instance = new self();
100 }
101 return self::$instance;
102 }
103 }
104