Freemius::_sync_plugin_license()   F
last analyzed

Complexity

Conditions 46
Paths 488

Size

Total Lines 259
Code Lines 166

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 1
Metric Value
c 6
b 0
f 1
dl 0
loc 259
rs 2.875
nc 488
cc 46
eloc 166
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 13 and the first side effect is on line 9.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
	/**
3
	 * @package     Freemius
4
	 * @copyright   Copyright (c) 2015, Freemius, Inc.
5
	 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
6
	 * @since       1.0.3
7
	 */
8
	if ( ! defined( 'ABSPATH' ) ) {
9
		exit;
10
	}
11
12
	// "final class"
13
	class Freemius extends Freemius_Abstract {
14
		/**
15
		 * SDK Version
16
		 *
17
		 * @var string
18
		 */
19
		public $version = WP_FS__SDK_VERSION;
20
21
		#region Plugin Info
22
23
		/**
24
		 * @since 1.0.1
25
		 *
26
		 * @var string
27
		 */
28
		private $_slug;
29
30
		/**
31
		 * @since 1.0.0
32
		 *
33
		 * @var string
34
		 */
35
		private $_plugin_basename;
36
		/**
37
		 * @since 1.0.0
38
		 *
39
		 * @var string
40
		 */
41
		private $_free_plugin_basename;
42
		/**
43
		 * @since 1.0.0
44
		 *
45
		 * @var string
46
		 */
47
		private $_plugin_dir_path;
48
		/**
49
		 * @since 1.0.0
50
		 *
51
		 * @var string
52
		 */
53
		private $_plugin_dir_name;
54
		/**
55
		 * @since 1.0.0
56
		 *
57
		 * @var string
58
		 */
59
		private $_plugin_main_file_path;
60
		/**
61
		 * @var string[]
62
		 */
63
		private $_plugin_data;
64
		/**
65
		 * @since 1.0.9
66
		 *
67
		 * @var string
68
		 */
69
		private $_plugin_name;
70
71
		#endregion Plugin Info
72
73
		/**
74
		 * @since 1.0.9
75
		 *
76
		 * @var bool If false, don't turn Freemius on.
77
		 */
78
		private $_is_on;
79
80
		/**
81
		 * @since 1.1.3
82
		 *
83
		 * @var bool If false, don't turn Freemius on.
84
		 */
85
		private $_is_anonymous;
86
87
		/**
88
		 * @since 1.0.9
89
		 * @var bool If false, issues with connectivity to Freemius API.
90
		 */
91
		private $_has_api_connection;
92
93
		/**
94
		 * @since 1.0.9
95
		 * @var bool Hints the SDK if plugin can support anonymous mode (if skip connect is visible).
96
		 */
97
		private $_enable_anonymous;
98
99
		/**
100
		 * @since 1.1.7.5
101
		 * @var bool Hints the SDK if plugin should run in anonymous mode (only adds feedback form).
102
		 */
103
		private $_anonymous_mode;
104
105
		/**
106
		 * @since 1.0.8
107
		 * @var bool Hints the SDK if the plugin has any paid plans.
108
		 */
109
		private $_has_paid_plans;
110
111
		/**
112
		 * @since 1.0.7
113
		 * @var bool Hints the SDK if the plugin is WordPress.org compliant.
114
		 */
115
		private $_is_org_compliant;
116
117
		/**
118
		 * @since 1.0.7
119
		 * @var bool Hints the SDK if the plugin is has add-ons.
120
		 */
121
		private $_has_addons;
122
123
		/**
124
		 * @since 1.1.6
125
		 * @var string[]bool.
126
		 */
127
		private $_permissions;
128
129
		/**
130
		 * @var FS_Key_Value_Storage
131
		 */
132
		private $_storage;
133
134
		/**
135
		 * @since 1.0.0
136
		 *
137
		 * @var FS_Logger
138
		 */
139
		private $_logger;
140
		/**
141
		 * @since 1.0.4
142
		 *
143
		 * @var FS_Plugin
144
		 */
145
		private $_plugin = false;
146
		/**
147
		 * @since 1.0.4
148
		 *
149
		 * @var FS_Plugin
150
		 */
151
		private $_parent_plugin = false;
152
		/**
153
		 * @since 1.1.1
154
		 *
155
		 * @var Freemius
156
		 */
157
		private $_parent = false;
158
		/**
159
		 * @since 1.0.1
160
		 *
161
		 * @var FS_User
162
		 */
163
		private $_user = false;
164
		/**
165
		 * @since 1.0.1
166
		 *
167
		 * @var FS_Site
168
		 */
169
		private $_site = false;
170
		/**
171
		 * @since 1.0.1
172
		 *
173
		 * @var FS_Plugin_License
174
		 */
175
		private $_license;
176
		/**
177
		 * @since 1.0.2
178
		 *
179
		 * @var FS_Plugin_Plan[]
180
		 */
181
		private $_plans = false;
182
		/**
183
		 * @var FS_Plugin_License[]
184
		 * @since 1.0.5
185
		 */
186
		private $_licenses = false;
187
188
		/**
189
		 * @since 1.0.1
190
		 *
191
		 * @var FS_Admin_Menu_Manager
192
		 */
193
		private $_menu;
194
195
		/**
196
		 * @var FS_Admin_Notice_Manager
197
		 */
198
		private $_admin_notices;
199
200
		/**
201
		 * @since 1.1.6
202
		 *
203
		 * @var FS_Admin_Notice_Manager
204
		 */
205
		private static $_global_admin_notices;
206
207
		/**
208
		 * @var FS_Logger
209
		 * @since 1.0.0
210
		 */
211
		private static $_static_logger;
212
213
		/**
214
		 * @var FS_Option_Manager
215
		 * @since 1.0.2
216
		 */
217
		private static $_accounts;
218
219
		/**
220
		 * @var Freemius[]
221
		 */
222
		private static $_instances = array();
223
224
225
		/* Ctor
226
------------------------------------------------------------------------------------------------------------------*/
227
228
		private function __construct( $slug ) {
229
			$this->_slug = $slug;
230
231
			$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
232
233
			$this->_storage = FS_Key_Value_Storage::instance( 'plugin_data', $this->_slug );
234
235
			$this->_plugin_main_file_path = $this->_find_caller_plugin_file();
236
			$this->_plugin_dir_path       = plugin_dir_path( $this->_plugin_main_file_path );
237
			$this->_plugin_basename       = plugin_basename( $this->_plugin_main_file_path );
238
			$this->_free_plugin_basename  = str_replace( '-premium/', '/', $this->_plugin_basename );
239
240
			$base_name_split        = explode( '/', $this->_plugin_basename );
241
			$this->_plugin_dir_name = $base_name_split[0];
242
243
			if ( $this->_logger->is_on() ) {
244
				$this->_logger->info( 'plugin_main_file_path = ' . $this->_plugin_main_file_path );
245
				$this->_logger->info( 'plugin_dir_path = ' . $this->_plugin_dir_path );
246
				$this->_logger->info( 'plugin_basename = ' . $this->_plugin_basename );
247
				$this->_logger->info( 'free_plugin_basename = ' . $this->_free_plugin_basename );
248
				$this->_logger->info( 'plugin_dir_name = ' . $this->_plugin_dir_name );
249
			}
250
251
			// Remember link between file to slug.
252
			$this->store_file_slug_map();
253
254
			// Store plugin's initial install timestamp.
255
			if ( ! isset( $this->_storage->install_timestamp ) ) {
256
				$this->_storage->install_timestamp = WP_FS__SCRIPT_START_TIME;
0 ignored issues
show
Documentation introduced by
The property install_timestamp does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
257
			}
258
259
			$this->_plugin = FS_Plugin_Manager::instance( $this->_slug )->get();
260
261
			$this->_admin_notices = FS_Admin_Notice_Manager::instance(
262
				$slug,
263
				is_object( $this->_plugin ) ? $this->_plugin->title : ''
264
			);
265
266
			if ( 'true' === fs_request_get( 'fs_clear_api_cache' ) ||
267
			     'true' === fs_request_is_action( 'restart_freemius' )
268
			) {
269
				FS_Api::clear_cache();
270
			}
271
272
			$this->_register_hooks();
273
274
			$this->_load_account();
275
276
			$this->_version_updates_handler();
277
		}
278
279
		/**
280
		 * @author Vova Feldman (@svovaf)
281
		 * @since  1.0.9
282
		 */
283
		private function _version_updates_handler() {
284
			if ( ! isset( $this->_storage->sdk_version ) || $this->_storage->sdk_version != $this->version ) {
0 ignored issues
show
Documentation introduced by
The property sdk_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
285
				// Freemius version upgrade mode.
286
				$this->_storage->sdk_last_version = $this->_storage->sdk_version;
0 ignored issues
show
Documentation introduced by
The property sdk_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property sdk_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
287
				$this->_storage->sdk_version      = $this->version;
0 ignored issues
show
Documentation introduced by
The property sdk_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
288
289
				if ( empty( $this->_storage->sdk_last_version ) ||
0 ignored issues
show
Documentation introduced by
The property sdk_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
290
				     version_compare( $this->_storage->sdk_last_version, $this->version, '<' )
0 ignored issues
show
Documentation introduced by
The property sdk_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
291
				) {
292
					$this->_storage->sdk_upgrade_mode   = true;
0 ignored issues
show
Documentation introduced by
The property sdk_upgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
293
					$this->_storage->sdk_downgrade_mode = false;
0 ignored issues
show
Documentation introduced by
The property sdk_downgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
294
				} else {
295
					$this->_storage->sdk_downgrade_mode = true;
0 ignored issues
show
Documentation introduced by
The property sdk_downgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
296
					$this->_storage->sdk_upgrade_mode   = false;
0 ignored issues
show
Documentation introduced by
The property sdk_upgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
297
298
				}
299
300
				$this->do_action( 'sdk_version_update', $this->_storage->sdk_last_version, $this->version );
0 ignored issues
show
Documentation introduced by
The property sdk_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
301
			}
302
303
			$plugin_version = $this->get_plugin_version();
304
			if ( ! isset( $this->_storage->plugin_version ) || $this->_storage->plugin_version != $plugin_version ) {
0 ignored issues
show
Documentation introduced by
The property plugin_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
305
				// Plugin version upgrade mode.
306
				$this->_storage->plugin_last_version = $this->_storage->plugin_version;
0 ignored issues
show
Documentation introduced by
The property plugin_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property plugin_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
307
				$this->_storage->plugin_version      = $plugin_version;
0 ignored issues
show
Documentation introduced by
The property plugin_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
308
309
				if ( empty( $this->_storage->plugin_last_version ) ||
0 ignored issues
show
Documentation introduced by
The property plugin_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
310
				     version_compare( $this->_storage->plugin_last_version, $plugin_version, '<' )
0 ignored issues
show
Documentation introduced by
The property plugin_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
311
				) {
312
					$this->_storage->plugin_upgrade_mode   = true;
0 ignored issues
show
Documentation introduced by
The property plugin_upgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
313
					$this->_storage->plugin_downgrade_mode = false;
0 ignored issues
show
Documentation introduced by
The property plugin_downgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
314
				} else {
315
					$this->_storage->plugin_downgrade_mode = true;
0 ignored issues
show
Documentation introduced by
The property plugin_downgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
316
					$this->_storage->plugin_upgrade_mode   = false;
0 ignored issues
show
Documentation introduced by
The property plugin_upgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
317
				}
318
319
				if ( ! empty( $this->_storage->plugin_last_version ) ) {
0 ignored issues
show
Documentation introduced by
The property plugin_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
320
					// Different version of the plugin was installed before, therefore it's an update.
321
					$this->_storage->is_plugin_new_install = false;
0 ignored issues
show
Documentation introduced by
The property is_plugin_new_install does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
322
				}
323
324
				$this->do_action( 'plugin_version_update', $this->_storage->plugin_last_version, $plugin_version );
0 ignored issues
show
Documentation introduced by
The property plugin_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
325
			}
326
		}
327
328
		/**
329
		 * @author Vova Feldman (@svovaf)
330
		 * @since  1.1.5
331
		 *
332
		 * @param string $sdk_prev_version
333
		 * @param string $sdk_version
334
		 */
335
		function _data_migration( $sdk_prev_version, $sdk_version ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
336
			/**
337
			 * @since 1.1.7.3 Fixed unwanted connectivity test cleanup.
338
			 */
339
			if ( empty( $sdk_prev_version ) ) {
340
				return;
341
			}
342
343
			if ( version_compare( $sdk_prev_version, '1.1.5', '<' ) &&
344
			     version_compare( $sdk_version, '1.1.5', '>=' )
345
			) {
346
				// On version 1.1.5 merged connectivity and is_on data.
347
				if ( isset( $this->_storage->connectivity_test ) ) {
348
					if ( ! isset( $this->_storage->is_on ) ) {
349
						unset( $this->_storage->connectivity_test );
350
					} else {
351
						$connectivity_data              = $this->_storage->connectivity_test;
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
352
						$connectivity_data['is_active'] = $this->_storage->is_on['is_active'];
0 ignored issues
show
Documentation introduced by
The property is_on does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
353
						$connectivity_data['timestamp'] = $this->_storage->is_on['timestamp'];
0 ignored issues
show
Documentation introduced by
The property is_on does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
354
355
						// Override.
356
						$this->_storage->connectivity_test = $connectivity_data;
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
357
358
						// Remove previous structure.
359
						unset( $this->_storage->is_on );
360
					}
361
362
				}
363
			}
364
		}
365
366
		/**
367
		 * @author Vova Feldman (@svovaf)
368
		 * @since  1.0.9
369
		 */
370
		private function _register_hooks() {
371
			if ( is_admin() ) {
372
				// Hook to plugin activation
373
				register_activation_hook( $this->_plugin_main_file_path, array(
374
					&$this,
375
					'_activate_plugin_event_hook'
376
				) );
377
378
				// Hook to plugin uninstall.
379
				register_uninstall_hook( $this->_plugin_main_file_path, array( 'Freemius', '_uninstall_plugin_hook' ) );
380
381
				if ( ! $this->is_ajax() ) {
382
					if ( ! $this->is_addon() ) {
383
						add_action( 'init', array( &$this, '_add_default_submenu_items' ), WP_FS__LOWEST_PRIORITY );
384
						add_action( 'admin_menu', array( &$this, '_prepare_admin_menu' ), WP_FS__LOWEST_PRIORITY );
385
					}
386
				}
387
			}
388
389
			register_deactivation_hook( $this->_plugin_main_file_path, array( &$this, '_deactivate_plugin_hook' ) );
390
391
			add_action( 'init', array( &$this, '_redirect_on_clicked_menu_link' ), WP_FS__LOWEST_PRIORITY );
392
393
			$this->add_action( 'after_plans_sync', array( &$this, '_check_for_trial_plans' ) );
394
395
			$this->add_action( 'sdk_version_update', array( &$this, '_data_migration' ), WP_FS__DEFAULT_PRIORITY, 2 );
396
		}
397
398
		/**
399
		 * @author Vova Feldman (@svovaf)
400
		 * @since  1.0.9
401
		 */
402
		private function _register_account_hooks() {
403
			if ( is_admin() ) {
404
				if ( ! $this->is_ajax() ) {
405
					if ( $this->has_trial_plan() ) {
406
						$last_time_trial_promotion_shown = $this->_storage->get( 'trial_promotion_shown', false );
407
						if ( ! $this->_site->is_trial_utilized() &&
408
						     (
409
							     // Show promotion if never shown it yet and 24 hours after initial activation.
410
							     ( false === $last_time_trial_promotion_shown && $this->_storage->activation_timestamp < ( time() - WP_FS__TIME_24_HOURS_IN_SEC ) ) ||
0 ignored issues
show
Documentation introduced by
The property activation_timestamp does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
411
							     // Show promotion in every 30 days.
412
							     ( is_numeric( $last_time_trial_promotion_shown ) && 30 * WP_FS__TIME_24_HOURS_IN_SEC < time() - $last_time_trial_promotion_shown ) )
413
						) {
414
							$this->add_action( 'after_init_plugin_registered', array( &$this, '_add_trial_notice' ) );
415
						}
416
					}
417
				}
418
419
				// If user is paying or in trial and have the free version installed,
420
				// assume that the deactivation is for the upgrade process.
421
				if ( ! $this->is_paying_or_trial() || $this->is_premium() ) {
422
					add_action( 'wp_ajax_submit-uninstall-reason', array( &$this, '_submit_uninstall_reason_action' ) );
423
424
					global $pagenow;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
425
					if ( 'plugins.php' === $pagenow ) {
426
						add_action( 'admin_footer', array( &$this, '_add_deactivation_feedback_dialog_box' ) );
427
					}
428
				}
429
			}
430
		}
431
432
		/**
433
		 * Leverage backtrace to find caller plugin file path.
434
		 *
435
		 * @author Vova Feldman (@svovaf)
436
		 * @since  1.0.6
437
		 *
438
		 * @return string
439
		 *
440
		 * @uses   fs_find_caller_plugin_file
441
		 */
442
		private function _find_caller_plugin_file() {
443
			// Try to load the cached value of the file path.
444
			if ( isset( $this->_storage->plugin_main_file ) ) {
445
				if ( file_exists( $this->_storage->plugin_main_file->path ) ) {
0 ignored issues
show
Documentation introduced by
The property plugin_main_file does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
446
					return $this->_storage->plugin_main_file->path;
0 ignored issues
show
Documentation introduced by
The property plugin_main_file does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
447
				}
448
			}
449
450
			$plugin_file = fs_find_caller_plugin_file();
451
452
			$this->_storage->plugin_main_file = (object) array(
0 ignored issues
show
Documentation introduced by
The property plugin_main_file does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
453
				'path' => fs_normalize_path( $plugin_file ),
454
			);
455
456
			return $plugin_file;
457
		}
458
459
460
		#region Deactivation Feedback Form ------------------------------------------------------------------
461
462
		/**
463
		 * Displays a confirmation and feedback dialog box when the user clicks on the "Deactivate" link on the plugins
464
		 * page.
465
		 *
466
		 * @author Vova Feldman (@svovaf)
467
		 * @author Leo Fajardo (@leorw)
468
		 * @since  1.1.2
469
		 */
470
		function _add_deactivation_feedback_dialog_box() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
471
			fs_enqueue_local_style( 'fs_deactivation_feedback', '/admin/deactivation-feedback.css' );
472
473
			/* Check the type of user:
474
			 * 1. Long-term (long-term)
475
			 * 2. Non-registered and non-anonymous short-term (non-registered-and-non-anonymous-short-term).
476
			 * 3. Short-term (short-term)
477
			 */
478
			$is_long_term_user = true;
479
480
			// Check if the site is at least 2 days old.
481
			$time_installed = $this->_storage->install_timestamp;
0 ignored issues
show
Documentation introduced by
The property install_timestamp does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
482
483
			// Difference in seconds.
484
			$date_diff = time() - $time_installed;
485
486
			// Convert seconds to days.
487
			$date_diff_days = floor( $date_diff / ( 60 * 60 * 24 ) );
488
489
			if ( $date_diff_days < 2 ) {
490
				$is_long_term_user = false;
491
			}
492
493
			$is_long_term_user = $this->apply_filters( 'is_long_term_user', $is_long_term_user );
494
495
			if ( $is_long_term_user ) {
496
				$user_type = 'long-term';
497
			} else {
498
				if ( ! $this->is_registered() && ! $this->is_anonymous() ) {
499
					$user_type = 'non-registered-and-non-anonymous-short-term';
500
				} else {
501
					$user_type = 'short-term';
502
				}
503
			}
504
505
			$uninstall_reasons = $this->_get_uninstall_reasons( $user_type );
506
507
			// Load the HTML template for the deactivation feedback dialog box.
508
			$vars = array(
509
				'reasons' => $uninstall_reasons,
510
				'slug'    => $this->_slug
511
			);
512
513
			/**
514
			 * @todo Deactivation form core functions should be loaded only once! Otherwise, when there are multiple Freemius powered plugins the same code is loaded multiple times. The only thing that should be loaded differently is the various deactivation reasons object based on the state of the plugin.
515
			 */
516
			fs_require_template( 'deactivation-feedback-modal.php', $vars );
517
		}
518
519
		/**
520
		 * @author Leo Fajardo (leorw)
521
		 * @since  1.1.2
522
		 *
523
		 * @param string $user_type
524
		 *
525
		 * @return array The uninstall reasons for the specified user type.
526
		 */
527
		function _get_uninstall_reasons( $user_type = 'long-term' ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
528
			$reason_found_better_plugin = array(
529
				'id'                => 2,
530
				'text'              => __fs( 'reason-found-a-better-plugin', $this->_slug ),
531
				'input_type'        => 'textfield',
532
				'input_placeholder' => __fs( 'placeholder-plugin-name', $this->_slug )
533
			);
534
535
			$reason_other = array(
536
				'id'                => 7,
537
				'text'              => __fs( 'reason-other', $this->_slug ),
538
				'input_type'        => 'textfield',
539
				'input_placeholder' => ''
540
			);
541
542
			$long_term_user_reasons = array(
543
				array(
544
					'id'                => 1,
545
					'text'              => __fs( 'reason-no-longer-needed', $this->_slug ),
546
					'input_type'        => '',
547
					'input_placeholder' => ''
548
				),
549
				$reason_found_better_plugin,
550
				array(
551
					'id'                => 3,
552
					'text'              => __fs( 'reason-needed-for-a-short-period', $this->_slug ),
553
					'input_type'        => '',
554
					'input_placeholder' => ''
555
				),
556
				array(
557
					'id'                => 4,
558
					'text'              => __fs( 'reason-broke-my-site', $this->_slug ),
559
					'input_type'        => '',
560
					'input_placeholder' => ''
561
				),
562
				array(
563
					'id'                => 5,
564
					'text'              => __fs( 'reason-suddenly-stopped-working', $this->_slug ),
565
					'input_type'        => '',
566
					'input_placeholder' => ''
567
				)
568
			);
569
570
			if ( $this->is_paying() ) {
571
				$long_term_user_reasons[] = array(
572
					'id'                => 6,
573
					'text'              => __fs( 'reason-cant-pay-anymore', $this->_slug ),
574
					'input_type'        => 'textfield',
575
					'input_placeholder' => __fs( 'placeholder-comfortable-price', $this->_slug )
576
				);
577
			}
578
579
			$long_term_user_reasons[] = $reason_other;
580
581
			$uninstall_reasons = array(
582
				'long-term'                                   => $long_term_user_reasons,
583
				'non-registered-and-non-anonymous-short-term' => array(
584
					array(
585
						'id'                => 8,
586
						'text'              => __fs( 'reason-didnt-work', $this->_slug ),
587
						'input_type'        => '',
588
						'input_placeholder' => ''
589
					),
590
					array(
591
						'id'                => 9,
592
						'text'              => __fs( 'reason-dont-like-to-share-my-information', $this->_slug ),
593
						'input_type'        => '',
594
						'input_placeholder' => ''
595
					),
596
					$reason_found_better_plugin,
597
					$reason_other
598
				),
599
				'short-term'                                  => array(
600
					array(
601
						'id'                => 10,
602
						'text'              => __fs( 'reason-couldnt-make-it-work', $this->_slug ),
603
						'input_type'        => '',
604
						'input_placeholder' => ''
605
					),
606
					$reason_found_better_plugin,
607
					array(
608
						'id'                => 11,
609
						'text'              => __fs( 'reason-great-but-need-specific-feature', $this->_slug ),
610
						'input_type'        => 'textarea',
611
						'input_placeholder' => __fs( 'placeholder-feature', $this->_slug )
612
					),
613
					array(
614
						'id'                => 12,
615
						'text'              => __fs( 'reason-not-working', $this->_slug ),
616
						'input_type'        => 'textarea',
617
						'input_placeholder' => __fs( 'placeholder-share-what-didnt-work', $this->_slug )
618
					),
619
					array(
620
						'id'                => 13,
621
						'text'              => __fs( 'reason-not-what-i-was-looking-for', $this->_slug ),
622
						'input_type'        => 'textarea',
623
						'input_placeholder' => __fs( 'placeholder-what-youve-been-looking-for', $this->_slug )
624
					),
625
					array(
626
						'id'                => 14,
627
						'text'              => __fs( 'reason-didnt-work-as-expected', $this->_slug ),
628
						'input_type'        => 'textarea',
629
						'input_placeholder' => __fs( 'placeholder-what-did-you-expect', $this->_slug )
630
					),
631
					$reason_other
632
				)
633
			);
634
635
			$uninstall_reasons = $this->apply_filters( 'uninstall_reasons', $uninstall_reasons );
636
637
			return $uninstall_reasons[ $user_type ];
638
		}
639
640
		/**
641
		 * Called after the user has submitted his reason for deactivating the plugin.
642
		 *
643
		 * @author Leo Fajardo (@leorw)
644
		 * @since  1.1.2
645
		 */
646
		function _submit_uninstall_reason_action() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
647
			if ( ! isset( $_POST['reason_id'] ) ) {
648
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method _submit_uninstall_reason_action() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
649
			}
650
651
			$reason_info = isset( $_REQUEST['reason_info'] ) ? trim( stripslashes( $_REQUEST['reason_info'] ) ) : '';
652
653
			$reason = (object) array(
654
				'id'   => $_POST['reason_id'],
655
				'info' => substr( $reason_info, 0, 128 )
656
			);
657
658
			$this->_storage->store( 'uninstall_reason', $reason );
659
660
			// Print '1' for successful operation.
661
			echo 1;
662
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method _submit_uninstall_reason_action() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
663
		}
664
665
		#endregion Deactivation Feedback Form ------------------------------------------------------------------
666
667
		#region Instance ------------------------------------------------------------------
668
669
		/**
670
		 * Main singleton instance.
671
		 *
672
		 * @author Vova Feldman (@svovaf)
673
		 * @since  1.0.0
674
		 *
675
		 * @param $slug
676
		 *
677
		 * @return Freemius
678
		 */
679
		static function instance( $slug ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
680
			$slug = strtolower( $slug );
681
682
			if ( ! isset( self::$_instances[ $slug ] ) ) {
683
				if ( 0 === count( self::$_instances ) ) {
684
					self::_load_required_static();
685
				}
686
687
				self::$_instances[ $slug ] = new Freemius( $slug );
688
			}
689
690
			return self::$_instances[ $slug ];
691
		}
692
693
		/**
694
		 * @author Vova Feldman (@svovaf)
695
		 * @since  1.0.6
696
		 *
697
		 * @param string|number $slug_or_id
698
		 *
699
		 * @return bool
700
		 */
701
		private static function has_instance( $slug_or_id ) {
702
			return ! is_numeric( $slug_or_id ) ?
703
				isset( self::$_instances[ strtolower( $slug_or_id ) ] ) :
704
				( false !== self::get_instance_by_id( $slug_or_id ) );
705
		}
706
707
		/**
708
		 * @author Vova Feldman (@svovaf)
709
		 * @since  1.0.6
710
		 *
711
		 * @param $id
712
		 *
713
		 * @return false|Freemius
714
		 */
715
		static function get_instance_by_id( $id ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
716
			foreach ( self::$_instances as $slug => $instance ) {
717
				if ( $id == $instance->get_id() ) {
718
					return $instance;
719
				}
720
			}
721
722
			return false;
723
		}
724
725
		/**
726
		 *
727
		 * @author Vova Feldman (@svovaf)
728
		 * @since  1.0.1
729
		 *
730
		 * @param $plugin_file
731
		 *
732
		 * @return false|Freemius
733
		 */
734
		static function get_instance_by_file( $plugin_file ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
735
			$slug = self::find_slug_by_basename( $plugin_file );
736
737
			return ( false !== $slug ) ?
738
				self::instance( $slug ) :
739
				false;
740
		}
741
742
		/**
743
		 * @author Vova Feldman (@svovaf)
744
		 * @since  1.0.6
745
		 *
746
		 * @return false|Freemius
747
		 */
748
		function get_parent_instance() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
749
			return self::get_instance_by_id( $this->_plugin->parent_plugin_id );
750
		}
751
752
		/**
753
		 * @author Vova Feldman (@svovaf)
754
		 * @since  1.0.6
755
		 *
756
		 * @param $slug_or_id
757
		 *
758
		 * @return bool|Freemius
759
		 */
760
		function get_addon_instance( $slug_or_id ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
761
			return ! is_numeric( $slug_or_id ) ?
762
				self::instance( strtolower( $slug_or_id ) ) :
763
				self::get_instance_by_id( $slug_or_id );
764
		}
765
766
		#endregion ------------------------------------------------------------------
767
768
		/**
769
		 * @author Vova Feldman (@svovaf)
770
		 * @since  1.0.6
771
		 *
772
		 * @return bool
773
		 */
774
		function is_parent_plugin_installed() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
775
			return self::has_instance( $this->_plugin->parent_plugin_id );
776
		}
777
778
		/**
779
		 * Check if add-on parent plugin in activation mode.
780
		 *
781
		 * @author Vova Feldman (@svovaf)
782
		 * @since  1.0.7
783
		 *
784
		 * @return bool
785
		 */
786
		function is_parent_in_activation() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
787
			$parent_fs = $this->get_parent_instance();
788
			if ( ! is_object( $parent_fs ) ) {
789
				return false;
790
			}
791
792
			return ( $parent_fs->is_activation_mode() );
793
		}
794
795
		/**
796
		 * Is plugin in activation mode.
797
		 *
798
		 * @author Vova Feldman (@svovaf)
799
		 * @since  1.0.7
800
		 *
801
		 * @return bool
802
		 */
803
		function is_activation_mode() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
804
			return (
805
				! $this->is_registered() &&
806
				( ! $this->enable_anonymous() ||
807
				  ( ! $this->is_anonymous() && ! $this->is_pending_activation() ) )
808
			);
809
		}
810
811
		/**
812
		 * Get collection of all active plugins.
813
		 *
814
		 * @author Vova Feldman (@svovaf)
815
		 * @since  1.0.9
816
		 *
817
		 * @return array[string]array
0 ignored issues
show
Documentation introduced by
The doc-type array[string]array could not be parsed: Expected "]" at position 2, but found "string". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
818
		 */
819
		private function get_active_plugins() {
820
			self::require_plugin_essentials();
821
822
			$active_plugin            = array();
823
			$all_plugins              = get_plugins();
824
			$active_plugins_basenames = get_option( 'active_plugins' );
825
826
			foreach ( $active_plugins_basenames as $plugin_basename ) {
827
				$active_plugin[ $plugin_basename ] = $all_plugins[ $plugin_basename ];
828
			}
829
830
			return $active_plugin;
831
		}
832
833
		private static $_statics_loaded = false;
834
835
		/**
836
		 * Load static resources.
837
		 *
838
		 * @author Vova Feldman (@svovaf)
839
		 * @since  1.0.1
840
		 */
841
		private static function _load_required_static() {
842
			if ( self::$_statics_loaded ) {
843
				return;
844
			}
845
846
			self::$_static_logger = FS_Logger::get_logger( WP_FS__SLUG, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
847
848
			self::$_static_logger->entrance();
849
850
			self::$_accounts = FS_Option_Manager::get_manager( WP_FS__ACCOUNTS_OPTION_NAME, true );
851
852
			self::$_global_admin_notices = FS_Admin_Notice_Manager::instance( 'global' );
853
854
			// Configure which Freemius powered plugins should be auto updated.
855
//			add_filter( 'auto_update_plugin', '_include_plugins_in_auto_update', 10, 2 );
856
857
			add_action( 'admin_menu', array( 'Freemius', 'add_debug_page' ) );
858
859
			add_action( "wp_ajax_fs_toggle_debug_mode", array( 'Freemius', '_toggle_debug_mode' ) );
860
861
			self::$_statics_loaded = true;
862
		}
863
864
		#region Debugging ------------------------------------------------------------------
865
866
		/**
867
		 * @author Vova Feldman (@svovaf)
868
		 * @since  1.0.8
869
		 */
870
		static function add_debug_page() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
871
			if ( ! current_user_can( 'activate_plugins' ) ) {
872
				return;
873
			}
874
875
			self::$_static_logger->entrance();
876
877
			$title = sprintf( '%s [v.%s]', __fs( 'freemius-debug' ), WP_FS__SDK_VERSION );
878
879
			if ( WP_FS__DEV_MODE ) {
880
				// Add top-level debug menu item.
881
				$hook = add_object_page(
882
					$title,
883
					$title,
884
					'manage_options',
885
					'freemius',
886
					array( 'Freemius', '_debug_page_render' )
887
				);
888
			} else {
889
				// Add hidden debug page.
890
				$hook = add_submenu_page(
891
					null,
892
					$title,
893
					$title,
894
					'manage_options',
895
					'freemius',
896
					array( 'Freemius', '_debug_page_render' )
897
				);
898
			}
899
900
			add_action( "load-$hook", array( 'Freemius', '_debug_page_actions' ) );
901
		}
902
903
		/**
904
		 * @author Vova Feldman (@svovaf)
905
		 * @since  1.1.7.3
906
		 */
907
		static function _toggle_debug_mode() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
908
			if ( in_array( $_POST['is_on'], array( 0, 1 ) ) ) {
909
				update_option( 'fs_debug_mode', $_POST['is_on'] );
910
			}
911
912
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method _toggle_debug_mode() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
913
		}
914
915
		/**
916
		 * @author Vova Feldman (@svovaf)
917
		 * @since  1.0.8
918
		 */
919
		static function _debug_page_actions() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
920
			self::_clean_admin_content_section();
921
922
			if ( fs_request_is_action( 'restart_freemius' ) ) {
923
				check_admin_referer( 'restart_freemius' );
924
925
				self::$_accounts->clear( true );
926
927
928
				return;
929
			}
930
		}
931
932
		/**
933
		 * @author Vova Feldman (@svovaf)
934
		 * @since  1.0.8
935
		 */
936
		static function _debug_page_render() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
937
			self::$_static_logger->entrance();
938
939
			$sites          = self::get_all_sites();
940
			$users          = self::get_all_users();
941
			$addons         = self::get_all_addons();
942
			$account_addons = self::get_all_account_addons();
943
944
//			$plans    = self::get_all_plans();
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
945
//			$licenses = self::get_all_licenses();
946
947
			$vars = array(
948
				'sites'          => $sites,
949
				'users'          => $users,
950
				'addons'         => $addons,
951
				'account_addons' => $account_addons,
952
			);
953
954
			fs_enqueue_local_style( 'fs_account', '/admin/debug.css' );
955
			fs_require_once_template( 'debug.php', $vars );
956
		}
957
958
		#endregion ------------------------------------------------------------------
959
960
		#region Connectivity Issues ------------------------------------------------------------------
961
962
		/**
963
		 * Check if Freemius should be turned on for the current plugin install.
964
		 *
965
		 * Note:
966
		 *  $this->_is_on is updated in has_api_connectivity()
967
		 *
968
		 * @author Vova Feldman (@svovaf)
969
		 * @since  1.0.9
970
		 *
971
		 * @return bool
972
		 */
973
		function is_on() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
974
			self::$_static_logger->entrance();
975
976
			if ( isset( $this->_is_on ) ) {
977
				return $this->_is_on;
978
			}
979
980
			// If already installed or pending then sure it's on :)
981
			if ( $this->is_registered() || $this->is_pending_activation() ) {
982
				$this->_is_on = true;
983
984
				return true;
985
			}
986
987
			return false;
988
		}
989
990
		/**
991
		 * @author Vova Feldman (@svovaf)
992
		 * @since  1.1.7.3
993
		 *
994
		 * @param bool $flush_if_no_connectivity
995
		 *
996
		 * @return bool
997
		 */
998
		private function should_run_connectivity_test( $flush_if_no_connectivity = false ) {
999
			if ( ! isset( $this->_storage->connectivity_test ) ) {
1000
				// Connectivity test was never executed, or cache was cleared.
1001
				return true;
1002
			}
1003
1004
			if ( WP_FS__PING_API_ON_IP_OR_HOST_CHANGES ) {
1005
				if ( WP_FS__IS_HTTP_REQUEST ) {
1006
					if ( $_SERVER['HTTP_HOST'] != $this->_storage->connectivity_test['host'] ) {
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1007
						// Domain changed.
1008
						return true;
1009
					}
1010
1011
					if ( WP_FS__REMOTE_ADDR != $this->_storage->connectivity_test['server_ip'] ) {
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1012
						// Server IP changed.
1013
						return true;
1014
					}
1015
				}
1016
			}
1017
1018
			if ( $this->_storage->connectivity_test['is_connected'] &&
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1019
			     $this->_storage->connectivity_test['is_active']
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1020
			) {
1021
				// API connected and Freemius is active - no need to run connectivity check.
1022
				return false;
1023
			}
1024
1025
			if ( $flush_if_no_connectivity ) {
1026
				/**
1027
				 * If explicitly asked to flush when no connectivity - do it only
1028
				 * if at least 10 sec passed from the last API connectivity test.
1029
				 */
1030
				return ( isset( $this->_storage->connectivity_test['timestamp'] ) &&
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1031
				         ( WP_FS__SCRIPT_START_TIME - $this->_storage->connectivity_test['timestamp'] ) > 10 );
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1032
			}
1033
1034
			/**
1035
			 * @since 1.1.7 Don't check for connectivity on plugin downgrade.
1036
			 */
1037
			$version = $this->get_plugin_version();
1038
			if ( version_compare( $version, $this->_storage->connectivity_test['version'], '>' ) ) {
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1039
				// If it's a plugin version upgrade and Freemius is off or no connectivity, run connectivity test.
1040
				return true;
1041
			}
1042
1043
			return false;
1044
		}
1045
1046
		/**
1047
		 * @author Vova Feldman (@svovaf)
1048
		 * @since  1.1.7.4
1049
		 *
1050
		 * @return object|false
1051
		 */
1052
		private function ping() {
1053
			if ( WP_FS__SIMULATE_NO_API_CONNECTIVITY ) {
1054
				return false;
1055
			}
1056
1057
			$version = $this->get_plugin_version();
1058
1059
			$is_update = $this->apply_filters( 'is_plugin_update', $this->is_plugin_update() );
1060
1061
			return $this->get_api_plugin_scope()->ping(
1062
				$this->get_anonymous_id(),
1063
				array(
1064
					'is_update' => json_encode( $is_update ),
1065
					'version'   => $version,
1066
					'sdk'       => $this->version,
1067
					'is_admin'  => json_encode( is_admin() ),
1068
					'is_ajax'   => json_encode( $this->is_ajax() ),
1069
					'is_cron'   => json_encode( $this->is_cron() ),
1070
					'is_http'   => json_encode( WP_FS__IS_HTTP_REQUEST ),
1071
				)
1072
			);
1073
		}
1074
1075
		/**
1076
		 * Check if there's any connectivity issue to Freemius API.
1077
		 *
1078
		 * @author Vova Feldman (@svovaf)
1079
		 * @since  1.0.9
1080
		 *
1081
		 * @param bool $flush_if_no_connectivity
1082
		 *
1083
		 * @return bool
1084
		 */
1085
		function has_api_connectivity( $flush_if_no_connectivity = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1086
			$this->_logger->entrance();
1087
1088
			if ( isset( $this->_has_api_connection ) && ( $this->_has_api_connection || ! $flush_if_no_connectivity ) ) {
1089
				return $this->_has_api_connection;
1090
			}
1091
1092
			if ( WP_FS__SIMULATE_NO_API_CONNECTIVITY &&
1093
			     isset( $this->_storage->connectivity_test ) &&
1094
			     true === $this->_storage->connectivity_test['is_connected']
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1095
			) {
1096
				unset( $this->_storage->connectivity_test );
1097
			}
1098
1099
			if ( ! $this->should_run_connectivity_test( $flush_if_no_connectivity ) ) {
1100
				$this->_has_api_connection = $this->_storage->connectivity_test['is_connected'];
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1101
				/**
1102
				 * @since 1.1.6 During dev mode, if there's connectivity - turn Freemius on regardless the configuration.
1103
				 */
1104
				$this->_is_on = $this->_storage->connectivity_test['is_active'] ||
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1105
				                ( WP_FS__DEV_MODE && $this->_has_api_connection );
1106
1107
				return $this->_has_api_connection;
1108
			}
1109
1110
			$pong         = $this->ping();
1111
			$is_connected = $this->get_api_plugin_scope()->is_valid_ping( $pong );
1112
1113
			if ( ! $is_connected ) {
1114
				// API failure.
1115
				$this->_add_connectivity_issue_message( $pong );
1116
			}
1117
1118
			$this->store_connectivity_info( $pong, $is_connected );
1119
1120
			return $this->_has_api_connection;
1121
		}
