Completed
Push — renovate/mocha-8.x ( 07030e...e8e64c )
by
unknown
28:17 queued 19:09
created

Tracking::tracks_build_event_obj()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 8
nop 4
dl 0
loc 24
rs 9.536
c 0
b 0
f 0
1
<?php
2
/**
3
 * Nosara Tracks for Jetpack
4
 *
5
 * @package automattic/jetpack-tracking
6
 */
7
8
namespace Automattic\Jetpack;
9
10
/**
11
 * The Tracking class, used to record events in wpcom
12
 */
13
class Tracking {
14
	/**
15
	 * Slug of the product that we are tracking.
16
	 *
17
	 * @var string
18
	 */
19
	private $product_name;
20
21
	/**
22
	 * Connection manager object.
23
	 *
24
	 * @var Object
25
	 */
26
	private $connection;
27
28
	/**
29
	 * Creates the Tracking object.
30
	 *
31
	 * @param String                                $product_name the slug of the product that we are tracking.
32
	 * @param Automattic\Jetpack\Connection\Manager $connection   the connection manager object.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $connection not be Automattic\Jetpack\Connection\Manager|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
33
	 */
34
	public function __construct( $product_name = 'jetpack', $connection = null ) {
35
		$this->product_name = $product_name;
36
		$this->connection   = $connection;
37
		if ( is_null( $this->connection ) ) {
38
			// TODO We should always pass a Connection.
39
			$this->connection = new Connection\Manager();
40
		}
41
	}
42
43
	/**
44
	 * Enqueue script necessary for tracking.
45
	 */
46 View Code Duplication
	public function enqueue_tracks_scripts() {
47
		wp_enqueue_script( 'jptracks', plugins_url( '_inc/lib/tracks/tracks-ajax.js', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION, true );
48
		wp_localize_script(
49
			'jptracks',
50
			'jpTracksAJAX',
51
			array(
52
				'ajaxurl'            => admin_url( 'admin-ajax.php' ),
53
				'jpTracksAJAX_nonce' => wp_create_nonce( 'jp-tracks-ajax-nonce' ),
54
			)
55
		);
56
	}
57
58
	/**
59
	 * Send an event in Tracks.
60
	 *
61
	 * @param string $event_type Type of the event.
62
	 * @param array  $data       Data to send with the event.
63
	 * @param mixed  $user       username, user_id, or WP_user object.
64
	 */
65
	public function record_user_event( $event_type, $data = array(), $user = null ) {
66
		if ( ! $user ) {
67
			$user = wp_get_current_user();
68
		}
69
		$site_url = get_option( 'siteurl' );
70
71
		$data['_via_ua']  = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
72
		$data['_via_ip']  = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
73
		$data['_lg']      = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
74
		$data['blog_url'] = $site_url;
75
		$data['blog_id']  = \Jetpack_Options::get_option( 'id' );
76
77
		// Top level events should not be namespaced.
78
		if ( '_aliasUser' !== $event_type ) {
79
			$event_type = $this->product_name . '_' . $event_type;
80
		}
81
82
		$data['jetpack_version'] = defined( 'JETPACK__VERSION' ) ? JETPACK__VERSION : '0';
83
84
		return $this->tracks_record_event( $user, $event_type, $data );
85
	}
86
87
	/**
88
	 * Record an event in Tracks - this is the preferred way to record events from PHP.
89
	 *
90
	 * @param mixed  $user                   username, user_id, or WP_user object.
91
	 * @param string $event_name             The name of the event.
92
	 * @param array  $properties             Custom properties to send with the event.
93
	 * @param int    $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $event_timestamp_millis not be false|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
94
	 *
95
	 * @return bool true for success | \WP_Error if the event pixel could not be fired
96
	 */
97
	public function tracks_record_event( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
98
99
		// We don't want to track user events during unit tests/CI runs.
100
		if ( $user instanceof \WP_User && 'wptests_capabilities' === $user->cap_key ) {
0 ignored issues
show
Bug introduced by
The class WP_User does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
101
			return false;
102
		}
103
		$terms_of_service = new Terms_Of_Service();
104
		$status           = new Status();
105
		// Don't track users who have not agreed to our TOS.
106
		if ( ! $this->should_enable_tracking( $terms_of_service, $status ) ) {
0 ignored issues
show
Documentation introduced by
$terms_of_service is of type object<Automattic\Jetpack\Terms_Of_Service>, but the function expects a object<Automattic\Jetpac...tpack\Terms_Of_Service>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$status is of type object<Automattic\Jetpack\Status>, but the function expects a object<Automattic\Jetpac...omattic\Jetpack\Status>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
107
			return false;
108
		}
109
110
		$event_obj = $this->tracks_build_event_obj( $user, $event_name, $properties, $event_timestamp_millis );
111
112
		if ( is_wp_error( $event_obj->error ) ) {
113
			return $event_obj->error;
114
		}
115
116
		return $event_obj->record();
0 ignored issues
show
Bug introduced by
The method record does only exist in Jetpack_Tracks_Event, but not in WP_Error.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
117
	}
118
119
	/**
120
	 * Determines whether tracking should be enabled.
121
	 *
122
	 * @param Automattic\Jetpack\Terms_Of_Service $terms_of_service A Terms_Of_Service object.
123
	 * @param Automattic\Jetpack\Status           $status A Status object.
124
	 *
125
	 * @return boolean True if tracking should be enabled, else false.
126
	 */
127
	public function should_enable_tracking( $terms_of_service, $status ) {
128
		if ( $status->is_offline_mode() ) {
129
			return false;
130
		}
131
132
		return $terms_of_service->has_agreed() || $this->connection->is_user_connected();
133
	}
134
135
	/**
136
	 * Procedurally build a Tracks Event Object.
137
	 * NOTE: Use this only when the simpler Automattic\Jetpack\Tracking->jetpack_tracks_record_event() function won't work for you.
138
	 *
139
	 * @param WP_user $user                   WP_user object.
140
	 * @param string  $event_name             The name of the event.
141
	 * @param array   $properties             Custom properties to send with the event.
142
	 * @param int     $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $event_timestamp_millis not be false|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
143
	 *
144
	 * @return \Jetpack_Tracks_Event|\WP_Error
145
	 */
146
	private function tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
147
		$identity = $this->tracks_get_identity( $user->ID );
148
149
		$properties['user_lang'] = $user->get( 'WPLANG' );
150
151
		$blog_details = array(
152
			'blog_lang' => isset( $properties['blog_lang'] ) ? $properties['blog_lang'] : get_bloginfo( 'language' ),
153
		);
154
155
		$timestamp        = ( false !== $event_timestamp_millis ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
156
		$timestamp_string = is_string( $timestamp ) ? $timestamp : number_format( $timestamp, 0, '', '' );
157
158
		return new \Jetpack_Tracks_Event(
159
			array_merge(
0 ignored issues
show
Documentation introduced by
array_merge($blog_detail... => $timestamp_string)) is of type array, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
160
				$blog_details,
161
				(array) $properties,
162
				$identity,
163
				array(
164
					'_en' => $event_name,
165
					'_ts' => $timestamp_string,
166
				)
167
			)
168
		);
169
	}
170
171
	/**
172
	 * Get the identity to send to tracks.
173
	 *
174
	 * @param int $user_id The user id of the local user.
175
	 *
176
	 * @return array $identity
177
	 */
178
	public function tracks_get_identity( $user_id ) {
179
180
		// Meta is set, and user is still connected.  Use WPCOM ID.
181
		$wpcom_id = get_user_meta( $user_id, 'jetpack_tracks_wpcom_id', true );
182
		if ( $wpcom_id && $this->connection->is_user_connected( $user_id ) ) {
183
			return array(
184
				'_ut' => 'wpcom:user_id',
185
				'_ui' => $wpcom_id,
186
			);
187
		}
188
189
		// User is connected, but no meta is set yet.  Use WPCOM ID and set meta.
190
		if ( $this->connection->is_user_connected( $user_id ) ) {
191
			$wpcom_user_data = $this->connection->get_connected_user_data( $user_id );
192
			update_user_meta( $user_id, 'jetpack_tracks_wpcom_id', $wpcom_user_data['ID'] );
193
194
			return array(
195
				'_ut' => 'wpcom:user_id',
196
				'_ui' => $wpcom_user_data['ID'],
197
			);
198
		}
199
200
		// User isn't linked at all.  Fall back to anonymous ID.
201
		$anon_id = get_user_meta( $user_id, 'jetpack_tracks_anon_id', true );
202
		if ( ! $anon_id ) {
203
			$anon_id = \Jetpack_Tracks_Client::get_anon_id();
204
			add_user_meta( $user_id, 'jetpack_tracks_anon_id', $anon_id, false );
205
		}
206
207
		if ( ! isset( $_COOKIE['tk_ai'] ) && ! headers_sent() ) {
208
			setcookie( 'tk_ai', $anon_id );
209
		}
210
211
		return array(
212
			'_ut' => 'anon',
213
			'_ui' => $anon_id,
214
		);
215
216
	}
217
}
218