Completed
Push — develop ( f29211...ed262e )
by Remco
10:29 queued 57s
created

SubscriptionsDataStoreCPT::update_post_meta()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 20
nc 1
nop 1
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
1
<?php
2
/**
3
 * Subscriptions Data Store CPT
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2018 Pronamic
7
 * @license   GPL-3.0-or-later
8
 * @package   Pronamic\WordPress\Pay\Subscriptions
9
 */
10
11
namespace Pronamic\WordPress\Pay\Subscriptions;
12
13
use DatePeriod;
14
use DateTimeZone;
15
use Pronamic\WordPress\Pay\AbstractDataStoreCPT;
16
use Pronamic\WordPress\Pay\DateTime;
17
use Pronamic\WordPress\Pay\Core\Statuses;
18
19
/**
20
 * Title: Subscriptions data store CPT
21
 *
22
 * @see https://woocommerce.com/2017/04/woocommerce-3-0-release/
23
 * @see https://woocommerce.wordpress.com/2016/10/27/the-new-crud-classes-in-woocommerce-2-7/
24
 * @author Remco Tolsma
25
 * @version 3.7.0
26
 * @since 3.7.0
27
 */
28
class SubscriptionsDataStoreCPT extends AbstractDataStoreCPT {
29
	/**
30
	 * Construct subscriptions data store CPT object.
31
	 */
32
	public function __construct() {
33
		$this->meta_key_prefix = '_pronamic_subscription_';
34
	}
35
36
	/**
37
	 * Create subscription.
38
	 *
39
	 * @see https://github.com/woocommerce/woocommerce/blob/3.2.6/includes/data-stores/abstract-wc-order-data-store-cpt.php#L47-L76
40
	 * @param Subscription $subscription Create the specified subscription in this data store.
41
	 */
42
	public function create( $subscription ) {
43
		$result = wp_insert_post(
44
			array(
45
				'post_type'     => 'pronamic_pay_subscr',
46
				'post_date_gmt' => $subscription->date->format( 'Y-m-d H:i:s' ),
47
				'post_title'    => sprintf(
48
					'Subscription – %s',
49
					date_i18n( _x( '@todo', 'Subscription title date format parsed by `date_i18n`.', 'pronamic_ideal' ) )
50
				),
51
				'post_status'   => $this->get_post_status( $subscription->get_status() ),
52
				'post_author'   => $subscription->user_id,
0 ignored issues
show
Bug introduced by
The property user_id does not seem to exist on Pronamic\WordPress\Pay\Subscriptions\Subscription.
Loading history...
53
			), true
54
		);
55
56
		if ( is_wp_error( $result ) ) {
57
			return false;
58
		}
59
60
		$subscription->set_id( $result );
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type WP_Error; however, parameter $id of Pronamic\WordPress\Pay\S...\Subscription::set_id() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

60
		$subscription->set_id( /** @scrutinizer ignore-type */ $result );
Loading history...
61
		$subscription->post = get_post( $result );
0 ignored issues
show
Documentation Bug introduced by
It seems like get_post($result) can also be of type array. However, the property $post is declared as type WP_Post. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
Bug introduced by
It seems like $result can also be of type WP_Error; however, parameter $post of get_post() does only seem to accept null|integer|WP_Post, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

61
		$subscription->post = get_post( /** @scrutinizer ignore-type */ $result );
Loading history...
62
63
		$this->update_post_meta( $subscription );
64
65
		do_action( 'pronamic_pay_new_subscription', $subscription );
66
67
		return true;
68
	}
69
70
	/**
71
	 * Read subscription.
72
	 *
73
	 * @see https://github.com/woocommerce/woocommerce/blob/3.2.6/includes/data-stores/abstract-wc-order-data-store-cpt.php#L78-L111
74
	 * @see https://github.com/woocommerce/woocommerce/blob/3.2.6/includes/data-stores/class-wc-order-data-store-cpt.php#L81-L136
75
	 * @param Subscription $subscription The subscription to read the additional data for.
76
	 */
77
	public function read( $subscription ) {
78
		$subscription->post    = get_post( $subscription->get_id() );
0 ignored issues
show
Documentation Bug introduced by
It seems like get_post($subscription->get_id()) can also be of type array. However, the property $post is declared as type WP_Post. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
Bug introduced by
$subscription->get_id() of type string is incompatible with the type null|integer|WP_Post expected by parameter $post of get_post(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

78
		$subscription->post    = get_post( /** @scrutinizer ignore-type */ $subscription->get_id() );
Loading history...
79
		$subscription->title   = get_the_title( $subscription->get_id() );
0 ignored issues
show
Bug introduced by
The property title does not seem to exist on Pronamic\WordPress\Pay\Subscriptions\Subscription.
Loading history...
Bug introduced by
$subscription->get_id() of type string is incompatible with the type integer|WP_Post expected by parameter $post of get_the_title(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

79
		$subscription->title   = get_the_title( /** @scrutinizer ignore-type */ $subscription->get_id() );
Loading history...
80
		$subscription->date    = new DateTime( get_post_field( 'post_date_gmt', $subscription->get_id(), 'raw' ), new DateTimeZone( 'UTC' ) );
0 ignored issues
show
Bug Best Practice introduced by
The property date does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Bug introduced by
$subscription->get_id() of type string is incompatible with the type integer|WP_Post expected by parameter $post of get_post_field(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

80
		$subscription->date    = new DateTime( get_post_field( 'post_date_gmt', /** @scrutinizer ignore-type */ $subscription->get_id(), 'raw' ), new DateTimeZone( 'UTC' ) );
Loading history...
81
		$subscription->user_id = get_post_field( 'post_author', $subscription->get_id(), 'raw' );
0 ignored issues
show
Bug introduced by
The property user_id does not seem to exist on Pronamic\WordPress\Pay\Subscriptions\Subscription.
Loading history...
82
83
		$this->read_post_meta( $subscription );
84
	}
85
86
	/**
87
	 * Update subscription.
88
	 *
89
	 * @see https://github.com/woocommerce/woocommerce/blob/3.2.6/includes/data-stores/abstract-wc-order-data-store-cpt.php#L113-L154
90
	 * @see https://github.com/woocommerce/woocommerce/blob/3.2.6/includes/data-stores/class-wc-order-data-store-cpt.php#L154-L257
91
	 * @param Subscription $subscription The subscription to update in this data store.
92
	 */
93
	public function update( $subscription ) {
94
		$data = array(
95
			'ID' => $subscription->get_id(),
96
		);
97
98
		$post_status = $this->get_post_status( $subscription->get_status(), null );
99
100
		if ( null !== $post_status ) {
0 ignored issues
show
introduced by
The condition null !== $post_status is always true.
Loading history...
101
			$data['post_status'] = $post_status;
102
		}
103
104
		wp_update_post( $data );
105
106
		$this->update_post_meta( $subscription );
107
	}
108
109
	/**
110
	 * Get post status.
111
	 *
112
	 * @param string $meta_status The subscription meta status to get the post status for.
113
	 * @param string $default     The deafult post status if the meta status could not be converted to a post status.
114
	 * @return string
115
	 */
116
	public function get_post_status( $meta_status, $default = 'subscr_pending' ) {
117
		switch ( $meta_status ) {
118
			case Statuses::CANCELLED:
119
				return 'subscr_cancelled';
120
			case Statuses::EXPIRED:
121
				return 'subscr_expired';
122
			case Statuses::FAILURE:
123
				return 'subscr_failed';
124
			case Statuses::SUCCESS:
125
				return 'subscr_active';
126
			case Statuses::OPEN:
127
				return 'subscr_pending';
128
			case Statuses::COMPLETED:
129
				return 'subscr_completed';
130
			default:
131
				return $default;
132
		}
133
	}
134
135
	/**
136
	 * Get meta status label.
137
	 *
138
	 * @param string $meta_status The subscription meta status to get the status label for.
139
	 * @return string|boolean
140
	 */
141
	public function get_meta_status_label( $meta_status ) {
142
		$post_status = $this->get_post_status( $meta_status, null );
143
144
		if ( empty( $post_status ) ) {
145
			return false;
146
		}
147
148
		$status_object = get_post_status_object( $post_status );
149
150
		if ( isset( $status_object, $status_object->label ) ) {
151
			return $status_object->label;
152
		}
153
154
		return false;
155
	}
156
157
	/**
158
	 * Read post meta.
159
	 *
160
	 * @see https://github.com/woocommerce/woocommerce/blob/3.2.6/includes/abstracts/abstract-wc-data.php#L462-L507
161
	 * @param Subscription $subscription The subscription to read the post meta for.
162
	 */
163
	private function read_post_meta( $subscription ) {
164
		$id = $subscription->get_id();
165
166
		$subscription->config_id       = $this->get_meta( $id, 'config_id' );
0 ignored issues
show
Bug introduced by
$id of type string is incompatible with the type integer expected by parameter $id of Pronamic\WordPress\Pay\A...ataStoreCPT::get_meta(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

166
		$subscription->config_id       = $this->get_meta( /** @scrutinizer ignore-type */ $id, 'config_id' );
Loading history...
167
		$subscription->key             = $this->get_meta( $id, 'key' );
168
		$subscription->source          = $this->get_meta( $id, 'source' );
169
		$subscription->source_id       = $this->get_meta( $id, 'source_id' );
170
		$subscription->frequency       = $this->get_meta( $id, 'frequency' );
171
		$subscription->interval        = $this->get_meta( $id, 'interval' );
0 ignored issues
show
Documentation Bug introduced by
The property $interval was declared of type integer, but $this->get_meta($id, 'interval') is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
172
		$subscription->interval_period = $this->get_meta( $id, 'interval_period' );
0 ignored issues
show
Documentation Bug introduced by
The property $interval_period was declared of type integer, but $this->get_meta($id, 'interval_period') is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
173
		$subscription->currency        = $this->get_meta( $id, 'currency' );
174
		$subscription->amount          = $this->get_meta( $id, 'amount' );
0 ignored issues
show
Documentation Bug introduced by
The property $amount was declared of type double, but $this->get_meta($id, 'amount') is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
175
		$subscription->transaction_id  = $this->get_meta( $id, 'transaction_id' );
176
		$subscription->status          = $this->get_meta( $id, 'status' );
177
		$subscription->description     = $this->get_meta( $id, 'description' );
178
		$subscription->email           = $this->get_meta( $id, 'email' );
179
		$subscription->customer_name   = $this->get_meta( $id, 'customer_name' );
180
		$subscription->payment_method  = $this->get_meta( $id, 'payment_method' );
181
182
		$first_payment = $subscription->get_first_payment();
183
184
		if ( is_object( $first_payment ) ) {
185
			if ( empty( $subscription->config_id ) ) {
186
				$subscription->config_id = $first_payment->config_id;
187
			}
188
189
			if ( empty( $subscription->user_id ) ) {
190
				$subscription->user_id = $first_payment->user_id;
0 ignored issues
show
Bug introduced by
The property user_id does not seem to exist on Pronamic\WordPress\Pay\Subscriptions\Subscription.
Loading history...
191
			}
192
193
			if ( empty( $subscription->payment_method ) ) {
194
				$subscription->payment_method = $first_payment->method;
195
			}
196
		}
197
198
		// Start Date.
199
		$start_date = $this->get_meta_date( $id, 'start_date' );
0 ignored issues
show
Bug introduced by
$id of type string is incompatible with the type integer expected by parameter $id of Pronamic\WordPress\Pay\A...oreCPT::get_meta_date(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

199
		$start_date = $this->get_meta_date( /** @scrutinizer ignore-type */ $id, 'start_date' );
Loading history...
200
201
		if ( empty( $start_date ) ) {
202
			// If no meta start date is set, use subscription date.
203
			$start_date = clone $subscription->date;
204
		}
205
206
		$subscription->start_date = $start_date;
207
208
		// End Date.
209
		$end_date = $this->get_meta_date( $id, 'end_date' );
210
211
		if ( empty( $end_date ) && $subscription->frequency ) {
212
			$interval = $subscription->get_date_interval();
213
214
			// @see https://stackoverflow.com/a/10818981/6411283
215
			$period = new DatePeriod( $start_date, $interval, $subscription->frequency );
0 ignored issues
show
Bug introduced by
$subscription->frequency of type string is incompatible with the type DateTimeInterface expected by parameter $end of DatePeriod::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

215
			$period = new DatePeriod( $start_date, $interval, /** @scrutinizer ignore-type */ $subscription->frequency );
Loading history...
216
217
			$dates = iterator_to_array( $period );
218
219
			$end_date = end( $dates );
220
		}
221
222
		$subscription->end_date = $end_date;
223
224
		// Expiry Date.
225
		$expiry_date = $this->get_meta_date( $id, 'expiry_date' );
226
227
		if ( empty( $expiry_date ) ) {
228
			// If no meta expiry date is set, use start date + 1 interval period.
229
			$expiry_date = clone $start_date;
230
231
			$expiry_date->add( $subscription->get_date_interval() );
232
		}
233
234
		$subscription->expiry_date = $expiry_date;
235
236
		// Next Payment Date.
237
		$subscription->next_payment = $this->get_meta_date( $id, 'next_payment' );
238
	}
239
240
	/**
241
	 * Update payment post meta.
242
	 *
243
	 * @see https://github.com/woocommerce/woocommerce/blob/3.2.6/includes/data-stores/class-wc-order-data-store-cpt.php#L154-L257
244
	 * @param Subscription $subscription The subscription to update the post meta for.
245
	 */
246
	private function update_post_meta( $subscription ) {
247
		$id = $subscription->get_id();
248
249
		$this->update_meta( $id, 'config_id', $subscription->config_id );
0 ignored issues
show
Bug introduced by
$id of type string is incompatible with the type integer expected by parameter $id of Pronamic\WordPress\Pay\A...StoreCPT::update_meta(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

249
		$this->update_meta( /** @scrutinizer ignore-type */ $id, 'config_id', $subscription->config_id );
Loading history...
250
		$this->update_meta( $id, 'key', $subscription->key );
251
		$this->update_meta( $id, 'source', $subscription->source );
252
		$this->update_meta( $id, 'source_id', $subscription->source_id );
253
		$this->update_meta( $id, 'frequency', $subscription->frequency );
254
		$this->update_meta( $id, 'interval', $subscription->interval );
255
		$this->update_meta( $id, 'interval_period', $subscription->interval_period );
256
		$this->update_meta( $id, 'currency', $subscription->currency );
257
		$this->update_meta( $id, 'amount', $subscription->amount );
258
		$this->update_meta( $id, 'transaction_id', $subscription->transaction_id );
259
		$this->update_meta( $id, 'description', $subscription->description );
260
		$this->update_meta( $id, 'email', $subscription->email );
261
		$this->update_meta( $id, 'customer_name', $subscription->customer_name );
262
		$this->update_meta( $id, 'payment_method', $subscription->payment_method );
263
		$this->update_meta( $id, 'start_date', $subscription->start_date );
264
		$this->update_meta( $id, 'end_date', $subscription->end_date );
265
		$this->update_meta( $id, 'expiry_date', $subscription->expiry_date );
266
		$this->update_meta( $id, 'next_payment', $subscription->next_payment );
267
268
		$this->update_meta_status( $subscription );
269
	}
270
271
	/**
272
	 * Update meta status.
273
	 *
274
	 * @param Subscription $subscription The subscription to update the status for.
275
	 */
276
	public function update_meta_status( $subscription ) {
277
		$id = $subscription->get_id();
278
279
		$previous_status = $this->get_meta( $id, 'status' );
0 ignored issues
show
Bug introduced by
$id of type string is incompatible with the type integer expected by parameter $id of Pronamic\WordPress\Pay\A...ataStoreCPT::get_meta(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

279
		$previous_status = $this->get_meta( /** @scrutinizer ignore-type */ $id, 'status' );
Loading history...
280
		$previous_status = strtolower( $previous_status );
281
		$previous_status = empty( $previous_status ) ? 'unknown' : $previous_status;
282
283
		$this->update_meta( $id, 'status', $subscription->status );
0 ignored issues
show
Bug introduced by
$id of type string is incompatible with the type integer expected by parameter $id of Pronamic\WordPress\Pay\A...StoreCPT::update_meta(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

283
		$this->update_meta( /** @scrutinizer ignore-type */ $id, 'status', $subscription->status );
Loading history...
284
285
		if ( $previous_status !== $subscription->status ) {
286
			$can_redirect = false;
287
288
			do_action( 'pronamic_subscription_status_update_' . $subscription->source . '_' . strtolower( $previous_status ) . '_to_' . strtolower( $subscription->status ), $subscription, $can_redirect );
289
			do_action( 'pronamic_subscription_status_update_' . $subscription->source, $subscription, $can_redirect );
290
			do_action( 'pronamic_subscription_status_update', $subscription, $can_redirect );
291
		}
292
	}
293
}
294