1122
1123
		/**
1124
		 * @author Vova Feldman (@svovaf)
1125
		 * @since  1.1.7.4
1126
		 *
1127
		 * @param object $pong
1128
		 * @param bool   $is_connected
1129
		 */
1130
		private function store_connectivity_info( $pong, $is_connected ) {
1131
			$this->_logger->entrance();
1132
1133
			$version = $this->get_plugin_version();
1134
1135
			$is_active = $this->apply_filters(
1136
				'is_on',
1137
				( ! $is_connected ) ? false :
1138
					( isset( $pong->is_active ) && true == $pong->is_active ),
1139
				$this->is_plugin_update(),
1140
				$version
1141
			);
1142
1143
			$this->_storage->connectivity_test = array(
0 ignored issues
show
Documentation introduced by
The property connectivity_test does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1144
				'is_connected' => $is_connected,
1145
				'host'         => $_SERVER['HTTP_HOST'],
1146
				'server_ip'    => WP_FS__REMOTE_ADDR,
1147
				'is_active'    => $is_active,
1148
				'timestamp'    => WP_FS__SCRIPT_START_TIME,
1149
				// Last version with connectivity attempt.
1150
				'version'      => $version,
1151
			);
1152
1153
			$this->_has_api_connection = $is_connected;
1154
			$this->_is_on              = $is_active || ( WP_FS__DEV_MODE && $is_connected );
1155
		}
1156
1157
		/**
1158
		 * Anonymous and unique site identifier (Hash).
1159
		 *
1160
		 * @author Vova Feldman (@svovaf)
1161
		 * @since  1.1.0
1162
		 *
1163
		 * @return string
1164
		 */
1165
		function get_anonymous_id() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1166
			if ( ! self::$_accounts->has_option( 'unique_id' ) ) {
1167
				$key = get_site_url();
1168
1169
				// If localhost, assign microtime instead of domain.
1170
				if ( WP_FS__IS_LOCALHOST || false !== strpos( $key, 'localhost' ) ) {
1171
					$key = microtime();
1172
				}
1173
1174
				self::$_accounts->set_option( 'unique_id', md5( $key ), true );
1175
			}
1176
1177
			return self::$_accounts->get_option( 'unique_id' );
1178
		}
1179
1180
		/**
1181
		 * @author Vova Feldman (@svovaf)
1182
		 * @since  1.1.7.4
1183
		 *
1184
		 * @return \WP_User
1185
		 */
1186
		static function _get_current_wp_user() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1187
			self::require_pluggable_essentials();
1188
1189
			return wp_get_current_user();
1190
		}
1191
1192
		/**
1193
		 * Generate API connectivity issue message.
1194
		 *
1195
		 * @author Vova Feldman (@svovaf)
1196
		 * @since  1.0.9
1197
		 *
1198
		 * @param mixed $api_result
1199
		 * @param bool  $is_first_failure
1200
		 */
1201
		function _add_connectivity_issue_message( $api_result, $is_first_failure = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1202
			if ( $this->_enable_anonymous ) {
1203
				// Don't add message if can run anonymously.
1204
				return;
1205
			}
1206
1207
			if ( ! function_exists( 'wp_nonce_url' ) ) {
1208
				require_once( ABSPATH . 'wp-includes/functions.php' );
1209
			}
1210
1211
			$current_user = self::_get_current_wp_user();
1212
//			$admin_email = get_option( 'admin_email' );
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1213
			$admin_email = $current_user->user_email;
1214
1215
			$message = false;
1216
			if ( is_object( $api_result ) &&
1217
			     isset( $api_result->error ) &&
1218
			     isset( $api_result->error->code )
1219
			) {
1220
				switch ( $api_result->error->code ) {
1221
					case 'curl_missing':
1222
						$message = sprintf(
1223
							__fs( 'x-requires-access-to-api', $this->_slug ) . ' ' .
1224
							__fs( 'curl-missing-message', $this->_slug ) . ' ' .
1225
							' %s',
1226
							'<b>' . $this->get_plugin_name() . '</b>',
1227
							sprintf(
1228
								'<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
1229
								sprintf(
1230
									'<a class="fs-resolve" data-type="curl" href="#"><b>%s</b></a>%s',
1231
									__fs( 'curl-missing-no-clue-title', $this->_slug ),
1232
									' - ' . sprintf(
1233
										__fs( 'curl-missing-no-clue-desc', $this->_slug ),
1234
										'<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
1235
									)
1236
								),
1237
								sprintf(
1238
									'<b>%s</b> - %s',
1239
									__fs( 'sysadmin-title', $this->_slug ),
1240
									__fs( 'curl-missing-sysadmin-desc', $this->_slug )
1241
								),
1242
								sprintf(
1243
									'<a href="%s"><b>%s</b></a>%s',
1244
									wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
1245
									__fs( 'deactivate-plugin-title', $this->_slug ),
1246
									' - ' . __fs( 'deactivate-plugin-desc', 'freemius', $this->_slug )
0 ignored issues
show
Unused Code introduced by
The call to __fs() has too many arguments starting with $this->_slug.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1247
								)
1248
							)
1249
						);
1250
						break;
1251
					case 'cloudflare_ddos_protection':
1252
						$message = sprintf(
1253
							__fs( 'x-requires-access-to-api', $this->_slug ) . ' ' .
1254
							__fs( 'cloudflare-blocks-connection-message', $this->_slug ) . ' ' .
1255
							__fs( 'happy-to-resolve-issue-asap', $this->_slug ) .
1256
							' %s',
1257
							'<b>' . $this->get_plugin_name() . '</b>',
1258
							sprintf(
1259
								'<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
1260
								sprintf(
1261
									'<a class="fs-resolve" data-type="cloudflare" href="#"><b>%s</b></a>%s',
1262
									__fs( 'fix-issue-title', $this->_slug ),
1263
									' - ' . sprintf(
1264
										__fs( 'fix-issue-desc', $this->_slug ),
1265
										'<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
1266
									)
1267
								),
1268
								sprintf(
1269
									'<a href="%s" target="_blank"><b>%s</b></a>%s',
1270
									sprintf( 'https://wordpress.org/plugins/%s/download/', $this->_slug ),
1271
									__fs( 'install-previous-title', $this->_slug ),
1272
									' - ' . __fs( 'install-previous-desc', $this->_slug )
1273
								),
1274
								sprintf(
1275
									'<a href="%s"><b>%s</b></a>%s',
1276
									wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
1277
									__fs( 'deactivate-plugin-title', $this->_slug ),
1278
									' - ' . __fs( 'deactivate-plugin-desc', $this->_slug )
1279
								)
1280
							)
1281
						);
1282
						break;
1283
					case 'squid_cache_block':
1284
						$message = sprintf(
1285
							__fs( 'x-requires-access-to-api', $this->_slug ) . ' ' .
1286
							__fs( 'squid-blocks-connection-message', $this->_slug ) .
1287
							' %s',
1288
							'<b>' . $this->get_plugin_name() . '</b>',
1289
							sprintf(
1290
								'<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
1291
								sprintf(
1292
									'<a class="fs-resolve" data-type="squid" href="#"><b>%s</b></a>%s',
1293
									__fs( 'squid-no-clue-title', $this->_slug ),
1294
									' - ' . sprintf(
1295
										__fs( 'squid-no-clue-desc', $this->_slug ),
1296
										'<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
1297
									)
1298
								),
1299
								sprintf(
1300
									'<b>%s</b> - %s',
1301
									__fs( 'sysadmin-title', $this->_slug ),
1302
									sprintf(
1303
										__fs( 'squid-sysadmin-desc', $this->_slug ),
1304
										// We use a filter since the plugin might require additional API connectivity.
1305
										'<b>' . implode( ', ', $this->apply_filters( 'api_domains', array( 'api.freemius.com' ) ) ) . '</b>' )
1306
								),
1307
								sprintf(
1308
									'<a href="%s"><b>%s</b></a>%s',
1309
									wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
1310
									__fs( 'deactivate-plugin-title', $this->_slug ),
1311
									' - ' . __fs( 'deactivate-plugin-desc', $this->_slug )
1312
								)
1313
							)
1314
						);
1315
						break;
1316
//					default:
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1317
//						$message = __fs( 'connectivity-test-fails-message', $this->_slug );
1318
//						break;
1319
				}
1320
			}
1321
1322
			$message_id = 'failed_connect_api';
1323
			$type       = 'error';
1324
1325
			if ( false === $message ) {
1326
				if ( $is_first_failure ) {
1327
					// First attempt failed.
1328
					$message = sprintf(
1329
						__fs( 'x-requires-access-to-api', $this->_slug ) . ' ' .
1330
						__fs( 'connectivity-test-fails-message', $this->_slug ) . ' ' .
1331
						__fs( 'connectivity-test-maybe-temporary', $this->_slug ) . '<br><br>' .
1332
						'%s',
1333
						'<b>' . $this->get_plugin_name() . '</b>',
1334
						sprintf(
1335
							'<div id="fs_firewall_issue_options">%s %s</div>',
1336
							sprintf(
1337
								'<a  class="button button-primary fs-resolve" data-type="retry_ping" href="#">%s</a>',
1338
								__fs( 'yes-do-your-thing', $this->_slug )
1339
							),
1340
							sprintf(
1341
								'<a href="%s" class="button">%s</a>',
1342
								wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
1343
								__fs( 'no-deactivate', $this->_slug )
1344
							)
1345
						)
1346
					);
1347
1348
					$message_id = 'failed_connect_api_first';
1349
					$type       = 'promotion';
1350
				} else {
1351
					// Second connectivity attempt failed.
1352
					$message = sprintf(
1353
						__fs( 'x-requires-access-to-api', $this->_slug ) . ' ' .
1354
						__fs( 'connectivity-test-fails-message', $this->_slug ) . ' ' .
1355
						__fs( 'happy-to-resolve-issue-asap', $this->_slug ) .
1356
						' %s',
1357
						'<b>' . $this->get_plugin_name() . '</b>',
1358
						sprintf(
1359
							'<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
1360
							sprintf(
1361
								'<a class="fs-resolve" data-type="general" href="#"><b>%s</b></a>%s',
1362
								__fs( 'fix-issue-title', $this->_slug ),
1363
								' - ' . sprintf(
1364
									__fs( 'fix-issue-desc', $this->_slug ),
1365
									'<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
1366
								)
1367
							),
1368
							sprintf(
1369
								'<a href="%s" target="_blank"><b>%s</b></a>%s',
1370
								sprintf( 'https://wordpress.org/plugins/%s/download/', $this->_slug ),
1371
								__fs( 'install-previous-title', $this->_slug ),
1372
								' - ' . __fs( 'install-previous-desc', $this->_slug )
1373
							),
1374
							sprintf(
1375
								'<a href="%s"><b>%s</b></a>%s',
1376
								wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
1377
								__fs( 'deactivate-plugin-title', $this->_slug ),
1378
								' - ' . __fs( 'deactivate-plugin-desc', $this->_slug )
1379
							)
1380
						)
1381
					);
1382
				}
1383
			}
1384
1385
			$this->_admin_notices->add_sticky(
1386
				$message,
1387
				$message_id,
1388
				__fs( 'oops', $this->_slug ) . '...',
1389
				$type
1390
			);
1391
		}
1392
1393
		/**
1394
		 * Handle user request to resolve connectivity issue.
1395
		 * This method will send an email to Freemius API technical staff for resolution.
1396
		 * The email will contain server's info and installed plugins (might be caching issue).
1397
		 *
1398
		 * @author Vova Feldman (@svovaf)
1399
		 * @since  1.0.9
1400
		 */
1401
		function _email_about_firewall_issue() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1402
			$this->_admin_notices->remove_sticky( 'failed_connect_api' );
1403
1404
			$pong = $this->ping();
1405
1406
			$is_connected = $this->get_api_plugin_scope()->is_valid_ping( $pong );
1407
1408
			if ( $is_connected ) {
1409
				$this->store_connectivity_info( $pong, $is_connected );
1410
1411
				echo $this->get_after_plugin_activation_redirect_url();
1412
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method _email_about_firewall_issue() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1413
			}
1414
1415
			$current_user = self::_get_current_wp_user();
1416
			$admin_email  = $current_user->user_email;
1417
1418
			$error_type = fs_request_get( 'error_type', 'general' );
1419
1420
			switch ( $error_type ) {
1421
				case 'squid':
1422
					$title = 'Squid ACL Blocking Issue';
1423
					break;
1424
				case 'cloudflare':
1425
					$title = 'CloudFlare Blocking Issue';
1426
					break;
1427
				default:
1428
					$title = 'API Connectivity Issue';
1429
					break;
1430
			}
1431
1432
			$custom_email_sections = array();
1433
1434
			if ( 'squid' === $error_type ) {
1435
				// Override the 'Site' email section.
1436
				$custom_email_sections['site'] = array(
1437
					'rows' => array(
1438
						'hosting_company' => array( 'Hosting Company', fs_request_get( 'hosting_company' ) )
1439
					)
1440
				);
1441
			}
1442
1443
			// Add 'API Error' custom email section.
1444
			$custom_email_sections['api_error'] = array(
1445
				'title' => 'API Error',
1446
				'rows'  => array(
1447
					'ping' => array( is_string( $pong ) ? htmlentities( $pong ) : json_encode( $pong ) )
1448
				)
1449
			);
1450
1451
			// Send email with technical details to resolve CloudFlare's firewall unnecessary protection.
1452
			$this->send_email(
1453
				'[email protected]',                              // recipient
1454
				$title . ' [' . $this->get_plugin_name() . ']',  // subject
1455
				$custom_email_sections,
1456
				array( "Reply-To: $admin_email <$admin_email>" ) // headers
1457
			);
1458
1459
			$this->_admin_notices->add_sticky(
1460
				sprintf(
1461
					__fs( 'fix-request-sent-message', $this->_slug ),
1462
					'<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
1463
				),
1464
				'server_details_sent'
1465
			);
1466
1467
			// Action was taken, tell that API connectivity troubleshooting should be off now.
1468
1469
			echo "1";
1470
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method _email_about_firewall_issue() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1471
		}
1472
1473
		/**
1474
		 * Handle connectivity test retry approved by the user.
1475
		 *
1476
		 * @author Vova Feldman (@svovaf)
1477
		 * @since  1.1.7.4
1478
		 */
1479
		function _retry_connectivity_test() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1480
			$this->_admin_notices->remove_sticky( 'failed_connect_api_first' );
1481
1482
			$pong = $this->ping();
1483
1484
			$is_connected = $this->get_api_plugin_scope()->is_valid_ping( $pong );
1485
1486
			if ( $is_connected ) {
1487
				$this->store_connectivity_info( $pong, $is_connected );
1488
1489
				echo $this->get_after_plugin_activation_redirect_url();
1490
			} else {
1491
				// Add connectivity issue message after 2nd failed attempt.
1492
				$this->_add_connectivity_issue_message( $pong, false );
1493
1494
				echo "1";
1495
			}
1496
1497
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method _retry_connectivity_test() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1498
		}
1499
1500
		static function _add_firewall_issues_javascript() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1501
			$params = array();
1502
			fs_require_once_template( 'firewall-issues-js.php', $params );
1503
		}
1504
1505
		#endregion Connectivity Issues ------------------------------------------------------------------
1506
1507
		#region Email ------------------------------------------------------------------
1508
1509
		/**
1510
		 * Generates and sends an HTML email with customizable sections.
1511
		 *
1512
		 * @author Leo Fajardo (@leorw)
1513
		 * @since  1.1.2
1514
		 *
1515
		 * @param string $to_address
1516
		 * @param string $subject
1517
		 * @param array  $sections
1518
		 * @param array  $headers
1519
		 *
1520
		 * @return bool Whether the email contents were sent successfully.
1521
		 */
1522
		private function send_email(
1523
			$to_address,
1524
			$subject,
1525
			$sections = array(),
1526
			$headers = array()
1527
		) {
1528
			$default_sections = $this->get_email_sections();
1529
1530
			// Insert new sections or replace the default email sections.
1531
			if ( is_array( $sections ) && ! empty( $sections ) ) {
1532
				foreach ( $sections as $section_id => $custom_section ) {
1533
					if ( ! isset( $default_sections[ $section_id ] ) ) {
1534
						// If the section does not exist, add it.
1535
						$default_sections[ $section_id ] = $custom_section;
1536
					} else {
1537
						// If the section already exists, override it.
1538
						$current_section = $default_sections[ $section_id ];
1539
1540
						// Replace the current section's title if a custom section title exists.
1541
						if ( isset( $custom_section['title'] ) ) {
1542
							$current_section['title'] = $custom_section['title'];
1543
						}
1544
1545
						// Insert new rows under the current section or replace the default rows.
1546
						if ( isset( $custom_section['rows'] ) && is_array( $custom_section['rows'] ) && ! empty( $custom_section['rows'] ) ) {
1547
							foreach ( $custom_section['rows'] as $row_id => $row ) {
1548
								$current_section['rows'][ $row_id ] = $row;
1549
							}
1550
						}
1551
1552
						$default_sections[ $section_id ] = $current_section;
1553
					}
1554
				}
1555
			}
1556
1557
			$vars    = array( 'sections' => $default_sections );
1558
			$message = fs_get_template( 'email.php', $vars );
1559
1560
			// Set the type of email to HTML.
1561
			$headers[] = 'Content-type: text/html';
1562
1563
			$header_string = implode( "\r\n", $headers );
1564
1565
			return wp_mail(
1566
				$to_address,
1567
				$subject,
1568
				$message,
1569
				$header_string
1570
			);
1571
		}
1572
1573
		/**
1574
		 * Generates the data for the sections of the email content.
1575
		 *
1576
		 * @author Leo Fajardo (@leorw)
1577
		 * @since  1.1.2
1578
		 *
1579
		 * @return array
1580
		 */
1581
		private function get_email_sections() {
1582
			// Retrieve the current user's information so that we can get the user's email, first name, and last name below.
1583
			$current_user = self::_get_current_wp_user();
1584
1585
			// Retrieve the cURL version information so that we can get the version number below.
1586
			$curl_version_information = curl_version();
1587
1588
			$active_plugin = $this->get_active_plugins();
1589
1590
			// Generate the list of active plugins separated by new line. 
1591
			$active_plugin_string = '';
1592
			foreach ( $active_plugin as $plugin ) {
1593
				$active_plugin_string .= sprintf(
1594
					'<a href="%s">%s</a> [v%s]<br>',
1595
					$plugin['PluginURI'],
1596
					$plugin['Name'],
1597
					$plugin['Version']
1598
				);
1599
			}
1600
1601
			$server_ip = WP_FS__REMOTE_ADDR;
1602
1603
			// Generate the default email sections.
1604
			$sections = array(
1605
				'sdk'     => array(
1606
					'title' => 'SDK',
1607
					'rows'  => array(
1608
						'fs_version'   => array( 'FS Version', $this->version ),
1609
						'curl_version' => array( 'cURL Version', $curl_version_information['version'] )
1610
					)
1611
				),
1612
				'plugin'  => array(
1613
					'title' => 'Plugin',
1614
					'rows'  => array(
1615
						'name'    => array( 'Name', $this->get_plugin_name() ),
1616
						'version' => array( 'Version', $this->get_plugin_version() )
1617
					)
1618
				),
1619
				'site'    => array(
1620
					'title' => 'Site',
1621
					'rows'  => array(
1622
						'address'     => array( 'Address', site_url() ),
1623
						'host'        => array(
1624
							'HTTP_HOST',
1625
							( ! empty( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '' )
1626
						),
1627
						'server_addr' => array(
1628
							'SERVER_ADDR',
1629
							'<a href="http://www.projecthoneypot.org/ip_' . $server_ip . '">' . $server_ip . '</a>'
1630
						)
1631
					)
1632
				),
1633
				'user'    => array(
1634
					'title' => 'User',
1635
					'rows'  => array(
1636
						'email' => array( 'Email', $current_user->user_email ),
1637
						'first' => array( 'First', $current_user->user_firstname ),
1638
						'last'  => array( 'Last', $current_user->user_lastname )
1639
					)
1640
				),
1641
				'plugins' => array(
1642
					'title' => 'Plugins',
1643
					'rows'  => array(
1644
						'active_plugins' => array( 'Active Plugins', $active_plugin_string )
1645
					)
1646
				),
1647
			);
1648
1649
			// Allow the sections to be modified by other code.
1650
			$sections = $this->apply_filters( 'email_template_sections', $sections );
1651
1652
			return $sections;
1653
		}
1654
1655
		#endregion Email ------------------------------------------------------------------
1656
1657
		#region Initialization ------------------------------------------------------------------
1658
1659
		/**
1660
		 * Init plugin's Freemius instance.
1661
		 *
1662
		 * @author Vova Feldman (@svovaf)
1663
		 * @since  1.0.1
1664
		 *
1665
		 * @param number $id
1666
		 * @param string $public_key
1667
		 * @param bool   $is_live
1668
		 * @param bool   $is_premium
1669
		 */
1670
		function init( $id, $public_key, $is_live = true, $is_premium = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1671
			$this->_logger->entrance();
1672
1673
			$this->dynamic_init( array(
1674
				'id'         => $id,
1675
				'public_key' => $public_key,
1676
				'is_live'    => $is_live,
1677
				'is_premium' => $is_premium,
1678
			) );
1679
		}
1680
1681
		/**
1682
		 * Dynamic initiator, originally created to support initiation
1683
		 * with parent_id for add-ons.
1684
		 *
1685
		 * @author Vova Feldman (@svovaf)
1686
		 * @since  1.0.6
1687
		 *
1688
		 * @param array $plugin_info
1689
		 *
1690
		 * @throws Freemius_Exception
1691
		 */
1692
		function dynamic_init( array $plugin_info ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1693
			$this->_logger->entrance();
1694
1695
			$this->parse_settings( $plugin_info );
1696
1697
			if ( $this->should_stop_execution() ) {
1698
				return;
1699
			}
1700
1701
			if ( ! $this->is_registered() ) {
1702
				if ( $this->is_anonymous() ) {
1703
					// If user skipped, no need to test connectivity.
1704
					$this->_has_api_connection = true;
1705
					$this->_is_on              = true;
1706
				} else {
1707
					if ( ! $this->has_api_connectivity() ) {
1708
						if ( $this->_admin_notices->has_sticky( 'failed_connect_api_first' ) ||
1709
						     $this->_admin_notices->has_sticky( 'failed_connect_api' )
1710
						) {
1711
							if ( ! $this->_enable_anonymous ) {
1712
								// If anonymous mode is disabled, add firewall admin-notice message.
1713
								add_action( 'admin_footer', array( 'Freemius', '_add_firewall_issues_javascript' ) );
1714
1715
								add_action( "wp_ajax_{$this->_slug}_resolve_firewall_issues", array(
1716
									&$this,
1717
									'_email_about_firewall_issue'
1718
								) );
1719
1720
								add_action( "wp_ajax_{$this->_slug}_retry_connectivity_test", array(
1721
									&$this,
1722
									'_retry_connectivity_test'
1723
								) );
1724
							}
1725
						}
1726
1727
						return;
1728
					} else {
1729
						$this->_admin_notices->remove_sticky( array(
1730
							'failed_connect_api_first',
1731
							'failed_connect_api',
1732
						) );
1733
1734
						if ( $this->_anonymous_mode ) {
1735
							// Simulate anonymous mode.
1736
							$this->_is_anonymous = true;
1737
						}
1738
					}
1739
				}
1740
1741
				// Check if Freemius is on for the current plugin.
1742
				// This MUST be executed after all the plugin variables has been loaded.
1743
				if ( ! $this->is_on() ) {
1744
					return;
1745
				}
1746
			}
1747
1748
			if ( $this->has_api_connectivity() && $this->is_user_in_admin() ) {
1749
				/**
1750
				 * Schedule daily data sync cron if:
1751
				 *
1752
				 *  1. User opted-in (for tracking).
1753
				 *  2. If plugin has add-ons (update add-ons data).
1754
				 *  3. If skipped, but later upgraded (opted-in via upgrade).
1755
				 *
1756
				 * @author Vova Feldman (@svovaf)
1757
				 * @since  1.1.7.3
1758
				 *
1759
				 */
1760
				if ( $this->is_registered() ||
1761
				     ( ! $this->is_activation_mode() && $this->_has_addons )
1762
				) {
1763
1764
					$this->hook_callback_to_sync_cron();
1765
1766
					if ( ! $this->is_sync_cron_on() ) {
1767
						$this->schedule_sync_cron();
1768
					}
1769
				}
1770
1771
				/**
1772
				 * Check if requested for manual blocking background sync.
1773
				 */
1774
				if ( fs_request_has( 'background_sync' ) ) {
1775
					$this->run_manual_sync();
1776
				}
1777
			}
1778
1779
			if ( $this->is_registered() ) {
1780
				$this->hook_callback_to_install_sync();
1781
			}
1782
1783
			if ( $this->is_addon() ) {
1784
				if ( $this->is_parent_plugin_installed() ) {
1785
					// Link to parent FS.
1786
					$this->_parent = self::get_instance_by_id( $this->_plugin->parent_plugin_id );
1787
1788
					// Get parent plugin reference.
1789
					$this->_parent_plugin = $this->_parent->get_plugin();
1790
				}
1791
			}
1792
1793
			if ( $this->is_user_in_admin() ) {
1794
				global $pagenow;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1795
				if ( 'plugins.php' === $pagenow ) {
1796
					$this->hook_plugin_action_links();
1797
				}
1798
1799
				if ( $this->is_addon() ) {
1800
					if ( ! $this->is_parent_plugin_installed() ) {
1801
						$parent_name = $this->get_option( $plugin_info, 'parent_name', null );
1802
1803
						if ( isset( $plugin_info['parent'] ) ) {
1804
							$parent_name = $this->get_option( $plugin_info['parent'], 'name', null );
1805
						}
1806
1807
						$this->_admin_notices->add(
1808
							( ! empty( $parent_name ) ?
1809
								sprintf( __fs( 'addon-x-cannot-run-without-y', $this->_slug ), $this->get_plugin_name(), $parent_name ) :
1810
								sprintf( __fs( 'addon-x-cannot-run-without-parent', $this->_slug ), $this->get_plugin_name() )
1811
							),
1812
							__fs( 'oops', $this->_slug ) . '...',
1813
							'error'
1814
						);
1815
1816
						return;
1817
					} else {
1818
						if ( $this->_parent->is_registered() && ! $this->is_registered() ) {
1819
							// If parent plugin activated, automatically install add-on for the user.
1820
							$this->_activate_addon_account( $this->_parent );
1821
						}
1822
1823
						// @todo This should be only executed on activation. It should be migrated to register_activation_hook() together with other activation related logic.
1824
						if ( $this->is_premium() ) {
1825
							// Remove add-on download admin-notice.
1826
							$this->_parent->_admin_notices->remove_sticky( 'addon_plan_upgraded_' . $this->_slug );
1827
						}
1828
1829
						$this->deactivate_premium_only_addon_without_license();
1830
					}
1831
				} else {
1832
					add_action( 'admin_init', array( &$this, '_admin_init_action' ) );
1833
1834
					if ( $this->has_addons() &&
1835
					     'plugin-information' === fs_request_get( 'tab', false ) &&
1836
					     $this->get_id() == fs_request_get( 'parent_plugin_id', false )
1837
					) {
1838
						require_once WP_FS__DIR_INCLUDES . '/fs-plugin-info-dialog.php';
1839
1840
						new FS_Plugin_Info_Dialog( $this );
1841
					}
1842
				}
1843
1844
				if ( $this->is_premium() ) {
1845
					new FS_Plugin_Updater( $this );
1846
				}
1847
1848
//				if ( $this->is_registered() ||
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1849
//				     $this->is_anonymous() ||
1850
//				     $this->is_pending_activation()
1851
//				) {
1852
//					$this->_init_admin();
1853
//				}
1854
			}
1855
1856
			$this->do_action( 'initiated' );
1857
1858
			if ( ! $this->is_addon() ) {
1859
				if ( $this->is_registered() ) {
1860
					// Fix for upgrade from versions < 1.0.9.
1861
					if ( ! isset( $this->_storage->activation_timestamp ) ) {
1862
						$this->_storage->activation_timestamp = WP_FS__SCRIPT_START_TIME;
0 ignored issues
show
Documentation introduced by
The property activation_timestamp does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1863
					}
1864
					if ( $this->_storage->prev_is_premium !== $this->_plugin->is_premium ) {
0 ignored issues
show
Documentation introduced by
The property prev_is_premium does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1865
						if ( isset( $this->_storage->prev_is_premium ) ) {
1866
							add_action( is_admin() ? 'admin_init' : 'init', array(
1867
								&$this,
1868
								'_plugin_code_type_changed'
1869
							) );
1870
						} else {
1871
							// Set for code type for the first time.
1872
							$this->_storage->prev_is_premium = $this->_plugin->is_premium;
0 ignored issues
show
Documentation introduced by
The property prev_is_premium does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1873
						}
1874
					}
1875
1876
					$this->do_action( 'after_init_plugin_registered' );
1877
				} else if ( $this->is_anonymous() ) {
1878
					$this->do_action( 'after_init_plugin_anonymous' );
1879
				} else if ( $this->is_pending_activation() ) {
1880
					$this->do_action( 'after_init_plugin_pending_activations' );
1881
				}
1882
			} else {
1883
				if ( $this->is_registered() ) {
1884
					$this->do_action( 'after_init_addon_registered' );
1885
				} else if ( $this->is_anonymous() ) {
1886
					$this->do_action( 'after_init_addon_anonymous' );
1887
				} else if ( $this->is_pending_activation() ) {
1888
					$this->do_action( 'after_init_addon_pending_activations' );
1889
				}
1890
			}
1891
		}
1892
1893
		/**
1894
		 * Parse plugin's settings (as defined by the plugin dev).
1895
		 *
1896
		 * @author Vova Feldman (@svovaf)
1897
		 * @since  1.1.7.3
1898
		 *
1899
		 * @param array $plugin_info
1900
		 *
1901
		 * @throws \Freemius_Exception
1902
		 */
1903
		private function parse_settings( &$plugin_info ) {
1904
			$this->_logger->entrance();
1905
1906
			$id          = $this->get_numeric_option( $plugin_info, 'id', false );
1907
			$public_key  = $this->get_option( $plugin_info, 'public_key', false );
1908
			$secret_key  = $this->get_option( $plugin_info, 'secret_key', null );
1909
			$parent_id   = $this->get_numeric_option( $plugin_info, 'parent_id', null );
1910
			$parent_name = $this->get_option( $plugin_info, 'parent_name', null );
1911
1912
			if ( isset( $plugin_info['parent'] ) ) {
1913
				$parent_id = $this->get_numeric_option( $plugin_info['parent'], 'id', null );
1914
//				$parent_slug       = $this->get_option( $plugin_info['parent'], 'slug', null );
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1915
//				$parent_public_key = $this->get_option( $plugin_info['parent'], 'public_key', null );
1916
				$parent_name = $this->get_option( $plugin_info['parent'], 'name', null );
1917
			}
1918
1919
			if ( false === $id ) {
1920
				throw new Freemius_Exception( 'Plugin id parameter is not set.' );
1921
			}
1922
			if ( false === $public_key ) {
1923
				throw new Freemius_Exception( 'Plugin public_key parameter is not set.' );
1924
			}
1925
1926
			$plugin = ( $this->_plugin instanceof FS_Plugin ) ?
1927
				$this->_plugin :
1928
				new FS_Plugin();
1929
1930
			$plugin->update( array(
1931
				'id'               => $id,
1932
				'public_key'       => $public_key,
1933
				'slug'             => $this->_slug,
1934
				'parent_plugin_id' => $parent_id,
1935
				'version'          => $this->get_plugin_version(),
1936
				'title'            => $this->get_plugin_name(),
1937
				'file'             => $this->_plugin_basename,
1938
				'is_premium'       => $this->get_bool_option( $plugin_info, 'is_premium', true ),
1939
				'is_live'          => $this->get_bool_option( $plugin_info, 'is_live', true ),
1940
//				'secret_key' => $secret_key,
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1941
			) );
1942
1943
			if ( $plugin->is_updated() ) {
1944
				// Update plugin details.
1945
				$this->_plugin = FS_Plugin_Manager::instance( $this->_slug )->store( $plugin );
1946
			}
1947
			// Set the secret key after storing the plugin, we don't want to store the key in the storage.
1948
			$this->_plugin->secret_key = $secret_key;
1949
1950
			if ( ! isset( $plugin_info['menu'] ) ) {
1951
				// Back compatibility to 1.1.2
1952
				$plugin_info['menu'] = array(
1953
					'slug' => isset( $plugin_info['menu_slug'] ) ?
1954
						$plugin_info['menu_slug'] :
1955
						$this->_slug
1956
				);
1957
			}
1958
1959
			$this->_menu = FS_Admin_Menu_Manager::instance( $this->_slug );
1960
			$this->_menu->init( $plugin_info['menu'], $this->is_addon() );
1961
1962
			$this->_has_addons       = $this->get_bool_option( $plugin_info, 'has_addons', false );
1963
			$this->_has_paid_plans   = $this->get_bool_option( $plugin_info, 'has_paid_plans', true );
1964
			$this->_is_org_compliant = $this->get_bool_option( $plugin_info, 'is_org_compliant', true );
1965
			$this->_enable_anonymous = $this->get_bool_option( $plugin_info, 'enable_anonymous', true );
1966
			$this->_anonymous_mode   = $this->get_bool_option( $plugin_info, 'anonymous_mode', false );
1967
			$this->_permissions      = $this->get_option( $plugin_info, 'permissions', array() );
1968
		}
1969
1970
		/**
1971
		 * @param string[] $options
1972
		 * @param string   $key
1973
		 * @param mixed    $default
1974
		 *
1975
		 * @return bool
1976
		 */
1977
		private function get_option( &$options, $key, $default = false ) {
1978
			return ! empty( $options[ $key ] ) ? $options[ $key ] : $default;
1979
		}
1980
1981
		private function get_bool_option( &$options, $key, $default = false ) {
1982
			return isset( $options[ $key ] ) && is_bool( $options[ $key ] ) ? $options[ $key ] : $default;
1983
		}
1984
1985
		private function get_numeric_option( &$options, $key, $default = false ) {
1986
			return isset( $options[ $key ] ) && is_numeric( $options[ $key ] ) ? $options[ $key ] : $default;
1987
		}
1988
1989
		/**
1990
		 * Gate keeper.
1991
		 *
1992
		 * @author Vova Feldman (@svovaf)
1993
		 * @since  1.1.7.3
1994
		 *
1995
		 * @return bool
1996
		 */
1997
		private function should_stop_execution() {
1998
			if ( $this->is_activation_mode() ) {
1999
				if ( ! is_admin() ) {
2000
					/**
2001
					 * If in activation mode, don't execute Freemius outside of the
2002
					 * admin dashboard.
2003
					 *
2004
					 * @author Vova Feldman (@svovaf)
2005
					 * @since  1.1.7.3
2006
					 */
2007
					return true;
2008
				}
2009
2010
				if ( ! WP_FS__IS_HTTP_REQUEST ) {
2011
					/**
2012
					 * If in activation and executed without HTTP context (e.g. CLI, Cronjob),
2013
					 * then don't start Freemius.
2014
					 *
2015
					 * @author Vova Feldman (@svovaf)
2016
					 * @since  1.1.6.3
2017
					 *
2018
					 * @link   https://wordpress.org/support/topic/errors-in-the-freemius-class-when-running-in-wordpress-in-cli
2019
					 */
2020
					return true;
2021
				}
2022
2023
				if ( $this->is_cron() ) {
2024
					/**
2025
					 * If in activation mode, don't execute Freemius during wp crons
2026
					 * (wp crons have HTTP context - called as HTTP request).
2027
					 *
2028
					 * @author Vova Feldman (@svovaf)
2029
					 * @since  1.1.7.3
2030
					 */
2031
					return true;
2032
				}
2033
2034
				if ( $this->is_ajax() &&
2035
				     ! $this->_admin_notices->has_sticky( 'failed_connect_api_first' ) &&
2036
				     ! $this->_admin_notices->has_sticky( 'failed_connect_api' )
2037
				) {
2038
					/**
2039
					 * During activation, if running in AJAX mode, unless there's a sticky
2040
					 * connectivity issue notice, don't run Freemius.
2041
					 *
2042
					 * @author Vova Feldman (@svovaf)
2043
					 * @since  1.1.7.3
2044
					 */
2045
					return true;
2046
				}
2047
			}
2048
2049
			return false;
2050
		}
2051
2052
		/**
2053
		 * Handles plugin's code type change (free <--> premium).
2054
		 *
2055
		 * @author Vova Feldman (@svovaf)
2056
		 * @since  1.0.9
2057
		 */
2058
		function _plugin_code_type_changed() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2059
			// Schedule code type changes event.
2060
//			$this->sync_install();
2061
			$this->schedule_install_sync();
2062
2063
			if ( $this->is_premium() ) {
2064
				// Activated premium code.
2065
				$this->do_action( 'after_premium_version_activation' );
2066
2067
				// Remove all sticky messages related to download of the premium version.
2068
				$this->_admin_notices->remove_sticky( array(
2069
					'trial_started',
2070
					'plan_upgraded',
2071
					'plan_changed',
2072
				) );
2073
2074
				$this->_admin_notices->add_sticky(
2075
					__fs( 'premium-activated-message', $this->_slug ),
2076
					'premium_activated',
2077
					__fs( 'woot', $this->_slug ) . '!'
2078
				);
2079
			} else {
2080
				// Activated free code (after had the premium before).
2081
				$this->do_action( 'after_free_version_reactivation' );
2082
2083
				if ( $this->is_paying() && ! $this->is_premium() ) {
2084
					$this->_admin_notices->add_sticky(
2085
						sprintf(
2086
							__fs( 'you-have-x-license', $this->_slug ),
2087
							$this->_site->plan->title
2088
						) . ' ' . $this->_get_latest_download_link( sprintf(
2089
							__fs( 'download-x-version-now', $this->_slug ),
2090
							$this->_site->plan->title
2091
						) ),
2092
						'plan_upgraded',
2093
						__fs( 'yee-haw', $this->_slug ) . '!'
2094
					);
2095
				}
2096
			}
2097
2098
			// Update is_premium of latest version.
2099
			$this->_storage->prev_is_premium = $this->_plugin->is_premium;
0 ignored issues
show
Documentation introduced by
The property prev_is_premium does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2100
		}
2101
2102
		#endregion Initialization ------------------------------------------------------------------
2103
2104
		#region Add-ons -------------------------------------------------------------------------
2105
2106
		/**
2107
		 * Check if add-on installed and activated on site.
2108
		 *
2109
		 * @author Vova Feldman (@svovaf)
2110
		 * @since  1.0.6
2111
		 *
2112
		 * @param string|number $slug_or_id
2113
		 *
2114
		 * @return bool
2115
		 */
2116
		function is_addon_activated( $slug_or_id ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2117
			return self::has_instance( $slug_or_id );
2118
		}
