Metadata.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. <?php
  2. namespace YahnisElsts\PluginUpdateChecker\v5p1;
  3. use LogicException;
  4. use stdClass;
  5. use WP_Error;
  6. if ( !class_exists(Metadata::class, false) ):
  7. /**
  8. * A base container for holding information about updates and plugin metadata.
  9. *
  10. * @author Janis Elsts
  11. * @copyright 2016
  12. * @access public
  13. */
  14. abstract class Metadata {
  15. /**
  16. * Additional dynamic properties, usually copied from the API response.
  17. *
  18. * @var array<string,mixed>
  19. */
  20. protected $extraProperties = array();
  21. /**
  22. * Create an instance of this class from a JSON document.
  23. *
  24. * @abstract
  25. * @param string $json
  26. * @return self
  27. */
  28. public static function fromJson($json) {
  29. throw new LogicException('The ' . __METHOD__ . ' method must be implemented by subclasses');
  30. }
  31. /**
  32. * @param string $json
  33. * @param self $target
  34. * @return bool
  35. */
  36. protected static function createFromJson($json, $target) {
  37. /** @var \StdClass $apiResponse */
  38. $apiResponse = json_decode($json);
  39. if ( empty($apiResponse) || !is_object($apiResponse) ){
  40. $errorMessage = "Failed to parse update metadata. Try validating your .json file with https://jsonlint.com/";
  41. do_action('puc_api_error', new WP_Error('puc-invalid-json', $errorMessage));
  42. //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- For plugin developers.
  43. trigger_error(esc_html($errorMessage), E_USER_NOTICE);
  44. return false;
  45. }
  46. $valid = $target->validateMetadata($apiResponse);
  47. if ( is_wp_error($valid) ){
  48. do_action('puc_api_error', $valid);
  49. //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- For plugin developers.
  50. trigger_error(esc_html($valid->get_error_message()), E_USER_NOTICE);
  51. return false;
  52. }
  53. foreach(get_object_vars($apiResponse) as $key => $value){
  54. $target->$key = $value;
  55. }
  56. return true;
  57. }
  58. /**
  59. * No validation by default! Subclasses should check that the required fields are present.
  60. *
  61. * @param \StdClass $apiResponse
  62. * @return bool|\WP_Error
  63. */
  64. protected function validateMetadata($apiResponse) {
  65. return true;
  66. }
  67. /**
  68. * Create a new instance by copying the necessary fields from another object.
  69. *
  70. * @abstract
  71. * @param \StdClass|self $object The source object.
  72. * @return self The new copy.
  73. */
  74. public static function fromObject($object) {
  75. throw new LogicException('The ' . __METHOD__ . ' method must be implemented by subclasses');
  76. }
  77. /**
  78. * Create an instance of StdClass that can later be converted back to an
  79. * update or info container. Useful for serialization and caching, as it
  80. * avoids the "incomplete object" problem if the cached value is loaded
  81. * before this class.
  82. *
  83. * @return \StdClass
  84. */
  85. public function toStdClass() {
  86. $object = new stdClass();
  87. $this->copyFields($this, $object);
  88. return $object;
  89. }
  90. /**
  91. * Transform the metadata into the format used by WordPress core.
  92. *
  93. * @return object
  94. */
  95. abstract public function toWpFormat();
  96. /**
  97. * Copy known fields from one object to another.
  98. *
  99. * @param \StdClass|self $from
  100. * @param \StdClass|self $to
  101. */
  102. protected function copyFields($from, $to) {
  103. $fields = $this->getFieldNames();
  104. if ( property_exists($from, 'slug') && !empty($from->slug) ) {
  105. //Let plugins add extra fields without having to create subclasses.
  106. $fields = apply_filters($this->getPrefixedFilter('retain_fields') . '-' . $from->slug, $fields);
  107. }
  108. foreach ($fields as $field) {
  109. if ( property_exists($from, $field) ) {
  110. $to->$field = $from->$field;
  111. }
  112. }
  113. }
  114. /**
  115. * @return string[]
  116. */
  117. protected function getFieldNames() {
  118. return array();
  119. }
  120. /**
  121. * @param string $tag
  122. * @return string
  123. */
  124. protected function getPrefixedFilter($tag) {
  125. return 'puc_' . $tag;
  126. }
  127. public function __set($name, $value) {
  128. $this->extraProperties[$name] = $value;
  129. }
  130. public function __get($name) {
  131. return isset($this->extraProperties[$name]) ? $this->extraProperties[$name] : null;
  132. }
  133. public function __isset($name) {
  134. return isset($this->extraProperties[$name]);
  135. }
  136. public function __unset($name) {
  137. unset($this->extraProperties[$name]);
  138. }
  139. }
  140. endif;