如何将 WC_Data_Store 转换为 WC_Product_Variable_Data_Store_CPT 或具有 $prices_array 属性的东西?

问题描述

我必须执行以下反射代码,因为我可用的 WooCommerce 版本有一个错误 (v4.9.2)。

请查看下面代码中的注释:

// checked before the existence of the class with class_exists
$rp = new ReflectionProperty('WC_Product_Variable_Data_Store_CPT','prices_array');
$rp->setAccessible(true);
var_dump('start'); // echoes something to the html code
$protected_prices_array = $rp->getValue($ths); // crashes the PHP script instance
var_dump('stop'); // this is not printed anymore

如果需要,我可以提供更多代码

目前我正在尝试继承给定的类,看看我是否可以解决这个错误

在临时站点上,我有 PHP 7.4.16。

更新 1

我在自己的 function my_read_price_data( $ths,&$product,$for_display = false ) { ... 中有此代码,它与 WC 数据存储的 read_price_data 公共方法相同,该方法访问受保护的价格数组属性

更新 2

/**
 * Modified function from WC.
 *
 * @param WC_Product_Variable_Data_Store_CPT $ths
 * @param WC_Product_Variable $product
 * @param boolean $for_display
 * @return void
 */
function my_read_price_data( $ths,$for_display = false ) {

  /**
   * Transient name for storing prices for this product (note: Max transient length is 45)
   *
   * @since 2.5.0 a single transient is used per product for all prices,rather than many transients per product.
   */
  $transient_name    = 'wc_var_prices_' . $product->get_id();
  $transient_version = WC_Cache_Helper::get_transient_version( 'product' );
  $price_hash = my_get_price_hash($ths,$product,$for_display); // with this it does not crash (*)

  // NOTE: maybe inherit from WC_Product_Variable_Data_Store_CPT to not use reflection.
  $rp = new ReflectionProperty('WC_Product_Variable_Data_Store_CPT','prices_array'); // the class exists
  $rp->setAccessible(true);
  var_dump('start');
  $protected_prices_array = $rp->getValue($ths);  // (*) until this
  var_dump('stop');

  // Check if prices array is stale.
  if ( ! isset( $protected_prices_array['version'] ) || $protected_prices_array['version'] !== $transient_version ) {
    $rp->setValue($ths,array(
      'version' => $transient_version,));
  }

  $protected_prices_array = $rp->getValue($ths); 

  /**
   * $this->prices_array is an array of values which may have been modified from what is stored in transients - this may not match $transient_cached_prices_array.
   * If the value has already been generated,we don't need to grab the values again so just return them. They are already filtered.
   */
  if ( empty( $protected_prices_array[ $price_hash ] ) ) {
    $transient_cached_prices_array = array_filter( (array) json_decode( strval( get_transient( $transient_name ) ),true ) );

    // If the product version has changed since the transient was last saved,reset the transient cache.
    if ( ! isset( $transient_cached_prices_array['version'] ) || $transient_version !== $transient_cached_prices_array['version'] ) {
      $transient_cached_prices_array = array(
        'version' => $transient_version,);
    }

    // If the prices are not stored for this hash,generate them and add to the transient.
    if ( empty( $transient_cached_prices_array[ $price_hash ] ) ) {
      $prices_array = array(
        'price'         => array(),'regular_price' => array(),'sale_price'    => array(),);

      $variation_ids = $product->get_visible_children();

      if ( is_callable( '_prime_post_caches' ) ) {
        _prime_post_caches( $variation_ids );
      }

      foreach ( $variation_ids as $variation_id ) {
        $variation = wc_get_product( $variation_id );

        if ( $variation ) {
          $price         = apply_filters( 'woocommerce_variation_prices_price',$variation->get_price( 'edit' ),$variation,$product );
          $regular_price = apply_filters( 'woocommerce_variation_prices_regular_price',$variation->get_regular_price( 'edit' ),$product );
          $sale_price    = apply_filters( 'woocommerce_variation_prices_sale_price',$variation->get_sale_price( 'edit' ),$product );

          // Skip empty prices.
          if ( '' === $price ) {
            continue;
          }

          // If sale price does not equal price,the product is not yet on sale.
          if ( $sale_price === $regular_price || $sale_price !== $price ) {
            $sale_price = $regular_price;
          }

          // If we are getting prices for display,we need to account for taxes.
          if ( $for_display ) {
            if ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) {
              $price         = '' === $price ? '' : wc_get_price_including_tax(
                $variation,array(
                  'qty'   => 1,'price' => $price,)
              );
              $regular_price = '' === $regular_price ? '' : wc_get_price_including_tax(
                $variation,'price' => $regular_price,)
              );
              $sale_price    = '' === $sale_price ? '' : wc_get_price_including_tax(
                $variation,'price' => $sale_price,)
              );
            } else {
              $price         = '' === $price ? '' : wc_get_price_excluding_tax(
                $variation,)
              );
              $regular_price = '' === $regular_price ? '' : wc_get_price_excluding_tax(
                $variation,)
              );
              $sale_price    = '' === $sale_price ? '' : wc_get_price_excluding_tax(
                $variation,)
              );
            }
          }

          $prices_array['price'][ $variation_id ]         = wc_format_decimal( $price,wc_get_price_decimals() );
          $prices_array['regular_price'][ $variation_id ] = wc_format_decimal( $regular_price,wc_get_price_decimals() );
          $prices_array['sale_price'][ $variation_id ]    = wc_format_decimal( $sale_price,wc_get_price_decimals() );

          $prices_array = apply_filters( 'woocommerce_variation_prices_array',$prices_array,$for_display );
        }
      }

      // Add all pricing data to the transient array.
      foreach ( $prices_array as $key => $values ) {
        $transient_cached_prices_array[ $price_hash ][ $key ] = $values;
      }

      set_transient( $transient_name,wp_json_encode( $transient_cached_prices_array ),DAY_IN_SECONDS * 30 );
    }

    /**
     * Give plugins one last chance to filter the variation prices array which has been generated and store locally to the class.
     * This value may differ from the transient cache. It is filtered once before storing locally.
     */
    $protected_prices_array = $rp->getValue($ths);

    $protected_prices_array[$price_hash] = apply_filters( 'woocommerce_variation_prices',$transient_cached_prices_array[ $price_hash ],$for_display );

    $rp->setValue($ths,$protected_prices_array);
  }
  return $rp->getValue($ths)[ $price_hash ];
}