2119
2120
		/**
2121
		 * Check if add-on was connected to install
2122
		 *
2123
		 * @author Vova Feldman (@svovaf)
2124
		 * @since  1.1.7
2125
		 *
2126
		 * @param string $slug
2127
		 *
2128
		 * @return bool
2129
		 */
2130
		function is_addon_connected( $slug ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2131
			$sites = self::get_all_sites();
2132
2133
			if ( ! isset( $sites[ $slug ] ) ) {
2134
				return false;
2135
			}
2136
2137
			$site = $sites[ $slug ];
2138
2139
			$plugin = FS_Plugin_Manager::instance( $slug )->get();
2140
2141
			if ( $plugin->parent_plugin_id != $this->_plugin->id ) {
2142
				// The given slug do NOT belong to any of the plugin's add-ons.
2143
				return false;
2144
			}
2145
2146
			return ( is_object( $site ) &&
2147
			         is_numeric( $site->id ) &&
2148
			         is_numeric( $site->user_id ) &&
2149
			         is_object( $site->plan )
2150
			);
2151
		}
2152
2153
		/**
2154
		 * Determines if add-on installed.
2155
		 *
2156
		 * NOTE: This is a heuristic and only works if the folder/file named as the slug.
2157
		 *
2158
		 * @author Vova Feldman (@svovaf)
2159
		 * @since  1.0.6
2160
		 *
2161
		 * @param string $slug
2162
		 *
2163
		 * @return bool
2164
		 */
2165
		function is_addon_installed( $slug ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2166
			return file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $this->get_addon_basename( $slug ) ) );
2167
		}
2168
2169
		/**
2170
		 * Get add-on basename.
2171
		 *
2172
		 * @author Vova Feldman (@svovaf)
2173
		 * @since  1.0.6
2174
		 *
2175
		 * @param string $slug
2176
		 *
2177
		 * @return string
2178
		 */
2179
		function get_addon_basename( $slug ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2180
			if ( $this->is_addon_activated( $slug ) ) {
2181
				self::instance( $slug )->get_plugin_basename();
0 ignored issues
show
Unused Code introduced by
The call to the method Freemius::get_plugin_basename() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
2182
			}
2183
2184
			$premium_basename = $slug . '-premium/' . $slug . '.php';
2185
2186
			if ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $premium_basename ) ) ) {
2187
				return $premium_basename;
2188
			}
2189
2190
			$free_basename = $slug . '/' . $slug . '.php';
2191
2192
			return $free_basename;
2193
		}
2194
2195
		/**
2196
		 * Get installed add-ons instances.
2197
		 *
2198
		 * @author Vova Feldman (@svovaf)
2199
		 * @since  1.0.6
2200
		 *
2201
		 * @return Freemius[]
2202
		 */
2203
		function get_installed_addons() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2204
			$installed_addons = array();
2205
			foreach ( self::$_instances as $slug => $instance ) {
2206
				if ( $instance->is_addon() && is_object( $instance->_parent_plugin ) ) {
2207
					if ( $this->_plugin->id == $instance->_parent_plugin->id ) {
2208
						$installed_addons[] = $instance;
2209
					}
2210
				}
2211
			}
2212
2213
			return $installed_addons;
2214
		}
2215
2216
		/**
2217
		 * Check if any add-ons of the plugin are installed.
2218
		 *
2219
		 * @author Leo Fajardo (@leorw)
2220
		 * @since  1.1.1
2221
		 *
2222
		 * @return bool
2223
		 */
2224
		function has_installed_addons() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2225
			if ( ! $this->has_addons() ) {
2226
				return false;
2227
			}
2228
2229
			foreach ( self::$_instances as $slug => $instance ) {
2230
				if ( $instance->is_addon() && is_object( $instance->_parent_plugin ) ) {
2231
					if ( $this->_plugin->id == $instance->_parent_plugin->id ) {
2232
						return true;
2233
					}
2234
				}
2235
			}
2236
2237
			return false;
2238
		}
2239
2240
		/**
2241
		 * Tell Freemius that the current plugin is an add-on.
2242
		 *
2243
		 * @author Vova Feldman (@svovaf)
2244
		 * @since  1.0.6
2245
		 *
2246
		 * @param number $parent_plugin_id The parent plugin ID
2247
		 */
2248
		function init_addon( $parent_plugin_id ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2249
			$this->_plugin->parent_plugin_id = $parent_plugin_id;
2250
		}
2251
2252
		/**
2253
		 * @author Vova Feldman (@svovaf)
2254
		 * @since  1.0.6
2255
		 *
2256
		 * @return bool
2257
		 */
2258
		function is_addon() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2259
			return isset( $this->_plugin->parent_plugin_id ) && is_numeric( $this->_plugin->parent_plugin_id );
2260
		}
2261
2262
		/**
2263
		 * Deactivate add-on if it's premium only and the user does't have a valid license.
2264
		 *
2265
		 * @param bool $is_after_trial_cancel
2266
		 *
2267
		 * @return bool If add-on was deactivated.
2268
		 */
2269
		private function deactivate_premium_only_addon_without_license( $is_after_trial_cancel = false ) {
2270
			if ( ! $this->has_free_plan() &&
2271
			     ! $this->has_features_enabled_license() &&
2272
			     ! $this->_has_premium_license()
2273
			) {
2274
				deactivate_plugins( array( $this->_plugin_basename ), true );
2275
2276
				$this->_parent->_admin_notices->add_sticky(
2277
					sprintf(
2278
						__fs( ( $is_after_trial_cancel ?
2279
								'addon-trial-cancelled-message' :
2280
								'addon-no-license-message' ),
2281
							$this->_parent->_slug
2282
						),
2283
						'<b>' . $this->_plugin->title . '</b>'
2284
					) . ' ' . sprintf(
2285
						'<a href="%s" aria-label="%s" class="button button-primary" style="margin-left: 10px; vertical-align: middle;">%s &nbsp;&#10140;</a>',
2286
						$this->_parent->addon_url( $this->_slug ),
2287
						esc_attr( sprintf( __fs( 'more-information-about-x', $this->_parent->_slug ), $this->_plugin->title ) ),
2288
						__fs( 'purchase-license', $this->_parent->_slug )
2289
					),
2290
					'no_addon_license',
2291
					( $is_after_trial_cancel ? '' : __fs( 'oops', $this->_parent->_slug ) . '...' ),
2292
					( $is_after_trial_cancel ? 'success' : 'error' )
2293
				);
2294
2295
				return true;
2296
			}
2297
2298
			return false;
2299
		}
2300
2301
		#endregion ------------------------------------------------------------------
2302
2303
		#region Sandbox ------------------------------------------------------------------
2304
2305
		/**
2306
		 * Set Freemius into sandbox mode for debugging.
2307
		 *
2308
		 * @author Vova Feldman (@svovaf)
2309
		 * @since  1.0.4
2310
		 *
2311
		 * @param string $secret_key
2312
		 */
2313
		function init_sandbox( $secret_key ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2314
			$this->_plugin->secret_key = $secret_key;
2315
2316
			// Update plugin details.
2317
			FS_Plugin_Manager::instance( $this->_slug )->update( $this->_plugin, true );
2318
		}
2319
2320
		/**
2321
		 * Check if running payments in sandbox mode.
2322
		 *
2323
		 * @author Vova Feldman (@svovaf)
2324
		 * @since  1.0.4
2325
		 *
2326
		 * @return bool
2327
		 */
2328
		function is_payments_sandbox() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2329
			return ( ! $this->is_live() ) || isset( $this->_plugin->secret_key );
2330
		}
2331
2332
		#endregion Sandbox ------------------------------------------------------------------
2333
2334
		/**
2335
		 * Check if running test vs. live plugin.
2336
		 *
2337
		 * @author Vova Feldman (@svovaf)
2338
		 * @since  1.0.5
2339
		 *
2340
		 * @return bool
2341
		 */
2342
		function is_live() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2343
			return $this->_plugin->is_live;
2344
		}
2345
2346
		/**
2347
		 * Check if the user skipped connecting the account with Freemius.
2348
		 *
2349
		 * @author Vova Feldman (@svovaf)
2350
		 * @since  1.0.7
2351
		 *
2352
		 * @return bool
2353
		 */
2354
		function is_anonymous() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2355
			if ( ! isset( $this->_is_anonymous ) ) {
2356
				if ( ! isset( $this->_storage->is_anonymous ) ) {
2357
					// Not skipped.
2358
					$this->_is_anonymous = false;
2359
				} else if ( is_bool( $this->_storage->is_anonymous ) ) {
0 ignored issues
show
Documentation introduced by
The property is_anonymous does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2360
					// For back compatibility, since the variable was boolean before.
2361
					$this->_is_anonymous = $this->_storage->is_anonymous;
0 ignored issues
show
Documentation introduced by
The property is_anonymous does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2362
2363
					// Upgrade stored data format to 1.1.3 format.
2364
					$this->set_anonymous_mode( $this->_storage->is_anonymous );
0 ignored issues
show
Documentation introduced by
The property is_anonymous does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2365
				} else {
2366
					// Version 1.1.3 and later.
2367
					$this->_is_anonymous = $this->_storage->is_anonymous['is'];
0 ignored issues
show
Documentation introduced by
The property is_anonymous does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2368
				}
2369
			}
2370
2371
			return $this->_is_anonymous;
2372
		}
2373
2374
		/**
2375
		 * Check if user connected his account and install pending email activation.
2376
		 *
2377
		 * @author Vova Feldman (@svovaf)
2378
		 * @since  1.0.7
2379
		 *
2380
		 * @return bool
2381
		 */
2382
		function is_pending_activation() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2383
			return $this->_storage->get( 'is_pending_activation', false );
2384
		}
2385
2386
		/**
2387
		 * Check if plugin must be WordPress.org compliant.
2388
		 *
2389
		 * @since 1.0.7
2390
		 *
2391
		 * @return bool
2392
		 */
2393
		function is_org_repo_compliant() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2394
			return $this->_is_org_compliant;
2395
		}
2396
2397
		#region Daily Sync Cron ------------------------------------------------------------------
2398
2399
		/**
2400
		 * @author Vova Feldman (@svovaf)
2401
		 * @since  1.1.7.3
2402
		 */
2403
		private function run_manual_sync() {
2404
			$this->require_pluggable_essentials();
2405
2406
			if ( ! current_user_can( 'activate_plugins' ) ) {
2407
				return;
2408
			}
2409
2410
			// Run manual sync.
2411
			$this->_sync_cron();
2412
2413
			// Reschedule next cron to run 24 hours from now (performance optimization).
2414
			$this->clear_sync_cron();
2415
2416
			$this->schedule_sync_cron( time() + WP_FS__TIME_24_HOURS_IN_SEC, false );
2417
		}
2418
2419
		/**
2420
		 * Data sync cron job. Replaces the background sync non blocking HTTP request
2421
		 * that doesn't halt page loading.
2422
		 *
2423
		 * @author Vova Feldman (@svovaf)
2424
		 * @since  1.1.7.3
2425
		 */
2426
		function _sync_cron() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2427
			$this->_logger->entrance();
2428
2429
			// Store the last time data sync was executed.
2430
			$this->_storage->sync_timestamp = time();
0 ignored issues
show
Documentation introduced by
The property sync_timestamp does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2431
2432
			// Check if API is temporary down.
2433
			if ( FS_Api::is_temporary_down() ) {
2434
				return;
2435
			}
2436
2437
			// @todo Add logic that identifies API latency, and reschedule the next background sync randomly between 8-16 hours.
2438
2439
			if ( $this->is_registered() ) {
2440
				if ( $this->has_paid_plan() ) {
2441
					// Initiate background plan sync.
2442
					$this->_sync_license( true );
2443
2444
					if ( $this->is_paying() ) {
2445
						// Check for premium plugin updates.
2446
						$this->_check_updates( true );
2447
					}
2448
				} else {
2449
					// Sync install (only if something changed locally).
2450
					$this->sync_install();
2451
				}
2452
			}
2453
2454
			if ( ! $this->is_addon() && $this->_has_addons ) {
2455
				// Sync add-ons collection.
2456
				$this->_sync_addons( true );
2457
			}
2458
		}
2459
2460
		/**
2461
		 * Check if sync was executed in the last $period of seconds.
2462
		 *
2463
		 * @author Vova Feldman (@svovaf)
2464
		 * @since  1.1.7.3
2465
		 *
2466
		 * @param int $period In seconds
2467
		 *
2468
		 * @return bool
2469
		 */
2470
		private function is_sync_executed( $period = WP_FS__TIME_24_HOURS_IN_SEC ) {
2471
			if ( ! isset( $this->_storage->sync_timestamp ) ) {
2472
				return false;
2473
			}
2474
2475
			return ( $this->_storage->sync_timestamp > ( WP_FS__SCRIPT_START_TIME - $period ) );
0 ignored issues
show
Documentation introduced by
The property sync_timestamp does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2476
		}
2477
2478
		/**
2479
		 * @author Vova Feldman (@svovaf)
2480
		 * @since  1.1.7.3
2481
		 *
2482
		 * @return bool
2483
		 */
2484
		private function is_sync_cron_on() {
2485
			/**
2486
			 * @var object $sync_cron_data
2487
			 */
2488
			$sync_cron_data = $this->_storage->get( 'sync_cron', null );
2489
2490
			return ( ! is_null( $sync_cron_data ) && true === $sync_cron_data->on );
2491
		}
2492
2493
		/**
2494
		 * @author Vova Feldman (@svovaf)
2495
		 * @since  1.1.7.3
2496
		 *
2497
		 * @param int  $start_at        Defaults to now.
2498
		 * @param bool $randomize_start If true, schedule first job randomly during the next 12 hours. Otherwise,
2499
		 *                              schedule job to start right away.
2500
		 */
2501
		private function schedule_sync_cron( $start_at = WP_FS__SCRIPT_START_TIME, $randomize_start = true ) {
2502
			$this->_logger->entrance();
2503
2504
			if ( $randomize_start ) {
2505
				// Schedule first sync with a random 12 hour time range from now.
2506
				$start_at += rand( 0, ( WP_FS__TIME_24_HOURS_IN_SEC / 2 ) );
2507
			}
2508
2509
			// Schedule daily WP cron.
2510
			wp_schedule_event(
2511
				$start_at,
2512
				'daily',
2513
				$this->get_action_tag( 'data_sync' )
2514
			);
2515
2516
			$this->_storage->store( 'sync_cron', (object) array(
2517
				'version'     => $this->get_plugin_version(),
2518
				'sdk_version' => $this->version,
2519
				'timestamp'   => WP_FS__SCRIPT_START_TIME,
2520
				'on'          => true,
2521
			) );
2522
		}
2523
2524
		/**
2525
		 * Add the actual sync function to the cron job hook.
2526
		 *
2527
		 * @author Vova Feldman (@svovaf)
2528
		 * @since  1.1.7.3
2529
		 */
2530
		private function hook_callback_to_sync_cron() {
2531
			$this->add_action( 'data_sync', array( &$this, '_sync_cron' ) );
2532
		}
2533
2534
		/**
2535
		 * @author Vova Feldman (@svovaf)
2536
		 * @since  1.1.7.3
2537
		 */
2538
		private function clear_sync_cron() {
2539
			$this->_logger->entrance();
2540
2541
			if ( ! $this->is_sync_cron_on() ) {
2542
				return;
2543
			}
2544
2545
			$this->_storage->remove( 'sync_cron' );
2546
2547
			wp_clear_scheduled_hook( $this->get_action_tag( 'data_sync' ) );
2548
		}
2549
2550
		/**
2551
		 * Unix timestamp for next sync cron execution or false if not scheduled.
2552
		 *
2553
		 * @author Vova Feldman (@svovaf)
2554
		 * @since  1.1.7.3
2555
		 *
2556
		 * @return int|false
2557
		 */
2558
		function next_sync_cron() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2559
			$this->_logger->entrance();
2560
2561
			if ( ! $this->is_sync_cron_on() ) {
2562
				return false;
2563
			}
2564
2565
			return wp_next_scheduled( $this->get_action_tag( 'data_sync' ) );
2566
		}
2567
2568
		/**
2569
		 * Unix timestamp for previous sync cron execution or false if never executed.
2570
		 *
2571
		 * @author Vova Feldman (@svovaf)
2572
		 * @since  1.1.7.3
2573
		 *
2574
		 * @return int|false
2575
		 */
2576
		function last_sync_cron() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2577
			$this->_logger->entrance();
2578
2579
			return $this->_storage->get( 'sync_timestamp' );
2580
		}
2581
2582
		#endregion Daily Sync Cron ------------------------------------------------------------------
2583
2584
		#region Async Install Sync ------------------------------------------------------------------
2585
2586
		/**
2587
		 * @author Vova Feldman (@svovaf)
2588
		 * @since  1.1.7.3
2589
		 *
2590
		 * @return bool
2591
		 */
2592
		private function is_install_sync_scheduled() {
2593
			/**
2594
			 * @var object $cron_data
2595
			 */
2596
			$cron_data = $this->_storage->get( 'install_sync_cron', null );
2597
2598
			return ( ! is_null( $cron_data ) && true === $cron_data->on );
2599
		}
2600
2601
		/**
2602
		 * Instead of running blocking install sync event, execute non blocking scheduled wp-cron.
2603
		 *
2604
		 * @author Vova Feldman (@svovaf)
2605
		 * @since  1.1.7.3
2606
		 */
2607
		private function schedule_install_sync() {
2608
			$this->_logger->entrance();
2609
2610
			$this->clear_install_sync_cron();
2611
2612
			// Schedule immediate install sync.
2613
			wp_schedule_single_event(
2614
				WP_FS__SCRIPT_START_TIME,
2615
				$this->get_action_tag( 'install_sync' )
2616
			);
2617
2618
			$this->_storage->store( 'install_sync_cron', (object) array(
2619
				'version'     => $this->get_plugin_version(),
2620
				'sdk_version' => $this->version,
2621
				'timestamp'   => WP_FS__SCRIPT_START_TIME,
2622
				'on'          => true,
2623
			) );
2624
		}
2625
2626
		/**
2627
		 * Unix timestamp for previous install sync cron execution or false if never executed.
2628
		 *
2629
		 * @todo   There's some very strange bug that $this->_storage->install_sync_timestamp value is not being
2630
		 *         updated. But for sure the sync event is working.
2631
		 *
2632
		 * @author Vova Feldman (@svovaf)
2633
		 * @since  1.1.7.3
2634
		 *
2635
		 * @return int|false
2636
		 */
2637
		function last_install_sync() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2638
			$this->_logger->entrance();
2639
2640
			return $this->_storage->get( 'install_sync_timestamp' );
2641
		}
2642
2643
		/**
2644
		 * Unix timestamp for next install sync cron execution or false if not scheduled.
2645
		 *
2646
		 * @author Vova Feldman (@svovaf)
2647
		 * @since  1.1.7.3
2648
		 *
2649
		 * @return int|false
2650
		 */
2651
		function next_install_sync() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2652
			$this->_logger->entrance();
2653
2654
			if ( ! $this->is_install_sync_scheduled() ) {
2655
				return false;
2656
			}
2657
2658
			return wp_next_scheduled( $this->get_action_tag( 'install_sync' ) );
2659
		}
2660
2661
		/**
2662
		 * Add the actual install sync function to the cron job hook.
2663
		 *
2664
		 * @author Vova Feldman (@svovaf)
2665
		 * @since  1.1.7.3
2666
		 */
2667
		private function hook_callback_to_install_sync() {
2668
			$this->add_action( 'install_sync', array( &$this, '_run_sync_install' ) );
2669
		}
2670
2671
		/**
2672
		 * @author Vova Feldman (@svovaf)
2673
		 * @since  1.1.7.3
2674
		 */
2675
		private function clear_install_sync_cron() {
2676
			$this->_logger->entrance();
2677
2678
			if ( ! $this->is_install_sync_scheduled() ) {
2679
				return;
2680
			}
2681
2682
			$this->_storage->remove( 'install_sync_cron' );
2683
2684
			wp_clear_scheduled_hook( $this->get_action_tag( 'install_sync' ) );
2685
		}
2686
2687
		/**
2688
		 * @author Vova Feldman (@svovaf)
2689
		 * @since  1.1.7.3
2690
		 */
2691
		public function _run_sync_install() {
2692
			$this->_logger->entrance();
2693
2694
			// Update last install sync timestamp.
2695
			$this->_storage->install_sync_timestamp = time();
0 ignored issues
show
Documentation introduced by
The property install_sync_timestamp does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2696
2697
			$this->sync_install( array(), true );
2698
		}
2699
2700
		#endregion Async Install Sync ------------------------------------------------------------------
2701
2702
		/**
2703
		 * Show a notice that activation is currently pending.
2704
		 *
2705
		 * @author Vova Feldman (@svovaf)
2706
		 * @since  1.0.7
2707
		 *
2708
		 * @param bool|string $email
2709
		 */
2710
		function _add_pending_activation_notice( $email = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2711
			if ( ! is_string( $email ) ) {
2712
				$current_user = self::_get_current_wp_user();
2713
				$email        = $current_user->user_email;
2714
			}
2715
2716
			$this->_admin_notices->add_sticky(
2717
				sprintf(
2718
					__fs( 'pending-activation-message', $this->_slug ),
2719
					'<b>' . $this->get_plugin_name() . '</b>',
2720
					'<b>' . $email . '</b>'
2721
				),
2722
				'activation_pending',
2723
				'Thanks!'
2724
			);
2725
		}
2726
2727
		/**
2728
		 * Check if currently in plugin activation.
2729
		 *
2730
		 * @author Vova Feldman (@svovaf)
2731
		 * @since  1.1.4
2732
		 *
2733
		 * @return bool
2734
		 */
2735
		function is_plugin_activation() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2736
			return get_option( "fs_{$this->_slug}_activated", false );
2737
		}
2738
2739
		/**
2740
		 *
2741
		 * NOTE: admin_menu action executed before admin_init.
2742
		 *
2743
		 * @author Vova Feldman (@svovaf)
2744
		 * @since  1.0.7
2745
		 */
2746
		function _admin_init_action() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2747
			/**
2748
			 * Automatically redirect to connect/activation page after plugin activation.
2749
			 *
2750
			 * @since 1.1.7 Do NOT redirect to opt-in when running in network admin mode.
2751
			 */
2752
			if ( $this->is_plugin_activation() ) {
2753
				delete_option( "fs_{$this->_slug}_activated" );
2754
2755
				if ( ! function_exists( 'is_network_admin' ) || ! is_network_admin() ) {
2756
					$this->_redirect_on_activation_hook();
2757
2758
					return;
2759
				}
2760
			}
2761
2762
			if ( fs_request_is_action( $this->_slug . '_skip_activation' ) ) {
2763
				check_admin_referer( $this->_slug . '_skip_activation' );
2764
2765
				$this->skip_connection();
2766
2767
				if ( fs_redirect( $this->get_after_activation_url( 'after_skip_url' ) ) ) {
2768
					exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method _admin_init_action() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2769
				}
2770
			}
2771
2772
			if ( ! $this->is_addon() && ! $this->is_registered() && ! $this->is_anonymous() ) {
2773
				if ( ! $this->is_pending_activation() ) {
2774
					if ( ! $this->_menu->is_activation_page() ) {
2775
						if ( $this->is_plugin_new_install() ) {
2776
							// Show notice for new plugin installations.
2777
							$this->_admin_notices->add(
2778
								sprintf(
2779
									__fs( 'you-are-step-away', $this->_slug ),
2780
									sprintf( '<b><a href="%s">%s</a></b>',
2781
										$this->get_activation_url(),
2782
										sprintf( __fs( 'activate-x-now', $this->_slug ), $this->get_plugin_name() )
2783
									)
2784
								),
2785
								'',
2786
								'update-nag'
2787
							);
2788
						} else {
2789
							if ( ! isset( $this->_storage->sticky_optin_added ) ) {
2790
								$this->_storage->sticky_optin_added = true;
0 ignored issues
show
Documentation introduced by
The property sticky_optin_added does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2791
2792
								// Show notice for new plugin installations.
2793
								$this->_admin_notices->add_sticky(
2794
									sprintf(
2795
										__fs( 'few-plugin-tweaks', $this->_slug ),
2796
										sprintf( '<b><a href="%s">%s</a></b>',
2797
											$this->get_activation_url(),
2798
											sprintf( __fs( 'optin-x-now', $this->_slug ), $this->get_plugin_name() )
2799
										)
2800
									),
2801
									'connect_account',
2802
									'',
2803
									'update-nag'
2804
								);
2805
							}
2806
2807
							if ( $this->has_filter( 'optin_pointer_element' ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->has_filter('optin_pointer_element') of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2808
								// Don't show admin nag if plugin update.
2809
								wp_enqueue_script( 'wp-pointer' );
2810
								wp_enqueue_style( 'wp-pointer' );
2811
2812
								$this->_enqueue_connect_essentials();
2813
2814
								add_action( 'admin_print_footer_scripts', array(
2815
									$this,
2816
									'_add_connect_pointer_script'
2817
								) );
2818
							}
2819
2820
						}
2821
					}
2822
				}
2823
			}
2824
2825
			$this->_add_upgrade_action_link();
2826
		}
2827
2828
		/**
2829
		 * Enqueue connect requires scripts and styles.
2830
		 *
2831
		 * @author Vova Feldman (@svovaf)
2832
		 * @since  1.1.4
2833
		 */
2834
		function _enqueue_connect_essentials() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2835
			wp_enqueue_script( 'jquery' );
2836
			wp_enqueue_script( 'json2' );
2837
2838
			fs_enqueue_local_script( 'postmessage', 'nojquery.ba-postmessage.min.js' );
2839
			fs_enqueue_local_script( 'fs-postmessage', 'postmessage.js' );
2840
2841
			fs_enqueue_local_style( 'fs_connect', '/admin/connect.css' );
2842
		}
2843
2844
		/**
2845
		 * Add connect / opt-in pointer.
2846
		 *
2847
		 * @author Vova Feldman (@svovaf)
2848
		 * @since  1.1.4
2849
		 */
2850
		function _add_connect_pointer_script() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2851
			$vars            = array( 'slug' => $this->_slug );
2852
			$pointer_content = fs_get_template( 'connect.php', $vars );
2853
			?>
2854
			<script type="text/javascript">// <![CDATA[
2855
				jQuery(document).ready(function ($) {
2856
					if ('undefined' !== typeof(jQuery().pointer)) {
2857
2858
						var element = <?php echo $this->apply_filters('optin_pointer_element', '$("#non_existing_element");') ?>;
2859
2860
						if (element.length > 0) {
2861
							var optin = $(element).pointer($.extend(true, {}, {
2862
								content     : <?php echo json_encode($pointer_content) ?>,
2863
								position    : {
2864
									edge : 'left',
2865
									align: 'center'
2866
								},
2867
								buttons     : function () {
2868
									// Don't show pointer buttons.
2869
									return '';
2870
								},
2871
								pointerWidth: 482
2872
							}, <?php echo $this->apply_filters('optin_pointer_options_json', '{}') ?>));
2873
2874
							<?php
2875
							echo $this->apply_filters('optin_pointer_execute', "
2876
2877
							optin.pointer('open');
2878
2879
							// Tag the opt-in pointer with custom class.
2880
							$('.wp-pointer #fs_connect')
2881
								.parents('.wp-pointer.wp-pointer-top')
2882
								.addClass('fs-opt-in-pointer');
2883
2884
							", 'element', 'optin') ?>
2885
						}
2886
					}
2887
				});
2888
				// ]]></script>
2889
		<?php
2890
		}
2891
2892
		/**
2893
		 * Return current page's URL.
2894
		 *
2895
		 * @author Vova Feldman (@svovaf)
2896
		 * @since  1.0.7
2897
		 *
2898
		 * @return string
2899
		 */
2900
		function current_page_url() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2901
			$url = 'http';
2902
2903
			if ( isset( $_SERVER["HTTPS"] ) ) {
2904
				if ( $_SERVER["HTTPS"] == "on" ) {
2905
					$url .= "s";
2906
				}
2907
			}
2908
			$url .= "://";
2909
			if ( $_SERVER["SERVER_PORT"] != "80" ) {
2910
				$url .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
2911
			} else {
2912
				$url .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
2913
			}
2914
2915
			return esc_url( $url );
2916
		}
2917
2918
		/**
2919
		 * Check if the current page is the plugin's main admin settings page.
2920
		 *
2921
		 * @author Vova Feldman (@svovaf)
2922
		 * @since  1.0.7
2923
		 *
2924
		 * @return bool
2925
		 */
2926
		function _is_plugin_page() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2927
			return fs_is_plugin_page( $this->_menu->get_raw_slug() ) ||
2928
			       fs_is_plugin_page( $this->_slug );
2929
		}
2930
2931
		/* Events
2932
		------------------------------------------------------------------------------------------------------------------*/
2933
		/**
2934
		 * Delete site install from Database.
2935
		 *
2936
		 * @author Vova Feldman (@svovaf)
2937
		 * @since  1.0.1
2938
		 *
2939
		 * @param bool $store
2940
		 */
2941
		function _delete_site( $store = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2942
			$sites = self::get_all_sites();
2943
2944
			if ( isset( $sites[ $this->_slug ] ) ) {
2945
				unset( $sites[ $this->_slug ] );
2946
			}
2947
2948
			self::$_accounts->set_option( 'sites', $sites, $store );
2949
		}
2950
2951
		/**
2952
		 * Delete plugin's plans information.
2953
		 *
2954
		 * @param bool $store Flush to Database if true.
2955
		 *
2956
		 * @author Vova Feldman (@svovaf)
2957
		 * @since  1.0.9
2958
		 */
2959
		private function _delete_plans( $store = true ) {
2960
			$this->_logger->entrance();
2961
2962
			$plans = self::get_all_plans();
2963
2964
			unset( $plans[ $this->_slug ] );
2965
2966
			self::$_accounts->set_option( 'plans', $plans, $store );
2967
		}
2968
2969
		/**
2970
		 * Delete all plugin licenses.
2971
		 *
2972
		 * @author Vova Feldman (@svovaf)
2973
		 * @since  1.0.9
2974
		 *
2975
		 * @param bool        $store
2976
		 * @param string|bool $plugin_slug
2977
		 */
2978
		private function _delete_licenses( $store = true, $plugin_slug = false ) {
2979
			$this->_logger->entrance();
2980
2981
			$all_licenses = self::get_all_licenses();
2982
2983
			if ( ! is_string( $plugin_slug ) ) {
2984
				$plugin_slug = $this->_slug;
2985
			}
2986
2987
			unset( $all_licenses[ $plugin_slug ] );
2988
2989
			self::$_accounts->set_option( 'licenses', $all_licenses, $store );
2990
		}
2991
2992
		/**
2993
		 * Check if Freemius was added on new plugin installation.
2994
		 *
2995
		 * @author Vova Feldman (@svovaf)
2996
		 * @since  1.1.5
2997
		 *
2998
		 * @return bool
2999
		 */
3000
		function is_plugin_new_install() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3001
			return isset( $this->_storage->is_plugin_new_install ) &&
3002
			       $this->_storage->is_plugin_new_install;
0 ignored issues
show
Documentation introduced by
The property is_plugin_new_install does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3003
		}
3004
3005
		/**
3006
		 * Plugin activated hook.
3007
		 *
3008
		 * @author Vova Feldman (@svovaf)
3009
		 * @since  1.0.1
3010
		 *
3011
		 * @uses   FS_Api
3012
		 */
3013
		function _activate_plugin_event_hook() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3014
			$this->_logger->entrance( 'slug = ' . $this->_slug );
3015
3016
			if ( ! current_user_can( 'activate_plugins' ) ) {
3017
				return;
3018
			}
3019
3020
			// Clear API cache on activation.
3021
			FS_Api::clear_cache();
3022
3023
			if ( $this->is_registered() ) {
3024
				// Schedule re-activation event and sync.
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3025
//				$this->sync_install( array(), true );
3026
				$this->schedule_install_sync();
3027
3028
				/**
3029
				 * @todo Work on automatic deactivation of the Free plugin version. It doesn't work since the slug of the free & premium versions is identical. Therefore, only one instance of Freemius is created and the activation hook of the premium version is not being added.
3030
				 */
3031
				if ( $this->_plugin_basename !== $this->_free_plugin_basename ) {
3032
					// Deactivate Free plugin version on premium plugin activation.
3033
					deactivate_plugins( $this->_free_plugin_basename );
3034
3035
					$this->_admin_notices->add(
3036
						sprintf( __fs( 'successful-version-upgrade-message', $this->_slug ), sprintf( '<b>%s</b>', $this->_plugin->title ) ),
3037
						__fs( 'woot', $this->_slug ) . '!'
3038
					);
3039
				}
3040
			} else if ( $this->is_anonymous() ) {
3041
				/**
3042
				 * Reset "skipped" click cache on the following:
3043
				 *  1. Development mode.
3044
				 *  2. If the user skipped the exact same version before.
3045
				 *
3046
				 * @todo 3. If explicitly asked to retry after every activation.
3047
				 */
3048
				if ( WP_FS__DEV_MODE ||
3049
				     $this->get_plugin_version() == $this->_storage->is_anonymous['version']
0 ignored issues
show
Documentation introduced by
The property is_anonymous does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3050
				) {
3051
					$this->reset_anonymous_mode();
3052
				}
3053
			}
3054
3055
			if ( ! isset( $this->_storage->is_plugin_new_install ) ) {
3056
				/**
3057
				 * If no previous version of plugin's version exist, it means that it's either
3058
				 * the first time that the plugin installed on the site, or the plugin was installed
3059
				 * before but didn't have Freemius integrated.
3060
				 *
3061
				 * Since register_activation_hook() do NOT fires on updates since 3.1, and only fires
3062
				 * on manual activation via the dashboard, is_plugin_activation() is TRUE
3063
				 * only after immediate activation.
3064
				 *
3065
				 * @since 1.1.4
3066
				 * @link  https://make.wordpress.org/core/2010/10/27/plugin-activation-hooks-no-longer-fire-for-updates/
3067
				 */
3068
				$this->_storage->is_plugin_new_install = empty( $this->_storage->plugin_last_version );
0 ignored issues
show
Documentation introduced by
The property is_plugin_new_install does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property plugin_last_version does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3069
			}
3070
3071
			if ( ! $this->_anonymous_mode && $this->has_api_connectivity( WP_FS__DEV_MODE ) ) {
3072
				// Store hint that the plugin was just activated to enable auto-redirection to settings.
3073
				add_option( "fs_{$this->_slug}_activated", true );
3074
			}
3075
		}
3076
3077
		/**
3078
		 * Delete account.
3079
		 *
3080
		 * @author Vova Feldman (@svovaf)
3081
		 * @since  1.0.3
3082
		 *
3083
		 * @param bool $check_user Enforce checking if user have plugins activation privileges.
3084
		 */
3085
		function delete_account_event( $check_user = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3086
			$this->_logger->entrance( 'slug = ' . $this->_slug );
3087
3088
			if ( $check_user && ! current_user_can( 'activate_plugins' ) ) {
3089
				return;
3090
			}
3091
3092
			$this->do_action( 'before_account_delete' );
3093
3094
			// Clear all admin notices.
3095
			$this->_admin_notices->clear_all_sticky();
3096
3097
			$this->_delete_site( false );
3098
3099
			$this->_delete_plans( false );
3100
3101
			$this->_delete_licenses( false );
3102
3103
			// Delete add-ons related to plugin's account.
3104
			$this->_delete_account_addons( false );
3105
3106
			// @todo Delete plans and licenses of add-ons.
3107
3108
			self::$_accounts->store();
3109
3110
			/**
3111
			 * IMPORTANT:
3112
			 *  Clear crons must be executed before clearing all storage.
3113
			 *  Otherwise, the cron will not be cleared.
3114
			 */
3115
			$this->clear_sync_cron();
3116
			$this->clear_install_sync_cron();
3117
3118
			// Clear all storage data.
3119
			$this->_storage->clear_all( true, array(
3120
				'connectivity_test',
3121
				'is_on',
3122
			) );
3123
3124
			// Send delete event.
3125
			$this->get_api_site_scope()->call( '/', 'delete' );
3126
3127
			$this->do_action( 'after_account_delete' );
3128
		}
3129
3130
		/**
3131
		 * Plugin deactivation hook.
3132
		 *
3133
		 * @author Vova Feldman (@svovaf)
3134
		 * @since  1.0.1
3135
		 */
3136
		function _deactivate_plugin_hook() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3137
			$this->_logger->entrance( 'slug = ' . $this->_slug );
3138
3139
			if ( ! current_user_can( 'activate_plugins' ) ) {
3140
				return;
3141
			}
3142
3143
			$this->_admin_notices->clear_all_sticky();
3144
			if ( isset( $this->_storage->sticky_optin_added ) ) {
3145
				unset( $this->_storage->sticky_optin_added );
3146
			}
3147
3148
			if ( ! isset( $this->_storage->is_plugin_new_install ) ) {
3149
				// Remember that plugin was already installed.
3150
				$this->_storage->is_plugin_new_install = false;
0 ignored issues
show
Documentation introduced by
The property is_plugin_new_install does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3151
			}
3152
3153
			$this->clear_sync_cron();
3154
			$this->clear_install_sync_cron();
3155
3156
			if ( $this->is_registered() ) {
3157
				// Send deactivation event.
3158
				$this->sync_install( array(
3159
					'is_active' => false,
3160
				) );
3161
			} else {
3162
				if ( ! $this->has_api_connectivity() ) {
3163
					// Reset connectivity test cache.
3164
					unset( $this->_storage->connectivity_test );
3165
				}
3166
			}
3167
3168
			// Clear API cache on deactivation.
3169
			FS_Api::clear_cache();
3170
3171
			$this->remove_sdk_reference();
3172
		}
3173
3174
		/**
3175
		 * @author Vova Feldman (@svovaf)
3176
		 * @since  1.1.6
3177
		 */
3178
		private function remove_sdk_reference() {
3179
			global $fs_active_plugins;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3180
3181
			foreach ( $fs_active_plugins->plugins as $sdk_path => &$data ) {
3182
				if ( $this->_plugin_basename == $data->plugin_path ) {
3183
					unset( $fs_active_plugins->plugins[ $sdk_path ] );
3184
					break;
3185
				}
3186
			}
3187
3188
			fs_fallback_to_newest_active_sdk();
3189
		}
3190
3191
		/**
3192
		 * @author Vova Feldman (@svovaf)
3193
		 * @since  1.1.3
3194
		 *
3195
		 * @param bool $is_anonymous
3196
		 */
3197
		private function set_anonymous_mode( $is_anonymous = true ) {
3198
			// Store information regarding skip to try and opt-in the user
3199
			// again in the future.
3200
			$this->_storage->is_anonymous = array(
0 ignored issues
show
Documentation introduced by
The property is_anonymous does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3201
				'is'        => $is_anonymous,
3202
				'timestamp' => WP_FS__SCRIPT_START_TIME,
3203
				'version'   => $this->get_plugin_version(),
3204
			);
3205
3206
			// Update anonymous mode cache.
3207
			$this->_is_anonymous = $is_anonymous;
3208
		}
3209
3210
		/**
3211
		 * @author Vova Feldman (@svovaf)
3212
		 * @since  1.1.3
3213
		 */
3214
		private function reset_anonymous_mode() {
3215
			unset( $this->_storage->is_anonymous );
3216
		}
3217
3218
		/**
3219
		 * Clears the anonymous mode and redirects to the opt-in screen.
3220
		 *
3221
		 * @author Vova Feldman (@svovaf)
3222
		 * @since  1.1.7
3223
		 */
3224
		function connect_again() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3225
			if ( ! $this->is_anonymous() ) {
3226
				return;
3227
			}
3228
3229
			$this->reset_anonymous_mode();
