Issues (234)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/functions/shared.php (7 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Shared functions
4
 *
5
 * Functions shared by both back end and front end components.
6
 *
7
 * @package SimpleCalendar/Functions
8
 */
9
10
if ( ! defined( 'ABSPATH' ) ) {
11
	exit;
12
}
13
14
/**
15
 * Check if there is a calendar.
16
 *
17
 * @since  3.0.0
18
 *
19
 * @return bool
20
 */
21
function is_simple_calendar() {
22
23
	if ( is_singular() ) {
24
25
		global $post, $post_type;
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...
26
27
		if ( 'calendar' == $post_type ) {
28
			return true;
29
		} else {
30
			if ( false !== get_post_meta( $post->ID, '_simcal_attach_calendar_id', true ) ) {
31
				return true;
32
			}
33
			if ( has_shortcode( $post->post_content, 'calendar' ) ) {
34
				return true;
35
			}
36
		}
37
	}
38
39
	return false;
40
}
41
42
/**
43
 * Get plugin URL.
44
 *
45
 * @param  string $url
46
 *
47
 * @return string
48
 */
49
function simcal_get_url( $url ) {
50
	return \SimpleCalendar\plugin()->get_url( $url );
51
}
52
53
/**
54
 * Get events feed types.
55
 *
56
 * @since  3.0.0
57
 *
58
 * @return array
59
 */
60
function simcal_get_feed_types() {
61
	$objects = \SimpleCalendar\plugin()->objects;
62
	return $objects instanceof \SimpleCalendar\Objects ? $objects->get_feed_types() : array();
63
}
64
65
/**
66
 * Get an events feed.
67
 *
68
 * @since  3.0.0
69
 *
70
 * @param  string|int|object $object
71
 *
72
 * @return null|\SimpleCalendar\Abstracts\Feed
0 ignored issues
show
Should the return type not be object|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
73
 */
74
function simcal_get_feed( $object ) {
75
	$objects = \SimpleCalendar\plugin()->objects;
76
	return $objects instanceof \SimpleCalendar\Objects ? $objects->get_feed( $object ) : null;
77
}
78
79
/**
80
 * Get calendar types.
81
 *
82
 * @since  3.0.0
83
 *
84
 * @return array
85
 */
86
function simcal_get_calendar_types() {
87
	$objects = \SimpleCalendar\plugin()->objects;
88
	return $objects instanceof \SimpleCalendar\Objects ? $objects->get_calendar_types() : array();
89
}
90
91
/**
92
 * Get a calendar.
93
 *
94
 * @since  3.0.0
95
 *
96
 * @param  string|int|object|WP_Post $object
97
 *
98
 * @return null|\SimpleCalendar\Abstracts\Calendar
0 ignored issues
show
Should the return type not be object|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
99
 */
100
function simcal_get_calendar( $object ) {
101
	$objects = \SimpleCalendar\plugin()->objects;
102
	return $objects instanceof \SimpleCalendar\Objects ? $objects->get_calendar( $object ) : null;
103
}
104
105
/**
106
 * Get a calendar view.
107
 *
108
 * @since  3.0.0
109
 *
110
 * @param  int    $id
111
 * @param  string $name
112
 *
113
 * @return mixed
114
 */
115
function simcal_get_calendar_view( $id = 0, $name = '' ) {
116
	$objects = \SimpleCalendar\plugin()->objects;
117
	return $objects instanceof \SimpleCalendar\Objects ? $objects->get_calendar_view( $id, $name ) : false;
118
}
119
120
/**
121
 * Print a calendar.
122
 *
123
 * @since  3.0.0
124
 *
125
 * @param  int|object|WP_Post $object
126
 *
127
 * @return void
128
 */
129
function simcal_print_calendar( $object ) {
130
131
	$calendar = simcal_get_calendar( $object );
132
133
	if ( $calendar instanceof \SimpleCalendar\Abstracts\Calendar ) {
134
		$calendar->html();
135
	}
136
}
137
138
/**
139
 * Common scripts variables.
140
 *
141
 * Variables to print in scripts localization
142
 *
143
 * @since  3.0.0
144
 *
145
 * @return array
146
 */
147
function simcal_common_scripts_variables() {
148
149
	$vars = array(
150
		'ajax_url'  => \SimpleCalendar\plugin()->ajax_url(),
151
		'nonce'     => wp_create_nonce( 'simcal' ),
152
		'locale'    => \SimpleCalendar\plugin()->locale,
153
		'text_dir'  => is_rtl() ? 'rtl' : 'ltr',
154
		'months'    => array(
155
			'full'  => simcal_get_calendar_names_i18n( 'month', 'full' ),
156
			'short' => simcal_get_calendar_names_i18n( 'month', 'short' ),
157
		),
158
		'days'      => array(
159
			'full'  => simcal_get_calendar_names_i18n( 'day', 'full' ),
160
			'short' => simcal_get_calendar_names_i18n( 'day', 'short' ),
161
		),
162
		'meridiem' => simcal_get_calendar_names_i18n( 'meridiem' ),
163
	);
164
165
	return array_merge( $vars, apply_filters( 'simcal_common_scripts_variables', array() ) );
166
}
167
168
/**
169
 * Get feed IDs and names.
170
 *
171
 * @since  3.0.0
172
 *
173
 * @param  string|int|array $exclude Id or array of ids to drop from results.
174
 * @param  bool $cached Use cached query.
175
 *
176
 * @return array Associative array with ids as keys and feed titles as values.
177
 */
178
function simcal_get_calendars( $exclude = '', $cached = true ) {
179
180
	$calendars = get_transient( '_simple-calendar_feed_ids' );
181
182
	if ( ! $calendars || $cached === false ) {
183
184
		$posts = get_posts( array(
185
			'post_type' => 'calendar',
186
			'nopaging'  => true,
187
		) );
188
189
		$calendars = array();
190
		foreach ( $posts as $post ) {
191
			$calendars[ $post->ID ] = $post->post_title;
192
		}
193
		asort( $calendars );
194
195
		set_transient( '_simple-calendar_feed_ids', $calendars, 604800 );
196
	}
197
198
	if ( ! empty( $exclude ) ) {
199
		if ( is_numeric( $exclude ) ) {
200
			unset( $calendars[ intval( $exclude ) ] );
201
		} elseif ( is_array( $exclude ) ) {
202
			array_diff_key( $calendars, array_map( 'intval', array_keys( $exclude ) ) );
203
		}
204
	}
205
206
	return $calendars;
207
}
208
209
/**
210
 * Get localized list of months or day names.
211
 *
212
 * Each day or month matches the array index (0-11 for months or 0-6 for days).
213
 *
214
 * @since  3.0.0
215
 *
216
 * @param  string $group Either 'month', 'day' or 'meridiem' names to localize.
217
 * @param  string $style Return names in 'short' or 'full' form (default full long form).
218
 *
219
 * @return array
220
 */
221
function simcal_get_calendar_names_i18n( $group, $style = 'full' ) {
222
223
	$names = array();
224
225
	if ( in_array( $group, array( 'month', 'day', 'meridiem' ) ) ) {
226
227
		$format = '';
228
		$length = 0;
229
230
		$date = \Carbon\Carbon::now();
231
232
		if ( 'month' == $group ) {
233
			$date->month( 0 )->startOfMonth();
234
			$format = 'short' == $style ? 'M' : 'F';
235
			$length = 11;
236
		} elseif ( 'day' == $group ) {
237
			$date->next( 6 );
238
			$format = 'short' == $style ? 'D' : 'l';
239
			$length = 6;
240
		} elseif ( 'meridiem' == $group ) {
241
			$date->startOfDay();
242
			$am = $date->addHour( 1 )->getTimestamp();
243
			$pm = $date->addHours( 13 )->getTimestamp();
244
			return array(
245
				'AM' => date_i18n( 'A', $am ),
246
				'am' => date_i18n( 'a', $am ),
247
				'PM' => date_i18n( 'A', $pm ),
248
				'pm' => date_i18n( 'a', $pm ),
249
			);
250
		}
251
252
		$i = 0;
253
		while ( $i <= $length ) {
254
			if ( 'month' == $group ) {
255
				$date->addMonths( 1 );
256
			} else {
257
				$date->addDays( 1 );
258
			}
259
			$names[ strval( $i ) ] = date_i18n( $format, $date->getTimestamp() );
260
			$i++;
261
		}
262
263
	}
264
265
	return $names;
266
}
267
268
/**
269
 * Default event template.
270
 *
271
 * @since  3.0.0
272
 *
273
 * @return string
274
 */
275
function simcal_default_event_template() {
276
277
	$content  = '<strong>' . '[title]' . '</strong>';
278
	$content .= "\n\n";
279
	$content .= '[when]' . "\n";
280
	$content .= '[location]';
281
	$content .= "\n";
282
	$content .= '<div>' . '[description]' . '</div>';
283
	$content .= "\n" . '[link newwindow="yes"]' . __( 'See more details', 'google-calendar-events' ) . '[/link]';
284
285
	return apply_filters( 'simcal_default_event_template', $content );
286
}
287
288
/**
289
 * Get day, month, year order in a datetime format string.
290
 *
291
 * Returns an array with d, m, y for keys and a order number.
292
 * If either d, m, y is not found in date format, order value is false.
293
 *
294
 * @since  3.0.0
295
 *
296
 * @param  string $date_format
297
 *
298
 * @return array
299
 */
300
function simcal_get_date_format_order( $date_format ) {
301
302
	$pos = array(
303
		'd' => strpos( $date_format, strpbrk( $date_format, 'Dj' ) ),
304
		'm' => strpos( $date_format, strpbrk( $date_format, 'FMmn' ) ),
305
		'y' => strpos( $date_format, strpbrk( $date_format, 'Yy' ) ),
306
	);
307
308
	// @TODO When one date piece is not found, perhaps fallback to ISO standard position.
309
310
	$order = array();
311
	foreach ( $pos as $k => $v ) {
312
		$order[ $k ] = $v;
313
	}
314
	ksort( $order );
315
316
	return $order;
317
}
318
319
/**
320
 * Get WordPress timezone setting.
321
 *
322
 * Always returns a valid timezone string even when the setting is a GMT offset.
323
 *
324
 * @since  3.0.0
325
 *
326
 * @return null|string
327
 */
328
function simcal_get_wp_timezone() {
329
330
	$timezone = get_option( 'timezone_string' );
331
332
	if ( empty( $timezone ) ) {
333
		$gmt = get_option( 'gmt_offset' );
334
		$timezone = simcal_get_timezone_from_gmt_offset( $gmt );
335
	}
336
337
	return $timezone;
338
}
339
340
/**
341
 * Get a timezone from a GMT offset.
342
 *
343
 * Converts a numeric offset into a valid timezone string.
344
 *
345
 * @since  3.0.0
346
 *
347
 * @param  string|float $offset
348
 *
349
 * @return null|string
350
 */
351
function simcal_get_timezone_from_gmt_offset( $offset ) {
352
353
	if ( is_numeric( $offset ) ) {
354
355
		if ( 0 === intval( $offset ) ) {
356
			return 'UTC';
357
		} else {
358
			$offset = floatval( $offset ) * 3600;
359
		}
360
361
		$timezone = timezone_name_from_abbr( null, $offset, false );
362
		// This is buggy and might return false:
363
		// @see http://php.net/manual/en/function.timezone-name-from-abbr.php#86928
364
		// Therefore:
365
		if ( false == $timezone ) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $timezone of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
366
367
			$list = timezone_abbreviations_list();
368
			foreach ( $list as $abbr ) {
369
				foreach ( $abbr as $city ) {
370
					if ( $offset == $city['offset'] ) {
371
						return $city['timezone_id'];
372
					}
373
				}
374
			}
375
376
		}
377
378
		return $timezone;
379
	}
380
381
	return null;
382
}
383
384
/**
385
 * Convert a timezone string to a numeric offset.
386
 *
387
 * @since  3.0.0
388
 *
389
 * @param  string $timezone
390
 *
391
 * @return int Unix time offset
392
 */
