Keyed by concrete field key, sorted. */ public static function extract( \WC_Subscription $subscription, array $fields ) { $state = array(); foreach ( self::expand( $subscription, $fields ) as $field ) { $state[ $field ] = self::read( $subscription, $field ); } ksort( $state, SORT_STRING ); return $state; } /** * Expands item wildcards into concrete keys. * * @param \WC_Subscription $subscription Subscription. * @param string[] $fields Fields. * @return string[] */ public static function expand( \WC_Subscription $subscription, array $fields ) { $out = array(); foreach ( $fields as $field ) { $parsed = Fields::parse( $field ); if ( ! $parsed ) { continue; } if ( 'item' === $parsed['kind'] && '*' === $parsed['item_id'] ) { foreach ( $subscription->get_items() as $item_id => $item ) { if ( $item instanceof \WC_Order_Item_Product ) { $out[] = Fields::item( (int) $item_id, $parsed['prop'] ); } } continue; } $out[] = $field; } return array_values( array_unique( $out ) ); } /** * Reads one concrete field. * * @param \WC_Subscription $subscription Subscription. * @param string $field Field key. * @return mixed */ public static function read( \WC_Subscription $subscription, $field ) { $parsed = Fields::parse( $field ); if ( ! $parsed ) { return null; } switch ( $parsed['kind'] ) { case 'date': $date = (string) $subscription->get_date( $parsed['type'], 'gmt' ); return ( '' === $date || '0' === $date ) ? '0' : $date; case 'meta': if ( ! \Recast\WCS\Operation\Field\MetaOperation::is_allowed( $parsed['key'] ) ) { return null; // Internal keys are read through their getters, never generic meta. } // An absent field is null, so undo can delete it again instead of leaving an empty value. if ( ! $subscription->meta_exists( $parsed['key'] ) ) { return null; } $value = $subscription->get_meta( $parsed['key'], true ); return is_scalar( $value ) || null === $value ? ( null === $value ? '' : (string) $value ) : $value; case 'item': $item = $subscription->get_item( (int) $parsed['item_id'], false ); if ( ! $item instanceof \WC_Order_Item_Product ) { return null; } return self::read_item( $item, $parsed['prop'] ); } switch ( $field ) { case Fields::STATUS: return (string) $subscription->get_status(); case Fields::REQUIRES_MANUAL_RENEWAL: return (bool) $subscription->get_requires_manual_renewal(); case Fields::PAYMENT_METHOD: return (string) $subscription->get_payment_method(); case Fields::PAYMENT_METHOD_TITLE: return (string) $subscription->get_payment_method_title(); case Fields::BILLING_PERIOD: return (string) $subscription->get_billing_period(); case Fields::BILLING_INTERVAL: return (int) $subscription->get_billing_interval(); case Fields::CURRENCY: return (string) $subscription->get_currency(); case Fields::TOTAL: return Money::format( $subscription->get_total() ); case Fields::TOTAL_TAX: return Money::format( $subscription->get_total_tax() ); } return null; } /** * Reads one line item property. * * @param \WC_Order_Item_Product $item Item. * @param string $prop Property. * @return mixed */ private static function read_item( \WC_Order_Item_Product $item, $prop ) { switch ( $prop ) { case 'subtotal': return Money::format( $item->get_subtotal() ); case 'total': return Money::format( $item->get_total() ); case 'tax_class': return (string) $item->get_tax_class(); case 'quantity': return (int) $item->get_quantity(); case 'product_id': return (int) $item->get_product_id(); case 'variation_id': return (int) $item->get_variation_id(); case 'name': return (string) $item->get_name(); case 'attributes': return self::item_attributes( $item ); case 'taxes': $taxes = $item->get_taxes(); $out = array(); foreach ( array( 'subtotal', 'total' ) as $bucket ) { $out[ $bucket ] = array(); if ( ! empty( $taxes[ $bucket ] ) ) { foreach ( $taxes[ $bucket ] as $rate_id => $amount ) { $out[ $bucket ][ (string) $rate_id ] = Money::format( $amount ); } ksort( $out[ $bucket ], SORT_STRING ); } } return $out; } return null; } /** * The variation attributes stored on a line item (for example "Size: Large"), keyed like the * item meta. Only the attributes of the line's variation count, so other item meta stays out. * * @param \WC_Order_Item_Product $item Item. * @return array */ public static function item_attributes( \WC_Order_Item_Product $item ) { $out = array(); $variation_id = (int) $item->get_variation_id(); $variation = $variation_id ? wc_get_product( $variation_id ) : null; if ( ! $variation instanceof \WC_Product ) { return $out; } foreach ( array_keys( (array) $variation->get_attributes() ) as $key ) { $key = (string) $key; if ( $item->meta_exists( $key ) ) { $out[ $key ] = (string) $item->get_meta( $key, true ); } } ksort( $out, SORT_STRING ); return $out; } }