3230
3231
			if ( fs_redirect( $this->get_activation_url() ) ) {
3232
				exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method connect_again() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3233
			}
3234
		}
3235
3236
		/**
3237
		 * Skip account connect, and set anonymous mode.
3238
		 *
3239
		 * @author Vova Feldman (@svovaf)
3240
		 * @since  1.1.1
3241
		 */
3242
		private function skip_connection() {
3243
			$this->_logger->entrance();
3244
3245
			$this->_admin_notices->remove_sticky( 'connect_account' );
3246
3247
			$this->set_anonymous_mode();
3248
3249
			// Send anonymous skip event.
3250
			// No user identified info nor any tracking will be sent after the user skips the opt-in.
3251
			$this->get_api_plugin_scope()->call( 'skip.json', 'put', array(
3252
				'uid' => $this->get_anonymous_id(),
3253
			) );
3254
		}
3255
3256
		/**
3257
		 * Plugin version update hook.
3258
		 *
3259
		 * @author Vova Feldman (@svovaf)
3260
		 * @since  1.0.4
3261
		 */
3262
		private function update_plugin_version_event() {
3263
			$this->_logger->entrance();
3264
3265
			if ( ! $this->is_registered() ) {
3266
				return;
3267
			}
3268
3269
			$this->schedule_install_sync();
3270
//			$this->sync_install( array(), true );
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3271
		}
3272
3273
		/**
3274
		 * Update install details.
3275
		 *
3276
		 * @author Vova Feldman (@svovaf)
3277
		 * @since  1.1.2
3278
		 *
3279
		 * @param string[] string $override
3280
		 *
3281
		 * @return array
3282
		 */
3283
		private function get_install_data_for_api( $override = array() ) {
3284
			return array_merge( array(
3285
				'version'                      => $this->get_plugin_version(),
3286
				'is_premium'                   => $this->is_premium(),
3287
				'language'                     => get_bloginfo( 'language' ),
3288
				'charset'                      => get_bloginfo( 'charset' ),
3289
				'platform_version'             => get_bloginfo( 'version' ),
3290
				'programming_language_version' => phpversion(),
3291
				'title'                        => get_bloginfo( 'name' ),
3292
				'url'                          => get_site_url(),
3293
				// Special params.
3294
				'is_active'                    => true,
3295
				'is_uninstalled'               => false,
3296
			), $override );
3297
		}
3298
3299
		/**
3300
		 * Update install only if changed.
3301
		 *
3302
		 * @author Vova Feldman (@svovaf)
3303
		 * @since  1.0.9
3304
		 *
3305
		 * @param string[] string $override
3306
		 * @param bool     $flush
3307
		 *
3308
		 * @return false|object|string
3309
		 */
3310
		private function send_install_update( $override = array(), $flush = false ) {
3311
			$this->_logger->entrance();
3312
3313
			$check_properties = $this->get_install_data_for_api( $override );
3314
3315
			if ( $flush ) {
3316
				$params = $check_properties;
3317
			} else {
3318
				$params           = array();
3319
				$special          = array();
3320
				$special_override = false;
3321
3322
				foreach ( $check_properties as $p => $v ) {
3323
					if ( property_exists( $this->_site, $p ) ) {
3324
						if ( ! empty( $this->_site->{$p} ) &&
3325
						     $this->_site->{$p} != $v
3326
						) {
3327
							$this->_site->{$p} = $v;
3328
							$params[ $p ]      = $v;
3329
						}
3330
					} else {
3331
						$special[ $p ] = $v;
3332
3333
						if ( isset( $override[ $p ] ) ) {
3334
							$special_override = true;
3335
						}
3336
					}
3337
				}
3338
3339
				if ( $special_override || 0 < count( $params ) ) {
3340
					// Add special params only if has at least one
3341
					// standard param, or if explicitly requested to
3342
					// override a special param or a pram which is not exist
3343
					// in the install object.
3344
					$params = array_merge( $params, $special );
3345
				}
3346
			}
3347
3348
			if ( 0 < count( $params ) ) {
3349
				// Update last install sync timestamp.
3350
				$this->_storage->install_sync_timestamp = time();
0 ignored issues
show
Documentation introduced by
The property install_sync_timestamp does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3351
3352
				// Send updated values to FS.
3353
				$site = $this->get_api_site_scope()->call( '/', 'put', $params );
3354
3355
				if ( ! $this->is_api_error( $site ) ) {
3356
					// I successfully sent install update, clear scheduled sync if exist.
3357
					$this->clear_install_sync_cron();
3358
				}
3359
3360
				return $site;
3361
			}
3362
3363
			return false;
3364
		}
3365
3366
		/**
3367
		 * Update install only if changed.
3368
		 *
3369
		 * @author Vova Feldman (@svovaf)
3370
		 * @since  1.0.9
3371
		 *
3372
		 * @param string[] string $override
3373
		 * @param bool     $flush
3374
		 *
3375
		 * @return false|object|string
3376
		 */
3377
		private function sync_install( $override = array(), $flush = false ) {
3378
			$this->_logger->entrance();
3379
3380
			$site = $this->send_install_update( $override, $flush );
3381
3382
			if ( false === $site ) {
3383
				// No sync required.
3384
				return;
3385
			}
3386
3387
			if ( $this->is_api_error( $site ) ) {
3388
				// Failed to sync, don't update locally.
3389
				return;
3390
			}
3391
3392
			$plan              = $this->get_plan();
3393
			$this->_site       = new FS_Site( $site );
3394
			$this->_site->plan = $plan;
3395
3396
			$this->_store_site( true );
3397
		}
3398
3399
		/**
3400
		 * Plugin uninstall hook.
3401
		 *
3402
		 * @author Vova Feldman (@svovaf)
3403
		 * @since  1.0.1
3404
		 *
3405
		 * @param bool $check_user Enforce checking if user have plugins activation privileges.
3406
		 */
3407
		function _uninstall_plugin_event( $check_user = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3408
			$this->_logger->entrance( 'slug = ' . $this->_slug );
3409
3410
			if ( $check_user && ! current_user_can( 'activate_plugins' ) ) {
3411
				return;
3412
			}
3413
3414
			$params = array();
3415
			if ( isset( $this->_storage->uninstall_reason ) ) {
3416
				$params['reason_id']   = $this->_storage->uninstall_reason->id;
0 ignored issues
show
Documentation introduced by
The property uninstall_reason does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3417
				$params['reason_info'] = $this->_storage->uninstall_reason->info;
0 ignored issues
show
Documentation introduced by
The property uninstall_reason does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3418
			}
3419
3420
			if ( ! $this->is_registered() && isset( $this->_storage->uninstall_reason ) ) {
3421
				// Send anonymous uninstall event only if user submitted a feedback.
3422
				$params['uid'] = $this->get_anonymous_id();
3423
				$this->get_api_plugin_scope()->call( 'uninstall.json', 'put', $params );
3424
			} else {
3425
				// Send uninstall event.
3426
				$this->send_install_update( array_merge( $params, array(
3427
					'is_active'      => false,
3428
					'is_uninstalled' => true,
3429
				) ) );
3430
			}
3431
3432
			// @todo Decide if we want to delete plugin information from db.
3433
		}
3434
3435
		/**
3436
		 * @author Vova Feldman (@svovaf)
3437
		 * @since  1.1.1
3438
		 *
3439
		 * @return string
3440
		 */
3441
		private function premium_plugin_basename() {
3442
			return preg_replace( '/\//', '-premium/', $this->_free_plugin_basename, 1 );
3443
		}
3444
3445
		/**
3446
		 * Uninstall plugin hook. Called only when connected his account with Freemius for active sites tracking.
3447
		 *
3448
		 * @author Vova Feldman (@svovaf)
3449
		 * @since  1.0.2
3450
		 */
3451
		public static function _uninstall_plugin_hook() {
3452
			self::_load_required_static();
3453
3454
			self::$_static_logger->entrance();
3455
3456
			if ( ! current_user_can( 'activate_plugins' ) ) {
3457
				return;
3458
			}
3459
3460
			$plugin_file = substr( current_filter(), strlen( 'uninstall_' ) );
3461
3462
			self::$_static_logger->info( 'plugin = ' . $plugin_file );
3463
3464
			define( 'WP_FS__UNINSTALL_MODE', true );
3465
3466
			$fs = self::get_instance_by_file( $plugin_file );
3467
3468
			if ( is_object( $fs ) ) {
3469
				self::require_plugin_essentials();
3470
3471
				if ( is_plugin_active( $fs->_free_plugin_basename ) ||
3472
				     is_plugin_active( $fs->premium_plugin_basename() )
3473
				) {
3474
					// Deleting Free or Premium plugin version while the other version still installed.
3475
					return;
3476
				}
3477
3478
				$fs->_uninstall_plugin_event();
3479
3480
				$fs->do_action( 'after_uninstall' );
3481
			}
3482
		}
3483
3484
		#region Plugin Information ------------------------------------------------------------------
3485
3486
		/**
3487
		 * Load WordPress core plugin.php essential module.
3488
		 *
3489
		 * @author Vova Feldman (@svovaf)
3490
		 * @since  1.1.1
3491
		 */
3492
		private static function require_plugin_essentials() {
3493
			if ( ! function_exists( 'get_plugins' ) ) {
3494
				require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
3495
			}
3496
		}
3497
3498
		/**
3499
		 * Load WordPress core pluggable.php module.
3500
		 *
3501
		 * @author Vova Feldman (@svovaf)
3502
		 * @since  1.1.2
3503
		 */
3504
		private static function require_pluggable_essentials() {
3505
			if ( ! function_exists( 'wp_get_current_user' ) ) {
3506
				require_once( ABSPATH . 'wp-includes/pluggable.php' );
3507
			}
3508
		}
3509
3510
		/**
3511
		 * Return plugin data.
3512
		 *
3513
		 * @author Vova Feldman (@svovaf)
3514
		 * @since  1.0.1
3515
		 *
3516
		 * @return array
3517
		 */
3518
		function get_plugin_data() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3519
			if ( ! isset( $this->_plugin_data ) ) {
3520
				self::require_plugin_essentials();
3521
3522
				$this->_plugin_data = get_plugin_data( $this->_plugin_main_file_path );
3523
			}
3524
3525
			return $this->_plugin_data;
3526
		}
3527
3528
		/**
3529
		 * @author Vova Feldman (@svovaf)
3530
		 * @since  1.0.1
3531
		 *
3532
		 * @return string Plugin slug.
3533
		 */
3534
		function get_slug() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3535
			return $this->_slug;
3536
		}
3537
3538
		/**
3539
		 * @author Vova Feldman (@svovaf)
3540
		 * @since  1.0.1
3541
		 *
3542
		 * @return number Plugin ID.
3543
		 */
3544
		function get_id() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3545
			return $this->_plugin->id;
3546
		}
3547
3548
		/**
3549
		 * @author Vova Feldman (@svovaf)
3550
		 * @since  1.0.1
3551
		 *
3552
		 * @return string Plugin public key.
3553
		 */
3554
		function get_public_key() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3555
			return $this->_plugin->public_key;
3556
		}
3557
3558
		/**
3559
		 * Will be available only on sandbox mode.
3560
		 *
3561
		 * @author Vova Feldman (@svovaf)
3562
		 * @since  1.0.4
3563
		 *
3564
		 * @return mixed Plugin secret key.
3565
		 */
3566
		function get_secret_key() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3567
			return $this->_plugin->secret_key;
3568
		}
3569
3570
		/**
3571
		 * @author Vova Feldman (@svovaf)
3572
		 * @since  1.1.1
3573
		 *
3574
		 * @return bool
3575
		 */
3576
		function has_secret_key() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3577
			return ! empty( $this->_plugin->secret_key );
3578
		}
3579
3580
		/**
3581
		 * @author Vova Feldman (@svovaf)
3582
		 * @since  1.0.9
3583
		 *
3584
		 * @return string
3585
		 */
3586
		function get_plugin_name() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3587
			$this->_logger->entrance();
3588
3589
			if ( ! isset( $this->_plugin_name ) ) {
3590
				$plugin_data = $this->get_plugin_data();
3591
3592
				// Get name.
3593
				$this->_plugin_name = $plugin_data['Name'];
3594
3595
				// Check if plugin name contains [Premium] suffix and remove it.
3596
				$suffix     = '[premium]';
3597
				$suffix_len = strlen( $suffix );
3598
3599
				if ( strlen( $plugin_data['Name'] ) > $suffix_len &&
3600
				     $suffix === substr( strtolower( $plugin_data['Name'] ), - $suffix_len )
3601
				) {
3602
					$this->_plugin_name = substr( $plugin_data['Name'], 0, - $suffix_len );
3603
				}
3604
3605
				$this->_logger->departure( 'Name = ' . $this->_plugin_name );
3606
			}
3607
3608
			return $this->_plugin_name;
3609
		}
3610
3611
		/**
3612
		 * @author Vova Feldman (@svovaf)
3613
		 * @since  1.0.0
3614
		 *
3615
		 * @return string
3616
		 */
3617
		function get_plugin_version() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3618
			$this->_logger->entrance();
3619
3620
			$plugin_data = $this->get_plugin_data();
3621
3622
			$this->_logger->departure( 'Version = ' . $plugin_data['Version'] );
3623
3624
			return $this->apply_filters( 'plugin_version', $plugin_data['Version'] );
3625
		}
3626
3627
		/**
3628
		 * @author Vova Feldman (@svovaf)
3629
		 * @since  1.0.4
3630
		 *
3631
		 * @return string
3632
		 */
3633
		function get_plugin_basename() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3634
			return $this->_plugin_basename;
3635
		}
3636
3637
		function get_plugin_folder_name() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3638
			$this->_logger->entrance();
3639
3640
			$plugin_folder = $this->_plugin_basename;
3641
3642
			while ( '.' !== dirname( $plugin_folder ) ) {
3643
				$plugin_folder = dirname( $plugin_folder );
3644
			}
3645
3646
			$this->_logger->departure( 'Folder Name = ' . $plugin_folder );
3647
3648
			return $plugin_folder;
3649
		}
3650
3651
		#endregion ------------------------------------------------------------------
3652
3653
		/* Account
3654
		------------------------------------------------------------------------------------------------------------------*/
3655
3656
		/**
3657
		 * Find plugin's slug by plugin's basename.
3658
		 *
3659
		 * @author Vova Feldman (@svovaf)
3660
		 * @since  1.0.9
3661
		 *
3662
		 * @param string $plugin_base_name
3663
		 *
3664
		 * @return false|string
3665
		 */
3666
		private static function find_slug_by_basename( $plugin_base_name ) {
3667
			$file_slug_map = self::$_accounts->get_option( 'file_slug_map', array() );
3668
3669
			if ( ! array( $file_slug_map ) || ! isset( $file_slug_map[ $plugin_base_name ] ) ) {
3670
				return false;
3671
			}
3672
3673
			return $file_slug_map[ $plugin_base_name ];
3674
		}
3675
3676
		/**
3677
		 * Store the map between the plugin's basename to the slug.
3678
		 *
3679
		 * @author Vova Feldman (@svovaf)
3680
		 * @since  1.0.9
3681
		 */
3682
		private function store_file_slug_map() {
3683
			$file_slug_map = self::$_accounts->get_option( 'file_slug_map', array() );
3684
3685
			if ( ! array( $file_slug_map ) ) {
3686
				$file_slug_map = array();
3687
			}
3688
3689
			if ( ! isset( $file_slug_map[ $this->_plugin_basename ] ) ||
3690
			     $file_slug_map[ $this->_plugin_basename ] !== $this->_slug
3691
			) {
3692
				$file_slug_map[ $this->_plugin_basename ] = $this->_slug;
3693
				self::$_accounts->set_option( 'file_slug_map', $file_slug_map, true );
3694
			}
3695
		}
3696
3697
		/**
3698
		 * @return FS_User[]
3699
		 */
3700
		static function get_all_users() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3701
			$users = self::$_accounts->get_option( 'users', array() );
3702
3703
			if ( ! is_array( $users ) ) {
3704
				$users = array();
3705
			}
3706
3707
			return $users;
3708
		}
3709
3710
		/**
3711
		 * @return FS_Site[]
3712
		 */
3713
		private static function get_all_sites() {
3714
			$sites = self::$_accounts->get_option( 'sites', array() );
3715
3716
			if ( ! is_array( $sites ) ) {
3717
				$sites = array();
3718
			}
3719
3720
			return $sites;
3721
		}
3722
3723
		/**
3724
		 * @author Vova Feldman (@svovaf)
3725
		 * @since  1.0.6
3726
		 *
3727
		 * @return FS_Plugin_License[]
3728
		 */
3729
		private static function get_all_licenses() {
3730
			$licenses = self::$_accounts->get_option( 'licenses', array() );
3731
3732
			if ( ! is_array( $licenses ) ) {
3733
				$licenses = array();
3734
			}
3735
3736
			return $licenses;
3737
		}
3738
3739
		/**
3740
		 * @return FS_Plugin_Plan[]
3741
		 */
3742
		private static function get_all_plans() {
3743
			$plans = self::$_accounts->get_option( 'plans', array() );
3744
3745
			if ( ! is_array( $plans ) ) {
3746
				$plans = array();
3747
			}
3748
3749
			return $plans;
3750
		}
3751
3752
		/**
3753
		 * @author Vova Feldman (@svovaf)
3754
		 * @since  1.0.4
3755
		 *
3756
		 * @return FS_Plugin_Tag[]
3757
		 */
3758
		private static function get_all_updates() {
3759
			$updates = self::$_accounts->get_option( 'updates', array() );
3760
3761
			if ( ! is_array( $updates ) ) {
3762
				$updates = array();
3763
			}
3764
3765
			return $updates;
3766
		}
3767
3768
		/**
3769
		 * @author Vova Feldman (@svovaf)
3770
		 * @since  1.0.6
3771
		 *
3772
		 * @return FS_Plugin[]|false
3773
		 */
3774
		private static function get_all_addons() {
3775
			$addons = self::$_accounts->get_option( 'addons', array() );
3776
3777
			if ( ! is_array( $addons ) ) {
3778
				$addons = array();
3779
			}
3780
3781
			return $addons;
3782
		}
3783
3784
		/**
3785
		 * @author Vova Feldman (@svovaf)
3786
		 * @since  1.0.6
3787
		 *
3788
		 * @return FS_Plugin[]|false
3789
		 */
3790
		private static function get_all_account_addons() {
3791
			$addons = self::$_accounts->get_option( 'account_addons', array() );
3792
3793
			if ( ! is_array( $addons ) ) {
3794
				$addons = array();
3795
			}
3796
3797
			return $addons;
3798
		}
3799
3800
		/**
3801
		 * Check if user is registered.
3802
		 *
3803
		 * @author Vova Feldman (@svovaf)
3804
		 * @since  1.0.1
3805
		 * @return bool
3806
		 */
3807
		function is_registered() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3808
			return is_object( $this->_user );
3809
		}
3810
3811
		/**
3812
		 * @author Vova Feldman (@svovaf)
3813
		 * @since  1.0.4
3814
		 *
3815
		 * @return FS_Plugin
3816
		 */
3817
		function get_plugin() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3818
			return $this->_plugin;
3819
		}
3820
3821
		/**
3822
		 * @author Vova Feldman (@svovaf)
3823
		 * @since  1.0.3
3824
		 *
3825
		 * @return FS_User
3826
		 */
3827
		function get_user() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3828
			return $this->_user;
3829
		}
3830
3831
		/**
3832
		 * @author Vova Feldman (@svovaf)
3833
		 * @since  1.0.3
3834
		 *
3835
		 * @return FS_Site
3836
		 */
3837
		function get_site() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3838
			return $this->_site;
3839
		}
3840
3841
		/**
3842
		 * Get plugin add-ons.
3843
		 *
3844
		 * @author Vova Feldman (@svovaf)
3845
		 * @since  1.0.6
3846
		 *
3847
		 * @since  1.1.7.3 If not yet loaded, fetch data from the API.
3848
		 *
3849
		 * @return FS_Plugin[]|false
3850
		 */
3851
		function get_addons() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3852
			$this->_logger->entrance();
3853
3854
			$all_addons = self::get_all_addons();
3855
3856
			/**
3857
			 * @since 1.1.7.3 If not yet loaded, fetch data from the API.
3858
			 */
3859
			if ( ! is_array( $all_addons ) ||
3860
			     ! isset( $all_addons[ $this->_plugin->id ] ) ||
3861
			     ! is_array( $all_addons[ $this->_plugin->id ] ) ||
3862
			     empty( $all_addons[ $this->_plugin->id ] )
3863
			) {
3864
				if ( $this->_has_addons ) {
3865
					$addons = $this->_sync_addons();
3866
3867
					if ( ! empty( $addons ) ) {
3868
						$all_addons = self::get_all_addons();
3869
					}
3870
				}
3871
			}
3872
3873
			if ( ! is_array( $all_addons ) ||
3874
			     ! isset( $all_addons[ $this->_plugin->id ] ) ||
3875
			     ! is_array( $all_addons[ $this->_plugin->id ] ) ||
3876
			     empty( $all_addons[ $this->_plugin->id ] )
3877
			) {
3878
				return false;
3879
			}
3880
3881
			return $all_addons[ $this->_plugin->id ];
3882
		}
3883
3884
		/**
3885
		 * @author Vova Feldman (@svovaf)
3886
		 * @since  1.0.6
3887
		 *
3888
		 * @return FS_Plugin[]|false
3889
		 */
3890
		function get_account_addons() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3891
			$this->_logger->entrance();
3892
3893
			$addons = self::get_all_account_addons();
3894
3895
			if ( ! is_array( $addons ) ||
3896
			     ! isset( $addons[ $this->_plugin->id ] ) ||
3897
			     ! is_array( $addons[ $this->_plugin->id ] ) ||
3898
			     0 === count( $addons[ $this->_plugin->id ] )
3899
			) {
3900
				return false;
3901
			}
3902
3903
			return $addons[ $this->_plugin->id ];
3904
		}
3905
3906
		/**
3907
		 * Check if user has any
3908
		 *
3909
		 * @author Vova Feldman (@svovaf)
3910
		 * @since  1.1.6
3911
		 *
3912
		 * @return bool
3913
		 */
3914
		function has_account_addons() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3915
			$addons = $this->get_account_addons();
3916
3917
			return is_array( $addons ) && ( 0 < count( $addons ) );
3918
		}
3919
3920
3921
		/**
3922
		 * Get add-on by ID (from local data).
3923
		 *
3924
		 * @author Vova Feldman (@svovaf)
3925
		 * @since  1.0.6
3926
		 *
3927
		 * @param number $id
3928
		 *
3929
		 * @return FS_Plugin|false
3930
		 */
3931
		function get_addon( $id ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3932
			$this->_logger->entrance();
3933
3934
			$addons = $this->get_addons();
3935
3936
			if ( is_array( $addons ) ) {
3937
				foreach ( $addons as $addon ) {
3938
					if ( $id == $addon->id ) {
3939
						return $addon;
3940
					}
3941
				}
3942
			}
3943
3944
			return false;
3945
		}
3946
3947
		/**
3948
		 * Get add-on by slug (from local data).
3949
		 *
3950
		 * @author Vova Feldman (@svovaf)
3951
		 * @since  1.0.6
3952
		 *
3953
		 * @param string $slug
3954
		 *
3955
		 * @return FS_Plugin|false
3956
		 */
3957
		function get_addon_by_slug( $slug ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3958
			$this->_logger->entrance();
3959
3960
			$addons = $this->get_addons();
3961
3962
			if ( is_array( $addons ) ) {
3963
				foreach ( $addons as $addon ) {
3964
					if ( $slug == $addon->slug ) {
3965
						return $addon;
3966
					}
3967
				}
3968
			}
3969
3970
			return false;
3971
		}
3972
3973
		#region Plans & Licensing ------------------------------------------------------------------
3974
3975
		/**
3976
		 * Check if running premium plugin code.
3977
		 *
3978
		 * @author Vova Feldman (@svovaf)
3979
		 * @since  1.0.5
3980
		 *
3981
		 * @return bool
3982
		 */
3983
		function is_premium() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3984
			return $this->_plugin->is_premium;
3985
		}
3986
3987
		/**
3988
		 * Get site's plan ID.
3989
		 *
3990
		 * @author Vova Feldman (@svovaf)
3991
		 * @since  1.0.2
3992
		 *
3993
		 * @return number
3994
		 */
3995
		function get_plan_id() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3996
			return $this->_site->plan->id;
3997
		}
3998
3999
		/**
4000
		 * Get site's plan title.
4001
		 *
4002
		 * @author Vova Feldman (@svovaf)
4003
		 * @since  1.0.2
4004
		 *
4005
		 * @return string
4006
		 */
4007
		function get_plan_title() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4008
			return $this->_site->plan->title;
4009
		}
4010
4011
		/**
4012
		 * @author Vova Feldman (@svovaf)
4013
		 * @since  1.0.9
4014
		 *
4015
		 * @return FS_Plugin_Plan
4016
		 */
4017
		function get_plan() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4018
			return is_object( $this->_site->plan ) ? $this->_site->plan : false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression is_object($this->_site->...s->_site->plan : false; of type FS_Plugin_Plan|false adds false to the return on line 4018 which is incompatible with the return type documented by Freemius::get_plan of type FS_Plugin_Plan. It seems like you forgot to handle an error condition.
Loading history...
4019
		}
4020
4021
		/**
4022
		 * @author Vova Feldman (@svovaf)
4023
		 * @since  1.0.3
4024
		 *
4025
		 * @return bool
4026
		 */
4027
		function is_trial() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4028
			$this->_logger->entrance();
4029
4030
			if ( ! $this->is_registered() ) {
4031
				return false;
4032
			}
4033
4034
			return $this->_site->is_trial();
4035
		}
4036
4037
		/**
4038
		 * Check if currently in a trial with payment method (credit card or paypal).
4039
		 *
4040
		 * @author Vova Feldman (@svovaf)
4041
		 * @since  1.1.7
4042
		 *
4043
		 * @return bool
4044
		 */
4045
		function is_paid_trial() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4046
			$this->_logger->entrance();
4047
4048
			if ( ! $this->is_trial() ) {
4049
				return false;
4050
			}
4051
4052
			return $this->has_active_license() && ( $this->_site->trial_plan_id == $this->_license->plan_id );
4053
		}
4054
4055
		/**
4056
		 * Check if trial already utilized.
4057
		 *
4058
		 * @since 1.0.9
4059
		 *
4060
		 * @return bool
4061
		 */
4062
		function is_trial_utilized() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4063
			$this->_logger->entrance();
4064
4065
			if ( ! $this->is_registered() ) {
4066
				return false;
4067
			}
4068
4069
			return $this->_site->is_trial_utilized();
4070
		}
4071
4072
		/**
4073
		 * Get trial plan information (if in trial).
4074
		 *
4075
		 * @author Vova Feldman (@svovaf)
4076
		 * @since  1.0.9
4077
		 *
4078
		 * @return bool|FS_Plugin_Plan
4079
		 */
4080
		function get_trial_plan() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4081
			$this->_logger->entrance();
4082
4083
			if ( ! $this->is_trial() ) {
4084
				return false;
4085
			}
4086
4087
			return $this->_storage->trial_plan;
0 ignored issues
show
Documentation introduced by
The property trial_plan does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4088
		}
4089
4090
		/**
4091
		 * Check if the user has an activated and valid paid license on current plugin's install.
4092
		 *
4093
		 * @since 1.0.9
4094
		 *
4095
		 * @return bool
4096
		 */
4097
		function is_paying() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4098
			$this->_logger->entrance();
4099
4100
			if ( ! $this->is_registered() ) {
4101
				return false;
4102
			}
4103
4104
			if ( ! $this->has_paid_plan() ) {
4105
				return false;
4106
			}
4107
4108
			return (
4109
				! $this->is_trial() &&
4110
				'free' !== $this->_site->plan->name &&
4111
				$this->has_features_enabled_license()
4112
			);
4113
		}
4114
4115
		/**
4116
		 * @author Vova Feldman (@svovaf)
4117
		 * @since  1.0.4
4118
		 *
4119
		 * @return bool
4120
		 */
4121
		function is_free_plan() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4122
			if ( ! $this->is_registered() ) {
4123
				return true;
4124
			}
4125
4126
			if ( ! $this->has_paid_plan() ) {
4127
				return true;
4128
			}
4129
4130
			return (
4131
				'free' === $this->_site->plan->name ||
4132
				! $this->has_features_enabled_license()
4133
			);
4134
		}
4135
4136
		/**
4137
		 * @author Vova Feldman (@svovaf)
4138
		 * @since  1.0.5
4139
		 *
4140
		 * @return bool
4141
		 */
4142
		function _has_premium_license() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4143
			$this->_logger->entrance();
4144
4145
			$premium_license = $this->_get_available_premium_license();
4146
4147
			return ( false !== $premium_license );
4148
		}
4149
4150
		/**
4151
		 * Check if user has any licenses associated with the plugin (including expired or blocking).
4152
		 *
4153
		 * @author Vova Feldman (@svovaf)
4154
		 * @since  1.1.7.3
4155
		 *
4156
		 * @return bool
4157
		 */
4158
		private function has_any_license() {
4159
			return is_array( $this->_licenses ) && ( 0 < count( $this->_licenses ) );
4160
		}
4161
4162
		/**
4163
		 * @author Vova Feldman (@svovaf)
4164
		 * @since  1.0.5
4165
		 *
4166
		 * @return FS_Plugin_License
4167
		 */
4168
		function _get_available_premium_license() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4169
			$this->_logger->entrance();
4170
4171
			if ( ! $this->has_paid_plan() ) {
4172
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Freemius::_get_available_premium_license of type FS_Plugin_License.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4173
			}
4174
4175
			if ( is_array( $this->_licenses ) ) {
4176
				foreach ( $this->_licenses as $license ) {
4177
					if ( ! $license->is_utilized() && $license->is_features_enabled() ) {
4178
						return $license;
4179
					}
4180
				}
4181
			}
4182
4183
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Freemius::_get_available_premium_license of type FS_Plugin_License.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4184
		}
4185
4186
		/**
4187
		 * Sync local plugin plans with remote server.
4188
		 *
4189
		 * @author Vova Feldman (@svovaf)
4190
		 * @since  1.0.5
4191
		 *
4192
		 * @return FS_Plugin_Plan[]|object
4193
		 */
4194
		function _sync_plans() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4195
			$plans = $this->_fetch_plugin_plans();
4196
			if ( ! $this->is_api_error( $plans ) ) {
4197
				$this->_plans = $plans;
4198
				$this->_store_plans();
4199
			}
4200
4201
			$this->do_action( 'after_plans_sync', $plans );
4202
4203
			return $this->_plans;
4204
		}
4205
4206
		/**
4207
		 * @author Vova Feldman (@svovaf)
4208
		 * @since  1.0.5
4209
		 *
4210
		 * @param number $id
4211
		 *
4212
		 * @return FS_Plugin_Plan
4213
		 */
4214
		function _get_plan_by_id( $id ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4215
			$this->_logger->entrance();
4216
4217
			if ( ! is_array( $this->_plans ) || 0 === count( $this->_plans ) ) {
4218
				$this->_sync_plans();
4219
			}
4220
4221
			foreach ( $this->_plans as $plan ) {
4222
				if ( $id == $plan->id ) {
4223
					return $plan;
4224
				}
4225
			}
4226
4227
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Freemius::_get_plan_by_id of type FS_Plugin_Plan.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4228
		}
4229
4230
		/**
4231
		 * Sync local plugin plans with remote server.
4232
		 *
4233
		 * @author Vova Feldman (@svovaf)
4234
		 * @since  1.0.6
4235
		 *
4236
		 * @return FS_Plugin_License[]|object
4237
		 */
4238
		function _sync_licenses() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4239
			$licenses = $this->_fetch_licenses();
4240
			if ( ! $this->is_api_error( $licenses ) ) {
4241
				$this->_licenses = $licenses;
4242
				$this->_store_licenses();
4243
			}
4244
4245
			// Update current license.
4246
			if ( is_object( $this->_license ) ) {
4247
				$this->_license = $this->_get_license_by_id( $this->_license->id );
4248
			}
4249
4250
			return $this->_licenses;
4251
		}
4252
4253
		/**
4254
		 * @author Vova Feldman (@svovaf)
4255
		 * @since  1.0.5
4256
		 *
4257
		 * @param number $id
4258
		 *
4259
		 * @return FS_Plugin_License
4260
		 */
4261
		function _get_license_by_id( $id ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4262
			$this->_logger->entrance();
4263
4264
			if ( ! is_numeric( $id ) ) {
4265
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Freemius::_get_license_by_id of type FS_Plugin_License.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4266
			}
4267
4268
			if ( ! is_array( $this->_licenses ) || 0 === count( $this->_licenses ) ) {
4269
				$this->_sync_licenses();
4270
			}
4271
4272
			foreach ( $this->_licenses as $license ) {
4273
				if ( $id == $license->id ) {
4274
					return $license;
4275
				}
4276
			}
4277
4278
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Freemius::_get_license_by_id of type FS_Plugin_License.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4279
		}
4280
4281
		/**
4282
		 * Sync site's license with user licenses.
4283
		 *
4284
		 * @author Vova Feldman (@svovaf)
4285
		 * @since  1.0.6
4286
		 *
4287
		 * @param FS_Plugin_License|null $new_license
4288
		 */
4289
		function _update_site_license( $new_license ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4290
			$this->_logger->entrance();
4291
4292
			$this->_license = $new_license;
4293
4294
			if ( ! is_object( $new_license ) ) {
4295
				$this->_site->license_id = null;
4296
				$this->_sync_site_subscription( null );
4297
4298
				return;
4299
			}
4300
4301
			$this->_site->license_id = $this->_license->id;
4302
4303
			if ( ! is_array( $this->_licenses ) ) {
4304
				$this->_licenses = array();
4305
			}
4306
4307
			$is_license_found = false;
4308
			for ( $i = 0, $len = count( $this->_licenses ); $i < $len; $i ++ ) {
4309
				if ( $new_license->id == $this->_licenses[ $i ]->id ) {
4310
					$this->_licenses[ $i ] = $new_license;
4311
4312
					$is_license_found = true;
4313
					break;
4314
				}
4315
			}
4316
4317
			// If new license just append.
4318
			if ( ! $is_license_found ) {
4319
				$this->_licenses[] = $new_license;
4320
			}
4321
4322
			$this->_sync_site_subscription( $new_license );
4323
		}
4324
4325
		/**
4326
		 * Sync site's subscription.
4327
		 *
4328
		 * @author Vova Feldman (@svovaf)
4329
		 * @since  1.0.9
4330
		 *
4331
		 * @param FS_Plugin_License|null $license
4332
		 *
4333
		 * @return bool|\FS_Subscription
4334
		 */
4335
		private function _sync_site_subscription( $license ) {
4336
			if ( ! is_object( $license ) ) {
4337
				unset( $this->_storage->subscription );
4338
4339
				return false;
4340
			}
4341
4342
			// Load subscription details if not lifetime.
4343
			$subscription = $license->is_lifetime() ?
4344
				false :
4345
				$this->_fetch_site_license_subscription();
4346
4347
			if ( is_object( $subscription ) && ! isset( $subscription->error ) ) {
4348
				$this->_storage->subscription = $subscription;
0 ignored issues
show
Documentation introduced by
The property subscription does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4349
			} else {
4350
				unset( $this->_storage->subscription );
4351
			}
4352
4353
			return $subscription;
4354
		}
4355
4356
		/**
4357
		 * @author Vova Feldman (@svovaf)
4358
		 * @since  1.0.6
4359
		 *
4360
		 * @return bool|\FS_Plugin_License
4361
		 */
4362
		function _get_license() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4363
			return $this->_license;
4364
		}
4365
4366
		/**
4367
		 * @return bool|\FS_Subscription
4368
		 */
4369
		function _get_subscription() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4370
			return isset( $this->_storage->subscription ) ?
4371
				$this->_storage->subscription :
0 ignored issues
show
Documentation introduced by
The property subscription does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4372
				false;
4373
		}
4374
4375
		/**
4376
		 * @author Vova Feldman (@svovaf)
4377
		 * @since  1.0.2
4378
		 *
4379
		 * @param string $plan  Plan name
4380
		 * @param bool   $exact If true, looks for exact plan. If false, also check "higher" plans.
4381
		 *
4382
		 * @return bool
4383
		 */
4384
		function is_plan( $plan, $exact = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4385
			$this->_logger->entrance();
4386
4387
			if ( ! $this->is_registered() ) {
4388
				return false;
4389
			}
4390
4391
			$plan = strtolower( $plan );
4392
4393
			if ( $this->_site->plan->name === $plan ) // Exact plan.
4394
			{
4395
				return true;
4396
			} else if ( $exact ) // Required exact, but plans are different.
4397
			{
4398
				return false;
4399
			}
4400
4401
			$current_plan_order  = - 1;
4402
			$required_plan_order = - 1;
4403
			for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) {
4404
				if ( $plan === $this->_plans[ $i ]->name ) {
4405
					$required_plan_order = $i;
4406
				} else if ( $this->_site->plan->name === $this->_plans[ $i ]->name ) {
4407
					$current_plan_order = $i;
4408
				}
4409
			}
4410
4411
			return ( $current_plan_order > $required_plan_order );
4412
		}
4413
4414
		/**
4415
		 * Check if plan based on trial. If not in trial mode, should return false.
4416
		 *
4417
		 * @since  1.0.9
4418
		 *
4419
		 * @param string $plan  Plan name
4420
		 * @param bool   $exact If true, looks for exact plan. If false, also check "higher" plans.
4421
		 *
4422
		 * @return bool
4423
		 */
4424
		function is_trial_plan( $plan, $exact = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4425
			$this->_logger->entrance();
4426
4427
			if ( ! $this->is_registered() ) {
4428
				return false;
4429
			}
4430
4431
			if ( ! $this->is_trial() ) {
4432
				return false;
4433
			}
4434
4435
			if ( ! isset( $this->_storage->trial_plan ) ) {
4436
				// Store trial plan information.
4437
				$this->_enrich_site_trial_plan( true );
4438
			}
4439
4440
			if ( $this->_storage->trial_plan->name === $plan ) // Exact plan.
0 ignored issues
show
Documentation introduced by
The property trial_plan does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4441
			{
4442
				return true;
4443
			} else if ( $exact ) // Required exact, but plans are different.
4444
			{
4445
				return false;
4446
			}
4447
4448
			$current_plan_order  = - 1;
4449
			$required_plan_order = - 1;
4450
			for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) {
4451
				if ( $plan === $this->_plans[ $i ]->name ) {
4452
					$required_plan_order = $i;
4453
				} else if ( $this->_storage->trial_plan->name === $this->_plans[ $i ]->name ) {
0 ignored issues
show
Documentation introduced by
The property trial_plan does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4454
					$current_plan_order = $i;
4455
				}
4456
			}
4457
4458
			return ( $current_plan_order > $required_plan_order );
4459
		}
4460
4461
		/**
4462
		 * Check if plugin has any paid plans.
4463
		 *
4464
		 * @author Vova Feldman (@svovaf)
4465
		 * @since  1.0.7
4466
		 *
4467
		 * @return bool
4468
		 */
4469
		function has_paid_plan() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4470
			return $this->_has_paid_plans || FS_Plan_Manager::instance()->has_paid_plan( $this->_plans );
4471
		}
4472
4473
		/**
4474
		 * Check if plugin has any plan with a trail.
4475
		 *
4476
		 * @author Vova Feldman (@svovaf)
4477
		 * @since  1.0.9
4478
		 *
4479
		 * @return bool
4480
		 */
4481
		function has_trial_plan() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4482
			if ( ! $this->is_registered() ) {
4483
				return false;
4484
			}
