Completed
Push — add/opentable-mobile-fallback ( b47953...957b6a )
by
unknown
10:18
created

Tracking::tracks_record_event()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 4
nop 4
dl 0
loc 23
rs 8.9297
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
use Automattic\Jetpack\Terms_Of_Service;
11
12
/**
13
 * The Tracking class, used to record events in wpcom
14
 */
15
class Tracking {
16
	/**
17
	 * Slug of the product that we are tracking.
18
	 *
19
	 * @var string
20
	 */
21
	private $product_name;
22
23
	/**
24
	 * Connection manager object.
25
	 *
26
	 * @var Object
27
	 */
28
	private $connection;
29
30
	/**
31
	 * Creates the Tracking object.
32
	 *
33
	 * @param String                                $product_name the slug of the product that we are tracking.
34
	 * @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...
35
	 */
36
	public function __construct( $product_name = 'jetpack', $connection = null ) {
37
		$this->product_name = $product_name;
38
		$this->connection   = $connection;
39
		if ( is_null( $this->connection ) ) {
40
			// TODO We should always pass a Connection.
41
			$this->connection = new Connection\Manager();
42
		}
43
	}
44
45
	/**
46
	 * Enqueue script necessary for tracking.
47
	 */
48 View Code Duplication
	public function enqueue_tracks_scripts() {
49
		wp_enqueue_script( 'jptracks', plugins_url( '_inc/lib/tracks/tracks-ajax.js', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION, true );
50
		wp_localize_script(
51
			'jptracks',
52
			'jpTracksAJAX',
53
			array(
54
				'ajaxurl'            => admin_url( 'admin-ajax.php' ),
55
				'jpTracksAJAX_nonce' => wp_create_nonce( 'jp-tracks-ajax-nonce' ),
56
			)
57
		);
58
	}
59
60
	/**
61
	 * Send an event in Tracks.
62
	 *
63
	 * @param string $event_type Type of the event.
64
	 * @param array  $data       Data to send with the event.
65
	 * @param mixed  $user       username, user_id, or WP_user object.
66
	 */
67
	public function record_user_event( $event_type, $data = array(), $user = null ) {
68
		if ( ! $user ) {
69
			$user = wp_get_current_user();
70
		}
71
		$site_url = get_option( 'siteurl' );
72
73
		$data['_via_ua']  = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
74
		$data['_via_ip']  = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
75
		$data['_lg']      = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
76
		$data['blog_url'] = $site_url;
77
		$data['blog_id']  = \Jetpack_Options::get_option( 'id' );
78
79
		// Top level events should not be namespaced.
80
		if ( '_aliasUser' !== $event_type ) {
81
			$event_type = $this->product_name . '_' . $event_type;
82
		}
83
84
		$data['jetpack_version'] = defined( 'JETPACK__VERSION' ) ? JETPACK__VERSION : '0';
85
86
		return $this->tracks_record_event( $user, $event_type, $data );
87
	}
88
89
	/**
90
	 * Record an event in Tracks - this is the preferred way to record events from PHP.
91
	 *
92
	 * @param mixed  $user                   username, user_id, or WP_user object.
93
	 * @param string $event_name             The name of the event.
94
	 * @param array  $properties             Custom properties to send with the event.
95
	 * @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...
96
	 *
97
	 * @return bool true for success | \WP_Error if the event pixel could not be fired
98
	 */
99
	public function tracks_record_event( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
100
101
		// We don't want to track user events during unit tests/CI runs.
102
		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...
103
			return false;
104
		}
105
		$terms_of_service = new Terms_Of_Service();
106
		// Don't track users who have opted out or not agreed to our TOS, or are not running an active Jetpack.
107
		if (
108
			! $terms_of_service->has_agreed()
109
			|| ! $this->connection->is_user_connected()
110
		) {
111
			return false;
112
		}
113
114
		$event_obj = $this->tracks_build_event_obj( $user, $event_name, $properties, $event_timestamp_millis );
115
116
		if ( is_wp_error( $event_obj->error ) ) {
117
			return $event_obj->error;
118
		}
119
120
		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...
121
	}
122
123
	/**
124
	 * Procedurally build a Tracks Event Object.
125
	 * NOTE: Use this only when the simpler Automattic\Jetpack\Tracking->jetpack_tracks_record_event() function won't work for you.
126
	 *
127
	 * @param WP_user $user                   WP_user object.
128
	 * @param string  $event_name             The name of the event.
129
	 * @param array   $properties             Custom properties to send with the event.
130
	 * @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...
131
	 *
132
	 * @return \Jetpack_Tracks_Event|\WP_Error
133
	 */
134
	private function tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
135
		$identity = $this->tracks_get_identity( $user->ID );
136
137
		$properties['user_lang'] = $user->get( 'WPLANG' );
138
139
		$blog_details = array(
140
			'blog_lang' => isset( $properties['blog_lang'] ) ? $properties['blog_lang'] : get_bloginfo( 'language' ),
141
		);
142
143
		$timestamp        = ( false !== $event_timestamp_millis ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
144
		$timestamp_string = is_string( $timestamp ) ? $timestamp : number_format( $timestamp, 0, '', '' );
145
146
		return new \Jetpack_Tracks_Event(
147
			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...
148
				$blog_details,
149
				(array) $properties,
150
				$identity,
151
				array(
152
					'_en' => $event_name,
153
					'_ts' => $timestamp_string,
154
				)
155
			)
156
		);
157
	}
158
159
	/**
160
	 * Get the identity to send to tracks.
161
	 *
162
	 * @param int $user_id The user id of the local user.
163
	 *
164
	 * @return array $identity
165
	 */
166
	public function tracks_get_identity( $user_id ) {
167
168
		// Meta is set, and user is still connected.  Use WPCOM ID.
169
		$wpcom_id = get_user_meta( $user_id, 'jetpack_tracks_wpcom_id', true );
170
		if ( $wpcom_id && $this->connection->is_user_connected( $user_id ) ) {
171
			return array(
172
				'_ut' => 'wpcom:user_id',
173
				'_ui' => $wpcom_id,
174
			);
175
		}
176
177
		// User is connected, but no meta is set yet.  Use WPCOM ID and set meta.
178
		if ( $this->connection->is_user_connected( $user_id ) ) {
179
			$wpcom_user_data = $this->connection->get_connected_user_data( $user_id );
180
			update_user_meta( $user_id, 'jetpack_tracks_wpcom_id', $wpcom_user_data['ID'] );
181
182
			return array(
183
				'_ut' => 'wpcom:user_id',
184
				'_ui' => $wpcom_user_data['ID'],
185
			);
186
		}
187
188
		// User isn't linked at all.  Fall back to anonymous ID.
189
		$anon_id = get_user_meta( $user_id, 'jetpack_tracks_anon_id', true );
190
		if ( ! $anon_id ) {
191
			$anon_id = \Jetpack_Tracks_Client::get_anon_id();
192
			add_user_meta( $user_id, 'jetpack_tracks_anon_id', $anon_id, false );
193
		}
194
195
		if ( ! isset( $_COOKIE['tk_ai'] ) && ! headers_sent() ) {
196
			setcookie( 'tk_ai', $anon_id );
197
		}
198
199
		return array(
200
			'_ut' => 'anon',
201
			'_ui' => $anon_id,
202
		);
203
204
	}
205
}
206