更新 3

上面的函数 my_read_price_data 被调用

/**
 * Function modified from WC.
 *
 * @param WC_Product_Variable $p
 * @param boolean $for_display
 * @return void
 */
function my_get_variation_prices( $p,$for_display = false ) {
  $ds = $p->get_data_store(); // $p->data_store;
  $prices = my_read_price_data($ds,$p,$for_display);

  foreach ( $prices as $price_key => $variation_prices ) {
    $prices[ $price_key ] = asort( $variation_prices );
  }

  return $prices;
}

这是由以下函数调用的,该函数是 WC 函数修改版本,但这次修改是为了将输出更改为客户端想要的内容

function my_get_price_html( $price = '' ) {
  global $product;
  $prices = my_get_variation_prices($product,true);

  if ( empty( $prices['price'] ) ) {
    $price = apply_filters( 'woocommerce_variable_empty_price_html','',$product  );
  } else {
    $min_price     = current( $prices['price'] );
    $max_price     = end( $prices['price'] );
    $min_reg_price = current( $prices['regular_price'] );
    $max_reg_price = end( $prices['regular_price'] );

    if ( $min_price !== $max_price ) {
      $price = wc_format_price_range( $min_price,$max_price );
    } elseif ( $product->is_on_sale() && $min_reg_price === $max_reg_price ) {
      $price = my_wc_format_sale_price( $prices['regular_price'],$prices['price'] );
    } else {
      $price = wc_price( $min_price );
    }

    $price = apply_filters( 'woocommerce_variable_price_html',$price . $product->get_price_suffix(),$product );
  }

  return apply_filters( 'woocommerce_get_price_html',$price,$product );
}

正如你在上面看到的,我使用了 my_wc_format_sale_price,它在这里

/**
 * Format a sale price for display.
 *
 * @since  3.0.0
 * @param  float $regular_price Regular price.
 * @param  float $sale_price    Sale price.
 * @return string
 */
function my_wc_format_sale_price( $regular_price,$sale_price ) {
    $price = '<span>' . get_my_percent($regular_price,$sale_price) . '</span> <ins>' . ( is_numeric( $sale_price ) ? wc_price( $sale_price ) : $sale_price ) . '</ins>';
    return apply_filters( 'woocommerce_format_sale_price',$regular_price,$sale_price );
}

我认为这是最后一个重要的函数(它有一个文档注释说它返回一个字符串):

function get_my_percent($regular_price,$sale_price) {
  $a = ($regular_price - $sale_price) / $regular_price * 100;
  return "$a% reducere";
}

更新 4

我通过 https://stackoverflow.com/a/21429652/258462 发现了以下内容

提供给反射机制的对象似乎与预期类型不同。

来自 WooCommerce 4.9.2 的源代码

/**
 * WC Variable Product Data Store: Stored in CPT.
 *
 * @version 3.0.0
 */
class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT implements WC_Object_Data_Store_Interface,WC_Product_Variable_Data_Store_Interface {

    /**
     * Cached & hashed prices array for child variations.
     *
     * @var array
     */
    protected $prices_array = array()

...

那么问题是如何将 WC_Data_Store 转换为具有 $prices_array 属性内容

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)