4485
4486
			return $this->_storage->get( 'has_trial_plan', false );
4487
		}
4488
4489
		/**
4490
		 * Check if plugin has any free plan, or is it premium only.
4491
		 *
4492
		 * Note: If no plans configured, assume plugin is free.
4493
		 *
4494
		 * @author Vova Feldman (@svovaf)
4495
		 * @since  1.0.7
4496
		 *
4497
		 * @return bool
4498
		 */
4499
		function has_free_plan() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4500
			return FS_Plan_Manager::instance()->has_free_plan( $this->_plans );
4501
		}
4502
4503
		#region URL Generators
4504
4505
		/**
4506
		 * Alias to pricing_url().
4507
		 *
4508
		 * @author Vova Feldman (@svovaf)
4509
		 * @since  1.0.2
4510
		 *
4511
		 * @uses   pricing_url()
4512
		 *
4513
		 * @param string $period Billing cycle
4514
		 * @param bool   $is_trial
4515
		 *
4516
		 * @return string
4517
		 */
4518
		function get_upgrade_url( $period = WP_FS__PERIOD_ANNUALLY, $is_trial = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4519
			return $this->pricing_url( $period, $is_trial );
4520
		}
4521
4522
		/**
4523
		 * @author Vova Feldman (@svovaf)
4524
		 * @since  1.0.9
4525
		 *
4526
		 * @uses   get_upgrade_url()
4527
		 *
4528
		 * @return string
4529
		 */
4530
		function get_trial_url() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4531
			return $this->get_upgrade_url( WP_FS__PERIOD_ANNUALLY, true );
4532
		}
4533
4534
		/**
4535
		 * Plugin's pricing URL.
4536
		 *
4537
		 * @author Vova Feldman (@svovaf)
4538
		 * @since  1.0.4
4539
		 *
4540
		 * @param string $billing_cycle Billing cycle
4541
		 *
4542
		 * @param bool   $is_trial
4543
		 *
4544
		 * @return string
4545
		 */
4546
		function pricing_url( $billing_cycle = WP_FS__PERIOD_ANNUALLY, $is_trial = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4547
			$this->_logger->entrance();
4548
4549
			$params = array(
4550
				'billing_cycle' => $billing_cycle
4551
			);
4552
4553
			if ( $is_trial ) {
4554
				$params['trial'] = 'true';
4555
			}
4556
4557
			return $this->_get_admin_page_url( 'pricing', $params );
4558
		}
4559
4560
		/**
4561
		 * Checkout page URL.
4562
		 *
4563
		 * @author   Vova Feldman (@svovaf)
4564
		 * @since    1.0.6
4565
		 *
4566
		 * @param string $billing_cycle Billing cycle
4567
		 * @param bool   $is_trial
4568
		 * @param array  $extra         (optional) Extra parameters, override other query params.
4569
		 *
4570
		 * @return string
4571
		 */
4572
		function checkout_url(
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4573
			$billing_cycle = WP_FS__PERIOD_ANNUALLY,
4574
			$is_trial = false,
4575
			$extra = array()
4576
		) {
4577
			$this->_logger->entrance();
4578
4579
			$params = array(
4580
				'checkout'      => 'true',
4581
				'billing_cycle' => $billing_cycle,
4582
			);
4583
4584
			if ( $is_trial ) {
4585
				$params['trial'] = 'true';
4586
			}
4587
4588
			/**
4589
			 * Params in extra override other params.
4590
			 */
4591
			$params = array_merge( $params, $extra );
4592
4593
			return $this->_get_admin_page_url( 'pricing', $params );
4594
		}
4595
4596
		/**
4597
		 * Add-on checkout URL.
4598
		 *
4599
		 * @author   Vova Feldman (@svovaf)
4600
		 * @since    1.1.7
4601
		 *
4602
		 * @param number $addon_id
4603
		 * @param number $pricing_id
4604
		 * @param string $billing_cycle
4605
		 * @param bool   $is_trial
4606
		 *
4607
		 * @return string
4608
		 */
4609
		function addon_checkout_url(
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4610
			$addon_id,
4611
			$pricing_id,
4612
			$billing_cycle = WP_FS__PERIOD_ANNUALLY,
4613
			$is_trial = false
4614
		) {
4615
			return $this->checkout_url( $billing_cycle, $is_trial, array(
4616
				'plugin_id'  => $addon_id,
4617
				'pricing_id' => $pricing_id,
4618
			) );
4619
		}
4620
4621
		#endregion
4622
4623
		#endregion ------------------------------------------------------------------
4624
4625
		/**
4626
		 * Check if plugin has any add-ons.
4627
		 *
4628
		 * @author Vova Feldman (@svovaf)
4629
		 * @since  1.0.5
4630
		 *
4631
		 * @since  1.1.7.3 Base logic only on the parameter provided by the developer in the init function.
4632
		 *
4633
		 * @return bool
4634
		 */
4635
		function has_addons() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4636
			$this->_logger->entrance();
4637
4638
			return $this->_has_addons;
4639
		}
4640
4641
		/**
4642
		 * Check if plugin can work in anonymous mode.
4643
		 *
4644
		 * @author Vova Feldman (@svovaf)
4645
		 * @since  1.0.9
4646
		 *
4647
		 * @return bool
4648
		 */
4649
		function enable_anonymous() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4650
			return $this->_enable_anonymous;
4651
		}
4652
4653
		/**
4654
		 * Check if feature supported with current site's plan.
4655
		 *
4656
		 * @author Vova Feldman (@svovaf)
4657
		 * @since  1.0.1
4658
		 *
4659
		 * @todo   IMPLEMENT
4660
		 *
4661
		 * @param number $feature_id
4662
		 *
4663
		 * @throws Exception
4664
		 */
4665
		function is_feature_supported( $feature_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $feature_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4666
			throw new Exception( 'not implemented' );
4667
		}
4668
4669
		/**
4670
		 * @author Vova Feldman (@svovaf)
4671
		 * @since  1.0.1
4672
		 *
4673
		 * @return bool Is running in SSL/HTTPS
4674
		 */
4675
		function is_ssl() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4676
			return WP_FS__IS_HTTPS;
4677
		}
4678
4679
		/**
4680
		 * @author Vova Feldman (@svovaf)
4681
		 * @since  1.0.9
4682
		 *
4683
		 * @return bool Is running in AJAX call.
4684
		 *
4685
		 * @link   http://wordpress.stackexchange.com/questions/70676/how-to-check-if-i-am-in-admin-ajax
4686
		 */
4687
		function is_ajax() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4688
			return ( defined( 'DOING_AJAX' ) && DOING_AJAX );
4689
		}
4690
4691
		/**
4692
		 * @author Vova Feldman (@svovaf)
4693
		 * @since  1.1.7
4694
		 *
4695
		 * @return bool
4696
		 */
4697
		function is_cron() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4698
			return ( defined( 'DOING_CRON' ) && DOING_CRON );
4699
		}
4700
4701
		/**
4702
		 * Check if a real user is visiting the admin dashboard.
4703
		 *
4704
		 * @author Vova Feldman (@svovaf)
4705
		 * @since  1.1.7
4706
		 *
4707
		 * @return bool
4708
		 */
4709
		function is_user_in_admin() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4710
			return is_admin() && ! $this->is_ajax() && ! $this->is_cron();
4711
		}
4712
4713
		/**
4714
		 * Check if running in HTTPS and if site's plan matching the specified plan.
4715
		 *
4716
		 * @param string $plan
4717
		 * @param bool   $exact
4718
		 *
4719
		 * @return bool
4720
		 */
4721
		function is_ssl_and_plan( $plan, $exact = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4722
			return ( $this->is_ssl() && $this->is_plan( $plan, $exact ) );
4723
		}
4724
4725
		/**
4726
		 * Construct plugin's settings page URL.
4727
		 *
4728
		 * @author Vova Feldman (@svovaf)
4729
		 * @since  1.0.4
4730
		 *
4731
		 * @param string $page
4732
		 * @param array  $params
4733
		 *
4734
		 * @return string
4735
		 */
4736
		function _get_admin_page_url( $page = '', $params = array() ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4737
			if ( ! $this->_menu->is_top_level() ) {
4738
				$parent_slug = $this->_menu->get_parent_slug();
4739
				$menu_file   = ( false !== strpos( $parent_slug, '.php' ) ) ?
4740
					$parent_slug :
4741
					'admin.php';
4742
4743
				return add_query_arg( array_merge( $params, array(
4744
					'page' => $this->_menu->get_slug( $page ),
4745
				) ), admin_url( $menu_file, 'admin' ) );
4746
			}
4747
4748
			if ( $this->_menu->is_cpt() ) {
4749
				if ( empty( $page ) && $this->is_activation_mode() ) {
4750
					return add_query_arg( array_merge( $params, array(
4751
						'page' => $this->_menu->get_slug()
4752
					) ), admin_url( 'admin.php', 'admin' ) );
4753
				} else {
4754
					if ( ! empty( $page ) ) {
4755
						$params['page'] = $this->_menu->get_slug( $page );
4756
					}
4757
4758
					return add_query_arg( $params, admin_url( $this->_menu->get_raw_slug(), 'admin' ) );
4759
				}
4760
			} else {
4761
				return add_query_arg( array_merge( $params, array(
4762
					'page' => $this->_menu->get_slug( $page ),
4763
				) ), admin_url( 'admin.php', 'admin' ) );
4764
			}
4765
		}
4766
4767
4768
		/**
4769
		 * Plugin's account URL.
4770
		 *
4771
		 * @author Vova Feldman (@svovaf)
4772
		 * @since  1.0.4
4773
		 *
4774
		 * @param bool|string $action
4775
		 * @param array       $params
4776
		 *
4777
		 * @param bool        $add_action_nonce
4778
		 *
4779
		 * @return string
4780
		 */
4781
		function get_account_url( $action = false, $params = array(), $add_action_nonce = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4782
			if ( is_string( $action ) ) {
4783
				$params['fs_action'] = $action;
4784
			}
4785
4786
			self::require_pluggable_essentials();
4787
4788
			return ( $add_action_nonce && is_string( $action ) ) ?
4789
				wp_nonce_url( $this->_get_admin_page_url( 'account', $params ), $action ) :
4790
				$this->_get_admin_page_url( 'account', $params );
4791
		}
4792
4793
		/**
4794
		 * Plugin's account URL.
4795
		 *
4796
		 * @author Vova Feldman (@svovaf)
4797
		 * @since  1.0.4
4798
		 *
4799
		 * @param bool|string $topic
4800
		 * @param bool|string $message
4801
		 *
4802
		 * @return string
4803
		 */
4804
		function contact_url( $topic = false, $message = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4805
			$params = array();
4806
			if ( is_string( $topic ) ) {
4807
				$params['topic'] = $topic;
4808
			}
4809
			if ( is_string( $message ) ) {
4810
				$params['message'] = $message;
4811
			}
4812
4813
			if ( $this->is_addon() ) {
4814
				$params['addon_id'] = $this->get_id();
4815
4816
				return $this->get_parent_instance()->_get_admin_page_url( 'contact', $params );
4817
			} else {
4818
				return $this->_get_admin_page_url( 'contact', $params );
4819
			}
4820
		}
4821
4822
		/**
4823
		 * Add-on direct info URL.
4824
		 *
4825
		 * @author Vova Feldman (@svovaf)
4826
		 * @since  1.1.0
4827
		 *
4828
		 * @param string $slug
4829
		 *
4830
		 * @return string
4831
		 */
4832
		function addon_url( $slug ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4833
			return $this->_get_admin_page_url( 'addons', array(
4834
				'slug' => $slug
4835
			) );
4836
		}
4837
4838
		/* Logger
4839
		------------------------------------------------------------------------------------------------------------------*/
4840
		/**
4841
		 * @param string $id
4842
		 * @param bool   $prefix_slug
4843
		 *
4844
		 * @return FS_Logger
4845
		 */
4846
		function get_logger( $id = '', $prefix_slug = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4847
			return FS_Logger::get_logger( ( $prefix_slug ? $this->_slug : '' ) . ( ( ! $prefix_slug || empty( $id ) ) ? '' : '_' ) . $id );
4848
		}
4849
4850
		/**
4851
		 * @param      $id
4852
		 * @param bool $load_options
4853
		 * @param bool $prefix_slug
4854
		 *
4855
		 * @return FS_Option_Manager
4856
		 */
4857
		function get_options_manager( $id, $load_options = false, $prefix_slug = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4858
			return FS_Option_Manager::get_manager( ( $prefix_slug ? $this->_slug : '' ) . ( ( ! $prefix_slug || empty( $id ) ) ? '' : '_' ) . $id, $load_options );
4859
		}
4860
4861
		/* Security
4862
		------------------------------------------------------------------------------------------------------------------*/
4863
		private function _encrypt( $str ) {
4864
			if ( is_null( $str ) ) {
4865
				return null;
4866
			}
4867
4868
			return base64_encode( $str );
4869
		}
4870
4871
		private function _decrypt( $str ) {
4872
			if ( is_null( $str ) ) {
4873
				return null;
4874
			}
4875
4876
			return base64_decode( $str );
4877
		}
4878
4879
		/**
4880
		 * @author Vova Feldman (@svovaf)
4881
		 * @since  1.0.5
4882
		 *
4883
		 * @param FS_Entity $entity
4884
		 *
4885
		 * @return FS_Entity Return an encrypted clone entity.
4886
		 */
4887
		private function _encrypt_entity( FS_Entity $entity ) {
4888
			$clone = clone $entity;
4889
			$props = get_object_vars( $entity );
4890
4891
			foreach ( $props as $key => $val ) {
4892
				$clone->{$key} = $this->_encrypt( $val );
4893
			}
4894
4895
			return $clone;
4896
		}
4897
4898
		/**
4899
		 * @author Vova Feldman (@svovaf)
4900
		 * @since  1.0.5
4901
		 *
4902
		 * @param FS_Entity $entity
4903
		 *
4904
		 * @return FS_Entity Return an decrypted clone entity.
4905
		 */
4906
		private function _decrypt_entity( FS_Entity $entity ) {
4907
			$clone = clone $entity;
4908
			$props = get_object_vars( $entity );
4909
4910
			foreach ( $props as $key => $val ) {
4911
				$clone->{$key} = $this->_decrypt( $val );
4912
			}
4913
4914
			return $clone;
4915
		}
4916
4917
		/**
4918
		 * Tries to activate account based on POST params.
4919
		 *
4920
		 * @author Vova Feldman (@svovaf)
4921
		 * @since  1.0.2
4922
		 */
4923
		function _activate_account() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4924
			if ( $this->is_registered() ) {
4925
				// Already activated.
4926
				return;
4927
			}
4928
4929
			self::_clean_admin_content_section();
4930
4931
			if ( fs_request_is_action( 'activate' ) && fs_request_is_post() ) {
4932
//				check_admin_referer( 'activate_' . $this->_plugin->public_key );
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4933
4934
				// Verify matching plugin details.
4935
				if ( $this->_plugin->id != fs_request_get( 'plugin_id' ) || $this->_slug != fs_request_get( 'plugin_slug' ) ) {
4936
					return;
4937
				}
4938
4939
				$user              = new FS_User();
4940
				$user->id          = fs_request_get( 'user_id' );
4941
				$user->public_key  = fs_request_get( 'user_public_key' );
4942
				$user->secret_key  = fs_request_get( 'user_secret_key' );
4943
				$user->email       = fs_request_get( 'user_email' );
4944
				$user->first       = fs_request_get( 'user_first' );
4945
				$user->last        = fs_request_get( 'user_last' );
4946
				$user->is_verified = fs_request_get_bool( 'user_is_verified' );
4947
4948
				$site              = new FS_Site();
4949
				$site->id          = fs_request_get( 'install_id' );
4950
				$site->public_key  = fs_request_get( 'install_public_key' );
4951
				$site->secret_key  = fs_request_get( 'install_secret_key' );
4952
				$site->plan->id    = fs_request_get( 'plan_id' );
4953
				$site->plan->title = fs_request_get( 'plan_title' );
4954
				$site->plan->name  = fs_request_get( 'plan_name' );
4955
4956
				$plans      = array();
4957
				$plans_data = json_decode( urldecode( fs_request_get( 'plans' ) ) );
4958
				foreach ( $plans_data as $p ) {
4959
					$plans[] = new FS_Plugin_Plan( $p );
4960
				}
4961
4962
				$this->_set_account( $user, $site, $plans );
4963
4964
				// Reload the page with the keys.
4965
				if ( fs_redirect( $this->_get_admin_page_url() ) ) {
4966
					exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method _activate_account() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
4967
				}
4968
			}
4969
		}
4970
4971
		/**
4972
		 * @author Vova Feldman (@svovaf)
4973
		 * @since  1.0.7
4974
		 *
4975
		 * @param string $email
4976
		 *
4977
		 * @return FS_User|bool
4978
		 */
4979
		static function _get_user_by_email( $email ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4980
			self::$_static_logger->entrance();
4981
4982
			$email = trim( strtolower( $email ) );
4983
			$users = self::get_all_users();
4984
			if ( is_array( $users ) ) {
4985
				foreach ( $users as $u ) {
4986
					if ( $email === trim( strtolower( $u->email ) ) ) {
4987
						return $u;
4988
					}
4989
				}
4990
			}
4991
4992
			return false;
4993
		}
4994
4995
		#region Account (Loading, Updates & Activation) ------------------------------------------------------------------
4996
4997
		/***
4998
		 * Load account information (user + site).
4999
		 *
5000
		 * @author Vova Feldman (@svovaf)
5001
		 * @since  1.0.1
5002
		 */
5003
		private function _load_account() {
5004
			$this->_logger->entrance();
5005
5006
			$this->do_action( 'before_account_load' );
5007
5008
			$sites    = self::get_all_sites();
5009
			$users    = self::get_all_users();
5010
			$plans    = self::get_all_plans();
5011
			$licenses = self::get_all_licenses();
5012
5013
			if ( $this->_logger->is_on() && is_admin() ) {
5014
				$this->_logger->log( 'sites = ' . var_export( $sites, true ) );
5015
				$this->_logger->log( 'users = ' . var_export( $users, true ) );
5016
				$this->_logger->log( 'plans = ' . var_export( $plans, true ) );
5017
				$this->_logger->log( 'licenses = ' . var_export( $licenses, true ) );
5018
			}
5019
5020
			$site = isset( $sites[ $this->_slug ] ) ? $sites[ $this->_slug ] : false;
5021
5022
			if ( is_object( $site ) &&
5023
			     is_numeric( $site->id ) &&
5024
			     is_numeric( $site->user_id ) &&
5025
			     is_object( $site->plan )
5026
			) {
5027
				// Load site.
5028
				$this->_site       = clone $site;
5029
				$this->_site->plan = $this->_decrypt_entity( $this->_site->plan );
5030
5031
				// Load relevant user.
5032
				$this->_user = clone $users[ $this->_site->user_id ];
5033
5034
				// Load plans.
5035
				$this->_plans = $plans[ $this->_slug ];
5036
				if ( ! is_array( $this->_plans ) || empty( $this->_plans ) ) {
5037
					$this->_sync_plans( true );
0 ignored issues
show
Unused Code introduced by
The call to Freemius::_sync_plans() has too many arguments starting with true.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5038
				} else {
5039
					for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) {
5040
						if ( $this->_plans[ $i ] instanceof FS_Plugin_Plan ) {
5041
							$this->_plans[ $i ] = $this->_decrypt_entity( $this->_plans[ $i ] );
5042
						} else {
5043
							unset( $this->_plans[ $i ] );
5044
						}
5045
					}
5046
				}
5047
5048
				// Load licenses.
5049
				$this->_licenses = array();
5050
				if ( is_array( $licenses ) &&
5051
				     isset( $licenses[ $this->_slug ] ) &&
5052
				     isset( $licenses[ $this->_slug ][ $this->_user->id ] )
5053
				) {
5054
					$this->_licenses = $licenses[ $this->_slug ][ $this->_user->id ];
5055
				}
5056
5057
				$this->_license = $this->_get_license_by_id( $this->_site->license_id );
5058
5059
				if ( $this->_site->version != $this->get_plugin_version() ) {
5060
					// If stored install version is different than current installed plugin version,
5061
					// then update plugin version event.
5062
					$this->update_plugin_version_event();
5063
				}
5064
			}
5065
5066
			$this->_register_account_hooks();
5067
		}
5068
5069
		/**
5070
		 * @author Vova Feldman (@svovaf)
5071
		 * @since  1.0.1
5072
		 *
5073
		 * @param FS_User    $user
5074
		 * @param FS_Site    $site
5075
		 * @param bool|array $plans
5076
		 */
5077
		private function _set_account( FS_User $user, FS_Site $site, $plans = false ) {
5078
			$site->slug    = $this->_slug;
5079
			$site->user_id = $user->id;
5080
5081
			$this->_site = $site;
5082
			$this->_user = $user;
5083
			if ( false !== $plans ) {
5084
				$this->_plans = $plans;
5085
			}
5086
5087
			$this->send_install_update();
5088
5089
			$this->_store_account();
5090
5091
		}
5092
5093
		/**
5094
		 * @author Vova Feldman (@svovaf)
5095
		 * @since  1.1.7.4
5096
		 *
5097
		 * @param array $override_with
5098
		 *
5099
		 * @return array
5100
		 */
5101
		function get_opt_in_params( $override_with = array() ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5102
			$this->_logger->entrance();
5103
5104
			$current_user = self::_get_current_wp_user();
5105
5106
			$params = array(
5107
				'user_firstname'    => $current_user->user_firstname,
5108
				'user_lastname'     => $current_user->user_lastname,
5109
				'user_nickname'     => $current_user->user_nicename,
5110
				'user_email'        => $current_user->user_email,
5111
				'user_ip'           => WP_FS__REMOTE_ADDR,
5112
				'plugin_slug'       => $this->_slug,
5113
				'plugin_id'         => $this->get_id(),
5114
				'plugin_public_key' => $this->get_public_key(),
5115
				'plugin_version'    => $this->get_plugin_version(),
5116
				'return_url'        => wp_nonce_url( $this->_get_admin_page_url(
5117
					'',
5118
					array( 'fs_action' => $this->_slug . '_activate_new' )
5119
				), $this->_slug . '_activate_new' ),
5120
				'account_url'       => wp_nonce_url( $this->_get_admin_page_url(
5121
					'account',
5122
					array( 'fs_action' => 'sync_user' )
5123
				), 'sync_user' ),
5124
				'site_uid'          => $this->get_anonymous_id(),
5125
				'site_url'          => get_site_url(),
5126
				'site_name'         => get_bloginfo( 'name' ),
5127
				'platform_version'  => get_bloginfo( 'version' ),
5128
				'php_version'       => phpversion(),
5129
				'language'          => get_bloginfo( 'language' ),
5130
				'charset'           => get_bloginfo( 'charset' ),
5131
			);
5132
5133
			if ( WP_FS__SKIP_EMAIL_ACTIVATION && $this->has_secret_key() ) {
5134
				// Even though rand() is known for its security issues,
5135
				// the timestamp adds another layer of protection.
5136
				// It would be very hard for an attacker to get the secret key form here.
5137
				// Plus, this should never run in production since the secret should never
5138
				// be included in the production version.
5139
				$params['ts']     = WP_FS__SCRIPT_START_TIME;
5140
				$params['salt']   = md5( uniqid( rand() ) );
5141
				$params['secure'] = md5(
5142
					$params['ts'] .
5143
					$params['salt'] .
5144
					$this->get_secret_key()
5145
				);
5146
			}
5147
5148
			return array_merge( $params, $override_with );
5149
		}
5150
5151
		/**
5152
		 * @author Vova Feldman (@svovaf)
5153
		 * @since  1.1.7.4
5154
		 *
5155
		 * @param string|bool $email
5156
		 * @param string|bool $first
5157
		 * @param string|bool $last
5158
		 *
5159
		 * @return bool Is successful opt-in (or set to pending).
5160
		 */
5161
		function opt_in( $email = false, $first = false, $last = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5162
			$this->_logger->entrance();
5163
5164
			if ( false === $email ) {
5165
				$current_user = self::_get_current_wp_user();
5166
				$email        = $current_user->user_email;
5167
			}
5168
5169
			$fs_user = Freemius::_get_user_by_email( $email );
5170
			if ( is_object( $fs_user ) && ! $this->is_pending_activation() ) {
5171
				$this->install_with_current_user( false );
5172
5173
				return true;
5174
			}
5175
5176
			$user_info = array();
5177
			if ( ! empty( $email ) ) {
5178
				$user_info['user_email'] = $email;
5179
			}
5180
			if ( ! empty( $first ) ) {
5181
				$user_info['user_firstname'] = $first;
5182
			}
5183
			if ( ! empty( $last ) ) {
5184
				$user_info['user_lastname'] = $last;
5185
			}
5186
5187
			$params           = $this->get_opt_in_params( $user_info );
5188
			$params['format'] = 'json';
5189
5190
			$url = WP_FS__ADDRESS . '/action/service/user/install/';
5191
			if ( isset( $_COOKIE['XDEBUG_SESSION'] ) ) {
5192
				$url = add_query_arg( 'XDEBUG_SESSION', 'PHPSTORM', $url );
5193
			}
5194
5195
			$response = wp_remote_post( $url, array(
5196
				'method'  => 'POST',
5197
				'body'    => $params,
5198
				'timeout' => 15,
5199
			) );
5200
5201
			if ( $response instanceof WP_Error ) {
0 ignored issues
show
Bug introduced by
The class WP_Error 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...
5202
				if ( 'https://' === substr( $url, 0, 8 ) &&
5203
				     isset( $response->errors ) &&
5204
				     isset( $response->errors['http_request_failed'] ) &&
5205
				     false !== strpos( $response->errors['http_request_failed'][0], 'sslv3 alert handshake' )
5206
				) {
5207
					// Failed due to old version of cURL or Open SSL (SSLv3 is not supported by CloudFlare).
5208
					$url = 'http://' . substr( $url, 8 );
5209
5210
					$response = wp_remote_post( $url, array(
5211
						'method'  => 'POST',
5212
						'body'    => $params,
5213
						'timeout' => 15,
5214
					) );
5215
				}
5216
5217
				if ( $response instanceof WP_Error ) {
0 ignored issues
show
Bug introduced by
The class WP_Error 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...
5218
				return false;
5219
			}
5220
			}
5221
5222
			if ( is_wp_error( $response ) ) {
5223
				return false;
5224
			}
5225
5226
			$decoded = @json_decode( $response['body'] );
5227
5228
			if ( empty( $decoded ) ) {
5229
				return false;
5230
			}
5231
5232
			if ( isset( $decoded->error ) ) {
5233
				return false;
5234
			} else if ( isset( $decoded->pending_activation ) && $decoded->pending_activation ) {
5235
				// Pending activation, add message.
5236
				$this->set_pending_confirmation( false, false );
5237
5238
				return true;
5239
			} else if ( isset( $decoded->install_secret_key ) ) {
5240
				$this->install_with_new_user(
5241
					$decoded->user_id,
5242
					$decoded->user_public_key,
5243
					$decoded->user_secret_key,
5244
					$decoded->install_id,
5245
					$decoded->install_public_key,
5246
					$decoded->install_secret_key,
5247
					false
5248
				);
5249
5250
				return true;
5251
			}
5252
5253
			return false;
5254
		}
5255
5256
		/**
5257
		 * Set user and site identities.
5258
		 *
5259
		 * @author Vova Feldman (@svovaf)
5260
		 * @since  1.0.9
5261
		 *
5262
		 * @param FS_User $user
5263
		 * @param FS_Site $site
5264
		 * @param bool    $redirect
5265
		 *
5266
		 * @return bool False if account already set.
5267
		 */
5268
		function setup_account( FS_User $user, FS_Site $site, $redirect = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5269
			$this->_user = $user;
5270
			$this->_site = $site;
5271
5272
			$this->_sync_plans();
5273
5274
			$this->_enrich_site_plan( false );
5275
5276
			$this->_set_account( $user, $site );
5277
5278
			if ( $this->is_trial() ) {
5279
				// Store trial plan information.
5280
				$this->_enrich_site_trial_plan( true );
5281
			}
5282
5283
			$this->do_action( 'after_account_connection', $user, $site );
5284
5285
			if ( is_numeric( $site->license_id ) ) {
5286
				$this->_license = $this->_get_license_by_id( $site->license_id );
5287
			}
5288
5289
			if ( $this->is_pending_activation() ) {
5290
				// Remove pending activation sticky notice (if still exist).
5291
				$this->_admin_notices->remove_sticky( 'activation_pending' );
5292
5293
				// Remove plugin from pending activation mode.
5294
				unset( $this->_storage->is_pending_activation );
5295
5296
				if ( ! $this->is_paying() ) {
5297
					$this->_admin_notices->add_sticky(
5298
						sprintf( __fs( 'plugin-x-activation-message', $this->_slug ), '<b>' . $this->get_plugin_name() . '</b>' ),
5299
						'activation_complete'
5300
					);
5301
				}
5302
			}
5303
5304
			if ( $this->is_paying() && ! $this->is_premium() ) {
5305
				$this->_admin_notices->add_sticky(
5306
					sprintf(
5307
						__fs( 'activation-with-plan-x-message', $this->_slug ),
5308
						$this->_site->plan->title
5309
					) . ' ' . $this->_get_latest_download_link( sprintf(
5310
						__fs( 'download-latest-x-version', $this->_slug ),
5311
						$this->_site->plan->title
5312
					) ),
5313
					'plan_upgraded',
5314
					__fs( 'yee-haw', $this->_slug ) . '!'
5315
				);
5316
			}
5317
5318
			$plugin_id = fs_request_get( 'plugin_id', false );
5319
5320
			// Store activation time ONLY for plugins (not add-ons).
5321
			if ( ! is_numeric( $plugin_id ) || ( $plugin_id == $this->_plugin->id ) ) {
5322
				$this->_storage->activation_timestamp = WP_FS__SCRIPT_START_TIME;
0 ignored issues
show
Documentation introduced by
The property activation_timestamp does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
5323
			}
5324
5325
			if ( is_numeric( $plugin_id ) ) {
5326
				if ( $plugin_id != $this->_plugin->id ) {
5327
					// Add-on was installed - sync license right after install.
5328
					if ( $redirect && fs_redirect( fs_nonce_url( $this->_get_admin_page_url(
5329
							'account',
5330
							array(
5331
								'fs_action' => $this->_slug . '_sync_license',
5332
								'plugin_id' => $plugin_id
5333
							)
5334
						), $this->_slug . '_sync_license' ) )
5335
					) {
5336
						exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method setup_account() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
5337
					}
5338
5339
				}
5340
			} else {
5341
				// Reload the page with the keys.
5342
				if ( $redirect && fs_redirect( $this->get_after_activation_url( 'after_connect_url' ) ) ) {
5343
					exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method setup_account() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
5344
				}
5345
			}
5346
		}
5347
5348
		/**
5349
		 * Install plugin with new user information after approval.
5350
		 *
5351
		 * @author Vova Feldman (@svovaf)
5352
		 * @since  1.0.7
5353
		 */
5354
		function _install_with_new_user() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5355
			$this->_logger->entrance();
5356
5357
			if ( $this->is_registered() ) {
5358
				return;
5359
			}
5360
5361
			if ( fs_request_is_action( $this->_slug . '_activate_new' ) ) {
5362
//				check_admin_referer( $this->_slug . '_activate_new' );
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5363
5364
				$this->_admin_notices->remove_sticky( 'connect_account' );
5365
5366
				if ( fs_request_has( 'user_secret_key' ) ) {
5367
					$this->install_with_new_user(
5368
						fs_request_get( 'user_id' ),
5369
						fs_request_get( 'user_public_key' ),
5370
						fs_request_get( 'user_secret_key' ),
5371
						fs_request_get( 'install_id' ),
5372
						fs_request_get( 'install_public_key' ),
5373
						fs_request_get( 'install_secret_key' )
5374
					);
5375
				} else if ( fs_request_has( 'pending_activation' ) ) {
5376
					$this->set_pending_confirmation( fs_request_get( 'user_email' ), true );
5377
				}
5378
			}
5379
		}
5380
5381
		/**
5382
		 * Install plugin with new user.
5383
		 *
5384
		 * @author Vova Feldman (@svovaf)
5385
		 * @since  1.1.7.4
5386
		 *
5387
		 * @param number $user_id
5388
		 * @param string $user_public_key
5389
		 * @param string $user_secret_key
5390
		 * @param number $install_id
5391
		 * @param string $install_public_key
5392
		 * @param string $install_secret_key
5393
		 * @param bool   $redirect
5394
		 */
5395
		private function install_with_new_user(
5396
			$user_id,
5397
			$user_public_key,
5398
			$user_secret_key,
5399
			$install_id,
5400
			$install_public_key,
5401
			$install_secret_key,
5402
			$redirect = true
5403
		) {
5404
			$user             = new FS_User();
5405
			$user->id         = $user_id;
5406
			$user->public_key = $user_public_key;
5407
			$user->secret_key = $user_secret_key;
5408
5409
			$this->_user = $user;
5410
			$user_result = $this->get_api_user_scope()->get();
5411
			$user        = new FS_User( $user_result );
5412
			$this->_user = $user;
5413
5414
			$site             = new FS_Site();
5415
			$site->id         = $install_id;
5416
			$site->public_key = $install_public_key;
5417
			$site->secret_key = $install_secret_key;
5418
5419
			$this->_site = $site;
5420
			$site_result = $this->get_api_site_scope()->get();
5421
			$site        = new FS_Site( $site_result );
5422
			$this->_site = $site;
5423
5424
			$this->setup_account( $this->_user, $this->_site, $redirect );
5425
		}
5426
5427
		/**
5428
		 * @author Vova Feldman (@svovaf)
5429
		 * @since  1.1.7.4
5430
		 *
5431
		 * @param bool $email
5432
		 * @param bool $redirect
5433
		 */
5434
		private function set_pending_confirmation( $email = false, $redirect = true ) {
5435
			// Install must be activated via email since
5436
			// user with the same email already exist.
5437
			$this->_storage->is_pending_activation = true;
0 ignored issues
show
Documentation introduced by
The property is_pending_activation does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
5438
			$this->_add_pending_activation_notice( $email );
5439
5440
			// Reload the page with with pending activation message.
5441
			if ( $redirect && fs_redirect( $this->get_after_activation_url( 'after_pending_connect_url' ) ) ) {
5442
				exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method set_pending_confirmation() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
5443
			}
5444
		}
5445
5446
		/**
5447
		 * Install plugin with current logged WP user info.
5448
		 *
5449
		 * @author Vova Feldman (@svovaf)
5450
		 * @since  1.0.7
5451
		 */
5452
		function _install_with_current_user() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5453
			$this->_logger->entrance();
5454
5455
			if ( $this->is_registered() ) {
5456
				return;
5457
			}
5458
5459
			if ( fs_request_is_action( $this->_slug . '_activate_existing' ) && fs_request_is_post() ) {
5460
//				check_admin_referer( 'activate_existing_' . $this->_plugin->public_key );
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5461
5462
				$this->install_with_current_user();
5463
			}
5464
		}
5465
5466
5467
		/**
5468
		 * @author Vova Feldman (@svovaf)
5469
		 * @since  1.1.7.4
5470
		 *
5471
		 * @param bool $redirect
5472
		 */
5473
		private function install_with_current_user( $redirect = true ) {
5474
			$this->_admin_notices->remove_sticky( 'connect_account' );
5475
5476
			// Get current logged WP user.
5477
			$current_user = self::_get_current_wp_user();
5478
5479
			// Find the relevant FS user by the email.
5480
			$user = self::_get_user_by_email( $current_user->user_email );
5481
5482
			// We have to set the user before getting user scope API handler.
5483
			$this->_user = $user;
5484
5485
			// Install the plugin.
5486
			$install = $this->get_api_user_scope()->call(
5487
				"/plugins/{$this->get_id()}/installs.json",
5488
				'post',
5489
				$this->get_install_data_for_api( array(
5490
					'uid' => $this->get_anonymous_id(),
5491
				) )
5492
			);
5493
5494
			if ( isset( $install->error ) ) {
5495
				$this->_admin_notices->add(
5496
					sprintf( __fs( 'could-not-activate-x', $this->_slug ), $this->get_plugin_name() ) . ' ' .
5497
					__fs( 'contact-us-with-error-message', $this->_slug ) . ' ' . '<b>' . $install->error->message . '</b>',
5498
					__fs( 'oops', $this->_slug ) . '...',
5499
					'error'
5500
				);
5501
5502
				return;
5503
			}
5504
5505
			$site        = new FS_Site( $install );
5506
			$this->_site = $site;
5507
//				$this->_enrich_site_plan( false );
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5508
5509
//				$this->_set_account( $user, $site );
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5510
//				$this->_sync_plans();
5511
5512
			$this->setup_account( $this->_user, $this->_site, $redirect );
5513
		}
5514
5515
		/**
5516
		 * Tries to activate add-on account based on parent plugin info.
5517
		 *
5518
		 * @author Vova Feldman (@svovaf)
5519
		 * @since  1.0.6
5520
		 *
5521
		 * @param Freemius $parent_fs
5522
		 */
5523
		private function _activate_addon_account( Freemius $parent_fs ) {
5524
			if ( $this->is_registered() ) {
5525
				// Already activated.
5526
				return;
5527
			}
5528
5529
			// Activate add-on with parent plugin credentials.
5530
			$addon_install = $parent_fs->get_api_site_scope()->call(
5531
				"/addons/{$this->_plugin->id}/installs.json",
5532
				'post',
5533
				$this->get_install_data_for_api( array(
5534
					'uid' => $this->get_anonymous_id(),
5535
				) )
5536
			);
5537
5538
			if ( isset( $addon_install->error ) ) {
5539
				$this->_admin_notices->add(
5540
					sprintf( __fs( 'could-not-activate-x', $this->_slug ), $this->get_plugin_name() ) . ' ' .
5541
					__fs( 'contact-us-with-error-message', $this->_slug ) . ' ' . '<b>' . $addon_install->error->message . '</b>',
5542
					__fs( 'oops', $this->_slug ) . '...',
5543
					'error'
5544
				);
5545
5546
				return;
5547
			}
5548
5549
			// First of all, set site info - otherwise we won't
5550
			// be able to invoke API calls.
5551
			$this->_site = new FS_Site( $addon_install );
5552
5553
			// Sync add-on plans.
5554
			$this->_sync_plans();
5555
5556
			// Get site's current plan.
5557
			$this->_site->plan = $this->_get_plan_by_id( $this->_site->plan->id );
5558
5559
			// Get user information based on parent's plugin.
5560
			$user = $parent_fs->get_user();
5561
5562
			$this->_set_account( $user, $this->_site );
5563
5564
			// Sync licenses.
5565
			$this->_sync_licenses();
5566
5567
			// Try to activate premium license.
5568
			$this->_activate_license( true );
5569
		}
5570
5571
		#endregion ------------------------------------------------------------------
5572
5573
		#region Admin Menu Items ------------------------------------------------------------------
5574
5575
		private $_menu_items = array();
5576
5577
		/**
5578
		 * @author Vova Feldman (@svovaf)
5579
		 * @since  1.0.7
5580
		 *
5581
		 * @return string
5582
		 */
5583
		function get_menu_slug() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5584
			return $this->_menu->get_slug();
5585
		}
5586
5587
		/**
5588
		 * @author Vova Feldman (@svovaf)
5589
		 * @since  1.0.9
5590
		 */
5591
		function _prepare_admin_menu() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5592
			if ( ! $this->is_on() ) {
5593
				return;
5594
			}
5595
5596
			if ( ! $this->has_api_connectivity() && ! $this->enable_anonymous() ) {
5597
				$this->_menu->remove_menu_item();
5598
			} else {
5599
				$this->add_submenu_items();
5600
				$this->add_menu_action();
5601
			}
