PluginProbe
Media Cloud Sync / 1.4.1
Media Cloud Sync v1.4.1
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 1.2.9 All 35 releases
← All changes | includes/sdk/google/google/gax/src/Serializer.php +109 -40 1.2.111.4.1 View file →
@@ -29,15 +29,16 @@
29 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 32 */
33 -namespace Dudlewebs\WPMCS\Google\ApiCore;
33 +namespace Dudlewebs\WPMCS\GCP\Google\ApiCore;
34 34
35 -use Dudlewebs\WPMCS\Google\Protobuf\Any;
36 -use Dudlewebs\WPMCS\Google\Protobuf\Descriptor;
37 -use Dudlewebs\WPMCS\Google\Protobuf\DescriptorPool;
38 -use Dudlewebs\WPMCS\Google\Protobuf\FieldDescriptor;
39 -use Dudlewebs\WPMCS\Google\Protobuf\Internal\Message;
35 +use Exception;
36 +use Dudlewebs\WPMCS\GCP\Google\Protobuf\Any;
37 +use Dudlewebs\WPMCS\GCP\Google\Protobuf\Descriptor;
38 +use Dudlewebs\WPMCS\GCP\Google\Protobuf\DescriptorPool;
39 +use Dudlewebs\WPMCS\GCP\Google\Protobuf\FieldDescriptor;
40 +use Dudlewebs\WPMCS\GCP\Google\Protobuf\Internal\Message;
40 41 use RuntimeException;
41 42 /**
42 43 * Collection of methods to help with serialization of protobuf objects
43 44 */
@@ -45,13 +46,22 @@
45 46 {
46 47 const MAP_KEY_FIELD_NAME = 'key';
47 48 const MAP_VALUE_FIELD_NAME = 'value';
48 49 private static $phpArraySerializer;
49 - private static $metadataKnownTypes = ['google.rpc.retryinfo-bin' => \Dudlewebs\WPMCS\Google\Rpc\RetryInfo::class, 'google.rpc.debuginfo-bin' => \Dudlewebs\WPMCS\Google\Rpc\DebugInfo::class, 'google.rpc.quotafailure-bin' => \Dudlewebs\WPMCS\Google\Rpc\QuotaFailure::class, 'google.rpc.badrequest-bin' => \Dudlewebs\WPMCS\Google\Rpc\BadRequest::class, 'google.rpc.requestinfo-bin' => \Dudlewebs\WPMCS\Google\Rpc\RequestInfo::class, 'google.rpc.resourceinfo-bin' => \Dudlewebs\WPMCS\Google\Rpc\ResourceInfo::class, 'google.rpc.errorinfo-bin' => \Dudlewebs\WPMCS\Google\Rpc\ErrorInfo::class, 'google.rpc.help-bin' => \Dudlewebs\WPMCS\Google\Rpc\Help::class, 'google.rpc.localizedmessage-bin' => \Dudlewebs\WPMCS\Google\Rpc\LocalizedMessage::class];
50 + // Caches for different helper functions
51 + private static array $getterMap = [];
52 + private static array $setterMap = [];
53 + private static array $snakeCaseMap = [];
54 + private static array $camelCaseMap = [];
50 55 private $fieldTransformers;
51 56 private $messageTypeTransformers;
52 57 private $decodeFieldTransformers;
53 58 private $decodeMessageTypeTransformers;
59 + // Array of key-value pairs which specify a custom encoding function.
60 + // The key is the proto class and the value is the function
61 + // which will be used to convert the proto instead of the
62 + // encodeMessage method from the Serializer class.
63 + private $customEncoders;
54 64 private $descriptorMaps = [];
55 65 /**
56 66 * Serializer constructor.
57 67 *
@@ -59,14 +69,15 @@
59 69 * @param array $messageTypeTransformers An array mapping message names to transformation functions
60 70 * @param array $decodeFieldTransformers An array mapping field names to transformation functions
61 71 * @param array $decodeMessageTypeTransformers An array mapping message names to transformation functions
62 72 */
63 - public function __construct($fieldTransformers = [], $messageTypeTransformers = [], $decodeFieldTransformers = [], $decodeMessageTypeTransformers = [])
73 + public function __construct($fieldTransformers = [], $messageTypeTransformers = [], $decodeFieldTransformers = [], $decodeMessageTypeTransformers = [], $customEncoders = [])
64 74 {
65 75 $this->fieldTransformers = $fieldTransformers;
66 76 $this->messageTypeTransformers = $messageTypeTransformers;
67 77 $this->decodeFieldTransformers = $decodeFieldTransformers;
68 78 $this->decodeMessageTypeTransformers = $decodeMessageTypeTransformers;
79 + $this->customEncoders = $customEncoders;
69 80 }
70 81 /**
71 82 * Encode protobuf message as a PHP array
72 83 *
@@ -75,15 +86,22 @@
75 86 * @throws ValidationException
76 87 */
77 88 public function encodeMessage($message)
78 89 {
90 + $cls = \get_class($message);
91 + // If we have supplied a customEncoder for this class type,
92 + // then we use that instead of the general encodeMessage definition.
93 + if (\array_key_exists($cls, $this->customEncoders)) {
94 + $func = $this->customEncoders[$cls];
95 + return \call_user_func($func, $message);
96 + }
79 97 // Get message descriptor
80 98 $pool = DescriptorPool::getGeneratedPool();
81 - $messageType = $pool->getDescriptorByClassName(get_class($message));
99 + $messageType = $pool->getDescriptorByClassName(\get_class($message));
82 100 try {
83 101 return $this->encodeMessageImpl($message, $messageType);
84 102 } catch (\Exception $e) {
85 - throw new ValidationException("Error encoding message: " . $e->getMessage(), $e->getCode(), $e);
103 + throw new ValidationException('Error encoding message: ' . $e->getMessage(), $e->getCode(), $e);
86 104 }
87 105 }
88 106 /**
89 107 * Decode PHP array into the specified protobuf message
@@ -96,13 +114,13 @@
96 114 public function decodeMessage($message, array $data)
97 115 {
98 116 // Get message descriptor
99 117 $pool = DescriptorPool::getGeneratedPool();
100 - $messageType = $pool->getDescriptorByClassName(get_class($message));
118 + $messageType = $pool->getDescriptorByClassName(\get_class($message));
101 119 try {
102 120 return $this->decodeMessageImpl($message, $messageType, $data);
103 121 } catch (\Exception $e) {
104 - throw new ValidationException("Error decoding message: " . $e->getMessage(), $e->getCode(), $e);
122 + throw new ValidationException('Error decoding message: ' . $e->getMessage(), $e->getCode(), $e);
105 123 }
106 124 }
107 125 /**
108 126 * @param Message $message
@@ -110,9 +128,9 @@
110 128 * @throws ValidationException
111 129 */
112 130 public static function serializeToJson(Message $message)
113 131 {
114 - return json_encode(self::serializeToPhpArray($message), \JSON_PRETTY_PRINT);
132 + return \json_encode(self::serializeToPhpArray($message), \JSON_PRETTY_PRINT);
115 133 }
116 134 /**
117 135 * @param Message $message
118 136 * @return array PHP array representation of $message
@@ -125,37 +143,69 @@
125 143 /**
126 144 * Decode metadata received from gRPC status object
127 145 *
128 146 * @param array $metadata
147 + * @param null|array $errors
129 148 * @return array
130 149 */
131 - public static function decodeMetadata(array $metadata)
150 + public static function decodeMetadata(array $metadata, ?array &$errors = null)
132 151 {
133 - if (count($metadata) == 0) {
152 + if (\count($metadata) == 0) {
134 153 return [];
135 154 }
155 + // ensure known types are available from the descriptor pool
156 + KnownTypes::addKnownTypesToDescriptorPool();
136 157 $result = [];
158 + // If metadata contains a "status" bin, use that instead
159 + if (isset($metadata['grpc-status-details-bin'])) {
160 + $status = new \Dudlewebs\WPMCS\GCP\Google\Rpc\Status();
161 + $status->mergeFromString($metadata['grpc-status-details-bin'][0]);
162 + foreach ($status->getDetails() as $any) {
163 + if (isset(KnownTypes::TYPE_URLS[$any->getTypeUrl()])) {
164 + $class = KnownTypes::TYPE_URLS[$any->getTypeUrl()];
165 + new $class();
166 + // add known types to descriptor pool
167 + }
168 + try {
169 + $error = $any->unpack();
170 + } catch (Exception $ex) {
171 + // failed to unpack the $any object - keep the object as-is instead
172 + $error = $any;
173 + }
174 + if (!\is_null($errors)) {
175 + $errors[] = $error;
176 + }
177 + $result[] = ['@type' => $any->getTypeUrl()] + self::serializeToPhpArray($error);
178 + }
179 + return $result;
180 + }
181 + // look for individual error detail bins and decode those
182 + // NOTE: This method SHOULD be superceeded by 'grpc-status-details-bin' in every case, but
183 + // we are keeping it for now to be safe
137 184 foreach ($metadata as $key => $values) {
138 185 foreach ($values as $value) {
139 186 $decodedValue = ['@type' => $key];
140 187 if (self::hasBinaryHeaderSuffix($key)) {
141 - if (isset(self::$metadataKnownTypes[$key])) {
142 - $class = self::$metadataKnownTypes[$key];
188 + if (isset(KnownTypes::BIN_TYPES[$key])) {
189 + $class = KnownTypes::BIN_TYPES[$key];
143 190 /** @var Message $message */
144 191 $message = new $class();
145 192 try {
146 193 $message->mergeFromString($value);
147 194 $decodedValue += self::serializeToPhpArray($message);
195 + if (!\is_null($errors)) {
196 + $errors[] = $message;
197 + }
148 198 } catch (\Exception $e) {
149 199 // We encountered an error trying to deserialize the data
150 - $decodedValue += ['data' => '<Unable to deserialize data>'];
200 + $decodedValue['data'] = '<Unable to deserialize data>';
151 201 }
152 202 } else {
153 203 // The metadata contains an unexpected binary type
154 - $decodedValue += ['data' => '<Unknown Binary Data>'];
204 + $decodedValue['data'] = '<Unknown Binary Data>';
155 205 }
156 206 } else {
157 - $decodedValue += ['data' => $value];
207 + $decodedValue['data'] = $value;
158 208 }
159 209 $result[] = $decodedValue;
160 210 }
161 211 }
@@ -176,9 +226,8 @@
176 226 /** @var Message $unpacked */
177 227 $unpacked = $any->unpack();
178 228 $results[] = self::serializeToPhpArray($unpacked);
179 229 } catch (\Exception $ex) {
180 - echo "{$ex}\n";
181 230 // failed to unpack the $any object - show as unknown binary data
182 231 $results[] = ['typeUrl' => $any->getTypeUrl(), 'value' => '<Unknown Binary Data>'];
183 232 }
184 233 }
@@ -193,9 +242,9 @@
193 242 private function encodeElement(FieldDescriptor $field, $data)
194 243 {
195 244 switch ($field->getType()) {
196 245 case GPBType::MESSAGE:
197 - if (is_array($data)) {
246 + if (\is_array($data)) {
198 247 $result = $data;
199 248 } else {
200 249 $result = $this->encodeMessageImpl($data, $field->getMessageType());
201 250 }
@@ -244,22 +293,22 @@
244 293 */
245 294 private function encodeMessageImpl(Message $message, Descriptor $messageType)
246 295 {
247 296 $data = [];
248 - $fieldCount = $messageType->getFieldCount();
249 - for ($i = 0; $i < $fieldCount; $i++) {
250 - $field = $messageType->getField($i);
297 + // Call the getDescriptorMaps outside of the loop to save processing.
298 + // Use the same set of fields to loop over, instead of using field count.
299 + list($fields, $fieldsToOneof) = $this->getDescriptorMaps($messageType);
300 + foreach ($fields as $field) {
251 301 $key = $field->getName();
252 - $getter = $this->getGetter($key);
302 + $getter = self::getGetter($key);
253 303 $v = $message->{$getter}();
254 - if (is_null($v)) {
304 + if (\is_null($v)) {
255 305 continue;
256 306 }
257 307 // Check and skip unset fields inside oneofs
258 - list($_, $fieldsToOneof) = $this->getDescriptorMaps($messageType);
259 308 if (isset($fieldsToOneof[$key])) {
260 309 $oneofName = $fieldsToOneof[$key];
261 - $oneofGetter = $this->getGetter($oneofName);
310 + $oneofGetter = self::getGetter($oneofName);
262 311 if ($message->{$oneofGetter}() !== $key) {
263 312 continue;
264 313 }
265 314 }
@@ -271,9 +320,9 @@
271 320 foreach ($v as $k => $vv) {
272 321 $arr[$this->encodeElement($keyField, $k)] = $this->encodeElement($valueField, $vv);
273 322 }
274 323 $v = $arr;
275 - } elseif ($field->getLabel() === GPBLabel::REPEATED) {
324 + } elseif ($this->checkFieldRepeated($field)) {
276 325 $arr = [];
277 326 foreach ($v as $k => $vv) {
278 327 $arr[$k] = $this->encodeElement($field, $vv);
279 328 }
@@ -328,9 +377,9 @@
328 377 // Get the field by tag number or name
329 378 $fieldName = self::toSnakeCase($key);
330 379 // Unknown field found
331 380 if (!isset($fieldsByName[$fieldName])) {
332 - throw new RuntimeException(sprintf("cannot handle unknown field %s on message %s", $fieldName, $messageType->getFullName()));
381 + throw new RuntimeException(\sprintf('cannot handle unknown field %s on message %s', $fieldName, $messageType->getFullName()));
333 382 }
334 383 /** @var FieldDescriptor $field */
335 384 $field = $fieldsByName[$fieldName];
336 385 if ($field->isMap()) {
@@ -341,9 +390,9 @@
341 390 foreach ($v as $k => $vv) {
342 391 $arr[$this->decodeElement($keyField, $k)] = $this->decodeElement($valueField, $vv);
343 392 }
344 393 $value = $arr;
345 - } elseif ($field->getLabel() === GPBLabel::REPEATED) {
394 + } elseif ($this->checkFieldRepeated($field)) {
346 395 $arr = [];
347 396 foreach ($v as $k => $vv) {
348 397 $arr[$k] = $this->decodeElement($field, $vv);
349 398 }
@@ -350,9 +399,9 @@
350 399 $value = $arr;
351 400 } else {
352 401 $value = $this->decodeElement($field, $v);
353 402 }
354 - $setter = $this->getSetter($field->getName());
403 + $setter = self::getSetter($field->getName());
355 404 $message->{$setter}($value);
356 405 // We must unset $value here, otherwise the protobuf c extension will mix up the references
357 406 // and setting one value will change all others
358 407 unset($value);
@@ -359,14 +408,25 @@
359 408 }
360 409 return $message;
361 410 }
362 411 /**
412 + * @param FieldDescriptor $field
413 + * @return bool
414 + */
415 + private function checkFieldRepeated(FieldDescriptor $field) : bool
416 + {
417 + return \method_exists($field, 'isRepeated') ? $field->isRepeated() : $field->getLabel() === GPBLabel::REPEATED;
418 + }
419 + /**
363 420 * @param string $name
364 421 * @return string Getter function
365 422 */
366 423 public static function getGetter(string $name)
367 424 {
368 - return 'get' . ucfirst(self::toCamelCase($name));
425 + if (!isset(self::$getterMap[$name])) {
426 + self::$getterMap[$name] = 'get' . \ucfirst(self::toCamelCase($name));
427 + }
428 + return self::$getterMap[$name];
369 429 }
370 430 /**
371 431 * @param string $name
372 432 * @return string Setter function
@@ -372,9 +432,12 @@
372 432 * @return string Setter function
373 433 */
374 434 public static function getSetter(string $name)
375 435 {
376 - return 'set' . ucfirst(self::toCamelCase($name));
436 + if (!isset(self::$setterMap[$name])) {
437 + self::$setterMap[$name] = 'set' . \ucfirst(self::toCamelCase($name));
438 + }
439 + return self::$setterMap[$name];
377 440 }
378 441 /**
379 442 * Convert string from camelCase to snake_case
380 443 *
@@ -382,9 +445,12 @@
382 445 * @return string
383 446 */
384 447 public static function toSnakeCase(string $key)
385 448 {
386 - return strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $key));
449 + if (!isset(self::$snakeCaseMap[$key])) {
450 + self::$snakeCaseMap[$key] = \strtolower(\preg_replace(['/([a-z\\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $key));
451 + }
452 + return self::$snakeCaseMap[$key];
387 453 }
388 454 /**
389 455 * Convert string from snake_case to camelCase
390 456 *
@@ -392,17 +458,20 @@
392 458 * @return string
393 459 */
394 460 public static function toCamelCase(string $key)
395 461 {
396 - return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $key))));
462 + if (!isset(self::$camelCaseMap[$key])) {
463 + self::$camelCaseMap[$key] = \lcfirst(\str_replace(' ', '', \ucwords(\str_replace('_', ' ', $key))));
464 + }
465 + return self::$camelCaseMap[$key];
397 466 }
398 467 private static function hasBinaryHeaderSuffix(string $key)
399 468 {
400 - return substr_compare($key, "-bin", strlen($key) - 4) === 0;
469 + return \substr_compare($key, '-bin', \strlen($key) - 4) === 0;
401 470 }
402 471 private static function getPhpArraySerializer()
403 472 {
404 - if (is_null(self::$phpArraySerializer)) {
473 + if (\is_null(self::$phpArraySerializer)) {
405 474 self::$phpArraySerializer = new Serializer();
406 475 }
407 476 return self::$phpArraySerializer;
408 477 }
@@ -407,9 +476,9 @@
407 476 return self::$phpArraySerializer;
408 477 }
409 478 public static function loadKnownMetadataTypes()
410 479 {
411 - foreach (self::$metadataKnownTypes as $key => $class) {
480 + foreach (KnownTypes::allKnownTypes() as $key => $class) {
412 481 new $class();
413 482 }
414 483 }
415 484 }