393
function simcal_get_timezone_offset( $timezone ) {
394
	return \Carbon\Carbon::now( $timezone )->offset;
395
}
396
397
/**
398
 * Escape timezone string.
399
 *
400
 * @since  3.0.0
401
 *
402
 * @param  string $tz
403
 * @param  mixed  $default
404
 *
405
 * @return mixed|string
406
 */
407
function simcal_esc_timezone( $tz, $default = 'UTC' ) {
408
	return in_array( $tz, timezone_identifiers_list() ) ? $tz : $default;
409
}
410
411
/**
412
 * Clear feed transients cache.
413
 *
414
 * @since  3.0.0
415
 *
416
 * @param  string|int|array|\WP_Post $id
417
 *
418
 * @return bool
419
 */
420
function simcal_delete_feed_transients( $id = '' ) {
421
422
	$grouped_ids = get_post_meta( $id, '_grouped_calendars_ids', true );
423
424
	// If there are group IDs we need to construct an array to pass along with the grouped IDs + the original $post_id
425
	if ( is_array( $grouped_ids ) ) {
426
		$temp_id = $id;
427
		$id = $grouped_ids;
428
		$id[] = $temp_id;
429
	}
430
431
	if ( is_numeric( $id ) ) {
432
		$id = intval( $id ) > 0 ? absint( $id ) : simcal_get_calendars();
433
	} elseif ( $id instanceof WP_Post ) {
0 ignored issues
show
The class WP_Post 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...
434
		$id = $id->ID;
435
	} elseif ( is_array( $id ) ) {
436
		$id = array_map( 'absint', $id );
437
	} else {
438
		$id = simcal_get_calendars( '', true );
439
	}
440
441
	$feed_types = simcal_get_feed_types();
442
443
	if ( is_array( $id ) ) {
444
445
		$posts = get_posts( array(
446
				'post_type' => 'calendar',
447
				'fields'    => 'ids',
448
				'post__in'  => $id,
449
				'nopaging'  => true,
450
		) );
451
452
		foreach ( $posts as $post ) {
453
			$calendar = simcal_get_calendar( $post );
454 View Code Duplication
			if ( $calendar instanceof \SimpleCalendar\Abstracts\Calendar ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
455
				foreach ( $feed_types as $feed_type ) {
456
					delete_transient( '_simple-calendar_feed_id_' . strval( $calendar->id ) . '_' . $feed_type );
457
				}
458
			}
459
		}
460
461
	} else {
462
463
		$post = get_post( $id );
464
		$calendar = simcal_get_calendar( $post );
465 View Code Duplication
		if ( $calendar instanceof \SimpleCalendar\Abstracts\Calendar ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
466
			foreach ( $feed_types as $feed_type ) {
467
				delete_transient( '_simple-calendar_feed_id_' . strval( $calendar->id ) . '_' . $feed_type );
468
			}
469
		}
470
	}
471
472
	return delete_transient( '_simple-calendar_feed_ids' );
473
}