5602
		}
5603
5604
		/**
5605
		 * Admin dashboard menu items modifications.
5606
		 *
5607
		 * NOTE: admin_menu action executed before admin_init.
5608
		 *
5609
		 * @author Vova Feldman (@svovaf)
5610
		 * @since  1.0.7
5611
		 *
5612
		 */
5613
		private function add_menu_action() {
5614
			if ( $this->is_activation_mode() ) {
5615
				$this->override_plugin_menu_with_activation();
5616
			} else {
5617
				// If not registered try to install user.
5618
				if ( ! $this->is_registered() &&
5619
				     fs_request_is_action( $this->_slug . '_activate_new' )
5620
				) {
5621
					$this->_install_with_new_user();
5622
				}
5623
			}
5624
		}
5625
5626
		/**
5627
		 * @author Vova Feldman (@svovaf)
5628
		 * @since  1.0.1
5629
		 *
5630
		 * @return string
5631
		 */
5632
		function _redirect_on_clicked_menu_link() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5633
			$this->_logger->entrance();
5634
5635
			$page = strtolower( isset( $_REQUEST['page'] ) ? $_REQUEST['page'] : '' );
5636
5637
			$this->_logger->log( 'page = ' . $page );
5638
5639
			foreach ( $this->_menu_items as $priority => $items ) {
5640
				foreach ( $items as $item ) {
5641
					if ( isset( $item['url'] ) ) {
5642
						if ( $page === $item['menu_slug'] ) {
5643
							$this->_logger->log( 'Redirecting to ' . $item['url'] );
5644
5645
							fs_redirect( $item['url'] );
5646
						}
5647
					}
5648
				}
5649
			}
5650
		}
5651
5652
		/**
5653
		 * Remove plugin's all admin menu items & pages, and replace with activation page.
5654
		 *
5655
		 * @author Vova Feldman (@svovaf)
5656
		 * @since  1.0.1
5657
		 */
5658
		private function override_plugin_menu_with_activation() {
5659
			$this->_logger->entrance();
5660
5661
			$hook = false;
5662
5663
			if ( $this->_menu->is_top_level() ) {
5664
				$hook = $this->_menu->override_menu_item( array( &$this, '_connect_page_render' ) );
5665
5666
				if ( false === $hook ) {
5667
					// Create new menu item just for the opt-in.
5668
					$hook = add_menu_page(
5669
						$this->get_plugin_name(),
5670
						$this->get_plugin_name(),
5671
						'manage_options',
5672
						$this->_menu->get_slug(),
5673
						array( &$this, '_connect_page_render' )
5674
					);
5675
				}
5676
			} else {
5677
				$menus = array( $this->_menu->get_parent_slug() );
5678
5679
				if ( $this->_menu->is_override_exact() ) {
5680
					// Make sure the current page is matching the activation page.
5681
					$activation_url = strtolower( $this->get_activation_url() );
5682
					$request_url    = strtolower( $_SERVER['REQUEST_URI'] );
5683
5684
					if ( parse_url( $activation_url, PHP_URL_PATH ) !== parse_url( $request_url, PHP_URL_PATH ) ) {
5685
						// Different path - DO NOT OVERRIDE PAGE.
5686
						return;
5687
					}
5688
5689
					$activation_url_params = array();
5690
					parse_str( parse_url( $activation_url, PHP_URL_QUERY ), $activation_url_params );
5691
5692
					$request_url_params = array();
5693
					parse_str( parse_url( $request_url, PHP_URL_QUERY ), $request_url_params );
5694
5695
5696
					foreach ( $activation_url_params as $key => $val ) {
0 ignored issues
show
Bug introduced by
The expression $activation_url_params of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
5697
						if ( ! isset( $request_url_params[ $key ] ) || $val != $request_url_params[ $key ] ) {
5698
							// Not matching query string - DO NOT OVERRIDE PAGE.
5699
							return;
5700
						}
5701
					}
5702
				}
5703
5704
				foreach ( $menus as $parent_slug ) {
5705
					$hook = $this->_menu->override_submenu_action(
5706
						$parent_slug,
5707
						$this->_menu->get_raw_slug(),
5708
						array( &$this, '_connect_page_render' )
5709
					);
5710
5711
					if ( false !== $hook ) {
5712
						// Found plugin's submenu item.
5713
						break;
5714
					}
5715
				}
5716
			}
5717
5718
			if ( $this->_menu->is_activation_page() ) {
5719
				// Clean admin page from distracting content.
5720
				self::_clean_admin_content_section();
5721
			}
5722
5723
			if ( false !== $hook ) {
5724
				if ( fs_request_is_action( $this->_slug . '_activate_existing' ) ) {
5725
					add_action( "load-$hook", array( &$this, '_install_with_current_user' ) );
5726
				} else if ( fs_request_is_action( $this->_slug . '_activate_new' ) ) {
5727
					add_action( "load-$hook", array( &$this, '_install_with_new_user' ) );
5728
				}
5729
			}
5730
		}
5731
5732
		/**
5733
		 * @author Vova Feldman (@svovaf)
5734
		 * @since  1.0.0
5735
		 *
5736
		 * @return string
5737
		 */
5738
		private function get_top_level_menu_slug() {
5739
			return ( $this->is_addon() ?
5740
				$this->get_parent_instance()->_menu->get_top_level_menu_slug() :
5741
				$this->_menu->get_top_level_menu_slug() );
5742
		}
5743
5744
		/**
5745
		 * Add default Freemius menu items.
5746
		 *
5747
		 * @author Vova Feldman (@svovaf)
5748
		 * @since  1.0.0
5749
		 */
5750
		private function add_submenu_items() {
5751
			$this->_logger->entrance();
5752
5753
			$this->do_action( 'before_admin_menu_init' );
5754
5755
			if ( ! $this->is_addon() ) {
5756
				if ( ! $this->is_activation_mode() ) {
5757
					if ( $this->is_registered() ) {
5758
						// Add user account page.
5759
						$this->add_submenu_item(
5760
							__fs( 'account', $this->_slug ),
5761
							array( &$this, '_account_page_render' ),
5762
							$this->get_plugin_name() . ' &ndash; ' . __fs( 'account', $this->_slug ),
5763
							'manage_options',
5764
							'account',
5765
							array( &$this, '_account_page_load' ),
5766
							WP_FS__DEFAULT_PRIORITY,
5767
							$this->_menu->is_submenu_item_visible( 'account' )
5768
						);
5769
					}
5770
5771
					// Add contact page.
5772
					$this->add_submenu_item(
5773
						__fs( 'contact-us', $this->_slug ),
5774
						array( &$this, '_contact_page_render' ),
5775
						$this->get_plugin_name() . ' &ndash; ' . __fs( 'contact-us', $this->_slug ),
5776
						'manage_options',
5777
						'contact',
5778
						'Freemius::_clean_admin_content_section',
5779
						WP_FS__DEFAULT_PRIORITY,
5780
						$this->_menu->is_submenu_item_visible( 'contact' )
5781
					);
5782
5783
					if ( $this->has_addons() ) {
5784
						$this->add_submenu_item(
5785
							__fs( 'add-ons', $this->_slug ),
5786
							array( &$this, '_addons_page_render' ),
5787
							$this->get_plugin_name() . ' &ndash; ' . __fs( 'add-ons', $this->_slug ),
5788
							'manage_options',
5789
							'addons',
5790
							array( &$this, '_addons_page_load' ),
5791
							WP_FS__LOWEST_PRIORITY - 1,
5792
							$this->_menu->is_submenu_item_visible( 'addons' )
5793
						);
5794
					}
5795
5796
					$show_pricing = ( $this->has_paid_plan() && $this->_menu->is_submenu_item_visible( 'pricing' ) );
5797
					// If user don't have paid plans, add pricing page
5798
					// to support add-ons checkout but don't add the submenu item.
5799
					// || (isset( $_GET['page'] ) && $this->_menu->get_slug( 'pricing' ) == $_GET['page']);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5800
5801
					// Add upgrade/pricing page.
5802
					$this->add_submenu_item(
5803
						( $this->is_paying() ? __fs( 'pricing', $this->_slug ) : __fs( 'upgrade', $this->_slug ) . '&nbsp;&nbsp;&#x27a4;' ),
5804
						array( &$this, '_pricing_page_render' ),
5805
						$this->get_plugin_name() . ' &ndash; ' . __fs( 'pricing', $this->_slug ),
5806
						'manage_options',
5807
						'pricing',
5808
						'Freemius::_clean_admin_content_section',
5809
						WP_FS__LOWEST_PRIORITY,
5810
						$show_pricing
5811
					);
5812
				}
5813
			}
5814
5815
5816
			if ( 0 < count( $this->_menu_items ) ) {
5817
				if ( ! $this->_menu->is_top_level() ) {
5818
					fs_enqueue_local_style( 'fs_common', '/admin/common.css' );
5819
5820
					// Append submenu items right after the plugin's submenu item.
5821
					$this->order_sub_submenu_items();
5822
				} else {
5823
					// Append submenu items.
5824
					$this->embed_submenu_items();
5825
				}
5826
			}
5827
		}
5828
5829
		/**
5830
		 * Moved the actual submenu item additions to a separated function,
5831
		 * in order to support sub-submenu items when the plugin's settings
5832
		 * only have a submenu and not top-level menu item.
5833
		 *
5834
		 * @author Vova Feldman (@svovaf)
5835
		 * @since  1.1.4
5836
		 */
5837
		private function embed_submenu_items() {
5838
			$item_template = $this->_menu->is_top_level() ?
5839
				'<span class="fs-submenu-item">%s</span>' :
5840
				'<span class="fs-submenu-item fs-sub">%s</span>';
5841
5842
			ksort( $this->_menu_items );
5843
5844
			foreach ( $this->_menu_items as $priority => $items ) {
5845
				foreach ( $items as $item ) {
5846
					if ( ! isset( $item['url'] ) ) {
5847
						$hook = add_submenu_page(
5848
							$item['show_submenu'] ?
5849
								$this->get_top_level_menu_slug() :
5850
								null,
5851
							$item['page_title'],
5852
							sprintf( $item_template, $item['menu_title'] ),
5853
							$item['capability'],
5854
							$item['menu_slug'],
5855
							$item['render_function']
5856
						);
5857
5858
						if ( false !== $item['before_render_function'] ) {
5859
							add_action( "load-$hook", $item['before_render_function'] );
5860
						}
5861
					} else {
5862
						add_submenu_page(
5863
							$this->get_top_level_menu_slug(),
5864
							$item['page_title'],
5865
							sprintf( $item_template, $item['menu_title'] ),
5866
							$item['capability'],
5867
							$item['menu_slug'],
5868
							array( $this, '' )
5869
						);
5870
					}
5871
				}
5872
			}
5873
		}
5874
5875
		/**
5876
		 * Re-order the submenu items so all Freemius added new submenu items
5877
		 * are added right after the plugin's settings submenu item.
5878
		 *
5879
		 * @author Vova Feldman (@svovaf)
5880
		 * @since  1.1.4
5881
		 */
5882
		private function order_sub_submenu_items() {
5883
			global $submenu;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
5884
5885
			$top_level_menu = &$submenu[ $this->_menu->get_top_level_menu_slug() ];
5886
5887
			$all_submenu_items_after = array();
5888
5889
			$found_submenu_item = false;
5890
5891
			foreach ( $top_level_menu as $submenu_id => $meta ) {
5892
				if ( $found_submenu_item ) {
5893
					// Remove all submenu items after the plugin's submenu item.
5894
					$all_submenu_items_after[] = $meta;
5895
					unset( $top_level_menu[ $submenu_id ] );
5896
				}
5897
5898
				if ( $this->_menu->get_raw_slug() === $meta[2] ) {
5899
					// Found the submenu item, put all below.
5900
					$found_submenu_item = true;
5901
					continue;
5902
				}
5903
			}
5904
5905
			// Embed all plugin's new submenu items.
5906
			$this->embed_submenu_items();
5907
5908
			// Start with specially high number to make sure it's appended.
5909
			$i = max( 10000, max( array_keys( $top_level_menu ) ) + 1 );
5910
			foreach ( $all_submenu_items_after as $meta ) {
5911
				$top_level_menu[ $i ] = $meta;
5912
				$i ++;
5913
			}
5914
5915
			// Sort submenu items.
5916
			ksort( $top_level_menu );
5917
		}
5918
5919
		/**
5920
		 * Displays the Support Forum link when enabled.
5921
		 *
5922
		 * Can be filtered like so:
5923
		 *
5924
		 *  function _fs_show_support_menu( $is_visible, $menu_id ) {
5925
		 *      if ( 'support' === $menu_id ) {
5926
		 *            return _fs->is_registered();
5927
		 *        }
5928
		 *        return $is_visible;
5929
		 *    }
5930
		 *    _fs()->add_filter('is_submenu_visible', '_fs_show_support_menu', 10, 2);
5931
		 *
5932
		 */
5933
		function _add_default_submenu_items() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5934
			if ( ! $this->is_on() ) {
5935
				return;
5936
			}
5937
5938
			if ( $this->is_registered() || $this->is_anonymous() ) {
5939
				if ( $this->_menu->is_submenu_item_visible( 'support' ) ) {
5940
					$this->add_submenu_link_item(
5941
						$this->apply_filters( 'support_forum_submenu', __fs( 'support-forum', $this->_slug ) ),
5942
						$this->apply_filters( 'support_forum_url', 'https://wordpress.org/support/plugin/' . $this->_slug ),
5943
						'wp-support-forum',
5944
						'read',
5945
						50
5946
					);
5947
				}
5948
			}
5949
		}
5950
5951
		/**
5952
		 * @author Vova Feldman (@svovaf)
5953
		 * @since  1.0.1
5954
		 *
5955
		 * @param string        $menu_title
5956
		 * @param callable      $render_function
5957
		 * @param bool|string   $page_title
5958
		 * @param string        $capability
5959
		 * @param bool|string   $menu_slug
5960
		 * @param bool|callable $before_render_function
5961
		 * @param int           $priority
5962
		 * @param bool          $show_submenu
5963
		 */
5964
		function add_submenu_item(
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5965
			$menu_title,
5966
			$render_function,
5967
			$page_title = false,
5968
			$capability = 'manage_options',
5969
			$menu_slug = false,
5970
			$before_render_function = false,
5971
			$priority = WP_FS__DEFAULT_PRIORITY,
5972
			$show_submenu = true
5973
		) {
5974
			$this->_logger->entrance( 'Title = ' . $menu_title );
5975
5976
			if ( $this->is_addon() ) {
5977
				$parent_fs = $this->get_parent_instance();
5978
5979
				if ( is_object( $parent_fs ) ) {
5980
					$parent_fs->add_submenu_item(
5981
						$menu_title,
5982
						$render_function,
5983
						$page_title,
5984
						$capability,
5985
						$menu_slug,
5986
						$before_render_function,
5987
						$priority,
5988
						$show_submenu
5989
					);
5990
5991
					return;
5992
				}
5993
			}
5994
5995
			if ( ! isset( $this->_menu_items[ $priority ] ) ) {
5996
				$this->_menu_items[ $priority ] = array();
5997
			}
5998
5999
			$this->_menu_items[ $priority ][] = array(
6000
				'page_title'             => is_string( $page_title ) ? $page_title : $menu_title,
6001
				'menu_title'             => $menu_title,
6002
				'capability'             => $capability,
6003
				'menu_slug'              => $this->_menu->get_slug( is_string( $menu_slug ) ? $menu_slug : strtolower( $menu_title ) ),
6004
				'render_function'        => $render_function,
6005
				'before_render_function' => $before_render_function,
6006
				'show_submenu'           => $show_submenu,
6007
			);
6008
		}
6009
6010
		/**
6011
		 * @author Vova Feldman (@svovaf)
6012
		 * @since  1.0.1
6013
		 *
6014
		 * @param string $menu_title
6015
		 * @param string $url
6016
		 * @param bool   $menu_slug
6017
		 * @param string $capability
6018
		 * @param int    $priority
6019
		 *
6020
		 */
6021
		function add_submenu_link_item(
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6022
			$menu_title,
6023
			$url,
6024
			$menu_slug = false,
6025
			$capability = 'read',
6026
			$priority = WP_FS__DEFAULT_PRIORITY
6027
		) {
6028
			$this->_logger->entrance( 'Title = ' . $menu_title . '; Url = ' . $url );
6029
6030
			if ( $this->is_addon() ) {
6031
				$parent_fs = $this->get_parent_instance();
6032
6033
				if ( is_object( $parent_fs ) ) {
6034
					$parent_fs->add_submenu_link_item(
6035
						$menu_title,
6036
						$url,
6037
						$menu_slug,
6038
						$capability,
6039
						$priority
6040
					);
6041
6042
					return;
6043
				}
6044
			}
6045
6046
			if ( ! isset( $this->_menu_items[ $priority ] ) ) {
6047
				$this->_menu_items[ $priority ] = array();
6048
			}
6049
6050
			$this->_menu_items[ $priority ][] = array(
6051
				'menu_title'             => $menu_title,
6052
				'capability'             => $capability,
6053
				'menu_slug'              => $this->_menu->get_slug( is_string( $menu_slug ) ? $menu_slug : strtolower( $menu_title ) ),
6054
				'url'                    => $url,
6055
				'page_title'             => $menu_title,
6056
				'render_function'        => 'fs_dummy',
6057
				'before_render_function' => '',
6058
			);
6059
		}
6060
6061
		#endregion ------------------------------------------------------------------
6062
6063
		/* Actions / Hooks / Filters
6064
		------------------------------------------------------------------------------------------------------------------*/
6065
		/**
6066
		 * @author Vova Feldman (@svovaf)
6067
		 * @since  1.1.7
6068
		 *
6069
		 * @param string $tag
6070
		 *
6071
		 * @return string
6072
		 */
6073
		private function get_action_tag( $tag ) {
6074
			return 'fs_' . $tag . '_' . $this->_slug;
6075
		}
6076
6077
		/**
6078
		 * Do action, specific for the current context plugin.
6079
		 *
6080
		 * @author Vova Feldman (@svovaf)
6081
		 * @since  1.0.1
6082
		 *
6083
		 * @param string $tag     The name of the action to be executed.
6084
		 * @param mixed  $arg,... Optional. Additional arguments which are passed on to the
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6085
		 *                        functions hooked to the action. Default empty.
6086
		 *
6087
		 * @uses   do_action()
6088
		 */
6089
		function do_action( $tag, $arg = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $arg is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6090
			$this->_logger->entrance( $tag );
6091
6092
			$args = func_get_args();
6093
6094
			call_user_func_array( 'do_action', array_merge(
6095
					array( $this->get_action_tag( $tag ) ),
6096
					array_slice( $args, 1 ) )
6097
			);
6098
		}
6099
6100
		/**
6101
		 * Add action, specific for the current context plugin.
6102
		 *
6103
		 * @author Vova Feldman (@svovaf)
6104
		 * @since  1.0.1
6105
		 *
6106
		 * @param string   $tag
6107
		 * @param callable $function_to_add
6108
		 * @param int      $priority
6109
		 * @param int      $accepted_args
6110
		 *
6111
		 * @uses   add_action()
6112
		 */
6113
		function add_action( $tag, $function_to_add, $priority = WP_FS__DEFAULT_PRIORITY, $accepted_args = 1 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6114
			$this->_logger->entrance( $tag );
6115
6116
			add_action( $this->get_action_tag( $tag ), $function_to_add, $priority, $accepted_args );
6117
		}
6118
6119
		/**
6120
		 * Apply filter, specific for the current context plugin.
6121
		 *
6122
		 * @author Vova Feldman (@svovaf)
6123
		 * @since  1.0.9
6124
		 *
6125
		 * @param string $tag   The name of the filter hook.
6126
		 * @param mixed  $value The value on which the filters hooked to `$tag` are applied on.
6127
		 *
6128
		 * @return mixed The filtered value after all hooked functions are applied to it.
6129
		 *
6130
		 * @uses   apply_filters()
6131
		 */
6132
		function apply_filters( $tag, $value ) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6133
			$this->_logger->entrance( $tag );
6134
6135
			$args = func_get_args();
6136
			array_unshift( $args, $this->_slug );
6137
6138
			return call_user_func_array( 'fs_apply_filter', $args );
6139
		}
6140
6141
		/**
6142
		 * Add filter, specific for the current context plugin.
6143
		 *
6144
		 * @author Vova Feldman (@svovaf)
6145
		 * @since  1.0.9
6146
		 *
6147
		 * @param string   $tag
6148
		 * @param callable $function_to_add
6149
		 * @param int      $priority
6150
		 * @param int      $accepted_args
6151
		 *
6152
		 * @uses   add_filter()
6153
		 */
6154
		function add_filter( $tag, $function_to_add, $priority = WP_FS__DEFAULT_PRIORITY, $accepted_args = 1 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6155
			$this->_logger->entrance( $tag );
6156
6157
			add_filter( $this->get_action_tag( $tag ), $function_to_add, $priority, $accepted_args );
6158
		}
6159
6160
		/**
6161
		 * Check if has filter.
6162
		 *
6163
		 * @author Vova Feldman (@svovaf)
6164
		 * @since  1.1.4
6165
		 *
6166
		 * @param string        $tag
6167
		 * @param callable|bool $function_to_check Optional. The callback to check for. Default false.
6168
		 *
6169
		 * @return false|int
6170
		 *
6171
		 * @uses   has_filter()
6172
		 */
6173
		function has_filter( $tag, $function_to_check = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6174
			$this->_logger->entrance( $tag );
6175
6176
			return has_filter( $this->get_action_tag( $tag ), $function_to_check );
6177
		}
6178
6179
		/**
6180
		 * Override default i18n text phrases.
6181
		 *
6182
		 * @author Vova Feldman (@svovaf)
6183
		 * @since  1.1.6
6184
		 *
6185
		 * @param string[] string $key_value
6186
		 *
6187
		 * @uses   fs_override_i18n()
6188
		 */
6189
		function override_i18n( $key_value ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6190
			fs_override_i18n( $key_value, $this->_slug );
6191
		}
6192
6193
		/* Account Page
6194
		------------------------------------------------------------------------------------------------------------------*/
6195
		/**
6196
		 * Update site information.
6197
		 *
6198
		 * @author Vova Feldman (@svovaf)
6199
		 * @since  1.0.1
6200
		 *
6201
		 * @param bool $store Flush to Database if true.
6202
		 */
6203
		private function _store_site( $store = true ) {
6204
			$this->_logger->entrance();
6205
6206
			$encrypted_site       = clone $this->_site;
6207
			$encrypted_site->plan = $this->_encrypt_entity( $this->_site->plan );
6208
6209
			$sites                 = self::get_all_sites();
6210
			$sites[ $this->_slug ] = $encrypted_site;
6211
			self::$_accounts->set_option( 'sites', $sites, $store );
6212
		}
6213
6214
		/**
6215
		 * Update plugin's plans information.
6216
		 *
6217
		 * @author Vova Feldman (@svovaf)
6218
		 * @since  1.0.2
6219
		 *
6220
		 * @param bool $store Flush to Database if true.
6221
		 */
6222
		private function _store_plans( $store = true ) {
6223
			$this->_logger->entrance();
6224
6225
			$plans = self::get_all_plans();
6226
6227
			// Copy plans.
6228
			$encrypted_plans = array();
6229
			for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) {
6230
				$encrypted_plans[] = $this->_encrypt_entity( $this->_plans[ $i ] );
6231
			}
6232
6233
			$plans[ $this->_slug ] = $encrypted_plans;
6234
			self::$_accounts->set_option( 'plans', $plans, $store );
6235
		}
6236
6237
		/**
6238
		 * Update user's plugin licenses.
6239
		 *
6240
		 * @author Vova Feldman (@svovaf)
6241
		 * @since  1.0.5
6242
		 *
6243
		 * @param bool                $store
6244
		 * @param string|bool         $plugin_slug
6245
		 * @param FS_Plugin_License[] $licenses
6246
		 */
6247
		private function _store_licenses( $store = true, $plugin_slug = false, $licenses = array() ) {
6248
			$this->_logger->entrance();
6249
6250
			$all_licenses = self::get_all_licenses();
6251
6252
			if ( ! is_string( $plugin_slug ) ) {
6253
				$plugin_slug = $this->_slug;
6254
				$licenses    = $this->_licenses;
6255
			}
6256
6257
			if ( ! isset( $all_licenses[ $plugin_slug ] ) ) {
6258
				$all_licenses[ $plugin_slug ] = array();
6259
			}
6260
6261
			$all_licenses[ $plugin_slug ][ $this->_user->id ] = $licenses;
6262
6263
			self::$_accounts->set_option( 'licenses', $all_licenses, $store );
6264
		}
6265
6266
		/**
6267
		 * Update user information.
6268
		 *
6269
		 * @author Vova Feldman (@svovaf)
6270
		 * @since  1.0.1
6271
		 *
6272
		 * @param bool $store Flush to Database if true.
6273
		 */
6274
		private function _store_user( $store = true ) {
6275
			$this->_logger->entrance();
6276
6277
			$users                     = self::get_all_users();
6278
			$users[ $this->_user->id ] = $this->_user;
6279
			self::$_accounts->set_option( 'users', $users, $store );
6280
		}
6281
6282
		/**
6283
		 * Update new updates information.
6284
		 *
6285
		 * @author Vova Feldman (@svovaf)
6286
		 * @since  1.0.4
6287
		 *
6288
		 * @param FS_Plugin_Tag|null $update
6289
		 * @param bool               $store Flush to Database if true.
6290
		 * @param bool|number        $plugin_id
6291
		 */
6292
		private function _store_update( $update, $store = true, $plugin_id = false ) {
6293
			$this->_logger->entrance();
6294
6295
			if ( $update instanceof FS_Plugin_Tag ) {
6296
				$update->updated = time();
6297
			}
6298
6299
			if ( ! is_numeric( $plugin_id ) ) {
6300
				$plugin_id = $this->_plugin->id;
6301
			}
6302
6303
			$updates               = self::get_all_updates();
6304
			$updates[ $plugin_id ] = $update;
6305
			self::$_accounts->set_option( 'updates', $updates, $store );
6306
		}
6307
6308
		/**
6309
		 * Update new updates information.
6310
		 *
6311
		 * @author   Vova Feldman (@svovaf)
6312
		 * @since    1.0.6
6313
		 *
6314
		 * @param FS_Plugin[] $plugin_addons
6315
		 * @param bool        $store Flush to Database if true.
6316
		 */
6317
		private function _store_addons( $plugin_addons, $store = true ) {
6318
			$this->_logger->entrance();
6319
6320
			$addons                       = self::get_all_addons();
6321
			$addons[ $this->_plugin->id ] = $plugin_addons;
6322
			self::$_accounts->set_option( 'addons', $addons, $store );
6323
		}
6324
6325
		/**
6326
		 * Delete plugin's associated add-ons.
6327
		 *
6328
		 * @author   Vova Feldman (@svovaf)
6329
		 * @since    1.0.8
6330
		 *
6331
		 * @param bool $store
6332
		 *
6333
		 * @return bool
6334
		 */
6335
		private function _delete_account_addons( $store = true ) {
6336
			$all_addons = self::get_all_account_addons();
6337
6338
			if ( ! isset( $all_addons[ $this->_plugin->id ] ) ) {
6339
				return false;
6340
			}
6341
6342
			unset( $all_addons[ $this->_plugin->id ] );
6343
6344
			self::$_accounts->set_option( 'account_addons', $all_addons, $store );
6345
6346
			return true;
6347
		}
6348
6349
		/**
6350
		 * Update account add-ons list.
6351
		 *
6352
		 * @author   Vova Feldman (@svovaf)
6353
		 * @since    1.0.6
6354
		 *
6355
		 * @param FS_Plugin[] $addons
6356
		 * @param bool        $store Flush to Database if true.
6357
		 */
6358
		private function _store_account_addons( $addons, $store = true ) {
6359
			$this->_logger->entrance();
6360
6361
			$all_addons                       = self::get_all_account_addons();
6362
			$all_addons[ $this->_plugin->id ] = $addons;
6363
			self::$_accounts->set_option( 'account_addons', $all_addons, $store );
6364
		}
6365
6366
		/**
6367
		 * Store account params in the Database.
6368
		 *
6369
		 * @author Vova Feldman (@svovaf)
6370
		 * @since  1.0.1
6371
		 */
6372
		private function _store_account() {
6373
			$this->_logger->entrance();
6374
6375
			$this->_store_site( false );
6376
			$this->_store_user( false );
6377
			$this->_store_plans( false );
6378
			$this->_store_licenses( false );
6379
6380
			self::$_accounts->store();
6381
		}
6382
6383
		/**
6384
		 * Sync user's information.
6385
		 *
6386
		 * @author Vova Feldman (@svovaf)
6387
		 * @since  1.0.3
6388
		 * @uses   FS_Api
6389
		 */
6390
		private function _handle_account_user_sync() {
6391
			$this->_logger->entrance();
6392
6393
			$api = $this->get_api_user_scope();
6394
6395
			// Get user's information.
6396
			$user = $api->get( '/', true );
6397
6398
			if ( isset( $user->id ) ) {
6399
				$this->_user->first = $user->first;
6400
				$this->_user->last  = $user->last;
6401
				$this->_user->email = $user->email;
6402
6403
				$is_menu_item_account_visible = $this->_menu->is_submenu_item_visible( 'account' );
6404
6405
				if ( $user->is_verified &&
6406
				     ( ! isset( $this->_user->is_verified ) || false === $this->_user->is_verified )
6407
				) {
6408
					$this->_user->is_verified = true;
6409
6410
					$this->do_action( 'account_email_verified', $user->email );
6411
6412
					$this->_admin_notices->add(
6413
						__fs( 'email-verified-message', $this->_slug ),
6414
						__fs( 'right-on', $this->_slug ) . '!',
6415
						'success',
6416
						// Make admin sticky if account menu item is invisible,
6417
						// since the page will be auto redirected to the plugin's
6418
						// main settings page, and the non-sticky message
6419
						// will disappear.
6420
						! $is_menu_item_account_visible,
6421
						false,
6422
						'email_verified'
6423
					);
6424
				}
6425
6426
				// Flush user details to DB.
6427
				$this->_store_user();
6428
6429
				$this->do_action( 'after_account_user_sync', $user );
6430
6431
				/**
6432
				 * If account menu item is hidden, redirect to plugin's main settings page.
6433
				 *
6434
				 * @author Vova Feldman (@svovaf)
6435
				 * @since  1.1.6
6436
				 *
6437
				 * @link   https://github.com/Freemius/wordpress-sdk/issues/6
6438
				 */
6439
				if ( ! $is_menu_item_account_visible ) {
6440
					if ( fs_redirect( $this->_get_admin_page_url() ) ) {
6441
						exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method _handle_account_user_sync() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
6442
					}
6443
				}
6444
			}
6445
		}
6446
6447
		/**
6448
		 * @author Vova Feldman (@svovaf)
6449
		 * @since  1.0.5
6450
		 * @uses   FS_Api
6451
		 *
6452
		 * @param bool $flush
6453
		 *
6454
		 * @return object|\FS_Site
6455
		 */
6456
		private function _fetch_site( $flush = false ) {
6457
			$this->_logger->entrance();
6458
			$api = $this->get_api_site_scope();
6459
6460
			$site = $api->get( '/', $flush );
6461
6462
			if ( ! isset( $site->error ) ) {
6463
				$site          = new FS_Site( $site );
6464
				$site->slug    = $this->_slug;
6465
				$site->version = $this->get_plugin_version();
6466
			}
6467
6468
			return $site;
6469
		}
6470
6471
		/**
6472
		 * @param bool $store
6473
		 *
6474
		 * @return FS_Plugin_Plan|object|false
6475
		 */
6476
		private function _enrich_site_plan( $store = true ) {
6477
			// Try to load plan from local cache.
6478
			$plan = $this->_get_plan_by_id( $this->_site->plan->id );
6479
6480
			if ( false === $plan ) {
6481
				$plan = $this->_fetch_site_plan();
6482
			}
6483
6484
			if ( $plan instanceof FS_Plugin_Plan ) {
6485
				$this->_update_plan( $plan, $store );
6486
			}
6487
6488
			return $plan;
6489
		}
6490
6491
		/**
6492
		 * @author Vova Feldman (@svovaf)
6493
		 * @since  1.0.9
6494
		 * @uses   FS_Api
6495
		 *
6496
		 * @param bool $store
6497
		 *
6498
		 * @return FS_Plugin_Plan|object|false
6499
		 */
6500
		private function _enrich_site_trial_plan( $store = true ) {
6501
			// Try to load plan from local cache.
6502
			$trial_plan = $this->_get_plan_by_id( $this->_site->trial_plan_id );
6503
6504
			if ( false === $trial_plan ) {
6505
				$trial_plan = $this->_fetch_site_plan( $this->_site->trial_plan_id );
6506
			}
6507
6508
			if ( $trial_plan instanceof FS_Plugin_Plan ) {
6509
				$this->_storage->store( 'trial_plan', $trial_plan, $store );
6510
			}
6511
6512
			return $trial_plan;
6513
		}
6514
6515
		/**
6516
		 * @author Vova Feldman (@svovaf)
6517
		 * @since  1.0.9
6518
		 * @uses   FS_Api
6519
		 *
6520
		 * @param number|bool $license_id
6521
		 *
6522
		 * @return FS_Subscription|object|bool
6523
		 */
6524
		private function _fetch_site_license_subscription( $license_id = false ) {
6525
			$this->_logger->entrance();
6526
			$api = $this->get_api_site_scope();
6527
6528
			if ( ! is_numeric( $license_id ) ) {
6529
				$license_id = $this->_license->id;
6530
			}
6531
6532
			$result = $api->get( "/licenses/{$license_id}/subscriptions.json", true );
6533
6534
			return ! isset( $result->error ) ?
6535
				( ( is_array( $result->subscriptions ) && 0 < count( $result->subscriptions ) ) ?
6536
					new FS_Subscription( $result->subscriptions[0] ) :
6537
					false
6538
				) :
6539
				$result;
6540
		}
6541
6542
		/**
6543
		 * @author Vova Feldman (@svovaf)
6544
		 * @since  1.0.4
6545
		 * @uses   FS_Api
6546
		 *
6547
		 * @param number|bool $plan_id
6548
		 *
6549
		 * @return FS_Plugin_Plan|object
6550
		 */
6551
		private function _fetch_site_plan( $plan_id = false ) {
6552
			$this->_logger->entrance();
6553
			$api = $this->get_api_site_scope();
6554
6555
			if ( ! is_numeric( $plan_id ) ) {
6556
				$plan_id = $this->_site->plan->id;
6557
			}
6558
6559
			$plan = $api->get( "/plans/{$plan_id}.json", true );
6560
6561
			return ! isset( $plan->error ) ? new FS_Plugin_Plan( $plan ) : $plan;
6562
		}
6563
6564
		/**
6565
		 * @author Vova Feldman (@svovaf)
6566
		 * @since  1.0.5
6567
		 * @uses   FS_Api
6568
		 *
6569
		 * @return FS_Plugin_Plan[]|object
6570
		 */
6571
		private function _fetch_plugin_plans() {
6572
			$this->_logger->entrance();
6573
			$api = $this->get_api_site_scope();
6574
6575
			$result = $api->get( '/plans.json', true );
6576
6577
			if ( ! $this->is_api_error( $result ) ) {
6578
				for ( $i = 0, $len = count( $result->plans ); $i < $len; $i ++ ) {
6579
					$result->plans[ $i ] = new FS_Plugin_Plan( $result->plans[ $i ] );
6580
				}
6581
6582
				$result = $result->plans;
6583
			}
6584
6585
			return $result;
6586
		}
6587
6588
		/**
6589
		 * @author Vova Feldman (@svovaf)
6590
		 * @since  1.0.5
6591
		 * @uses   FS_Api
6592
		 *
6593
		 * @param number|bool $plugin_id
6594
		 *
6595
		 * @return FS_Plugin_License[]|object
6596
		 */
6597
		private function _fetch_licenses( $plugin_id = false ) {
6598
			$this->_logger->entrance();
6599
6600
			$api = $this->get_api_user_scope();
6601
6602
			if ( ! is_numeric( $plugin_id ) ) {
6603
				$plugin_id = $this->_plugin->id;
6604
			}
6605
6606
			$result = $api->get( "/plugins/{$plugin_id}/licenses.json", true );
6607
6608
			if ( ! isset( $result->error ) ) {
6609
				for ( $i = 0, $len = count( $result->licenses ); $i < $len; $i ++ ) {
6610
					$result->licenses[ $i ] = new FS_Plugin_License( $result->licenses[ $i ] );
6611
				}
6612
6613
				$result = $result->licenses;
6614
			}
6615
6616
			return $result;
6617
		}
6618
6619
		/**
6620
		 * @author Vova Feldman (@svovaf)
6621
		 * @since  1.0.4
6622
		 *
6623
		 * @param FS_Plugin_Plan $plan
6624
		 * @param bool           $store
6625
		 */
6626
		private function _update_plan( $plan, $store = false ) {
6627
			$this->_logger->entrance();
6628
6629
			$this->_site->plan = $plan;
6630
			$this->_store_site( $store );
6631
		}
6632
6633
		/**
6634
		 * @author Vova Feldman (@svovaf)
6635
		 * @since  1.0.5
6636
		 *
6637
		 * @param FS_Plugin_License[] $licenses
6638
		 * @param string|bool         $plugin_slug
6639
		 */
6640
		private function _update_licenses( $licenses, $plugin_slug = false ) {
6641
			$this->_logger->entrance();
6642
6643
			if ( is_array( $licenses ) ) {
6644
				for ( $i = 0, $len = count( $licenses ); $i < $len; $i ++ ) {
6645
					$licenses[ $i ]->updated = time();
6646
				}
6647
			}
6648
6649
			if ( ! is_string( $plugin_slug ) ) {
6650
				$this->_licenses = $licenses;
6651
			}
6652
6653
			$this->_store_licenses( true, $plugin_slug, $licenses );
6654
		}
6655
6656
		/**
6657
		 * @author Vova Feldman (@svovaf)
6658
		 * @since  1.0.4
6659
		 *
6660
		 * @param bool|number $plugin_id
6661
		 * @param bool        $flush Since 1.1.7.3
6662
		 *
6663
		 * @return object|false New plugin tag info if exist.
6664
		 */
6665
		private function _fetch_newer_version( $plugin_id = false, $flush = true ) {
6666
			$latest_tag = $this->_fetch_latest_version( $plugin_id, $flush );
6667
6668
			if ( ! is_object( $latest_tag ) ) {
6669
				return false;
6670
			}
6671
6672
			// Check if version is actually newer.
6673
			$has_new_version =
6674
				// If it's an non-installed add-on then always return latest.
6675
				( $this->_is_addon_id( $plugin_id ) && ! $this->is_addon_activated( $plugin_id ) ) ||
6676
				// Compare versions.
6677
				version_compare( $this->get_plugin_version(), $latest_tag->version, '<' );
6678
6679
			$this->_logger->departure( $has_new_version ? 'Found newer plugin version ' . $latest_tag->version : 'No new version' );
6680
6681
			return $has_new_version ? $latest_tag : false;
6682
		}
6683
6684
		/**
6685
		 * @author Vova Feldman (@svovaf)
6686
		 * @since  1.0.5
6687
		 *
6688
		 * @param bool|number $plugin_id
6689
		 * @param bool        $flush Since 1.1.7.3
6690
		 *
6691
		 * @return bool|FS_Plugin_Tag
6692
		 */
