Completed
Push — update/tracking-in-dev-mode ( 792194...7adc28 )
by
unknown
48:11 queued 40:32
created

Tracking   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 188
Duplicated Lines 5.85 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 11
loc 188
rs 10
c 0
b 0
f 0
wmc 26
lcom 1
cbo 5

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A enqueue_tracks_scripts() 11 11 1
B record_user_event() 0 21 7
A tracks_record_event() 0 20 5
A tracks_build_event_obj() 0 24 4
B tracks_get_identity() 0 39 7

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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
104
		// Don't track users who have opted out or not agreed to our TOS, or are not running an active Jetpack.
105
		if ( ! \Jetpack::jetpack_tos_agreed() ) {
106
			return false;
107
		}
108
109
		$event_obj = $this->tracks_build_event_obj( $user, $event_name, $properties, $event_timestamp_millis );
110
111
		if ( is_wp_error( $event_obj->error ) ) {
112
			return $event_obj->error;
113
		}
114
115
		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...
116
	}
117
118
	/**
119
	 * Procedurally build a Tracks Event Object.
120
	 * NOTE: Use this only when the simpler Automattic\Jetpack\Tracking->jetpack_tracks_record_event() function won't work for you.
121
	 *
122
	 * @param WP_user $user                   WP_user object.
123
	 * @param string  $event_name             The name of the event.
124
	 * @param array   $properties             Custom properties to send with the event.
125
	 * @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...
126
	 *
127
	 * @return \Jetpack_Tracks_Event|\WP_Error
128
	 */
129
	private function tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
130
		$identity = $this->tracks_get_identity( $user->ID );
131
132
		$properties['user_lang'] = $user->get( 'WPLANG' );
133
134
		$blog_details = array(
135
			'blog_lang' => isset( $properties['blog_lang'] ) ? $properties['blog_lang'] : get_bloginfo( 'language' ),
136
		);
137
138
		$timestamp        = ( false !== $event_timestamp_millis ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
139
		$timestamp_string = is_string( $timestamp ) ? $timestamp : number_format( $timestamp, 0, '', '' );
140
141
		return new \Jetpack_Tracks_Event(
142
			array_merge(
143
				$blog_details,
144
				(array) $properties,
145
				$identity,
146
				array(
147
					'_en' => $event_name,
148
					'_ts' => $timestamp_string,
149
				)
150
			)
151
		);
152
	}
153
154
	/**
155
	 * Get the identity to send to tracks.
156
	 *
157
	 * @param int $user_id The user id of the local user.
158
	 *
159
	 * @return array $identity
160
	 */
161
	public function tracks_get_identity( $user_id ) {
162
163
		// Meta is set, and user is still connected.  Use WPCOM ID.
164
		$wpcom_id = get_user_meta( $user_id, 'jetpack_tracks_wpcom_id', true );
165
		if ( $wpcom_id && $this->connection->is_user_connected( $user_id ) ) {
166
			return array(
167
				'_ut' => 'wpcom:user_id',
168
				'_ui' => $wpcom_id,
169
			);
170
		}
171
172
		// User is connected, but no meta is set yet.  Use WPCOM ID and set meta.
173
		if ( $this->connection->is_user_connected( $user_id ) ) {
174
			$wpcom_user_data = $this->connection->get_connected_user_data( $user_id );
175
			update_user_meta( $user_id, 'jetpack_tracks_wpcom_id', $wpcom_user_data['ID'] );
176
177
			return array(
178
				'_ut' => 'wpcom:user_id',
179
				'_ui' => $wpcom_user_data['ID'],
180
			);
181
		}
182
183
		// User isn't linked at all.  Fall back to anonymous ID.
184
		$anon_id = get_user_meta( $user_id, 'jetpack_tracks_anon_id', true );
185
		if ( ! $anon_id ) {
186
			$anon_id = \Jetpack_Tracks_Client::get_anon_id();
187
			add_user_meta( $user_id, 'jetpack_tracks_anon_id', $anon_id, false );
188
		}
189
190
		if ( ! isset( $_COOKIE['tk_ai'] ) && ! headers_sent() ) {
191
			setcookie( 'tk_ai', $anon_id );
192
		}
193
194
		return array(
195
			'_ut' => 'anon',
196
			'_ui' => $anon_id,
197
		);
198
199
	}
200
}
201