GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( eee123...78b81a )
by Christian
11s
created

inc/Model/Base.php (2 issues)

Severity
1
<?php
0 ignored issues
show
Filenames should be all lowercase with hyphens as word separators. Expected base.php, but found Base.php.
Loading history...
Class file names should be based on the class name with "class-" prepended. Expected class-base.php, but found Base.php.
Loading history...
2
/**
3
 * @author    Podlove <[email protected]>
4
 * @copyright Copyright (c) 2014-2018, Podlove
5
 * @license   https://github.com/podlove/podlove-subscribe-button-wp-plugin/blob/master/LICENSE MIT
6
 * @package   Podlove\PodloveSubscribeButton
7
 */
8
9
namespace PodloveSubscribeButton\Model;
10
11
abstract class Base {
12
	/**
13
	 * Property dictionary for all tables
14
	 */
15
	private static $properties = array();
16
17
	private $is_new = true;
18
19
	/**
20
	 * Contains property values
21
	 */
22
	private $data = array();
23
24
	public function __set( $name, $value ) {
25
		if ( static::has_property( $name ) ) {
26
			$this->set_property( $name, $value );
27
		} else {
28
			$this->$name = $value;
29
		}
30
	}
31
32
	private function set_property( $name, $value ) {
33
		$this->data[ $name ] = $value;
34
	}
35
36
	public function __get( $name ) {
37
		if ( static::has_property( $name ) ) {
38
			return $this->get_property( $name );
39
		} elseif ( property_exists( $this, $name ) ) {
40
			return $this->$name;
41
		} else {
42
			return null;
43
		}
44
	}
45
46
	private function get_property( $name ) {
47
		if ( isset( $this->data[ $name ] ) ) {
48
			return $this->data[ $name ];
49
		} else {
50
			return null;
51
		}
52
	}
53
54
	private static function unserialize_property( $property ) {
55
		if ( ! isset( $property ) )
56
			return;
57
58
		if ( $unserialized_string = is_serialized( $property ) )
59
			return unserialize( $property );
60
61
		return $property;
62
	}
63
64
	/**
65
	 * Retrieves the database table name.
66
	 *
67
	 * The name is derived from the namespace an class name. Additionally, it
68
	 * is prefixed with the global WordPress database table prefix.
69
	 * @todo cache
70
	 *
71
	 * @return string database table name
72
	 */
73
	public static function table_name() {
74
		global $wpdb;
75
76
		// prefix with $wpdb prefix
77
		return $wpdb->prefix . static::name();
78
	}
79
80
	/**
81
	 * Define a property with name and type.
82
	 *
83
	 * Currently only supports basics.
84
	 * @todo enable additional options like NOT NULL, DEFAULT etc.
85
	 *
86
	 * @param string $name Name of the property / column
87
	 * @param string $type mySQL column type
88
	 */
89
	public static function property( $name, $type, $args = array() ) {
90
		$class = get_called_class();
91
92
		if ( ! isset( static::$properties[ $class ] ) ) {
93
			static::$properties[ $class ] = array();
94
		}
95
96
		// "id" columns and those ending on "_id" get an index by default
97
		$index = $name == 'id' || stripos( $name, '_id' );
98
		// but if the argument is set, it overrides the default
99
		if ( isset( $args[ 'index' ] ) ) {
100
			$index = $args[ 'index' ];
101
		}
102
103
		static::$properties[ $class ][ ] = array(
104
			'name'  => $name,
105
			'type'  => $type,
106
			'index' => $index,
107
			'index_length' => isset( $args[ 'index_length' ] ) ? $args[ 'index_length' ] : null,
108
			'unique' => isset( $args[ 'unique' ] ) ? $args[ 'unique' ] : null
109
		);
110
	}
111
112
	/**
113
	 * Return a list of property dictionaries.
114
	 *
115
	 * @return array property list
116
	 */
117
	private static function properties() {
118
		$class = get_called_class();
119
120
		if ( ! isset( static::$properties[ $class ] ) ) {
121
			static::$properties[ $class ] = array();
122
		}
123
124
		return static::$properties[ $class ];
125
	}
126
127
	/**
128
	 * Does the given property exist?
129
	 *
130
	 * @param string $name name of the property to test
131
	 * @return bool True if the property exists, else false.
132
	 */
133
	public static function has_property( $name ) {
134
		return in_array( $name, static::property_names() );
135
	}
136
137
	/**
138
	 * Return a list of property names.
139
	 *
140
	 * @return array property names
141
	 */
142
	public static function property_names() {
143
		return array_map( function( $p ) { return $p[ 'name' ]; } , static::properties() );
144
	}
145
146
	/**
147
	 * Does the table have any entries?
148
	 *
149
	 * @return bool True if there is at least one entry, else false.
150
	 */
151
	public static function has_entries() {
152
		return static::count() > 0;
153
	}
154
155
	/**
156
	 * Return number of rows in the table.
157
	 *
158
	 * @return int number of rows
159
	 */
160
	public static function count() {
161
		global $wpdb;
162
163
		$sql = 'SELECT COUNT(*) FROM ' . static::table_name();
164
		return (int) $wpdb->get_var( $sql );
165
	}
166
167
	public static function find_by_id( $id ) {
168
		global $wpdb;
169
170
		$class = get_called_class();
171
		$model = new $class();
172
		$model->flag_as_not_new();
173
174
		$row = $wpdb->get_row( 'SELECT * FROM ' . static::table_name() . ' WHERE id = ' . (int) $id );
175
176
		if ( ! $row ) {
177
			return null;
178
		}
179
180
		foreach ( $row as $property => $value ) {
181
			$model->$property = static::unserialize_property( $value );
182
		}
183
184
		return $model;
185
	}
186
187
	public static function find_all_by_property( $property, $value ) {
188
		global $wpdb;
189
190
		$class = get_called_class();
191
		$models = array();
192
193
		$rows = $wpdb->get_results(
194
			'SELECT * FROM ' . static::table_name() . ' WHERE ' . $property . ' = \'' . $value . '\''
195
		);
196
197
		if ( ! $rows ) {
198
			return array();
199
		}
200
201
		foreach ( $rows as $row ) {
202
			$model = new $class();
203
			$model->flag_as_not_new();
204
			foreach ( $row as $property => $value ) {
205
				$model->$property = static::unserialize_property( $value );
206
			}
207
			$models[ ] = $model;
208
		}
209
210
		return $models;
211
	}
212
213
	public static function find_one_by_property( $property, $value ) {
214
		global $wpdb;
215
216
		$class = get_called_class();
217
		$model = new $class();
218
		$model->flag_as_not_new();
219
220
		$row = $wpdb->get_row(
221
			'SELECT * FROM ' . static::table_name() . ' WHERE ' . $property . ' = \'' . $value . '\' LIMIT 0,1'
222
		);
223
224
		if ( ! $row ) {
225
			return null;
226
		}
227
228
		foreach ( $row as $property => $value ) {
229
			$model->$property = static::unserialize_property( $value );
230
		}
231
232
		return $model;
233
	}
234
235
	public static function find_all_by_where( $where ) {
236
		global $wpdb;
237
238
		$class = get_called_class();
239
		$models = array();
240
241
		$rows = $wpdb->get_results(
242
			'SELECT * FROM ' . static::table_name() . ' WHERE ' . $where
243
		);
244
245
		if ( ! $rows ) {
246
			return array();
247
		}
248
249
		foreach ( $rows as $row ) {
250
			$model = new $class();
251
			$model->flag_as_not_new();
252
			foreach ( $row as $property => $value ) {
253
				$model->$property = static::unserialize_property( $value );
254
			}
255
			$models[ ] = $model;
256
		}
257
258
		return $models;
259
	}
260
261
	public static function find_one_by_where( $where ) {
262
		global $wpdb;
263
264
		$class = get_called_class();
265
		$model = new $class();
266
		$model->flag_as_not_new();
267
268
		$row = $wpdb->get_row(
269
			'SELECT * FROM ' . static::table_name() . ' WHERE ' . $where . ' LIMIT 0,1'
270
		);
271
272
		if ( ! $row ) {
273
			return null;
274
		}
275
276
		foreach ( $row as $property => $value ) {
277
			$model->$property = static::unserialize_property( $value );
278
		}
279
280
		return $model;
281
	}
282
	/**
283
	 * Retrieve all entries from the table.
284
	 *
285
	 * @param  string $sql_suffix optional SQL, appended after FROM clause
286
	 * @return array list of Model objects
287
	 */
288
	public static function all( $sql_suffix = '' ) {
289
		global $wpdb;
290
291
		$class = get_called_class();
292
		$models = array();
293
294
		$rows = $wpdb->get_results( 'SELECT * FROM ' . static::table_name() . ' ' . $sql_suffix );
295
296
		foreach ( $rows as $row ) {
297
			$model = new $class();
298
			$model->flag_as_not_new();
299
			foreach ( $row as $property => $value ) {
300
				$model->$property = static::unserialize_property( $value );
301
			}
302
			$models[ ] = $model;
303
		}
304
305
		return $models;
306
	}
307
308
	/**
309
	 * True if not yet saved to database. Else false.
310
	 */
311
	public function is_new() {
312
		return $this->is_new;
313
	}
314
315
	public function flag_as_not_new() {
316
		$this->is_new = false;
317
	}
318
319
	/**
320
	 * Rails-ish update_attributes for easy form handling.
321
	 *
322
	 * Takes an array of form values and takes care of serializing it.
323
	 *
324
	 * @param  array $attributes
325
	 * @return bool
326
	 */
327
	public function update_attributes( $attributes ) {
328
329
		if ( ! is_array( $attributes ) )
330
			return false;
331
332
		$request = filter_input_array( INPUT_POST ); // Do this for security reasons
333
334
		foreach ( $attributes as $key => $value ) {
335
			if ( is_array( $value ) ) {
336
				$this->{$key} = serialize( $value );
337
			} else {
338
				$this->{$key} = esc_sql( $value );
339
			}
340
		}
341
342
		if ( isset( $request[ 'checkboxes' ] ) && is_array( $request[ 'checkboxes' ] ) ) {
343
			foreach ( $request[ 'checkboxes' ] as $checkbox ) {
344
				if ( isset( $attributes[ $checkbox ] ) && $attributes[ $checkbox ] === 'on' ) {
345
					$this->$checkbox = 1;
346
				} else {
347
					$this->$checkbox = 0;
348
				}
349
			}
350
		}
351
352
		// @todo this is the wrong place to do this!
353
		// The feed password is the only "passphrase" which is saved. It is not encrypted!
354
		// However, we keep this function for later use
355
		if ( isset( $request[ 'passwords' ] ) && is_array( $request[ 'passwords' ] ) ) {
356
			foreach ( $request[ 'passwords' ] as $password ) {
357
				$this->$password = $attributes[ $password ];
358
			}
359
		}
360
		return $this->save();
361
	}
362
363
	/**
364
	 * Update and save a single attribute.
365
	 *
366
	 * @param  string $attribute attribute name
367
	 * @param  mixed  $value
368
	 * @return (bool) query success
369
	 */
370
	public function update_attribute( $attribute, $value ) {
371
		global $wpdb;
372
373
		$this->$attribute = $value;
374
375
		$sql = sprintf(
376
			"UPDATE %s SET %s = '%s' WHERE id = %s",
377
			static::table_name(),
378
			$attribute,
379
			mysqli_real_escape_string( $value ),
380
			$this->id
381
		);
382
383
		return $wpdb->query( $sql );
384
	}
385
386
	/**
387
	 * Saves changes to database.
388
	 *
389
	 * @todo use wpdb::insert()
390
	 */
391
	public function save() {
392
		global $wpdb;
393
394
		if ( $this->is_new() ) {
395
396
			$this->set_defaults();
397
398
			$sql = 'INSERT INTO '
399
			     . static::table_name()
400
			     . ' ( '
401
			     . implode( ',', static::property_names() )
402
			     . ' ) '
403
			     . 'VALUES'
404
			     . ' ( '
405
			     . implode( ',', array_map( array( $this, 'property_name_to_sql_value' ), static::property_names() ) )
406
			     . ' );'
407
			;
408
			$success = $wpdb->query( $sql );
409
			if ( $success ) {
410
				$this->id = $wpdb->insert_id;
411
			}
412
		} else {
413
			$sql = 'UPDATE ' . static::table_name()
414
			     . ' SET '
415
			     . implode( ',', array_map( array( $this, 'property_name_to_sql_update_statement' ), static::property_names() ) )
416
			     . ' WHERE id = ' . $this->id
417
			;
418
419
			$success = $wpdb->query( $sql );
420
		}
421
422
		$this->is_new = false;
423
424
		do_action( 'podlove_model_save', $this );
425
		do_action( 'podlove_model_change', $this );
426
427
		return $success;
428
	}
429
430
	/**
431
	 * Sets default values.
432
	 *
433
	 * @return array
434
	 */
435
	private function set_defaults() {
436
437
		$defaults = $this->default_values();
438
439
		if ( ! is_array( $defaults ) || empty( $defaults ) )
440
			return;
441
442
		foreach ( $defaults as $property => $value ) {
443
			if ( $this->$property === null )
444
				$this->$property = $value;
445
		}
446
447
	}
448
449
	/**
450
	 * Return default values for properties.
451
	 *
452
	 * Can be overridden by inheriting Model classes.
453
	 *
454
	 * @return array
455
	 */
456
	public function default_values() {
457
		return array();
458
	}
459
460
	public function delete() {
461
		global $wpdb;
462
463
		$sql = 'DELETE FROM '
464
		     . static::table_name()
465
		     . ' WHERE id = ' . $this->id;
466
467
		$rows_affected = $wpdb->query( $sql );
468
469
	    do_action( 'podlove_model_delete', $this );
470
	    do_action( 'podlove_model_change', $this );
471
472
		return $rows_affected !== false;
473
	}
474
475
	private function property_name_to_sql_update_statement( $p ) {
476
		global $wpdb;
477
478
		if ( $this->$p !== null && $this->$p !== '' ) {
479
			return sprintf( "%s = '%s'", $p, ( is_array( $this->$p ) ? serialize( $this->$p ) : $this->$p ) );
480
		} else {
481
			return "$p = NULL";
482
		}
483
	}
484
485
	private function property_name_to_sql_value( $p ) {
486
		global $wpdb;
487
488
		if ( $this->$p !== null && $this->$p !== '' ) {
489
			return sprintf( "'%s'", $this->$p );
490
		} else {
491
			return 'NULL';
492
		}
493
	}
494
495
	/**
496
	 * Create database table based on defined properties.
497
	 *
498
	 * Automatically includes an id column as auto incrementing primary key.
499
	 * @todo allow Model changes
500
	 */
501
	public static function build() {
502
		global $wpdb;
503
504
		$property_sql = array();
505
		foreach ( static::properties() as $property )
506
			$property_sql[ ] = "`{$property[ 'name' ]}` {$property[ 'type' ]}";
507
508
		$sql = 'CREATE TABLE IF NOT EXISTS '
509
		     . static::table_name()
510
		     . ' ('
511
		     . implode( ',', $property_sql )
512
		     . ' ) CHARACTER SET utf8;'
513
		;
514
515
		$wpdb->query( $sql );
516
517
		static::build_indices();
518
	}
519
520
	/**
521
	 * Convention based index generation.
522
	 *
523
	 * Creates default indices for all columns matching both:
524
	 * - equals "id" or contains "_id"
525
	 * - doesn't have an index yet
526
	 */
527
	public static function build_indices() {
528
		global $wpdb;
529
530
		$indices_sql = 'SHOW INDEX FROM `' . static::table_name() . '`';
531
		$indices = $wpdb->get_results( $indices_sql );
532
		$index_columns = array_map( function( $index ) { return $index->Column_name; }, $indices );
533
534
		foreach ( static::properties() as $property ) {
535
536
			if ( $property[ 'index' ] && ! in_array( $property[ 'name' ], $index_columns ) ) {
537
				$length = isset( $property[ 'index_length' ] ) ? '(' . (int) $property[ 'index_length' ] . ')' : '';
538
				$unique = isset( $property[ 'unique' ] ) && $property[ 'unique' ] ? 'UNIQUE' : '';
539
				$sql = 'ALTER TABLE `' . static::table_name() . '` ADD ' . $unique . ' INDEX `' . $property[ 'name' ] . '` (' . $property[ 'name' ] . $length . ')';
540
				$wpdb->query( $sql );
541
			}
542
		}
543
	}
544
545
	/**
546
	 * Model identifier.
547
	 */
548
	public static function name() {
549
		// get name of implementing class
550
		$table_name = get_called_class();
551
		// replace backslashes from namespace by underscores
552
		$table_name = str_replace( '\\', '_', $table_name );
553
		// remove Models subnamespace from name
554
		$table_name = str_replace( 'Model_', '', $table_name );
555
		// all lowercase
556
		$table_name = strtolower( $table_name );
557
558
		return $table_name;
559
	}
560
}