6693
		function get_update( $plugin_id = false, $flush = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6694
			$this->_logger->entrance();
6695
6696
			if ( ! is_numeric( $plugin_id ) ) {
6697
				$plugin_id = $this->_plugin->id;
6698
			}
6699
6700
			$this->_check_updates( true, $plugin_id, $flush );
6701
			$updates = $this->get_all_updates();
6702
6703
			return isset( $updates[ $plugin_id ] ) && is_object( $updates[ $plugin_id ] ) ? $updates[ $plugin_id ] : false;
6704
		}
6705
6706
		/**
6707
		 * Check if site assigned with active license.
6708
		 *
6709
		 * @author Vova Feldman (@svovaf)
6710
		 * @since  1.0.6
6711
		 */
6712
		function has_active_license() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6713
			return (
6714
				is_object( $this->_license ) &&
6715
				is_numeric( $this->_license->id ) &&
6716
				! $this->_license->is_expired()
6717
			);
6718
		}
6719
6720
		/**
6721
		 * Check if site assigned with license with enabled features.
6722
		 *
6723
		 * @author Vova Feldman (@svovaf)
6724
		 * @since  1.0.6
6725
		 *
6726
		 * @return bool
6727
		 */
6728
		function has_features_enabled_license() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6729
			return (
6730
				is_object( $this->_license ) &&
6731
				is_numeric( $this->_license->id ) &&
6732
				$this->_license->is_features_enabled()
6733
			);
6734
		}
6735
6736
		/**
6737
		 * Check if user is a trial or have feature enabled license.
6738
		 *
6739
		 * @author Vova Feldman (@svovaf)
6740
		 * @since  1.1.7
6741
		 *
6742
		 * @return bool
6743
		 */
6744
		function can_use_premium_code() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6745
			return $this->is_trial() || $this->has_features_enabled_license();
6746
		}
6747
6748
		/**
6749
		 * Sync site's plan.
6750
		 *
6751
		 * @author Vova Feldman (@svovaf)
6752
		 * @since  1.0.3
6753
		 *
6754
		 * @uses   FS_Api
6755
		 *
6756
		 * @param bool $background Hints the method if it's a background sync. If false, it means that was initiated by
6757
		 *                         the admin.
6758
		 */
6759
		private function _sync_license( $background = false ) {
6760
			$this->_logger->entrance();
6761
6762
			$plugin_id = fs_request_get( 'plugin_id', $this->get_id() );
6763
6764
			$is_addon_sync = ( ! $this->_plugin->is_addon() && $plugin_id != $this->get_id() );
6765
6766
			if ( $is_addon_sync ) {
6767
				$this->_sync_addon_license( $plugin_id, $background );
6768
			} else {
6769
				$this->_sync_plugin_license( $background );
6770
			}
6771
6772
			$this->do_action( 'after_account_plan_sync', $this->_site->plan->name );
6773
		}
6774
6775
		/**
6776
		 * Sync plugin's add-on license.
6777
		 *
6778
		 * @author Vova Feldman (@svovaf)
6779
		 * @since  1.0.6
6780
		 * @uses   FS_Api
6781
		 *
6782
		 * @param number $addon_id
6783
		 * @param bool   $background
6784
		 */
6785
		private function _sync_addon_license( $addon_id, $background ) {
6786
			$this->_logger->entrance();
6787
6788
			if ( $this->is_addon_activated( $addon_id ) ) {
6789
				// If already installed, use add-on sync.
6790
				$fs_addon = self::get_instance_by_id( $addon_id );
6791
				$fs_addon->_sync_license( $background );
6792
6793
				return;
6794
			}
6795
6796
			// Validate add-on exists.
6797
			$addon = $this->get_addon( $addon_id );
6798
6799
			if ( ! is_object( $addon ) ) {
6800
				return;
6801
			}
6802
6803
			// Add add-on into account add-ons.
6804
			$account_addons = $this->get_account_addons();
6805
			if ( ! is_array( $account_addons ) ) {
6806
				$account_addons = array();
6807
			}
6808
			$account_addons[] = $addon->id;
6809
			$account_addons   = array_unique( $account_addons );
6810
			$this->_store_account_addons( $account_addons );
6811
6812
			// Load add-on licenses.
6813
			$licenses = $this->_fetch_licenses( $addon->id );
6814
6815
			// Sync add-on licenses.
6816
			if ( ! isset( $licenses->error ) ) {
6817
				$this->_update_licenses( $licenses, $addon->slug );
6818
6819
				if ( ! $this->is_addon_installed( $addon->slug ) && FS_License_Manager::has_premium_license( $licenses ) ) {
6820
					$plans_result = $this->get_api_site_or_plugin_scope()->get( "/addons/{$addon_id}/plans.json" );
6821
6822
					if ( ! isset( $plans_result->error ) ) {
6823
						$plans = array();
6824
						foreach ( $plans_result->plans as $plan ) {
6825
							$plans[] = new FS_Plugin_Plan( $plan );
6826
						}
6827
6828
						$this->_admin_notices->add_sticky(
6829
							FS_Plan_Manager::instance()->has_free_plan( $plans ) ?
6830
								sprintf(
6831
									__fs( 'addon-successfully-upgraded-message', $this->_slug ),
6832
									$addon->title
6833
								) . ' ' . $this->_get_latest_download_link(
6834
									__fs( 'download-latest-version', $this->_slug ),
6835
									$addon_id
6836
								)
6837
								:
6838
								sprintf(
6839
									__fs( 'addon-successfully-purchased-message', $this->_slug ),
6840
									$addon->title
6841
								) . ' ' . $this->_get_latest_download_link(
6842
									__fs( 'download-latest-version', $this->_slug ),
6843
									$addon_id
6844
								),
6845
							'addon_plan_upgraded_' . $addon->slug,
6846
							__fs( 'yee-haw', $this->_slug ) . '!'
6847
						);
6848
					}
6849
				}
6850
			}
6851
		}
6852
6853
		/**
6854
		 * Sync site's plugin plan.
6855
		 *
6856
		 * @author Vova Feldman (@svovaf)
6857
		 * @since  1.0.6
6858
		 * @uses   FS_Api
6859
		 *
6860
		 * @param bool $background Hints the method if it's a background sync. If false, it means that was initiated by
6861
		 *                         the admin.
6862
		 */
6863
		private function _sync_plugin_license( $background = false ) {
6864
			$this->_logger->entrance();
6865
6866
			// Sync site info.
6867
			$site = $this->send_install_update( array(), true );
6868
6869
			$plan_change = 'none';
6870
6871
			if ( $this->is_api_error( $site ) ) {
6872
				// Show API messages only if not background sync or if paying customer.
6873
				if ( ! $background || $this->is_paying() ) {
6874
					// Try to ping API to see if not blocked.
6875
					if ( ! FS_Api::test() ) {
6876
						/**
6877
						 * Failed to ping API - blocked!
6878
						 *
6879
						 * @author Vova Feldman (@svovaf)
6880
						 * @since  1.1.6 Only show message related to one of the Freemius powered plugins. Once it will be resolved it will fix the issue for all plugins anyways. There's no point to scare users with multiple error messages.
6881
						 */
6882
						$api = $this->get_api_site_scope();
6883
6884
						if ( ! self::$_global_admin_notices->has_sticky( 'api_blocked' ) ) {
6885
							self::$_global_admin_notices->add(
6886
								sprintf(
6887
									__fs( 'server-blocking-access', $this->_slug ),
6888
									$this->get_plugin_name(),
6889
									'<a href="' . $api->get_url() . '" target="_blank">' . $api->get_url() . '</a>'
6890
								) . '<br> ' . __fs( 'server-error-message', $this->_slug ) . var_export( $site->error, true ),
6891
								__fs( 'oops', $this->_slug ) . '...',
6892
								'error',
6893
								$background,
6894
								false,
6895
								'api_blocked'
6896
							);
6897
						}
6898
					} else {
6899
						// Authentication params are broken.
6900
						$this->_admin_notices->add(
6901
							__fs( 'wrong-authentication-param-message', $this->_slug ),
6902
							__fs( 'oops', $this->_slug ) . '...',
6903
							'error'
6904
						);
6905
					}
6906
				}
6907
6908
				// No reason to continue with license sync while there are API issues.
6909
				return;
6910
			}
6911
6912
			// Remove sticky API connectivity message.
6913
			self::$_global_admin_notices->remove_sticky( 'api_blocked' );
6914
6915
			$site = new FS_Site( $site );
6916
6917
			// Sync plans.
6918
			$this->_sync_plans();
6919
6920
			if ( ! $this->has_paid_plan() ) {
6921
				$this->_site = $site;
6922
				$this->_enrich_site_plan( true );
6923
				$this->_store_site();
6924
			} else {
6925
				// Sync licenses.
6926
				$this->_sync_licenses();
6927
6928
				// Check if plan / license changed.
6929
				if ( ! FS_Entity::equals( $site->plan, $this->_site->plan ) ||
6930
				     // Check if trial started.
6931
				     $site->trial_plan_id != $this->_site->trial_plan_id ||
6932
				     $site->trial_ends != $this->_site->trial_ends ||
6933
				     // Check if license changed.
6934
				     $site->license_id != $this->_site->license_id
6935
				) {
6936
					if ( $site->is_trial() && ( ! $this->_site->is_trial() || $site->trial_ends != $this->_site->trial_ends ) ) {
6937
						// New trial started.
6938
						$this->_site = $site;
6939
						$plan_change = 'trial_started';
6940
6941
						// Store trial plan information.
6942
						$this->_enrich_site_trial_plan( true );
6943
6944
						// For trial with subscription use-case.
6945
						$new_license = is_null( $site->license_id ) ? null : $this->_get_license_by_id( $site->license_id );
6946
6947
						if ( is_object( $new_license ) && ! $new_license->is_expired() ) {
6948
							$this->_site = $site;
6949
							$this->_update_site_license( $new_license );
6950
							$this->_store_licenses();
6951
							$this->_enrich_site_plan( true );
6952
6953
							$this->_sync_site_subscription( $this->_license );
6954
						}
6955
					} else if ( $this->_site->is_trial() && ! $site->is_trial() && ! is_numeric( $site->license_id ) ) {
6956
						// Was in trial, but now trial expired and no license ID.
6957
						// New trial started.
6958
						$this->_site = $site;
6959
						$plan_change = 'trial_expired';
6960
6961
						// Clear trial plan information.
6962
						$this->_storage->trial_plan = null;
0 ignored issues
show
Documentation introduced by
The property trial_plan does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
6963
6964
					} else {
6965
						$is_free = $this->is_free_plan();
6966
6967
						// Make sure license exist and not expired.
6968
						$new_license = is_null( $site->license_id ) ? null : $this->_get_license_by_id( $site->license_id );
6969
6970
						if ( $is_free && ( ( ! is_object( $new_license ) || $new_license->is_expired() ) ) ) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
6971
							// The license is expired, so ignore upgrade method.
6972
						} else {
6973
							// License changed.
6974
							$this->_site = $site;
6975
							$this->_update_site_license( $new_license );
6976
							$this->_store_licenses();
6977
							$this->_enrich_site_plan( true );
6978
6979
							$plan_change = $is_free ?
6980
								'upgraded' :
6981
								( is_object( $new_license ) ?
6982
									'changed' :
6983
									'downgraded' );
6984
						}
6985
					}
6986
6987
					// Store updated site info.
6988
					$this->_store_site();
6989
				} else {
6990
					if ( is_object( $this->_license ) && $this->_license->is_expired() ) {
6991
						if ( ! $this->has_features_enabled_license() ) {
6992
							$this->_deactivate_license();
6993
							$plan_change = 'downgraded';
6994
						} else {
6995
							$plan_change = 'expired';
6996
						}
6997
					}
6998
6999
					if ( is_numeric( $site->license_id ) && is_object( $this->_license ) ) {
7000
						$this->_sync_site_subscription( $this->_license );
7001
					}
7002
				}
7003
			}
7004
7005
			if ( $this->has_paid_plan() ) {
7006
				switch ( $plan_change ) {
7007
					case 'none':
7008
						if ( ! $background && is_admin() ) {
7009
							$plan = $this->is_trial() ?
7010
								$this->_storage->trial_plan :
0 ignored issues
show
Documentation introduced by
The property trial_plan does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
7011
								$this->_site->plan;
7012
7013
							$this->_admin_notices->add(
7014
								sprintf(
7015
									__fs( 'plan-did-not-change-message', $this->_slug ) . ' ' .
7016
									sprintf(
7017
										'<a href="%s">%s</a>',
7018
										$this->contact_url(
7019
											'bug',
7020
											sprintf( __fs( 'plan-did-not-change-email-message', $this->_slug ),
7021
												strtoupper( $plan->name )
7022
											)
7023
										),
7024
										__fs( 'contact-us-here', $this->_slug )
7025
									),
7026
									'<i>' . $plan->title . ( $this->is_trial() ? ' ' . __fs( 'trial', $this->_slug ) : '' ) . '</i>'
7027
								),
7028
								__fs( 'hmm', $this->_slug ) . '...',
7029
								'error'
7030
							);
7031
						}
7032
						break;
7033
					case 'upgraded':
7034
						$this->_admin_notices->add_sticky(
7035
							sprintf(
7036
								__fs( 'plan-upgraded-message', $this->_slug ),
7037
								'<i>' . $this->get_plugin_name() . '</i>'
7038
							) . ( $this->is_premium() ? '' : ' ' . $this->_get_latest_download_link( sprintf(
7039
									__fs( 'download-latest-x-version', $this->_slug ),
7040
									$this->_site->plan->title
7041
								) )
7042
							),
7043
							'plan_upgraded',
7044
							__fs( 'yee-haw', $this->_slug ) . '!'
7045
						);
7046
7047
						$this->_admin_notices->remove_sticky( array(
7048
							'trial_started',
7049
							'trial_promotion',
7050
							'trial_expired',
7051
							'activation_complete',
7052
						) );
7053
						break;
7054
					case 'changed':
7055
						$this->_admin_notices->add_sticky(
7056
							sprintf(
7057
								__fs( 'plan-changed-to-x-message', $this->_slug ),
7058
								$this->_site->plan->title
7059
							),
7060
							'plan_changed'
7061
						);
7062
7063
						$this->_admin_notices->remove_sticky( array(
7064
							'trial_started',
7065
							'trial_promotion',
7066
							'trial_expired',
7067
							'activation_complete',
7068
						) );
7069
						break;
7070
					case 'downgraded':
7071
						$this->_admin_notices->add_sticky(
7072
							sprintf( __fs( 'license-expired-blocking-message', $this->_slug ) ),
7073
							'license_expired',
7074
							__fs( 'hmm', $this->_slug ) . '...'
7075
						);
7076
						$this->_admin_notices->remove_sticky( 'plan_upgraded' );
7077
						break;
7078
					case 'expired':
7079
						$this->_admin_notices->add_sticky(
7080
							sprintf( __fs( 'license-expired-non-blocking-message', $this->_slug ), $this->_site->plan->title ),
7081
							'license_expired',
7082
							__fs( 'hmm', $this->_slug ) . '...'
7083
						);
7084
						$this->_admin_notices->remove_sticky( 'plan_upgraded' );
7085
						break;
7086
					case 'trial_started':
7087
						$this->_admin_notices->add_sticky(
7088
							sprintf(
7089
								__fs( 'trial-started-message', $this->_slug ),
7090
								'<i>' . $this->get_plugin_name() . '</i>'
7091
							) . ( $this->is_premium() ? '' : ' ' . $this->_get_latest_download_link( sprintf(
7092
									__fs( 'download-latest-x-version', $this->_slug ),
7093
									$this->_storage->trial_plan->title
0 ignored issues
show
Documentation introduced by
The property trial_plan does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
7094
								) ) ),
7095
							'trial_started',
7096
							__fs( 'yee-haw', $this->_slug ) . '!'
7097
						);
7098
7099
						$this->_admin_notices->remove_sticky( array(
7100
							'trial_promotion',
7101
						) );
7102
						break;
7103
					case 'trial_expired':
7104
						$this->_admin_notices->add_sticky(
7105
							__fs( 'trial-expired-message', $this->_slug ),
7106
							'trial_expired',
7107
							__fs( 'hmm', $this->_slug ) . '...'
7108
						);
7109
						$this->_admin_notices->remove_sticky( array(
7110
							'trial_started',
7111
							'trial_promotion',
7112
							'plan_upgraded',
7113
						) );
7114
						break;
7115
				}
7116
			}
7117
7118
			if ( 'none' !== $plan_change ) {
7119
				$this->do_action( 'after_license_change', $plan_change, $this->_site->plan );
7120
			}
7121
		}
7122
7123
		/**
7124
		 * @author Vova Feldman (@svovaf)
7125
		 * @since  1.0.5
7126
		 *
7127
		 * @param bool $background
7128
		 */
7129
		protected function _activate_license( $background = false ) {
7130
			$this->_logger->entrance();
7131
7132
			$license_id = fs_request_get( 'license_id' );
7133
7134
			if ( FS_Plugin_License::is_valid_id( $license_id ) && $license_id == $this->_site->license_id ) {
7135
				// License is already activated.
7136
				return;
7137
			}
7138
7139
			$premium_license = FS_Plugin_License::is_valid_id( $license_id ) ?
7140
				$this->_get_license_by_id( $license_id ) :
7141
				$this->_get_available_premium_license();
7142
7143
			if ( ! is_object( $premium_license ) ) {
7144
				return;
7145
			}
7146
7147
			$api     = $this->get_api_site_scope();
7148
			$license = $api->call( "/licenses/{$premium_license->id}.json", 'put' );
7149
7150
			if ( $this->is_api_error( $license ) ) {
7151
				if ( ! $background ) {
7152
					$this->_admin_notices->add(
7153
						__fs( 'license-activation-failed-message', $this->_slug ) . '<br> ' .
7154
						__fs( 'server-error-message', $this->_slug ) . ' ' . var_export( $license, true ),
7155
						__fs( 'hmm', $this->_slug ) . '...',
7156
						'error'
7157
					);
7158
				}
7159
7160
				return;
7161
			}
7162
7163
			$premium_license = new FS_Plugin_License( $license );
7164
7165
			// Updated site plan.
7166
			$site = $this->get_api_site_scope()->get( '/', true );
7167
			if ( ! $this->is_api_error( $site ) ) {
7168
				$this->_site = new FS_Site( $site );
7169
			}
7170
			$this->_update_site_license( $premium_license );
7171
			$this->_enrich_site_plan( false );
7172
7173
			$this->_store_account();
7174
7175
			if ( ! $background ) {
7176
				$this->_admin_notices->add_sticky(
7177
					__fs( 'license-activated-message', $this->_slug ) .
7178
					( $this->is_premium() ? '' : ' ' . $this->_get_latest_download_link( sprintf(
7179
							__fs( 'download-latest-x-version', $this->_slug ),
7180
							$this->_site->plan->title
7181
						) ) ),
7182
					'license_activated',
7183
					__fs( 'yee-haw', $this->_slug ) . '!'
7184
				);
7185
			}
7186
7187
			$this->_admin_notices->remove_sticky( array(
7188
				'trial_promotion',
7189
				'license_expired',
7190
			) );
7191
		}
7192
7193
		/**
7194
		 * @author Vova Feldman (@svovaf)
7195
		 * @since  1.0.5
7196
		 *
7197
		 * @param bool $show_notice
7198
		 */
7199
		protected function _deactivate_license( $show_notice = true ) {
7200
			$this->_logger->entrance();
7201
7202
			if ( ! is_object( $this->_license ) ) {
7203
				$this->_admin_notices->add(
7204
					sprintf( __fs( 'no-active-license-message', $this->_slug ), $this->_site->plan->title ),
7205
					__fs( 'hmm', $this->_slug ) . '...'
7206
				);
7207
7208
				return;
7209
			}
7210
7211
			$api     = $this->get_api_site_scope();
7212
			$license = $api->call( "/licenses/{$this->_site->license_id}.json", 'delete' );
7213
7214
			if ( isset( $license->error ) ) {
7215
				$this->_admin_notices->add(
7216
					__fs( 'license-deactivation-failed-message', $this->_slug ) . '<br> ' .
7217
					__fs( 'server-error-message', $this->_slug ) . ' ' . var_export( $license->error, true ),
7218
					__fs( 'hmm', $this->_slug ) . '...',
7219
					'error'
7220
				);
7221
7222
				return;
7223
			}
7224
7225
			// Update license cache.
7226
			for ( $i = 0, $len = count( $this->_licenses ); $i < $len; $i ++ ) {
7227
				if ( $license->id == $this->_licenses[ $i ]->id ) {
7228
					$this->_licenses[ $i ] = new FS_Plugin_License( $license );
7229
				}
7230
			}
7231
7232
			// Updated site plan to default.
7233
			$this->_sync_plans();
7234
			$this->_site->plan->id = $this->_plans[0]->id;
7235
			// Unlink license from site.
7236
			$this->_update_site_license( null );
7237
			$this->_enrich_site_plan( false );
7238
7239
			$this->_store_account();
7240
7241
			if ( $show_notice ) {
7242
				$this->_admin_notices->add(
7243
					sprintf( __fs( 'license-deactivation-message', $this->_slug ), $this->_site->plan->title ),
7244
					__fs( 'ok', $this->_slug )
7245
				);
7246
			}
7247
7248
			$this->_admin_notices->remove_sticky( array(
7249
				'plan_upgraded',
7250
				'license_activated',
7251
			) );
7252
		}
7253
7254
		/**
7255
		 * Site plan downgrade.
7256
		 *
7257
		 * @author Vova Feldman (@svovaf)
7258
		 * @since  1.0.4
7259
		 *
7260
		 * @uses   FS_Api
7261
		 */
7262
		private function _downgrade_site() {
7263
			$this->_logger->entrance();
7264
7265
			$api  = $this->get_api_site_scope();
7266
			$site = $api->call( 'downgrade.json', 'put' );
7267
7268
			$plan_downgraded = false;
7269
			$plan            = false;
7270
			if ( ! isset( $site->error ) ) {
7271
				$prev_plan_id = $this->_site->plan->id;
7272
7273
				// Update new site plan id.
7274
				$this->_site->plan->id = $site->plan_id;
7275
7276
				$plan         = $this->_enrich_site_plan();
7277
				$subscription = $this->_sync_site_subscription( $this->_license );
7278
7279
				// Plan downgraded if plan was changed or subscription was cancelled.
7280
				$plan_downgraded = ( $plan instanceof FS_Plugin_Plan && $prev_plan_id != $plan->id ) ||
7281
				                   ( is_object( $subscription ) && ! isset( $subscription->error ) && ! $subscription->is_active() );
7282
			} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
7283
				// handle different error cases.
7284
7285
			}
7286
7287
			if ( $plan_downgraded ) {
7288
				// Remove previous sticky message about upgrade (if exist).
7289
				$this->_admin_notices->remove_sticky( 'plan_upgraded' );
7290
7291
				$this->_admin_notices->add(
7292
					sprintf( __fs( 'plan-x-downgraded-message', $this->_slug ),
7293
						$plan->title,
7294
						human_time_diff( time(), strtotime( $this->_license->expiration ) )
7295
					)
7296
				);
7297
7298
				// Store site updates.
7299
				$this->_store_site();
7300
			} else {
7301
				$this->_admin_notices->add(
7302
					__fs( 'plan-downgraded-failure-message', $this->_slug ),
7303
					__fs( 'oops', $this->_slug ) . '...',
7304
					'error'
7305
				);
7306
			}
7307
		}
7308
7309
		/**
7310
		 * Cancel site trial.
7311
		 *
7312
		 * @author Vova Feldman (@svovaf)
7313
		 * @since  1.0.9
7314
		 *
7315
		 * @uses   FS_Api
7316
		 */
7317
		private function _cancel_trial() {
7318
			$this->_logger->entrance();
7319
7320
			if ( ! $this->is_trial() ) {
7321
				$this->_admin_notices->add(
7322
					__fs( 'trial-cancel-no-trial-message', $this->_slug ),
7323
					__fs( 'oops', $this->_slug ) . '...',
7324
					'error'
7325
				);
7326
7327
				return;
7328
			}
7329
7330
			$api  = $this->get_api_site_scope();
7331
			$site = $api->call( 'trials.json', 'delete' );
7332
7333
			$trial_cancelled = false;
7334
7335
			if ( ! $this->is_api_error( $site ) ) {
7336
				$prev_trial_ends = $this->_site->trial_ends;
7337
7338
				if ( $this->is_paid_trial() ) {
7339
					$this->_license->expiration   = $site->trial_ends;
7340
					$this->_license->is_cancelled = true;
7341
					$this->_update_site_license( $this->_license );
7342
					$this->_store_licenses();
7343
7344
					// Clear subscription reference.
7345
					$this->_sync_site_subscription( null );
7346
				}
7347
7348
				// Update site info.
7349
				$this->_site = new FS_Site( $site );
7350
				$this->_enrich_site_plan();
7351
7352
				$trial_cancelled = ( $prev_trial_ends != $site->trial_ends );
7353
			} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
7354
				// handle different error cases.
7355
7356
			}
7357
7358
			if ( $trial_cancelled ) {
7359
				// Remove previous sticky messages about upgrade or trial (if exist).
7360
				$this->_admin_notices->remove_sticky( array(
7361
					'trial_started',
7362
					'trial_promotion',
7363
					'plan_upgraded',
7364
				) );
7365
7366
				// Store site updates.
7367
				$this->_store_site();
7368
7369
				// Clear trial plan information.
7370
				unset( $this->_storage->trial_plan );
7371
7372
				if ( ! $this->is_addon() ||
7373
				     ! $this->deactivate_premium_only_addon_without_license( true )
7374
				) {
7375
					$this->_admin_notices->add(
7376
						sprintf( __fs( 'trial-cancel-message', $this->_slug ), $this->_storage->trial_plan->title )
0 ignored issues
show
Documentation introduced by
The property trial_plan does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
7377
					);
7378
				}
7379
			} else {
7380
				$this->_admin_notices->add(
7381
					__fs( 'trial-cancel-failure-message', $this->_slug ),
7382
					__fs( 'oops', $this->_slug ) . '...',
7383
					'error'
7384
				);
7385
			}
7386
		}
7387
7388
		/**
7389
		 * @author Vova Feldman (@svovaf)
7390
		 * @since  1.0.6
7391
		 *
7392
		 * @param bool|number $plugin_id
7393
		 *
7394
		 * @return bool
7395
		 */
7396
		private function _is_addon_id( $plugin_id ) {
7397
			return is_numeric( $plugin_id ) && ( $this->get_id() != $plugin_id );
7398
		}
7399
7400
		/**
7401
		 * Check if user eligible to download premium version updates.
7402
		 *
7403
		 * @author Vova Feldman (@svovaf)
7404
		 * @since  1.0.6
7405
		 *
7406
		 * @return bool
7407
		 */
7408
		private function _can_download_premium() {
7409
			return $this->has_active_license() ||
7410
			       ( $this->is_trial() && ! $this->get_trial_plan()->is_free() );
7411
		}
7412
7413
		/**
7414
		 *
7415
		 * @author Vova Feldman (@svovaf)
7416
		 * @since  1.0.6
7417
		 *
7418
		 * @param bool|number $addon_id
7419
		 * @param string      $type "json" or "zip"
7420
		 *
7421
		 * @return string
7422
		 */
7423
		private function _get_latest_version_endpoint( $addon_id = false, $type = 'json' ) {
7424
7425
			$is_addon = $this->_is_addon_id( $addon_id );
7426
7427
			$is_premium = null;
7428
			if ( ! $is_addon ) {
7429
				$is_premium = $this->_can_download_premium();
7430
			} else if ( $this->is_addon_activated( $addon_id ) ) {
7431
				$is_premium = self::get_instance_by_id( $addon_id )->_can_download_premium();
7432
			}
7433
7434
			return // If add-on, then append add-on ID.
7435
				( $is_addon ? "/addons/$addon_id" : '' ) .
7436
				'/updates/latest.' . $type .
7437
				// If add-on and not yet activated, try to fetch based on server licensing.
7438
				( is_bool( $is_premium ) ? '?is_premium=' . json_encode( $is_premium ) : '' );
7439
		}
7440
7441
		/**
7442
		 * @author Vova Feldman (@svovaf)
7443
		 * @since  1.0.4
7444
		 *
7445
		 * @param bool|number $addon_id
7446
		 * @param bool        $flush Since 1.1.7.3
7447
		 *
7448
		 * @return object|false Plugin latest tag info.
7449
		 */
7450
		function _fetch_latest_version( $addon_id = false, $flush = true ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
7451
			$this->_logger->entrance();
7452
7453
			/**
7454
			 * @since 1.1.7.3 Check for plugin updates from Freemius only if opted-in.
7455
			 * @since 1.1.7.4 Also check updates for add-ons.
7456
			 */
7457
			if ( ! $this->is_registered() &&
7458
			     ! $this->_is_addon_id( $addon_id )
7459
			) {
7460
				return false;
7461
			}
7462
7463
			$tag = $this->get_api_site_or_plugin_scope()->get(
7464
				$this->_get_latest_version_endpoint( $addon_id, 'json' ),
7465
				$flush
7466
			);
7467
7468
			$latest_version = ( is_object( $tag ) && isset( $tag->version ) ) ? $tag->version : 'couldn\'t get';
7469
7470
			$this->_logger->departure( 'Latest version ' . $latest_version );
7471
7472
			return ( is_object( $tag ) && isset( $tag->version ) ) ? $tag : false;
7473
		}
7474
7475
		#region Download Plugin ------------------------------------------------------------------
7476
7477
		/**
7478
		 * Download latest plugin version, based on plan.
7479
		 * The download will be fetched via the API first.
7480
		 *
7481
		 * @author Vova Feldman (@svovaf)
7482
		 * @since  1.0.4
7483
		 *
7484
		 * @param bool|number $plugin_id
7485
		 *
7486
		 * @uses   FS_Api
7487
		 *
7488
		 * @deprecated
7489
		 */
7490
		private function _download_latest( $plugin_id = false ) {
7491
			$this->_logger->entrance();
7492
7493
			$is_addon = $this->_is_addon_id( $plugin_id );
7494
7495
			$is_premium = $this->_can_download_premium();
7496
7497
			$latest = $this->get_api_site_scope()->call(
7498
				$this->_get_latest_version_endpoint( $plugin_id, 'zip' )
7499
			);
7500
7501
			$slug = $this->_slug;
7502
			if ( $is_addon ) {
7503
				$addon = $this->get_addon( $plugin_id );
7504
				$slug  = is_object( $addon ) ? $addon->slug : 'addon';
7505
			}
7506
7507
			if ( ! is_object( $latest ) ) {
7508
				header( "Content-Type: application/zip" );
7509
				header( "Content-Disposition: attachment; filename={$slug}" . ( ! $is_addon && $is_premium ? '-premium' : '' ) . ".zip" );
7510
				header( "Content-Length: " . strlen( $latest ) );
7511
				echo $latest;
7512
7513
				exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method _download_latest() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
7514
			}
7515
		}
7516
7517
		/**
7518
		 * Download latest plugin version, based on plan.
7519
		 *
7520
		 * Not like _download_latest(), this will redirect the page
7521
		 * to secure download url to prevent dual download (from FS to WP server,
7522
		 * and then from WP server to the client / browser).
7523
		 *
7524
		 * @author Vova Feldman (@svovaf)
7525
		 * @since  1.0.9
7526
		 *
7527
		 * @param bool|number $plugin_id
7528
		 *
7529
		 * @uses   FS_Api
7530
		 * @uses   wp_redirect()
7531
		 */
7532
		private function _download_latest_directly( $plugin_id = false ) {
7533
			$this->_logger->entrance();
7534
7535
			wp_redirect( $this->_get_latest_download_api_url( $plugin_id ) );
7536
		}
7537
7538
		/**
7539
		 * Get latest plugin FS API download URL.
7540
		 *
7541
		 * @author Vova Feldman (@svovaf)
7542
		 * @since  1.0.9
7543
		 *
7544
		 * @param bool|number $plugin_id
7545
		 *
7546
		 * @return string
7547
		 */
7548
		private function _get_latest_download_api_url( $plugin_id = false ) {
7549
			$this->_logger->entrance();
7550
7551
			return $this->get_api_site_scope()->get_signed_url(
7552
				$this->_get_latest_version_endpoint( $plugin_id, 'zip' )
7553
			);
7554
		}
7555
7556
		/**
7557
		 * Get latest plugin download link.
7558
		 *
7559
		 * @author Vova Feldman (@svovaf)
7560
		 * @since  1.0.9
7561
		 *
7562
		 * @param string      $label
7563
		 * @param bool|number $plugin_id
7564
		 *
7565
		 * @return string
7566
		 */
7567
		private function _get_latest_download_link( $label, $plugin_id = false ) {
7568
			return sprintf(
7569
				'<a target="_blank" href="%s">%s</a>',
7570
				$this->_get_latest_download_local_url( $plugin_id ),
7571
				$label
7572
			);
7573
		}
7574
7575
		/**
7576
		 * Get latest plugin download local URL.
7577
		 *
7578
		 * @author Vova Feldman (@svovaf)
7579
		 * @since  1.0.9
7580
		 *
7581
		 * @param bool|number $plugin_id
7582
		 *
7583
		 * @return string
7584
		 */
7585
		function _get_latest_download_local_url( $plugin_id = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
7586
			// Add timestamp to protect from caching.
7587
			$params = array( 'ts' => WP_FS__SCRIPT_START_TIME );
7588
7589
			if ( ! empty( $plugin_id ) ) {
7590
				$params['plugin_id'] = $plugin_id;
7591
			}
7592
7593
			return $this->get_account_url( 'download_latest', $params );
7594
		}
7595
7596
		#endregion Download Plugin ------------------------------------------------------------------
7597
7598
		/**
7599
		 * @author Vova Feldman (@svovaf)
7600
		 * @since  1.0.4
7601
		 *
7602
		 * @uses   FS_Api
7603
		 *
7604
		 * @param bool        $background Hints the method if it's a background updates check. If false, it means that
7605
		 *                                was initiated by the admin.
7606
		 * @param bool|number $plugin_id
7607
		 * @param bool        $flush      Since 1.1.7.3
7608
		 */
7609
		private function _check_updates( $background = false, $plugin_id = false, $flush = true ) {
7610
			$this->_logger->entrance();
7611
7612
			// Check if there's a newer version for download.
7613
			$new_version = $this->_fetch_newer_version( $plugin_id, $flush );
7614
7615
			$update = null;
7616
			if ( is_object( $new_version ) ) {
7617
				$update = new FS_Plugin_Tag( $new_version );
7618
7619
				if ( ! $background ) {
7620
					$this->_admin_notices->add(
7621
						sprintf(
7622
							__fs( 'version-x-released', $this->_slug ) . ' ' . __fs( 'please-download-x', $this->_slug ),
7623
							$update->version,
7624
							sprintf(
7625
								'<a href="%s" target="_blank">%s</a>',
7626
								$this->get_account_url( 'download_latest' ),
7627
								sprintf( __fs( 'latest-x-version', $this->_slug ), $this->_site->plan->title )
7628
							)
7629
						),
7630
						__fs( 'new', $this->_slug ) . '!'
7631
					);
7632
				}
7633
			} else if ( false === $new_version && ! $background ) {
7634
				$this->_admin_notices->add(
7635
					__fs( 'you-have-latest', $this->_slug ),
7636
					__fs( 'you-are-good', $this->_slug )
7637
				);
7638
			}
7639
7640
			$this->_store_update( $update, true, $plugin_id );
7641
		}
7642
7643
		/**
7644
		 * @author Vova Feldman (@svovaf)
7645
		 * @since  1.0.4
7646
		 *
7647
		 * @param bool $flush Since 1.1.7.3 by default add 24 hour cache.
7648
		 *
7649
		 * @return FS_Plugin[]
7650
		 *
7651
		 * @uses   FS_Api
7652
		 */
7653
		private function _sync_addons( $flush = false ) {
7654
			$this->_logger->entrance();
7655
7656
			$result = $this->get_api_site_or_plugin_scope()->get( '/addons.json?enriched=true', $flush );
7657
7658
			$addons = array();
7659
			if ( ! $this->is_api_error( $result ) ) {
7660
				for ( $i = 0, $len = count( $result->plugins ); $i < $len; $i ++ ) {
7661
					$addons[ $i ] = new FS_Plugin( $result->plugins[ $i ] );
7662
				}
7663
7664
				$this->_store_addons( $addons, true );
7665
			}
7666
7667
			return $addons;
7668
		}
7669
7670
		/**
7671
		 * Handle user email update.
7672
		 *
7673
		 * @author Vova Feldman (@svovaf)
7674
		 * @since  1.0.3
7675
		 * @uses   FS_Api
7676
		 *
7677
		 * @param string $new_email
7678
		 *
7679
		 * @return object
7680
		 */
7681
		private function _update_email( $new_email ) {
7682
			$this->_logger->entrance();
7683
7684
7685
			$api  = $this->get_api_user_scope();
7686
			$user = $api->call( "?plugin_id={$this->_plugin->id}&fields=id,email,is_verified", 'put', array(
7687
				'email'                   => $new_email,
7688
				'after_email_confirm_url' => $this->_get_admin_page_url(
7689
					'account',
7690
					array( 'fs_action' => 'sync_user' )
7691
				),
7692
			) );
7693
7694
			if ( ! isset( $user->error ) ) {
7695
				$this->_user->email       = $user->email;
7696
				$this->_user->is_verified = $user->is_verified;
7697
				$this->_store_user();
7698
			} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
7699
				// handle different error cases.
7700
7701
			}
7702
7703
			return $user;
7704
		}
7705
7706
		/**
7707
		 * @author Vova Feldman (@svovaf)
7708
		 * @since  1.1.1
7709
		 *
7710
		 * @param mixed $result
7711
		 *
7712
		 * @return bool Is API result contains an error.
7713
		 */
7714
		private function is_api_error( $result ) {
7715
			return ( is_object( $result ) && isset( $result->error ) ) ||
7716
			       is_string( $result );
7717
		}
7718
7719
		/**
7720
		 * Start install ownership change.
7721
		 *
7722
		 * @author Vova Feldman (@svovaf)
7723
		 * @since  1.1.1
7724
		 * @uses   FS_Api
7725
		 *
7726
		 * @param string $candidate_email
7727
		 *
7728
		 * @return bool Is ownership change successfully initiated.
7729
		 */
7730
		private function init_change_owner( $candidate_email ) {
7731
			$this->_logger->entrance();
7732
7733
			$api    = $this->get_api_site_scope();
7734
			$result = $api->call( "/users/{$this->_user->id}.json", 'put', array(
7735
				'email'             => $candidate_email,
7736
				'after_confirm_url' => $this->_get_admin_page_url(
7737
					'account',
7738
					array( 'fs_action' => 'change_owner' )
7739
				),
7740
			) );
7741
7742
			return ! $this->is_api_error( $result );
7743
		}
7744
7745
		/**
7746
		 * Handle install ownership change.
7747
		 *
7748
		 * @author Vova Feldman (@svovaf)
7749
		 * @since  1.1.1
7750
		 * @uses   FS_Api
7751
		 *
7752
		 * @return bool Was ownership change successfully complete.
7753
		 */
7754
		private function complete_change_owner() {
7755
			$this->_logger->entrance();
7756
7757
			$site_result = $this->get_api_site_scope( true )->get();
7758
			$site        = new FS_Site( $site_result );
7759
			$this->_site = $site;
7760
7761
			$user     = new FS_User();
7762
			$user->id = fs_request_get( 'user_id' );
7763
7764
			// Validate install's user and given user.
7765
			if ( $user->id != $this->_site->user_id ) {
7766
				return false;
7767
			}
7768
7769
			$user->public_key = fs_request_get( 'user_public_key' );
7770
			$user->secret_key = fs_request_get( 'user_secret_key' );
7771
7772
			// Fetch new user information.
7773
			$this->_user = $user;
7774
			$user_result = $this->get_api_user_scope( true )->get();
7775
			$user        = new FS_User( $user_result );
7776
			$this->_user = $user;
7777
7778
			$this->_set_account( $user, $site );
7779
7780
			return true;
7781
		}
7782
7783
		/**
7784
		 * Handle user name update.
7785
		 *
7786
		 * @author Vova Feldman (@svovaf)
7787
		 * @since  1.0.9
7788
		 * @uses   FS_Api
7789
		 *
7790
		 * @return object
7791
		 */
7792
		private function update_user_name() {
7793
			$this->_logger->entrance();
7794
			$name = fs_request_get( 'fs_user_name_' . $this->_slug, '' );
7795
7796
			$api  = $this->get_api_user_scope();
7797
			$user = $api->call( "?plugin_id={$this->_plugin->id}&fields=id,first,last", 'put', array(
7798
				'name' => $name,
7799
			) );
7800
7801
			if ( ! isset( $user->error ) ) {
7802
				$this->_user->first = $user->first;
7803
				$this->_user->last  = $user->last;
7804
				$this->_store_user();
7805
			} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
7806
				// handle different error cases.
7807
7808
			}
7809
7810
			return $user;
7811
		}
7812
7813
		/**
7814
		 * Verify user email.
7815
		 *
7816
		 * @author Vova Feldman (@svovaf)
7817
		 * @since  1.0.3
7818
		 * @uses   FS_Api
7819
		 */
7820
		private function verify_email() {
7821
			$this->_handle_account_user_sync();
7822
7823
			if ( $this->_user->is_verified() ) {
7824
				return;
7825
			}
7826
7827
			$api    = $this->get_api_site_scope();
7828
			$result = $api->call( "/users/{$this->_user->id}/verify.json", 'put', array(
7829
				'after_email_confirm_url' => $this->_get_admin_page_url(
7830
					'account',
7831
					array( 'fs_action' => 'sync_user' )
7832
				)
7833
			) );
7834
7835
			if ( ! isset( $result->error ) ) {
7836
				$this->_admin_notices->add( sprintf(
7837
					__fs( 'verification-email-sent-message', $this->_slug ),
7838
					sprintf( '<a href="mailto:%1s">%2s</a>', esc_url( $this->_user->email ), $this->_user->email )
7839
				) );
7840
			} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
7841
				// handle different error cases.
7842
7843
			}
7844
		}
7845
7846
		/**
7847
		 * @author Vova Feldman (@svovaf)
7848
		 * @since  1.1.2
7849
		 *
7850
		 * @return string
7851
		 */
7852
		private function get_activation_url() {
7853
			return $this->apply_filters( 'connect_url', $this->_get_admin_page_url() );
7854
		}
7855
7856
		/**
7857
		 * Get the URL of the page that should be loaded after the user connect or skip in the opt-in screen.
7858
		 *
7859
		 * @author Vova Feldman (@svovaf)
7860
		 * @since  1.1.3
7861
		 *
7862
		 * @param string $filter Filter name.
7863
		 *
7864
		 * @return string
7865
		 */
7866
		private function get_after_activation_url( $filter ) {
7867
			$first_time_path = $this->_menu->get_first_time_path();
7868
7869
			return $this->apply_filters(
7870
				$filter,
7871
				empty( $first_time_path ) ?
7872
					$this->_get_admin_page_url() :
7873
					$first_time_path
7874
			);
7875
		}
7876
7877
		/**
7878
		 * Handle account page updates / edits / actions.
7879
		 *
7880
		 * @author Vova Feldman (@svovaf)
7881
		 * @since  1.0.2
7882
		 *
7883
		 */
7884
		private function _handle_account_edits() {
7885
			if ( ! current_user_can( 'activate_plugins' ) ) {
7886
				return;
7887
			}
7888
7889
			$plugin_id = fs_request_get( 'plugin_id', $this->get_id() );
7890
			$action    = fs_get_action();
7891
7892
			switch ( $action ) {
7893
				case 'delete_account':
7894
					check_admin_referer( $action );
7895
7896
					if ( $plugin_id == $this->get_id() ) {
7897
						$this->delete_account_event();
7898
7899
						// Clear user and site.
7900
						$this->_site = null;
7901
						$this->_user = null;
7902
7903
						if ( fs_redirect( $this->get_activation_url() ) ) {
7904
							exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method _handle_account_edits() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
7905
						}
7906
					} else {
7907
						if ( $this->is_addon_activated( $plugin_id ) ) {
7908
							$fs_addon = self::get_instance_by_id( $plugin_id );
7909
							$fs_addon->delete_account_event();
7910
7911
							if ( fs_redirect( $this->_get_admin_page_url( 'account' ) ) ) {
7912
								exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method _handle_account_edits() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
7913
							}
7914
						}
7915
					}
7916
7917
					return;
7918
7919
				case 'downgrade_account':
7920
					check_admin_referer( $action );
7921
					$this->_downgrade_site();
7922
7923
					return;
7924
7925
				case 'activate_license':
7926
					check_admin_referer( $action );
7927
7928
					if ( $plugin_id == $this->get_id() ) {
7929
						$this->_activate_license();
7930
					} else {
7931
						if ( $this->is_addon_activated( $plugin_id ) ) {
7932
							$fs_addon = self::get_instance_by_id( $plugin_id );
7933
							$fs_addon->_activate_license();
7934
						}
7935
					}
7936
7937
					return;
7938
7939
				case 'deactivate_license':
7940
					check_admin_referer( $action );
7941
7942
					if ( $plugin_id == $this->get_id() ) {
7943
						$this->_deactivate_license();
7944
					} else {
7945
						if ( $this->is_addon_activated( $plugin_id ) ) {
7946
							$fs_addon = self::get_instance_by_id( $plugin_id );
7947
							$fs_addon->_deactivate_license();
7948
						}
7949
					}
7950
7951
					return;
7952
7953
				case 'check_updates':
7954
					check_admin_referer( $action );
7955
					$this->_check_updates();
7956
7957
					return;
7958
7959
				case 'change_owner':
7960
					$state = fs_request_get( 'state', 'init' );
7961
					switch ( $state ) {
7962
						case 'init':
7963
							$candidate_email = fs_request_get( 'candidate_email', '' );
7964
7965
							if ( $this->init_change_owner( $candidate_email ) ) {
7966
								$this->_admin_notices->add( sprintf( __fs( 'change-owner-request-sent-x', $this->_slug ), '<b>' . $this->_user->email . '</b>' ) );
7967
							}
7968
							break;
7969
						case 'owner_confirmed':
7970
							$candidate_email = fs_request_get( 'candidate_email', '' );
7971
7972
							$this->_admin_notices->add( sprintf( __fs( 'change-owner-request_owner-confirmed', $this->_slug ), '<b>' . $candidate_email . '</b>' ) );
7973
							break;
7974
						case 'candidate_confirmed':
7975
							if ( $this->complete_change_owner() ) {
7976
								$this->_admin_notices->add_sticky(
7977
									sprintf( __fs( 'change-owner-request_candidate-confirmed', $this->_slug ), '<b>' . $this->_user->email . '</b>' ),
7978
									'ownership_changed',
7979
									__fs( 'congrats', $this->_slug ) . '!'
7980
								);
7981
							} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
7982
								// @todo Handle failed ownership change message.
7983
							}
7984
							break;
7985
					}
7986
7987
					return;
7988
7989
				case 'update_email':
7990
					check_admin_referer( 'update_email' );
7991
7992
					$new_email = fs_request_get( 'fs_email_' . $this->_slug, '' );
7993
					$result    = $this->_update_email( $new_email );
7994
7995
					if ( isset( $result->error ) ) {
7996
						switch ( $result->error->code ) {
7997
							case 'user_exist':
7998
								$this->_admin_notices->add(
7999
									__fs( 'user-exist-message', $this->_slug ) . ' ' .
8000
									sprintf( __fs( 'user-exist-message_ownership', $this->_slug ), '<b>' . $new_email . '</b>' ) .
8001
									sprintf(
8002
										'<a style="margin-left: 10px;" href="%s"><button class="button button-primary">%s &nbsp;&#10140;</button></a>',
8003
										$this->get_account_url( 'change_owner', array(
8004
											'state'           => 'init',
8005
											'candidate_email' => $new_email
8006
										) ),
8007
										__fs( 'change-ownership', $this->_slug )
8008
									),
8009
									__fs( 'oops', $this->_slug ) . '...',
8010
									'error'
8011
								);
8012
								break;
8013
						}
8014
					} else {
8015
						$this->_admin_notices->add( __fs( 'email-updated-message', $this->_slug ) );
8016
					}
8017
8018
					return;
8019
8020
				case 'update_user_name':
8021
					check_admin_referer( 'update_user_name' );
8022
8023
					$result = $this->update_user_name();
8024
8025
					if ( isset( $result->error ) ) {
8026
						$this->_admin_notices->add(
8027
							__fs( 'name-update-failed-message', $this->_slug ),
8028
							__fs( 'oops', $this->_slug ) . '...',
8029
							'error'
8030
						);
8031
					} else {
8032
						$this->_admin_notices->add( __fs( 'name-updated-message', $this->_slug ) );
8033
					}
8034
8035
					return;
8036
8037
				#region Actions that might be called from external links (e.g. email)
8038
8039
				case 'cancel_trial':
8040
					if ( $plugin_id == $this->get_id() ) {
8041
						$this->_cancel_trial();
8042
					} else {
8043
						if ( $this->is_addon_activated( $plugin_id ) ) {
8044
							$fs_addon = self::get_instance_by_id( $plugin_id );
8045
							$fs_addon->_cancel_trial();
8046
						}
8047
					}
8048
8049
					return;
8050
8051
				case 'verify_email':
8052
					$this->verify_email();
8053
8054
					return;
8055
8056
				case 'sync_user':
8057
					$this->_handle_account_user_sync();
8058
8059
					return;
8060
8061
				case $this->_slug . '_sync_license':
8062
					$this->_sync_license();
8063
8064
					return;
8065
8066
				case 'download_latest':
8067
					$this->_download_latest_directly( $plugin_id );
8068
8069
					return;
8070
8071
				#endregion
8072
			}
8073
8074
			if ( WP_FS__IS_POST_REQUEST ) {
8075
				$properties = array( 'site_secret_key', 'site_id', 'site_public_key' );
8076
				foreach ( $properties as $p ) {
8077
					if ( 'update_' . $p === $action ) {
8078
						check_admin_referer( $action );
8079
8080
						$this->_logger->log( $action );
8081
8082
						$site_property                      = substr( $p, strlen( 'site_' ) );
8083
						$site_property_value                = fs_request_get( 'fs_' . $p . '_' . $this->_slug, '' );
8084
						$this->get_site()->{$site_property} = $site_property_value;
8085
8086
						// Store account after modification.
8087
						$this->_store_site();
8088
8089
						$this->do_action( 'account_property_edit', 'site', $site_property, $site_property_value );
8090
8091
						$this->_admin_notices->add( sprintf(
8092
							__fs( 'x-updated', $this->_slug ),
8093
							'<b>' . str_replace( '_', ' ', $p ) . '</b>' ) );
8094
8095
						return;
8096
					}
8097
				}
8098
			}
8099
		}
8100
8101
		/**
8102
		 * Account page resources load.
8103
		 *
8104
		 * @author Vova Feldman (@svovaf)
8105
		 * @since  1.0.6
8106
		 */
8107
		function _account_page_load() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8108
			$this->_logger->entrance();
8109
8110
			$this->_logger->info( var_export( $_REQUEST, true ) );
8111
8112
			fs_enqueue_local_style( 'fs_account', '/admin/account.css' );
8113
8114
			if ( $this->has_addons() ) {
8115
				wp_enqueue_script( 'plugin-install' );
8116
				add_thickbox();
8117
8118
				function fs_addons_body_class( $classes ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8119
					$classes .= ' plugins-php';
8120
8121
					return $classes;
8122
				}
8123
8124
				add_filter( 'admin_body_class', 'fs_addons_body_class' );
8125
			}
8126
8127
			if ( $this->has_paid_plan() &&
8128
			     ! $this->has_any_license() &&
8129
			     ! $this->is_sync_executed()
8130
			) {
8131
				/**
8132
				 * If no licenses found and no sync job was executed during the last 24 hours,
8133
				 * just execute the sync job right away (blocking execution).
8134
				 *
8135
				 * @since 1.1.7.3
8136
				 */
8137
				$this->run_manual_sync();
8138
			}
8139
8140
			$this->_handle_account_edits();
8141
8142
			$this->do_action( 'account_page_load_before_departure' );
8143
		}
8144
8145
		/**
8146
		 * Render account page.
8147
		 *
8148
		 * @author Vova Feldman (@svovaf)
8149
		 * @since  1.0.0
8150
		 */
8151
		function _account_page_render() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8152
			$this->_logger->entrance();
8153
8154
			$vars = array( 'slug' => $this->_slug );
8155
			fs_require_once_template( 'account.php', $vars );
8156
		}
8157
8158
		/**
8159
		 * Render account connect page.
8160
		 *
8161
		 * @author Vova Feldman (@svovaf)
8162
		 * @since  1.0.7
8163
		 */
8164
		function _connect_page_render() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8165
			$this->_logger->entrance();
8166
8167
			$vars = array( 'slug' => $this->_slug );
8168
			fs_require_once_template( 'connect.php', $vars );
8169
		}
8170
8171
		/**
8172
		 * Load required resources before add-ons page render.
8173
		 *
8174
		 * @author Vova Feldman (@svovaf)
8175
		 * @since  1.0.6
8176
		 */
8177
		function _addons_page_load() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8178
			$this->_logger->entrance();
8179
8180
			fs_enqueue_local_style( 'fs_addons', '/admin/add-ons.css' );
8181
8182
			wp_enqueue_script( 'plugin-install' );
8183
			add_thickbox();
8184
8185
			function fs_addons_body_class( $classes ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Best Practice introduced by
The function fs_addons_body_class() has been defined more than once; this definition is ignored, only the first definition in this file (L8118-8122) is considered.

This check looks for functions that have already been defined in the same file.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
8186
				$classes .= ' plugins-php';
8187
8188
				return $classes;
8189
			}
8190
8191
			add_filter( 'admin_body_class', 'fs_addons_body_class' );
8192
8193
			if ( ! $this->is_registered() && $this->is_org_repo_compliant() ) {
8194
				$this->_admin_notices->add(
8195
					sprintf( __fs( 'addons-info-external-message', $this->_slug ), '<b>' . $this->get_plugin_name() . '</b>' ),
8196
					__fs( 'heads-up', $this->_slug ),
8197
					'update-nag'
8198
				);
8199
			}
8200
		}
8201
8202
		/**
8203
		 * Render add-ons page.
8204
		 *
8205
		 * @author Vova Feldman (@svovaf)
8206
		 * @since  1.0.6
8207
		 */
8208
		function _addons_page_render() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8209
			$this->_logger->entrance();
8210
8211
			$vars = array( 'slug' => $this->_slug );
8212
			fs_require_once_template( 'add-ons.php', $vars );
8213
		}
8214
8215
		/* Pricing & Upgrade
8216
		------------------------------------------------------------------------------------------------------------------*/
8217
		/**
8218
		 * Render pricing page.
8219
		 *
8220
		 * @author Vova Feldman (@svovaf)
8221
		 * @since  1.0.0
8222
		 */
8223
		function _pricing_page_render() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8224
			$this->_logger->entrance();
8225
8226
			$vars = array( 'slug' => $this->_slug );
8227
8228
			if ( 'true' === fs_request_get( 'checkout', false ) ) {
8229
				fs_require_once_template( 'checkout.php', $vars );
8230
			} else {
8231
				fs_require_once_template( 'pricing.php', $vars );
8232
			}
8233
		}
8234
8235
		#region Contact Us ------------------------------------------------------------------
8236
8237
		/**
8238
		 * Render contact-us page.
8239
		 *
8240
		 * @author Vova Feldman (@svovaf)
8241
		 * @since  1.0.3
8242
		 */
8243
		function _contact_page_render() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8244
			$this->_logger->entrance();
8245
8246
			$vars = array( 'slug' => $this->_slug );
8247
			fs_require_once_template( 'contact.php', $vars );
8248
		}
8249
8250
		#endregion ------------------------------------------------------------------
8251
8252
		/**
8253
		 * Hide all admin notices to prevent distractions.
8254
		 *
8255
		 * @author Vova Feldman (@svovaf)
8256
		 * @since  1.0.3
8257
		 *
8258
		 * @uses   remove_all_actions()
8259
		 */
8260
		private static function _hide_admin_notices() {
8261
			remove_all_actions( 'admin_notices' );
8262
			remove_all_actions( 'network_admin_notices' );
8263
			remove_all_actions( 'all_admin_notices' );
8264
			remove_all_actions( 'user_admin_notices' );
8265
		}
8266
8267
		static function _clean_admin_content_section_hook() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8268
			self::_hide_admin_notices();
8269
8270
			// Hide footer.
8271
			echo '<style>#wpfooter { display: none !important; }</style>';
8272
		}
8273
8274
		/**
8275
		 * Attach to admin_head hook to hide all admin notices.
8276
		 *
8277
		 * @author Vova Feldman (@svovaf)
8278
		 * @since  1.0.3
8279
		 */
8280
		static function _clean_admin_content_section() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8281
			add_action( 'admin_head', 'Freemius::_clean_admin_content_section_hook' );
8282
		}
8283
8284
		/* CSS & JavaScript
8285
		------------------------------------------------------------------------------------------------------------------*/
8286
		/*		function _enqueue_script($handle, $src) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8287
					$url = plugins_url( substr( WP_FS__DIR_JS, strlen( $this->_plugin_dir_path ) ) . '/assets/js/' . $src );
8288
8289
					$this->_logger->entrance( 'script = ' . $url );
8290
8291
					wp_enqueue_script( $handle, $url );
8292
				}*/
8293
8294
		/* SDK
8295
		------------------------------------------------------------------------------------------------------------------*/
8296
		private $_user_api;
8297
8298
		/**
8299
		 *
8300
		 * @author Vova Feldman (@svovaf)
8301
		 * @since  1.0.2
8302
		 *
8303
		 * @param bool $flush
8304
		 *
8305
		 * @return FS_Api
8306
		 */
8307
		function get_api_user_scope( $flush = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8308
			if ( ! isset( $this->_user_api ) || $flush ) {
8309
				$this->_user_api = FS_Api::instance(
8310
					$this->_slug,
8311
					'user',
8312
					$this->_user->id,
8313
					$this->_user->public_key,
8314
					! $this->is_live(),
8315
					$this->_user->secret_key
8316
				);
8317
			}
8318
8319
			return $this->_user_api;
8320
		}
8321
8322
		private $_site_api;
8323
8324
		/**
8325
		 *
8326
		 * @author Vova Feldman (@svovaf)
8327
		 * @since  1.0.2
8328
		 *
8329
		 * @param bool $flush
8330
		 *
8331
		 * @return FS_Api
8332
		 */
8333
		function get_api_site_scope( $flush = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8334
			if ( ! isset( $this->_site_api ) || $flush ) {
8335
				$this->_site_api = FS_Api::instance(
8336
					$this->_slug,
8337
					'install',
8338
					$this->_site->id,
8339
					$this->_site->public_key,
8340
					! $this->is_live(),
8341
					$this->_site->secret_key
8342
				);
8343
			}
8344
8345
			return $this->_site_api;
8346
		}
8347
8348
		private $_plugin_api;
8349
8350
		/**
8351
		 * Get plugin public API scope.
8352
		 *
8353
		 * @author Vova Feldman (@svovaf)
8354
		 * @since  1.0.7
8355
		 *
8356
		 * @return FS_Api
8357
		 */
8358
		function get_api_plugin_scope() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8359
			if ( ! isset( $this->_plugin_api ) ) {
8360
				$this->_plugin_api = FS_Api::instance(
8361
					$this->_slug,
8362
					'plugin',
8363
					$this->_plugin->id,
8364
					$this->_plugin->public_key,
8365
					! $this->is_live()
8366
				);
8367
			}
8368
8369
			return $this->_plugin_api;
8370
		}
8371
8372
		/**
8373
		 * Get site API scope object (fallback to public plugin scope when not registered).
8374
		 *
8375
		 * @author Vova Feldman (@svovaf)
8376
		 * @since  1.0.7
8377
		 *
8378
		 * @return FS_Api
8379
		 */
8380
		function get_api_site_or_plugin_scope() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8381
			return $this->is_registered() ?
8382
				$this->get_api_site_scope() :
8383
				$this->get_api_plugin_scope();
8384
		}
8385
8386
		/**
8387
		 * Show trial promotional notice (if any trial exist).
8388
		 *
8389
		 * @author Vova Feldman (@svovaf)
8390
		 * @since  1.0.9
8391
		 *
8392
		 * @param $plans
8393
		 */
8394
		function _check_for_trial_plans( $plans ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8395
			$this->_storage->has_trial_plan = FS_Plan_Manager::instance()->has_trial_plan( $plans );
0 ignored issues
show
Documentation introduced by
The property has_trial_plan does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
8396
		}
8397
8398
		/**
8399
		 * Show trial promotional notice (if any trial exist).
8400
		 *
8401
		 * @author Vova Feldman (@svovaf)
8402
		 * @since  1.0.9
8403
		 */
8404
		function _add_trial_notice() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8405
			// Check if trial already utilized.
8406
			if ( $this->_site->is_trial_utilized() ) {
8407
				return;
8408
			}
8409
8410
			// Check if already paying.
8411
			if ( $this->is_paying() ) {
8412
				return;
8413
			}
8414
8415
			// Check if trial message is already shown.
8416
			if ( $this->_admin_notices->has_sticky( 'trial_promotion' ) ) {
8417
				return;
8418
			}
8419
8420
			$trial_plans       = FS_Plan_Manager::instance()->get_trial_plans( $this->_plans );
8421
			$trial_plans_count = count( $trial_plans );
8422
8423
			// Check if any of the plans contains trial.
8424
			if ( 0 === $trial_plans_count ) {
8425
				return;
8426
			}
8427
8428
			/**
8429
			 * @var FS_Plugin_Plan $paid_plan
8430
			 */
8431
			$paid_plan            = $trial_plans[0];
8432
			$require_subscription = $paid_plan->is_require_subscription;
8433
			$upgrade_url          = $this->get_trial_url();
8434
			$cc_string            = $require_subscription ?
8435
				sprintf( __fs( 'no-commitment-for-x-days', $this->_slug ), $paid_plan->trial_period ) :
8436
				__fs( 'no-cc-required', $this->_slug ) . '!';
8437
8438
8439
			$total_paid_plans = count( $this->_plans ) - ( FS_Plan_Manager::instance()->has_free_plan( $this->_plans ) ? 1 : 0 );
8440
8441
			if ( $total_paid_plans === $trial_plans_count ) {
8442
				// All paid plans have trials.
8443
				$message = sprintf(
8444
					__fs( 'hey', $this->_slug ) . '! ' . __fs( 'trial-x-promotion-message', $this->_slug ),
8445
					sprintf( '<b>%s</b>', $this->get_plugin_name() ),
8446
					strtolower( __fs( 'awesome', $this->_slug ) ),
8447
					$paid_plan->trial_period
8448
				);
8449
			} else {
8450
				$plans_string = '';
8451
				for ( $i = 0; $i < $trial_plans_count; $i ++ ) {
8452
					$plans_string .= sprintf( '<a href="%s">%s</a>', $upgrade_url, $trial_plans[ $i ]->title );
8453
8454
					if ( $i < $trial_plans_count - 2 ) {
8455
						$plans_string .= ', ';
8456
					} else if ( $i == $trial_plans_count - 2 ) {
8457
						$plans_string .= ' and ';
8458
					}
8459
				}
8460
8461
				// Not all paid plans have trials.
8462
				$message = sprintf(
8463
					__fs( 'hey', $this->_slug ) . '! ' . __fs( 'trial-x-promotion-message', $this->_slug ),
8464
					sprintf( '<b>%s</b>', $this->get_plugin_name() ),
8465
					$plans_string,
8466
					$paid_plan->trial_period
8467
				);
8468
			}
8469
8470
			$message .= ' ' . $cc_string;
8471
8472
			// Add start trial button.
8473
			$message .= ' ' . sprintf(
8474
					'<a style="margin-left: 10px; vertical-align: super;" href="%s"><button class="button button-primary">%s &nbsp;&#10140;</button></a>',
8475
					$upgrade_url,
8476
					__fs( 'start-free-trial', $this->_slug )
8477
				);
8478
8479
			$this->_admin_notices->add_sticky(
8480
				$this->apply_filters( 'trial_promotion_message', $message ),
8481
				'trial_promotion',
8482
				'',
8483
				'promotion'
8484
			);
8485
8486
			$this->_storage->trial_promotion_shown = WP_FS__SCRIPT_START_TIME;
0 ignored issues
show
Documentation introduced by
The property trial_promotion_shown does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
8487
		}
8488
8489
		/* Action Links
8490
		------------------------------------------------------------------------------------------------------------------*/
8491
		private $_action_links_hooked = false;
8492
		private $_action_links = array();
8493
8494
		/**
8495
		 * @author Vova Feldman (@svovaf)
8496
		 * @since  1.0.0
8497
		 *
8498
		 * @return bool
8499
		 */
8500
		private function is_plugin_action_links_hooked() {
8501
			$this->_logger->entrance( json_encode( $this->_action_links_hooked ) );
8502
8503
			return $this->_action_links_hooked;
8504
		}
8505
8506
		/**
8507
		 * Hook to plugin action links filter.
8508
		 *
8509
		 * @author Vova Feldman (@svovaf)
8510
		 * @since  1.0.0
8511
		 */
8512
		private function hook_plugin_action_links() {
8513
			$this->_logger->entrance();
8514
8515
			$this->_action_links_hooked = true;
8516
8517
			$this->_logger->log( 'Adding action links hooks.' );
8518
8519
			// Add action link to settings page.
8520
			add_filter( 'plugin_action_links_' . $this->_plugin_basename, array(
8521
				&$this,
8522
				'_modify_plugin_action_links_hook'
8523
			), WP_FS__DEFAULT_PRIORITY, 2 );
8524
			add_filter( 'network_admin_plugin_action_links_' . $this->_plugin_basename, array(
8525
				&$this,
8526
				'_modify_plugin_action_links_hook'
8527
			), WP_FS__DEFAULT_PRIORITY, 2 );
8528
		}
8529
8530
		/**
8531
		 * Add plugin action link.
8532
		 *
8533
		 * @author Vova Feldman (@svovaf)
8534
		 * @since  1.0.0
8535
		 *
8536
		 * @param      $label
8537
		 * @param      $url
8538
		 * @param bool $external
8539
		 * @param int  $priority
8540
		 * @param bool $key
8541
		 */
8542
		function add_plugin_action_link( $label, $url, $external = false, $priority = WP_FS__DEFAULT_PRIORITY, $key = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8543
			$this->_logger->entrance();
8544
8545
			if ( ! isset( $this->_action_links[ $priority ] ) ) {
8546
				$this->_action_links[ $priority ] = array();
8547
			}
8548
8549
			if ( false === $key ) {
8550
				$key = preg_replace( "/[^A-Za-z0-9 ]/", '', strtolower( $label ) );
8551
			}
8552
8553
			$this->_action_links[ $priority ][] = array(
8554
				'label'    => $label,
8555
				'href'     => $url,
8556
				'key'      => $key,
8557
				'external' => $external
8558
			);
8559
		}
8560
8561
		/**
8562
		 * Adds Upgrade and Add-Ons links to the main Plugins page link actions collection.
8563
		 *
8564
		 * @author Vova Feldman (@svovaf)
8565
		 * @since  1.0.0
8566
		 */
8567
		function _add_upgrade_action_link() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8568
			$this->_logger->entrance();
8569
8570
			if ( $this->is_registered() ) {
8571
				if ( ! $this->is_paying() && $this->has_paid_plan() ) {
8572
					$this->add_plugin_action_link(
8573
						__fs( 'upgrade', $this->_slug ),
8574
						$this->get_upgrade_url(),
8575
						false,
8576
						7,
8577
						'upgrade'
8578
					);
8579
				}
8580
8581
				if ( $this->has_addons() ) {
8582
					$this->add_plugin_action_link(
8583
						__fs( 'add-ons', $this->_slug ),
8584
						$this->_get_admin_page_url( 'addons' ),
8585
						false,
8586
						9,
8587
						'addons'
8588
					);
8589
				}
8590
			}
8591
		}
8592
8593
		/**
8594
		 * Get the URL of the page that should be loaded right after the plugin activation.
8595
		 *
8596
		 * @author Vova Feldman (@svovaf)
8597
		 * @since  1.1.7.4
8598
		 *
8599
		 * @return string
8600
		 */
8601
		function get_after_plugin_activation_redirect_url() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8602
			$url       = false;
8603
			$plugin_fs = false;
8604
8605
			if ( ! $this->is_addon() ) {
8606
				$first_time_path = $this->_menu->get_first_time_path();
8607
				$plugin_fs       = $this;
8608
				$url             = $plugin_fs->is_activation_mode() ?
8609
					$plugin_fs->get_activation_url() :
8610
					( empty( $first_time_path ) ?
8611
						$this->_get_admin_page_url() :
8612
						$first_time_path );
8613
			} else {
8614
				if ( $this->is_parent_plugin_installed() ) {
8615
					$plugin_fs = self::get_parent_instance();
8616
				}
8617
8618
				if ( is_object( $plugin_fs ) ) {
8619
					if ( ! $plugin_fs->is_registered() ) {
8620
						// Forward to parent plugin connect when parent not registered.
8621
						$url = $plugin_fs->get_activation_url();
8622
					} else {
8623
						// Forward to account page.
8624
						$url = $plugin_fs->_get_admin_page_url( 'account' );
8625
					}
8626
				}
8627
			}
8628
8629
			return $url;
8630
		}
8631
8632
		/**
8633
		 * Forward page to activation page.
8634
		 *
8635
		 * @author Vova Feldman (@svovaf)
8636
		 * @since  1.0.3
8637
		 */
8638
		function _redirect_on_activation_hook() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8639
			$url = $this->get_after_plugin_activation_redirect_url();
8640
8641
			if ( is_string( $url ) ) {
8642
				fs_redirect( $url );
8643
				exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method _redirect_on_activation_hook() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
8644
			}
8645
		}
8646
8647
		/**
8648
		 * Modify plugin's page action links collection.
8649
		 *
8650
		 * @author Vova Feldman (@svovaf)
8651
		 * @since  1.0.0
8652
		 *
8653
		 * @param array $links
8654
		 * @param       $file
8655
		 *
8656
		 * @return array
8657
		 */
8658
		function _modify_plugin_action_links_hook( $links, $file ) {
0 ignored issues
show
Unused Code introduced by
The parameter $file is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8659
			$this->_logger->entrance();
8660
8661
			$passed_deactivate = false;
8662
			$deactivate_link   = '';
8663
			$before_deactivate = array();
8664
			$after_deactivate  = array();
8665
			foreach ( $links as $key => $link ) {
8666
				if ( 'deactivate' === $key ) {
8667
					$deactivate_link   = $link;
8668
					$passed_deactivate = true;
8669
					continue;
8670
				}
8671
8672
				if ( ! $passed_deactivate ) {
8673
					$before_deactivate[ $key ] = $link;
8674
				} else {
8675
					$after_deactivate[ $key ] = $link;
8676
				}
8677
			}
8678
8679
			ksort( $this->_action_links );
8680
8681
			foreach ( $this->_action_links as $new_links ) {
8682
				foreach ( $new_links as $link ) {
8683
					$before_deactivate[ $link['key'] ] = '<a href="' . $link['href'] . '"' . ( $link['external'] ? ' target="_blank"' : '' ) . '>' . $link['label'] . '</a>';
8684
				}
8685
			}
8686
8687
			if ( ! empty( $deactivate_link ) ) {
8688
				if ( ! $this->is_paying_or_trial() || $this->is_premium() ) {
8689
					/*
8690
					 * This HTML element is used to identify the correct plugin when attaching an event to its Deactivate link.
8691
					 *
8692
					 * If user is paying or in trial and have the free version installed,
8693
					 * assume that the deactivation is for the upgrade process, so this is not needed.
8694
					 */
8695
					$deactivate_link .= '<i class="fs-slug" data-slug="' . $this->_slug . '"></i>';
8696
				}
8697
8698
				// Append deactivation link.
8699
				$before_deactivate['deactivate'] = $deactivate_link;
8700
			}
8701
8702
			return array_merge( $before_deactivate, $after_deactivate );
8703
		}
8704
8705
		/**
8706
		 * Adds admin message.
8707
		 *
8708
		 * @author Vova Feldman (@svovaf)
8709
		 * @since  1.0.4
8710
		 *
8711
		 * @param string $message
8712
		 * @param string $title
8713
		 * @param string $type
8714
		 */
8715
		function add_admin_message( $message, $title = '', $type = 'success' ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8716
			$this->_admin_notices->add( $message, $title, $type );
8717
		}
8718
8719
		/**
8720
		 * Adds sticky admin message.
8721
		 *
8722
		 * @author Vova Feldman (@svovaf)
8723
		 * @since  1.1.0
8724
		 *
8725
		 * @param string $message
8726
		 * @param string $id
8727
		 * @param string $title
8728
		 * @param string $type
8729
		 */
8730
		function add_sticky_admin_message( $message, $id, $title = '', $type = 'success' ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8731
			$this->_admin_notices->add_sticky( $message, $id, $title, $type );
8732
		}
8733
8734
		/* Plugin Auto-Updates (@since 1.0.4)
8735
		------------------------------------------------------------------------------------------------------------------*/
8736
		/**
8737
		 * @var string[]
8738
		 */
8739
		private static $_auto_updated_plugins;
8740
8741
		/**
8742
		 * @todo   TEST IF IT WORKS!!!
8743
		 *
8744
		 * Include plugins for automatic updates based on stored settings.
8745
		 *
8746
		 * @see    http://wordpress.stackexchange.com/questions/131394/how-do-i-exclude-plugins-from-getting-automatically-updated/131404#131404
8747
		 *
8748
		 * @author Vova Feldman (@svovaf)
8749
		 * @since  1.0.4
8750
		 *
8751
		 * @param bool   $update Whether to update (not used for plugins)
8752
		 * @param object $item   The plugin's info
8753
		 *
8754
		 * @return bool
8755
		 */
8756
		static function _include_plugins_in_auto_update( $update, $item ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8757
			// Before version 3.8.2 the $item was the file name of the plugin,
8758
			// while in 3.8.2 statistics were added (https://core.trac.wordpress.org/changeset/27905).
8759
			$by_slug = ( (int) str_replace( '.', '', get_bloginfo( 'version' ) ) >= 382 );
8760
8761
			if ( ! isset( self::$_auto_updated_plugins ) ) {
8762
				$plugins = self::$_accounts->get_option( 'plugins', array() );
8763
8764
				$identifiers = array();
8765
				foreach ( $plugins as $p ) {
8766
					/**
8767
					 * @var FS_Plugin $p
8768
					 */
8769
					if ( isset( $p->auto_update ) && $p->auto_update ) {
8770
						$identifiers[] = ( $by_slug ? $p->slug : plugin_basename( $p->file ) );
8771
					}
8772
				}
8773
8774
				self::$_auto_updated_plugins = $identifiers;
8775
			}
8776
8777
			if ( in_array( $by_slug ? $item->slug : $item, self::$_auto_updated_plugins ) ) {
8778
				return true;
8779
			}
8780
8781
			// Pass update decision to next filters
8782
			return $update;
8783
		}
8784
8785
		#region Versioning ------------------------------------------------------------------
8786
8787
		/**
8788
		 * Check if Freemius in SDK upgrade mode.
8789
		 *
8790
		 * @author Vova Feldman (@svovaf)
8791
		 * @since  1.0.9
8792
		 *
8793
		 * @return bool
8794
		 */
8795
		function is_sdk_upgrade_mode() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8796
			return isset( $this->_storage->sdk_upgrade_mode ) ?
8797
				$this->_storage->sdk_upgrade_mode :
0 ignored issues
show
Documentation introduced by
The property sdk_upgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
8798
				false;
8799
		}
8800
8801
		/**
8802
		 * Turn SDK upgrade mode off.
8803
		 *
8804
		 * @author Vova Feldman (@svovaf)
8805
		 * @since  1.0.9
8806
		 *
8807
		 * @return bool
8808
		 */
8809
		function set_sdk_upgrade_complete() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8810
			$this->_storage->sdk_upgrade_mode = false;
0 ignored issues
show
Documentation introduced by
The property sdk_upgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
8811
		}
8812
8813
		/**
8814
		 * Check if plugin upgrade mode.
8815
		 *
8816
		 * @author Vova Feldman (@svovaf)
8817
		 * @since  1.0.9
8818
		 *
8819
		 * @return bool
8820
		 */
8821
		function is_plugin_upgrade_mode() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8822
			return isset( $this->_storage->plugin_upgrade_mode ) ?
8823
				$this->_storage->plugin_upgrade_mode :
0 ignored issues
show
Documentation introduced by
The property plugin_upgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
8824
				false;
8825
		}
8826
8827
		/**
8828
		 * Turn plugin upgrade mode off.
8829
		 *
8830
		 * @author Vova Feldman (@svovaf)
8831
		 * @since  1.0.9
8832
		 *
8833
		 * @return bool
8834
		 */
8835
		function set_plugin_upgrade_complete() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8836
			$this->_storage->plugin_upgrade_mode = false;
0 ignored issues
show
Documentation introduced by
The property plugin_upgrade_mode does not exist on object<FS_Key_Value_Storage>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
8837
		}
8838
8839
		#endregion ------------------------------------------------------------------
8840
8841
		#region Permissions ------------------------------------------------------------------
8842
8843
		/**
8844
		 * Check if specific permission requested.
8845
		 *
8846
		 * @author Vova Feldman (@svovaf)
8847
		 * @since  1.1.6
8848
		 *
8849
		 * @param string $permission
8850
		 *
8851
		 * @return bool
8852
		 */
8853
		function is_permission_requested( $permission ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8854
			return isset( $this->_permissions[ $permission ] ) && ( true === $this->_permissions[ $permission ] );
8855
		}
8856
8857
		#endregion Permissions ------------------------------------------------------------------
8858
8859
		#region Marketing ------------------------------------------------------------------
8860
8861
		/**
8862
		 * Check if current user purchased any other plugins before.
8863
		 *
8864
		 * @author Vova Feldman (@svovaf)
8865
		 * @since  1.0.9
8866
		 *
8867
		 * @return bool
8868
		 */
8869
		function has_purchased_before() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8870
			// TODO: Implement has_purchased_before() method.
8871
		}
8872
8873
		/**
8874
		 * Check if current user classified as an agency.
8875
		 *
8876
		 * @author Vova Feldman (@svovaf)
8877
		 * @since  1.0.9
8878
		 *
8879
		 * @return bool
8880
		 */
8881
		function is_agency() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8882
			// TODO: Implement is_agency() method.
8883
		}
8884
8885
		/**
8886
		 * Check if current user classified as a developer.
8887
		 *
8888
		 * @author Vova Feldman (@svovaf)
8889
		 * @since  1.0.9
8890
		 *
8891
		 * @return bool
8892
		 */
8893
		function is_developer() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8894
			// TODO: Implement is_developer() method.
8895
		}
8896
8897
		/**
8898
		 * Check if current user classified as a business.
8899
		 *
8900
		 * @author Vova Feldman (@svovaf)
8901
		 * @since  1.0.9
8902
		 *
8903
		 * @return bool
8904
		 */
8905
		function is_business() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
8906
			// TODO: Implement is_business() method.
8907
		}
8908
8909
		#endregion ------------------------------------------------------------------
8910
	}