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.

Fluid::load()   B
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 31
Code Lines 21

Duplication

Lines 31
Ratio 100 %

Importance

Changes 0
Metric Value
cc 5
eloc 21
nc 7
nop 2
dl 31
loc 31
rs 8.439
c 0
b 0
f 0
1
<?php 
2
3
namespace RedBeanPHP {
4
5
/**
6
 * RedBean Logging interface.
7
 * Provides a uniform and convenient logging
8
 * interface throughout RedBeanPHP.
9
 *
10
 * @file    RedBean/Logging.php
11
 * @author  Gabor de Mooij and the RedBeanPHP Community
12
 * @license BSD/GPLv2
13
 *
14
 * @copyright
15
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
16
 * This source file is subject to the BSD/GPLv2 License that is bundled
17
 * with this source code in the file license.txt.
18
 */
19
interface Logger
20
{
21
22
	/**
23
	 * A logger (for\PDO or OCI driver) needs to implement the log method.
24
	 * The log method will receive logging data. Note that the number of parameters is 0, this means
25
	 * all parameters are optional and the number may vary. This way the logger can be used in a very
26
	 * flexible way. Sometimes the logger is used to log a simple error message and in other
27
	 * situations sql and bindings are passed.
28
	 * The log method should be able to accept all kinds of parameters and data by using
29
	 * functions like func_num_args/func_get_args.
30
	 * 
31
	 * @return void
32
	 */
33
	public function log();
34
}
35
} 
36
37
namespace RedBeanPHP\Logger {
38
39
use RedBeanPHP\Logger as Logger;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
40
use RedBeanPHP\RedException as RedException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
41
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
42
43
/**
44
 * Logger. Provides a basic logging function for RedBeanPHP.
45
 *
46
 * @file    RedBeanPHP/Logger.php
47
 * @author  Gabor de Mooij and the RedBeanPHP Community
48
 * @license BSD/GPLv2
49
 *
50
 * @copyright
51
 * copyright (c) G.J.G.T. (Gabor) de Mooij
52
 * This source file is subject to the BSD/GPLv2 License that is bundled
53
 * with this source code in the file license.txt.
54
 */
55
class RDefault implements Logger
0 ignored issues
show
Coding Style Compatibility introduced by
Each interface must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
56
{
57
	/**
58
	 * Logger modes
59
	 */
60
	const C_LOGGER_ECHO  = 0;
61
	const C_LOGGER_ARRAY = 1;
62
63
	/**
64
	 * @var integer
65
	 */
66
	protected $mode = 0;
67
68
	/**
69
	 * @var array
70
	 */
71
	protected $logs = array();
72
73
	/**
74
	 * Default logger method logging to STDOUT.
75
	 * This is the default/reference implementation of a logger.
76
	 * This method will write the message value to STDOUT (screen) unless
77
	 * you have changed the mode of operation to C_LOGGER_ARRAY.
78
	 *
79
	 * @param $message (optional) message to log (might also be data or output)
80
	 *
81
	 * @return void
82
	 */
83
	public function log()
84
	{
85
		if ( func_num_args() < 1 ) return;
86
87
		foreach ( func_get_args() as $argument ) {
88
			if ( is_array( $argument ) ) {
89
				$log = print_r( $argument, TRUE );
90
				if ( $this->mode === self::C_LOGGER_ECHO ) {
91
					echo $log;
92
				} else {
93
					$this->logs[] = $log;
94
				}
95
			} else {
96
				if ( $this->mode === self::C_LOGGER_ECHO ) {
97
					echo $argument;
98
				} else {
99
					$this->logs[] = $argument;
100
				}
101
			}
102
103
			if ( $this->mode === self::C_LOGGER_ECHO ) echo "<br>\n";
104
		}
105
	}
106
	
107
	/**
108
	 * Returns the internal log array.
109
	 * The internal log array is where all log messages are stored.
110
	 * 
111
	 * @return array
112
	 */
113
	public function getLogs()
114
	{
115
		return $this->logs;
116
	}
117
	
118
	/**
119
	 * Clears the internal log array, removing all
120
	 * previously stored entries.
121
	 * 
122
	 * @return self
123
	 */
124
	public function clear()
125
	{
126
		$this->logs = array();
127
		return $this;
128
	}
129
	
130
	/**
131
	 * Selects a logging mode.
132
	 * There are several options available.
133
	 * 
134
	 * C_LOGGER_ARRAY - log silently, stores entries in internal log array only
135
	 * C_LOGGER_ECHO  - also forward log messages directly to STDOUT
136
	 *  
137
	 * @param integer $mode mode of operation for logging object
138
	 * 
139
	 * @return self
140
	 */
141
	public function setMode( $mode )
142
	{
143
		if ($mode !== self::C_LOGGER_ARRAY && $mode !== self::C_LOGGER_ECHO ) {
144
			throw new RedException( 'Invalid mode selected for logger, use C_LOGGER_ARRAY or C_LOGGER_ECHO.' );
145
		}
146
		$this->mode = $mode;
147
		return $this;
148
	}
149
	
150
	/**
151
	 * Searches for all log entries in internal log array
152
	 * for $needle and returns those entries.
153
	 * This method will return an array containing all matches for your
154
	 * search query.
155
	 *
156
	 * @param string $needle phrase to look for in internal log array
157
	 *
158
	 * @return array
159
	 */
160
	public function grep( $needle )
161
	{
162
		$found = array();
163
		foreach( $this->logs as $logEntry ) {
164
			if ( strpos( $logEntry, $needle ) !== FALSE ) $found[] = $logEntry;
165
		}
166
		return $found;
167
	}
168
}
169
}
170
171
namespace RedBeanPHP\Logger\RDefault {
172
173
use RedBeanPHP\Logger as Logger;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
174
use RedBeanPHP\Logger\RDefault as RDefault;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
175
use RedBeanPHP\RedException as RedException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
176
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
177
178
/**
179
 * Debug logger.
180
 * A special logger for debugging purposes.
181
 * Provides debugging logging functions for RedBeanPHP.
182
 *
183
 * @file    RedBeanPHP/Logger/RDefault/Debug.php
184
 * @author  Gabor de Mooij and the RedBeanPHP Community
185
 * @license BSD/GPLv2
186
 *
187
 * @copyright
188
 * copyright (c) G.J.G.T. (Gabor) de Mooij
189
 * This source file is subject to the BSD/GPLv2 License that is bundled
190
 * with this source code in the file license.txt.
191
 */
192
class Debug extends RDefault implements Logger
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
193
{
194
	/**
195
	 * @var integer
196
	 */
197
	private $strLen = 40;
198
199
	/**
200
	 * Writes a query for logging with all bindings / params filled
201
	 * in.
202
	 *
203
	 * @param string $newSql   the query
204
	 * @param array  $bindings the bindings to process (key-value pairs)
0 ignored issues
show
Documentation introduced by
There is no parameter named $bindings. Did you maybe mean $newBindings?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
205
	 *
206
	 * @return string
207
	 */
208
	private function writeQuery( $newSql, $newBindings )
209
	{
210
		//avoid str_replace collisions: slot1 and slot10 (issue 407).
211
		uksort( $newBindings, function( $a, $b ) {
212
			return ( strlen( $b ) - strlen( $a ) );
213
		} );
214
215
		$newStr = $newSql;
216
		foreach( $newBindings as $slot => $value ) {
217
			if ( strpos( $slot, ':' ) === 0 ) {
218
				$newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr );
219
			}
220
		}
221
		return $newStr;
222
	}
223
224
	/**
225
	 * Fills in a value of a binding and truncates the
226
	 * resulting string if necessary.
227
	 *
228
	 * @param mixed $value
229
	 *
230
	 * @return string
231
	 */
232
	protected function fillInValue( $value )
233
	{
234
		if ( is_null( $value ) ) $value = 'NULL';
235
236
		$value = strval( $value );
237
		if ( strlen( $value ) > ( $this->strLen ) ) {
238
			$value = substr( $value, 0, ( $this->strLen ) ).'... ';
239
		}
240
241
		if ( !is_numeric( $value ) && $value !== 'NULL') {
242
			$value = '\''.$value.'\'';
243
		}
244
245
		return $value;
246
	}
247
248
	/**
249
	 * Dependending on the current mode of operation,
250
	 * this method will either log and output to STDIN or
251
	 * just log.
252
	 *
253
	 * @param string $str string to log or output and log
254
	 *
255
	 * @return void
256
	 */
257
	protected function output( $str )
258
	{
259
		$this->logs[] = $str;
260
		if ( !$this->mode ) echo $str .'<br />';
261
	}
262
263
	/**
264
	 * Normalizes the slots in an SQL string.
265
	 * Replaces question mark slots with :slot1 :slot2 etc.
266
	 *
267
	 * @param string $sql sql to normalize
268
	 *
269
	 * @return string
270
	 */
271
	protected function normalizeSlots( $sql )
272
	{
273
		$i = 0;
274
		$newSql = $sql;
275
		while($i < 20 && strpos($newSql, '?') !== FALSE ){
276
			$pos   = strpos( $newSql, '?' );
277
			$slot  = ':slot'.$i;
278
			$begin = substr( $newSql, 0, $pos );
279
			$end   = substr( $newSql, $pos+1 );
280
			$newSql = $begin . $slot . $end;
281
			$i++;
282
		}
283
		return $newSql;
284
	}
285
286
	/**
287
	 * Normalizes the bindings.
288
	 * Replaces numeric binding keys with :slot1 :slot2 etc.
289
	 *
290
	 * @param array $bindings bindings to normalize
291
	 *
292
	 * @return array
293
	 */
294
	protected function normalizeBindings( $bindings )
295
	{
296
		$i = 0;
297
		$newBindings = array();
298
		foreach( $bindings as $key => $value ) {
299
			if ( is_numeric($key) ) {
300
				$newKey = ':slot'.$i;
301
				$newBindings[$newKey] = $value;
302
				$i++;
303
			} else {
304
				$newBindings[$key] = $value;
305
			}
306
		}
307
		return $newBindings;
308
	}
309
310
	/**
311
	 * Logger method.
312
	 *
313
	 * Takes a number of arguments tries to create
314
	 * a proper debug log based on the available data.
315
	 *
316
	 * @return void
317
	 */
318
	public function log()
319
	{
320
		if ( func_num_args() < 1 ) return;
321
322
		$sql = func_get_arg( 0 );
323
324
		if ( func_num_args() < 2) {
325
			$bindings = array();
326
		} else {
327
			$bindings = func_get_arg( 1 );
328
		}
329
330
		if ( !is_array( $bindings ) ) {
331
			return $this->output( $sql );
332
		}
333
334
		$newSql = $this->normalizeSlots( $sql );
335
		$newBindings = $this->normalizeBindings( $bindings );
336
		$newStr = $this->writeQuery( $newSql, $newBindings );
337
		$this->output( $newStr );
338
	}
339
340
	/**
341
	 * Sets the max string length for the parameter output in
342
	 * SQL queries. Set this value to a reasonable number to
343
	 * keep you SQL queries readable.
344
	 *
345
	 * @param integer $len string length
346
	 *
347
	 * @return self
348
	 */
349
	public function setParamStringLength( $len = 20 )
350
	{
351
		$this->strLen = max(0, $len);
352
		return $this;
353
	}
354
}
355
} 
356
357
namespace RedBeanPHP {
358
359
/**
360
 * Interface for database drivers.
361
 * The Driver API conforms to the ADODB pseudo standard
362
 * for database drivers.
363
 *
364
 * @file       RedBeanPHP/Driver.php
365
 * @author     Gabor de Mooij and the RedBeanPHP Community
366
 * @license    BSD/GPLv2
367
 *
368
 * @copyright
369
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
370
 * This source file is subject to the BSD/GPLv2 License that is bundled
371
 * with this source code in the file license.txt.
372
 */
373
interface Driver
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
374
{
375
	/**
376
	 * Runs a query and fetches results as a multi dimensional array.
377
	 *
378
	 * @param string $sql      SQL to be executed
379
	 * @param array  $bindings list of values to bind to SQL snippet
380
	 *
381
	 * @return array
382
	 */
383
	public function GetAll( $sql, $bindings = array() );
384
385
	/**
386
	 * Runs a query and fetches results as a column.
387
	 *
388
	 * @param string $sql      SQL Code to execute
389
	 * @param array  $bindings list of values to bind to SQL snippet
390
	 *
391
	 * @return array
392
	 */
393
	public function GetCol( $sql, $bindings = array() );
394
395
	/**
396
	 * Runs a query and returns results as a single cell.
397
	 *
398
	 * @param string $sql      SQL to execute
399
	 * @param array  $bindings list of values to bind to SQL snippet
400
	 *
401
	 * @return mixed
402
	 */
403
	public function GetOne( $sql, $bindings = array() );
404
	
405
	/**
406
	 * Runs a query and returns results as an associative array
407
	 * indexed by the first column.
408
	 *
409
	 * @param string $sql      SQL to execute
410
	 * @param array  $bindings list of values to bind to SQL snippet
411
	 *
412
	 * @return mixed
413
	 */
414
	public function GetAssocRow( $sql, $bindings = array() );
415
	
416
	/**
417
	 * Runs a query and returns a flat array containing the values of
418
	 * one row.
419
	 *
420
	 * @param string $sql      SQL to execute
421
	 * @param array  $bindings list of values to bind to SQL snippet
422
	 * 
423
	 * @return array
424
	 */
425
	public function GetRow( $sql, $bindings = array() );
426
427
	/**
428
	 * Executes SQL code and allows key-value binding.
429
	 * This function allows you to provide an array with values to bind
430
	 * to query parameters. For instance you can bind values to question
431
	 * marks in the query. Each value in the array corresponds to the
432
	 * question mark in the query that matches the position of the value in the
433
	 * array. You can also bind values using explicit keys, for instance
434
	 * array(":key"=>123) will bind the integer 123 to the key :key in the
435
	 * SQL. This method has no return value.
436
	 *
437
	 * @param string $sql      SQL Code to execute
438
	 * @param array  $bindings list of values to bind to SQL snippet
439
	 *
440
	 * @return array Affected Rows
441
	 */
442
	public function Execute( $sql, $bindings = array() );
443
444
	/**
445
	 * Returns the latest insert ID if driver does support this
446
	 * feature.
447
	 *
448
	 * @return integer
449
	 */
450
	public function GetInsertID();
451
452
	/**
453
	 * Returns the number of rows affected by the most recent query
454
	 * if the currently selected driver driver supports this feature.
455
	 *
456
	 * @return integer
457
	 */
458
	public function Affected_Rows();
459
460
	/**
461
	 * Returns a cursor-like object from the database.
462
	 *
463
	 * @param string $sql      SQL code to execute
464
	 * @param array  $bindings Bindings
465
	 *
466
	 * @return mixed
467
	 */
468
	public function GetCursor( $sql, $bindings = array() );
469
470
	/**
471
	 * Toggles debug mode. In debug mode the driver will print all
472
	 * SQL to the screen together with some information about the
473
	 * results. All SQL code that passes through the driver will be
474
	 * passes on to the screen for inspection.
475
	 * This method has no return value.
476
	 *
477
	 * @param boolean $trueFalse turn on/off
0 ignored issues
show
Bug introduced by
There is no parameter named $trueFalse. 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...
478
	 *
479
	 * @return void
480
	 */
481
	public function setDebugMode( $tf );
482
483
	/**
484
	 * Starts a transaction.
485
	 *
486
	 * @return void
487
	 */
488
	public function CommitTrans();
489
490
	/**
491
	 * Commits a transaction.
492
	 *
493
	 * @return void
494
	 */
495
	public function StartTrans();
496
497
	/**
498
	 * Rolls back a transaction.
499
	 *
500
	 * @return void
501
	 */
502
	public function FailTrans();
503
504
	/**
505
	 * Resets the internal Query Counter.
506
	 *
507
	 * @return self
508
	 */
509
	public function resetCounter();
510
511
	/**
512
	 * Returns the number of SQL queries processed.
513
	 *
514
	 * @return integer
515
	 */
516
	public function getQueryCount();
517
}
518
}
519
520
namespace RedBeanPHP\Driver {
521
522
use RedBeanPHP\Driver as Driver;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
523
use RedBeanPHP\Logger as Logger;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
524
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
525
use RedBeanPHP\RedException\SQL as SQL;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
526
use RedBeanPHP\Logger\RDefault as RDefault;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
527
use RedBeanPHP\PDOCompatible as PDOCompatible;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
528
use RedBeanPHP\Cursor\PDOCursor as PDOCursor;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
529
530
/**
531
 * PDO Driver
532
 * This Driver implements the RedBean Driver API.
533
 * for RedBeanPHP. This is the standard / default database driver
534
 * for RedBeanPHP.
535
 *
536
 * @file    RedBeanPHP/PDO.php
537
 * @author  Gabor de Mooij and the RedBeanPHP Community, Desfrenes
538
 * @license BSD/GPLv2
539
 *
540
 * @copyright
541
 * copyright (c) Desfrenes & Gabor de Mooij and the RedBeanPHP community
542
 * This source file is subject to the BSD/GPLv2 License that is bundled
543
 * with this source code in the file license.txt.
544
 */
545
class RPDO implements Driver
0 ignored issues
show
Coding Style Compatibility introduced by
Each interface must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
546
{
547
	/**
548
	 * @var integer
549
	 */
550
	protected $max;
551
552
	/**
553
	 * @var string
554
	 */
555
	protected $dsn;
556
557
	/**
558
	 * @var boolean
559
	 */
560
	protected $loggingEnabled = FALSE;
561
562
	/**
563
	 * @var Logger
564
	 */
565
	protected $logger = NULL;
566
567
	/**
568
	 * @var PDO
569
	 */
570
	protected $pdo;
571
572
	/**
573
	 * @var integer
574
	 */
575
	protected $affectedRows;
576
577
	/**
578
	 * @var integer
579
	 */
580
	protected $resultArray;
581
582
	/**
583
	 * @var array
584
	 */
585
	protected $connectInfo = array();
586
587
	/**
588
	 * @var boolean
589
	 */
590
	protected $isConnected = FALSE;
591
592
	/**
593
	 * @var bool
594
	 */
595
	protected $flagUseStringOnlyBinding = FALSE;
596
597
	/**
598
	 * @var integer
599
	 */
600
	protected $queryCounter = 0;
601
602
	/**
603
	 * @var string
604
	 */
605
	protected $mysqlEncoding = '';
606
607
	/**
608
	 * Binds parameters. This method binds parameters to a PDOStatement for
609
	 * Query Execution. This method binds parameters as NULL, INTEGER or STRING
610
	 * and supports both named keys and question mark keys.
611
	 *
612
	 * @param PDOStatement $statement PDO Statement instance
613
	 * @param array        $bindings  values that need to get bound to the statement
614
	 *
615
	 * @return void
616
	 */
617
	protected function bindParams( $statement, $bindings )
618
	{
619
		foreach ( $bindings as $key => &$value ) {
620
			if ( is_integer( $key ) ) {
621
				if ( is_null( $value ) ) {
622
					$statement->bindValue( $key + 1, NULL, \PDO::PARAM_NULL );
623 View Code Duplication
				} elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
624
					$statement->bindParam( $key + 1, $value, \PDO::PARAM_INT );
625
				} else {
626
					$statement->bindParam( $key + 1, $value, \PDO::PARAM_STR );
627
				}
628
			} else {
629
				if ( is_null( $value ) ) {
630
					$statement->bindValue( $key, NULL, \PDO::PARAM_NULL );
631 View Code Duplication
				} elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
632
					$statement->bindParam( $key, $value, \PDO::PARAM_INT );
633
				} else {
634
					$statement->bindParam( $key, $value, \PDO::PARAM_STR );
635
				}
636
			}
637
		}
638
	}
639
640
	/**
641
	 * This method runs the actual SQL query and binds a list of parameters to the query.
642
	 * slots. The result of the query will be stored in the protected property
643
	 * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
644
	 * is stored in protected property $affectedRows. If the debug flag is set
645
	 * this function will send debugging output to screen buffer.
646
	 *
647
	 * @param string $sql      the SQL string to be send to database server
648
	 * @param array  $bindings the values that need to get bound to the query slots
649
	 *
650
	 * @return void
651
	 *
652
	 * @throws SQL
653
	 */
654
	protected function runQuery( $sql, $bindings, $options = array() )
655
	{
656
		$this->connect();
657
		if ( $this->loggingEnabled && $this->logger ) {
658
			$this->logger->log( $sql, $bindings );
0 ignored issues
show
Unused Code introduced by
The call to Logger::log() has too many arguments starting with $sql.

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...
659
		}
660
		try {
661
			if ( strpos( 'pgsql', $this->dsn ) === 0 ) {
662
				$statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) );
663
			} else {
664
				$statement = $this->pdo->prepare( $sql );
665
			}
666
			$this->bindParams( $statement, $bindings );
667
			$statement->execute();
668
			$this->queryCounter ++;
669
			$this->affectedRows = $statement->rowCount();
670
			if ( $statement->columnCount() ) {
671
				$fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL;
672
				if ( isset( $options['noFetch'] ) && $options['noFetch'] ) {
673
					$this->resultArray = array();
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type integer of property $resultArray.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
674
					return $statement;
675
				}
676
				$this->resultArray = $statement->fetchAll( $fetchStyle );
677
				if ( $this->loggingEnabled && $this->logger ) {
678
					$this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' );
0 ignored issues
show
Unused Code introduced by
The call to Logger::log() has too many arguments starting with 'resultset: ' . count($t...>resultArray) . ' rows'.

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...
679
				}
680
			} else {
681
				$this->resultArray = array();
682
			}
683
		} catch (\PDOException $e ) {
684
			//Unfortunately the code field is supposed to be int by default (php)
685
			//So we need a property to convey the SQL State code.
686
			$err = $e->getMessage();
687
			if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err );
0 ignored issues
show
Unused Code introduced by
The call to Logger::log() has too many arguments starting with 'An error occurred: ' . $err.

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...
688
			$exception = new SQL( $err, 0 );
689
			$exception->setSQLState( $e->getCode() );
690
			throw $exception;
691
		}
692
	}
693
694
	/**
695
	 * Try to fix MySQL character encoding problems.
696
	 * MySQL < 5.5 does not support proper 4 byte unicode but they
697
	 * seem to have added it with version 5.5 under a different label: utf8mb4.
698
	 * We try to select the best possible charset based on your version data.
699
	 */
700
	protected function setEncoding()
701
	{
702
		$driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
703
		$version = floatval( $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION ) );
704
		if ($driver === 'mysql') {
705
			$encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8';
706
			$this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect
707
			$this->pdo->exec(' SET NAMES '. $encoding); //also for current connection
708
			$this->mysqlEncoding = $encoding;
709
		}
710
	}
711
712
	/**
713
	 * Constructor. You may either specify dsn, user and password or
714
	 * just give an existing PDO connection.
715
	 *
716
	 * Examples:
717
	 *    $driver = new RPDO($dsn, $user, $password);
718
	 *    $driver = new RPDO($existingConnection);
719
	 *
720
	 * @param string|object $dsn    database connection string
721
	 * @param string        $user   optional, usename to sign in
722
	 * @param string        $pass   optional, password for connection login
723
	 *
724
	 */
725
	public function __construct( $dsn, $user = NULL, $pass = NULL )
726
	{
727
		if ( is_object( $dsn ) ) {
728
			$this->pdo = $dsn;
729
			$this->isConnected = TRUE;
730
			$this->setEncoding();
731
			$this->pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
732
			$this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
733
			// make sure that the dsn at least contains the type
734
			$this->dsn = $this->getDatabaseType();
735
		} else {
736
			$this->dsn = $dsn;
737
			$this->connectInfo = array( 'pass' => $pass, 'user' => $user );
738
		}
739
740
		//PHP 5.3 PDO SQLite has a bug with large numbers:
741
		if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) || $this->dsn === 'test-sqlite-53' ) {
742
			$this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis.
743
		} elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) {
744
			$this->max = 2147483647; //bindParam in pdo_cubrid also fails...
745
		} else {
746
			$this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause)
747
		}
748
	}
749
750
	/**
751
	 * Returns the best possible encoding for MySQL based on version data.
752
	 *
753
	 * @return string
754
	 */
755
	public function getMysqlEncoding()
756
	{
757
		return $this->mysqlEncoding;
758
	}
759
760
	/**
761
	 * Whether to bind all parameters as strings.
762
	 * If set to TRUE this will cause all integers to be bound as STRINGS.
763
	 * This will NOT affect NULL values.
764
	 *
765
	 * @param boolean $yesNo pass TRUE to bind all parameters as strings.
766
	 *
767
	 * @return void
768
	 */
769
	public function setUseStringOnlyBinding( $yesNo )
770
	{
771
		$this->flagUseStringOnlyBinding = (boolean) $yesNo;
772
	}
773
774
	/**
775
	 * Establishes a connection to the database using PHP\PDO
776
	 * functionality. If a connection has already been established this
777
	 * method will simply return directly. This method also turns on
778
	 * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as
779
	 * PDO-FETCH-ASSOC.
780
	 *
781
	 * @throws\PDOException
782
	 *
783
	 * @return void
784
	 */
785
	public function connect()
786
	{
787
		if ( $this->isConnected ) return;
788
		try {
789
			$user = $this->connectInfo['user'];
790
			$pass = $this->connectInfo['pass'];
791
			$this->pdo = new \PDO(
0 ignored issues
show
Documentation Bug introduced by
It seems like new \PDO($this->dsn, $user, $pass) of type object<PDO> is incompatible with the declared type object<RedBeanPHP\Driver\PDO> of property $pdo.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
792
				$this->dsn,
793
				$user,
794
				$pass
795
			);
796
			$this->setEncoding();
797
			$this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, TRUE );
798
			//cant pass these as argument to constructor, CUBRID driver does not understand...
799
			$this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
800
			$this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC );
801
			$this->isConnected = TRUE;
802
		} catch ( \PDOException $exception ) {
803
			$matches = array();
804
			$dbname  = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?';
805
			throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() );
806
		}
807
	}
808
809
	/**
810
	 * Directly sets PDO instance into driver.
811
	 * This method might improve performance, however since the driver does
812
	 * not configure this instance terrible things may happen... only use
813
	 * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and
814
	 * you know your database server VERY WELL.
815
	 *
816
	 * @param PDO $pdo PDO instance
817
	 *
818
	 * @return void
819
	 */
820
	public function setPDO( \PDO $pdo ) {
821
		$this->pdo = $pdo;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pdo of type object<PDO> is incompatible with the declared type object<RedBeanPHP\Driver\PDO> of property $pdo.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
822
	}
823
824
	/**
825
	 * @see Driver::GetAll
826
	 */
827
	public function GetAll( $sql, $bindings = array() )
828
	{
829
		$this->runQuery( $sql, $bindings );
830
		return $this->resultArray;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->resultArray; (integer) is incompatible with the return type declared by the interface RedBeanPHP\Driver::GetAll of type array.

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...
831
	}
832
833
	/**
834
	 * @see Driver::GetAssocRow
835
	 */
836
	public function GetAssocRow( $sql, $bindings = array() )
837
	{
838
		$this->runQuery( $sql, $bindings, array(
839
				'fetchStyle' => \PDO::FETCH_ASSOC
840
			)
841
		);
842
		return $this->resultArray;
843
	}
844
845
	/**
846
	 * @see Driver::GetCol
847
	 */
848
	public function GetCol( $sql, $bindings = array() )
849
	{
850
		$rows = $this->GetAll( $sql, $bindings );
851
		$cols = array();
852
		if ( $rows && is_array( $rows ) && count( $rows ) > 0 ) {
853
			foreach ( $rows as $row ) {
854
				$cols[] = array_shift( $row );
855
			}
856
		}
857
858
		return $cols;
859
	}
860
861
	/**
862
	 * @see Driver::GetOne
863
	 */
864
	public function GetOne( $sql, $bindings = array() )
865
	{
866
		$arr = $this->GetAll( $sql, $bindings );
867
		$res = NULL;
868
		if ( !is_array( $arr ) ) return NULL;
869
		if ( count( $arr ) === 0 ) return NULL;
870
		$row1 = array_shift( $arr );
871
		if ( !is_array( $row1 ) ) return NULL;
872
		if ( count( $row1 ) === 0 ) return NULL;
873
		$col1 = array_shift( $row1 );
874
		return $col1;
875
	}
876
877
	/**
878
	 * Alias for getOne().
879
	 * Backward compatibility.
880
	 *
881
	 * @param string $sql      SQL
882
	 * @param array  $bindings bindings
883
	 *
884
	 * @return mixed
885
	 */
886
	public function GetCell( $sql, $bindings = array() )
887
	{
888
		return $this->GetOne( $sql, $bindings );
889
	}
890
891
	/**
892
	 * @see Driver::GetRow
893
	 */
894
	public function GetRow( $sql, $bindings = array() )
895
	{
896
		$arr = $this->GetAll( $sql, $bindings );
897
		return array_shift( $arr );
898
	}
899
900
	/**
901
	 * @see Driver::Excecute
902
	 */
903
	public function Execute( $sql, $bindings = array() )
904
	{
905
		$this->runQuery( $sql, $bindings );
906
		return $this->affectedRows;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->affectedRows; (integer) is incompatible with the return type declared by the interface RedBeanPHP\Driver::Execute of type array.

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...
907
	}
908
909
	/**
910
	 * @see Driver::GetInsertID
911
	 */
912
	public function GetInsertID()
913
	{
914
		$this->connect();
915
916
		return (int) $this->pdo->lastInsertId();
917
	}
918
919
	/**
920
	 * @see Driver::GetCursor
921
	 */
922
	public function GetCursor( $sql, $bindings = array() )
923
	{
924
		$statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) );
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $statement is correct as $this->runQuery($sql, $b...ray('noFetch' => TRUE)) (which targets RedBeanPHP\Driver\RPDO::runQuery()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
925
		$cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC );
0 ignored issues
show
Documentation introduced by
$statement is of type null, but the function expects a object<PDOStatement>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
926
		return $cursor;
927
	}
928
929
	/**
930
	 * @see Driver::Affected_Rows
931
	 */
932
	public function Affected_Rows()
933
	{
934
		$this->connect();
935
		return (int) $this->affectedRows;
936
	}
937
938
	/**
939
	 * Toggles debug mode. In debug mode the driver will print all
940
	 * SQL to the screen together with some information about the
941
	 * results.
942
	 *
943
	 * @param boolean $trueFalse turn on/off
0 ignored issues
show
Bug introduced by
There is no parameter named $trueFalse. 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...
944
	 * @param Logger  $logger    logger instance
945
	 *
946
	 * @return void
947
	 */
948
	public function setDebugMode( $tf, $logger = NULL )
949
	{
950
		$this->connect();
951
		$this->loggingEnabled = (bool) $tf;
952
		if ( $this->loggingEnabled and !$logger ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
953
			$logger = new RDefault();
954
		}
955
		$this->setLogger( $logger );
0 ignored issues
show
Bug introduced by
It seems like $logger defined by parameter $logger on line 948 can be null; however, RedBeanPHP\Driver\RPDO::setLogger() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
956
	}
957
958
	/**
959
	 * Injects Logger object.
960
	 * Sets the logger instance you wish to use.
961
	 *
962
	 * @param Logger $logger the logger instance to be used for logging
963
	 *
964
	 * @return void
965
	 */
966
	public function setLogger( Logger $logger )
967
	{
968
		$this->logger = $logger;
969
	}
970
971
	/**
972
	 * Gets Logger object.
973
	 * Returns the currently active Logger instance.
974
	 *
975
	 * @return Logger
976
	 */
977
	public function getLogger()
978
	{
979
		return $this->logger;
980
	}
981
982
	/**
983
	 * @see Driver::StartTrans
984
	 */
985
	public function StartTrans()
986
	{
987
		$this->connect();
988
		$this->pdo->beginTransaction();
989
	}
990
991
	/**
992
	 * @see Driver::CommitTrans
993
	 */
994
	public function CommitTrans()
995
	{
996
		$this->connect();
997
		$this->pdo->commit();
998
	}
999
1000
	/**
1001
	 * @see Driver::FailTrans
1002
	 */
1003
	public function FailTrans()
1004
	{
1005
		$this->connect();
1006
		$this->pdo->rollback();
1007
	}
1008
1009
	/**
1010
	 * Returns the name of database driver for PDO.
1011
	 * Uses the PDO attribute DRIVER NAME to obtain the name of the
1012
	 * PDO driver.
1013
	 *
1014
	 * @return string
1015
	 */
1016
	public function getDatabaseType()
1017
	{
1018
		$this->connect();
1019
1020
		return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
1021
	}
1022
1023
	/**
1024
	 * Returns the version number of the database.
1025
	 *
1026
	 * @return mixed $version version number of the database
1027
	 */
1028
	public function getDatabaseVersion()
1029
	{
1030
		$this->connect();
1031
		return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
1032
	}
1033
1034
	/**
1035
	 * Returns the underlying PHP PDO instance.
1036
	 *
1037
	 * @return PDO
1038
	 */
1039
	public function getPDO()
1040
	{
1041
		$this->connect();
1042
		return $this->pdo;
1043
	}
1044
1045
	/**
1046
	 * Closes database connection by destructing\PDO.
1047
	 *
1048
	 * @return void
1049
	 */
1050
	public function close()
1051
	{
1052
		$this->pdo         = NULL;
1053
		$this->isConnected = FALSE;
1054
	}
1055
1056
	/**
1057
	 * Returns TRUE if the current\PDO instance is connected.
1058
	 *
1059
	 * @return boolean
1060
	 */
1061
	public function isConnected()
1062
	{
1063
		return $this->isConnected && $this->pdo;
1064
	}
1065
1066
	/**
1067
	 * Toggles logging, enables or disables logging.
1068
	 *
1069
	 * @param boolean $enable TRUE to enable logging
1070
	 *
1071
	 * @return self
1072
	 */
1073
	public function setEnableLogging( $enable )
1074
	{
1075
		$this->loggingEnabled = (boolean) $enable;
1076
	}
1077
1078
	/**
1079
	 * Resets the internal Query Counter.
1080
	 *
1081
	 * @return self
1082
	 */
1083
	public function resetCounter()
1084
	{
1085
		$this->queryCounter = 0;
1086
		return $this;
1087
	}
1088
1089
	/**
1090
	 * Returns the number of SQL queries processed.
1091
	 *
1092
	 * @return integer
1093
	 */
1094
	public function getQueryCount()
1095
	{
1096
		return $this->queryCounter;
1097
	}
1098
1099
	/**
1100
	 * Returns the maximum value treated as integer parameter
1101
	 * binding.
1102
	 *
1103
	 * This method is mainly for testing purposes but it can help
1104
	 * you solve some issues relating to integer bindings.
1105
	 *
1106
	 * @return integer
1107
	 */
1108
	public function getIntegerBindingMax()
1109
	{
1110
		return $this->max;
1111
	}
1112
}
1113
}
1114
1115
namespace RedBeanPHP {
1116
1117
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
1118
use RedBeanPHP\BeanHelper as BeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
1119
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
1120
use RedBeanPHP\RedException as RedException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
1121
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
1122
1123
/**
1124
 * OODBBean (Object Oriented DataBase Bean).
1125
 * 
1126
 * to exchange information with the database. A bean represents
1127
 * a single table row and offers generic services for interaction
1128
 * with databases systems as well as some meta-data.
1129
 *
1130
 * @file    RedBeanPHP/OODBBean.php
1131
 * @author  Gabor de Mooij and the RedBeanPHP community
1132
 * @license BSD/GPLv2
1133
 * @desc    OODBBean represents a bean. RedBeanPHP uses beans
1134
 * 
1135
 * @copyright
1136
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
1137
 * This source file is subject to the BSD/GPLv2 License that is bundled
1138
 * with this source code in the file license.txt.
1139
 */
1140
class OODBBean implements\IteratorAggregate,\ArrayAccess,\Countable
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style introduced by
Expected 1 space before "IteratorAggregate"; 10 found
Loading history...
Coding Style introduced by
Expected 1 space before "ArrayAccess"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space before "Countable"; 0 found
Loading history...
1141
{
1142
	/**
1143
	 * FUSE error modes.
1144
	 */
1145
	const C_ERR_IGNORE    = FALSE;
1146
	const C_ERR_LOG       = 1;
1147
	const C_ERR_NOTICE    = 2;
1148
	const C_ERR_WARN      = 3;
1149
	const C_ERR_EXCEPTION = 4;
1150
	const C_ERR_FUNC      = 5;
1151
	const C_ERR_FATAL     = 6;
1152
1153
	/**
1154
	 * @var boolean
1155
	 */
1156
	protected static $errorHandlingFUSE = FALSE;
1157
1158
	/**
1159
	 * @var callable|NULL
1160
	 */
1161
	protected static $errorHandler = NULL;
1162
1163
	/**
1164
	 * @var array
1165
	 */
1166
	protected static $aliases = array();
1167
1168
	/**
1169
	 * @var boolean
1170
	 */
1171
	protected static $autoResolve = FALSE;
1172
1173
	/**
1174
	 * Sets the error mode for FUSE.
1175
	 * What to do if a FUSE model method does not exist?
1176
	 * You can set the following options:
1177
	 *
1178
	 * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
1179
	 * OODBBean::C_ERR_LOG, logs the incident using error_log
1180
	 * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
1181
	 * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
1182
	 * OODBBean::C_ERR_EXCEPTION, throws an exception
1183
	 * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
1184
	 * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
1185
	 *
1186
	 * Custom handler method signature: handler( array (
1187
	 * 	'message' => string
1188
	 * 	'bean' => OODBBean
1189
	 * 	'method' => string
1190
	 * ) )
1191
	 *
1192
	 * This method returns the old mode and handler as an array.
1193
	 *
1194
	 * @param integer       $mode mode
1195
	 * @param callable|NULL $func custom handler
1196
	 *
1197
	 * @return array
1198
	 */
1199
	public static function setErrorHandlingFUSE($mode, $func = NULL) {
1200
		if (
1201
			   $mode !== self::C_ERR_IGNORE
1202
			&& $mode !== self::C_ERR_LOG
1203
			&& $mode !== self::C_ERR_NOTICE
1204
			&& $mode !== self::C_ERR_WARN
1205
			&& $mode !== self::C_ERR_EXCEPTION
1206
			&& $mode !== self::C_ERR_FUNC
1207
			&& $mode !== self::C_ERR_FATAL
1208
		) throw new \Exception( 'Invalid error mode selected' );
1209
1210
		if ( $mode === self::C_ERR_FUNC && !is_callable( $func ) ) {
1211
			throw new \Exception( 'Invalid error handler' );
1212
		}
1213
1214
		$old = array( self::$errorHandlingFUSE, self::$errorHandler );
1215
		self::$errorHandlingFUSE = $mode;
0 ignored issues
show
Documentation Bug introduced by
The property $errorHandlingFUSE was declared of type boolean, but $mode is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1216
		if ( is_callable( $func ) ) {
1217
			self::$errorHandler = $func;
1218
		} else {
1219
			self::$errorHandler = NULL;
1220
		}
1221
		return $old;
1222
	}
1223
1224
	/**
1225
	 * Sets aliases.
1226
	 *
1227
	 * @param array $list
1228
	 *
1229
	 * @return void
1230
	 */
1231
	public static function aliases( $list )
1232
	{
1233
		self::$aliases = $list;
1234
	}
1235
1236
	/**
1237
	 * Enables or disables auto-resolving fetch types.
1238
	 * Auto-resolving aliased parent beans is convenient but can
1239
	 * be slower and can create infinite recursion if you
1240
	 * used aliases to break cyclic relations in your domain.
1241
	 *
1242
	 * @param boolean $automatic TRUE to enable automatic resolving aliased parents
1243
	 *
1244
	 * @return void
1245
	 */
1246
	public static function setAutoResolve( $automatic = TRUE )
1247
	{
1248
		self::$autoResolve = (boolean) $automatic;
1249
	}
1250
1251
	/**
1252
	 * This is where the real properties of the bean live. They are stored and retrieved
1253
	 * by the magic getter and setter (__get and __set).
1254
	 *
1255
	 * @var array $properties
1256
	 */
1257
	protected $properties = array();
1258
1259
	/**
1260
	 * Here we keep the meta data of a bean.
1261
	 *
1262
	 * @var array
1263
	 */
1264
	protected $__info = array();
1265
1266
	/**
1267
	 * The BeanHelper allows the bean to access the toolbox objects to implement
1268
	 * rich functionality, otherwise you would have to do everything with R or
1269
	 * external objects.
1270
	 *
1271
	 * @var BeanHelper
1272
	 */
1273
	protected $beanHelper = NULL;
1274
1275
	/**
1276
	 * @var null
1277
	 */
1278
	protected $fetchType = NULL;
1279
1280
	/**
1281
	 * @var string
1282
	 */
1283
	protected $withSql = '';
1284
1285
	/**
1286
	 * @var array
1287
	 */
1288
	protected $withParams = array();
1289
1290
	/**
1291
	 * @var string
1292
	 */
1293
	protected $aliasName = NULL;
1294
1295
	/**
1296
	 * @var string
1297
	 */
1298
	protected $via = NULL;
1299
1300
	/**
1301
	 * @var boolean
1302
	 */
1303
	protected $noLoad = FALSE;
1304
1305
	/**
1306
	 * @var boolean
1307
	 */
1308
	protected $all = FALSE;
1309
1310
	/**
1311
	 * Sets a meta property for all beans. This is a quicker way to set
1312
	 * the meta properties for a collection of beans because this method
1313
	 * can directly access the property arrays of the beans.
1314
	 * This method returns the beans.
1315
	 *
1316
	 * @param array  $beans    beans to set the meta property of
1317
	 * @param string $property property to set
1318
	 * @param mixed  $value    value
1319
	 *
1320
	 * @return array
1321
	 */
1322
	public static function setMetaAll( $beans, $property, $value )
1323
	{
1324
		foreach( $beans as $bean ) {
1325
			if ( $bean instanceof OODBBean ) $bean->__info[ $property ] = $value;
1326
		}
1327
1328
		return $beans;
1329
	}
1330
1331
	/**
1332
	 * Parses the join in the with-snippet.
1333
	 * For instance:
1334
	 *
1335
	 * $author
1336
	 * 	->withCondition(' @joined.detail.title LIKE ? ')
1337
	 *  ->ownBookList;
1338
	 *
1339
	 * will automatically join 'detail' on book to
1340
	 * access the title field.
1341
	 *
1342
	 * @note this feature requires Narrow Field Mode and Join Feature
1343
	 * to be both activated (default).
1344
	 *
1345
	 * @param string $type the source type for the join
1346
	 *
1347
	 * @return string $joinSql
1348
	 */
1349
	private function parseJoin( $type )
1350
	{
1351
		$joinSql = '';
1352
		$joins = array();
1353
		if ( strpos($this->withSql, '@joined.' ) !== FALSE ) {
1354
			$writer   = $this->beanHelper->getToolBox()->getWriter();
1355
			$oldParts = $parts = explode( '@joined.', $this->withSql );
1356
			array_shift( $parts );
1357
			foreach($parts as $part) {
1358
				$explosion = explode( '.', $part );
1359
				$joinInfo  = array_shift( $explosion );
1360
				//Dont join more than once..
1361
				if ( !isset( $joins[$joinInfo] ) ) {
1362
					$joins[ $joinInfo ] = true;
1363
					$joinSql  .= $writer->writeJoin( $type, $joinInfo, 'LEFT' );
1364
				}
1365
			}
1366
			$this->withSql = implode( '', $oldParts );
1367
			$joinSql      .= ' WHERE ';
1368
		}
1369
		return $joinSql;
1370
	}
1371
1372
	/**
1373
	 * Internal method.
1374
	 * Obtains a shared list for a certain type.
1375
	 *
1376
	 * @param string $type the name of the list you want to retrieve.
1377
	 *
1378
	 * @return array
1379
	 */
1380
	private function getSharedList( $type, $redbean, $toolbox )
1381
	{
1382
		$writer = $toolbox->getWriter();
1383
1384 View Code Duplication
		if ( $this->via ) {
1385
			$oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
1386
			if ( $oldName !== $this->via ) {
1387
				//set the new renaming rule
1388
				$writer->renameAssocTable( $oldName, $this->via );
1389
			}
1390
			$this->via = NULL;
1391
		}
1392
1393
		$beans = array();
1394
		if ($this->getID()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getID() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1395
			$type             = $this->beau( $type );
1396
			$assocManager     = $redbean->getAssociationManager();
1397
			$beans            = $assocManager->related( $this, $type, $this->withSql, $this->withParams );
1398
		}
1399
1400
		$this->withSql    = '';
1401
		$this->withParams = array();
1402
1403
		return $beans;
1404
	}
1405
1406
	/**
1407
	 * Internal method.
1408
	 * Obtains the own list of a certain type.
1409
	 *
1410
	 * @param string      $type   name of the list you want to retrieve
1411
	 * @param OODB        $oodb   The RB OODB object database instance
0 ignored issues
show
Bug introduced by
There is no parameter named $oodb. 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...
1412
	 *
1413
	 * @return array
1414
	 */
1415
	private function getOwnList( $type, $redbean )
1416
	{
1417
		$type = $this->beau( $type );
1418
1419
		if ( $this->aliasName ) {
1420
			$parentField = $this->aliasName;
1421
			$myFieldLink = $parentField . '_id';
1422
1423
			$this->__info['sys.alias.' . $type] = $this->aliasName;
1424
1425
			$this->aliasName = NULL;
1426
		} else {
1427
			$parentField = $this->__info['type'];
1428
			$myFieldLink = $parentField . '_id';
1429
		}
1430
1431
		$beans = array();
1432
1433
		if ( $this->getID() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getID() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1434
1435
			$firstKey = NULL;
1436 View Code Duplication
			if ( count( $this->withParams ) > 0 ) {
1437
				reset( $this->withParams );
1438
1439
				$firstKey = key( $this->withParams );
1440
			}
1441
1442
			$joinSql = $this->parseJoin( $type );
1443
1444
			if ( !is_numeric( $firstKey ) || $firstKey === NULL ) {
1445
				$bindings           = $this->withParams;
1446
				$bindings[':slot0'] = $this->getID();
1447
1448
				$beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
1449
			} else {
1450
				$bindings = array_merge( array( $this->getID() ), $this->withParams );
1451
1452
				$beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
1453
			}
1454
		}
1455
1456
		$this->withSql    = '';
1457
		$this->withParams = array();
1458
1459
		foreach ( $beans as $beanFromList ) {
1460
			$beanFromList->__info['sys.parentcache.' . $parentField] = $this;
1461
		}
1462
1463
		return $beans;
1464
	}
1465
1466
	/**
1467
	 * Initializes a bean. Used by OODB for dispensing beans.
1468
	 * It is not recommended to use this method to initialize beans. Instead
1469
	 * use the OODB object to dispense new beans. You can use this method
1470
	 * if you build your own bean dispensing mechanism.
1471
	 *
1472
	 * @param string             $type       type of the new bean
1473
	 * @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model
1474
	 *
1475
	 * @return void
1476
	 */
1477
	public function initializeForDispense( $type, BeanHelper $beanhelper )
1478
	{
1479
		$this->beanHelper         = $beanhelper;
1480
		$this->__info['type']     = $type;
1481
		$this->__info['sys.id']   = 'id';
1482
		$this->__info['sys.orig'] = array( 'id' => 0 );
1483
		$this->__info['tainted']  = TRUE;
1484
		$this->__info['changed']  = TRUE;
1485
		$this->properties['id']   = 0;
1486
	}
1487
1488
	/**
1489
	 * Sets the Bean Helper. Normally the Bean Helper is set by OODB.
1490
	 * Here you can change the Bean Helper. The Bean Helper is an object
1491
	 * providing access to a toolbox for the bean necessary to retrieve
1492
	 * nested beans (bean lists: ownBean, sharedBean) without the need to
1493
	 * rely on static calls to the facade (or make this class dep. on OODB).
1494
	 *
1495
	 * @param BeanHelper $helper
1496
	 *
1497
	 * @return void
1498
	 */
1499
	public function setBeanHelper( BeanHelper $helper )
1500
	{
1501
		$this->beanHelper = $helper;
1502
	}
1503
1504
	/**
1505
	 * Returns an\ArrayIterator so you can treat the bean like
1506
	 * an array with the properties container as its contents.
1507
	 * This method is meant for PHP and allows you to access beans as if
1508
	 * they were arrays, i.e. using array notation:
1509
	 *
1510
	 * $bean[$key] = $value;
1511
	 *
1512
	 * Note that not all PHP functions work with the array interface.
1513
	 *
1514
	 * @return \ArrayIterator
1515
	 */
1516
	public function getIterator()
1517
	{
1518
		return new\ArrayIterator( $this->properties );
1519
	}
1520
1521
	/**
1522
	 * Imports all values from an associative array $array. Chainable.
1523
	 * This method imports the values in the first argument as bean
1524
	 * propery and value pairs. Use the second parameter to provide a
1525
	 * selection. If a selection array is passed, only the entries
1526
	 * having keys mentioned in the selection array will be imported.
1527
	 * Set the third parameter to TRUE to preserve spaces in selection keys.
1528
	 *
1529
	 * @param array        $array     what you want to import
1530
	 * @param string|array $selection selection of values
1531
	 * @param boolean      $notrim    if TRUE selection keys will NOT be trimmed
1532
	 *
1533
	 * @return OODBBean
1534
	 */
1535
	public function import( $array, $selection = FALSE, $notrim = FALSE )
1536
	{
1537
		if ( is_string( $selection ) ) {
1538
			$selection = explode( ',', $selection );
1539
		}
1540
1541
		if ( !$notrim && is_array( $selection ) ) {
1542
			foreach ( $selection as $key => $selected ) {
1543
				$selection[$key] = trim( $selected );
1544
			}
1545
		}
1546
1547
		foreach ( $array as $key => $value ) {
1548
			if ( $key != '__info' ) {
1549
				if ( !$selection || ( $selection && in_array( $key, $selection ) ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $selection of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1550
					if ( is_array($value ) ) {
1551
						if ( isset( $value['_type'] ) ) {
1552
							$bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] );
1553
							unset( $value['_type'] );
1554
							$bean->import($value);
1555
							$this->$key = $bean;
1556
						} else {
1557
							$listBeans = array();
1558
							foreach( $value as $listKey => $listItem ) {
1559
								$bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] );
1560
								unset( $listItem['_type'] );
1561
								$bean->import($listItem);
1562
								$list = &$this->$key;
1563
								$list[ $listKey ] = $bean;
1564
							}
1565
						}
1566
					} else {
1567
						$this->$key = $value;
1568
					}
1569
				}
1570
			}
1571
		}
1572
1573
		return $this;
1574
	}
1575
1576
	/**
1577
	* Fast way to import a row.
1578
	* Does not perform any checks.
1579
	*
1580
	* @param array $row a database row
1581
	*
1582
	* @return self
1583
	*/
1584
	public function importRow( $row )
1585
	{
1586
		$this->properties = $row;
1587
		$this->__info['sys.orig'] = $row;
1588
		$this->__info['changed'] = FALSE;
1589
		return $this;
1590
	}
1591
1592
	/**
1593
	 * Imports data from another bean. Chainable.
1594
	 * Copies the properties from the source bean to the internal
1595
	 * property list.
1596
	 *
1597
	 * @param OODBBean $sourceBean the source bean to take properties from
1598
	 *
1599
	 * @return OODBBean
1600
	 */
1601
	public function importFrom( OODBBean $sourceBean )
1602
	{
1603
		$this->__info['tainted'] = TRUE;
1604
		$this->__info['changed'] = TRUE;
1605
		$this->properties = $sourceBean->properties;
1606
1607
		return $this;
1608
	}
1609
1610
	/**
1611
	 * Injects the properties of another bean but keeps the original ID.
1612
	 * Just like import() but keeps the original ID.
1613
	 * Chainable.
1614
	 *
1615
	 * @param OODBBean $otherBean the bean whose properties you would like to copy
1616
	 *
1617
	 * @return OODBBean
1618
	 */
1619
	public function inject( OODBBean $otherBean )
1620
	{
1621
		$myID = $this->properties['id'];
1622
1623
		$this->import( $otherBean->export() );
1624
1625
		$this->id = $myID;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
1626
1627
		return $this;
1628
	}
1629
1630
	/**
1631
	 * Exports the bean as an array.
1632
	 * This function exports the contents of a bean to an array and returns
1633
	 * the resulting array.
1634
	 *
1635
	 * @param boolean $meta    set to TRUE if you want to export meta data as well
1636
	 * @param boolean $parents set to TRUE if you want to export parents as well
1637
	 * @param boolean $onlyMe  set to TRUE if you want to export only this bean
1638
	 * @param array   $filters optional whitelist for export
1639
	 *
1640
	 * @return array
1641
	 */
1642
	public function export( $meta = FALSE, $parents = FALSE, $onlyMe = FALSE, $filters = array() )
1643
	{
1644
		$arr = array();
1645
1646
		if ( $parents ) {
1647
			foreach ( $this as $key => $value ) {
1648
				if ( substr( $key, -3 ) != '_id' ) continue;
1649
1650
				$prop = substr( $key, 0, strlen( $key ) - 3 );
1651
				$this->$prop;
1652
			}
1653
		}
1654
1655
		$hasFilters = is_array( $filters ) && count( $filters );
1656
1657
		foreach ( $this as $key => $value ) {
1658
			if ( !$onlyMe && is_array( $value ) ) {
1659
				$vn = array();
1660
1661
				foreach ( $value as $i => $b ) {
1662
					if ( !( $b instanceof OODBBean ) ) continue;
1663
					$vn[] = $b->export( $meta, FALSE, FALSE, $filters );
1664
					$value = $vn;
1665
				}
1666
			} elseif ( $value instanceof OODBBean ) {
1667
				if ( $hasFilters ) {
1668
					if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue;
1669
				}
1670
1671
				$value = $value->export( $meta, $parents, FALSE, $filters );
1672
			}
1673
1674
			$arr[$key] = $value;
1675
		}
1676
1677
		if ( $meta ) {
1678
			$arr['__info'] = $this->__info;
1679
		}
1680
1681
		return $arr;
1682
	}
1683
1684
	/**
1685
	 * Implements isset() function for use as an array.
1686
	 *
1687
	 * @param string $property name of the property you want to check
1688
	 *
1689
	 * @return boolean
1690
	 */
1691
	public function __isset( $property )
1692
	{
1693
		$property = $this->beau( $property );
1694
1695
		if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
1696
			$property = substr($property, 1);
1697
		}
1698
		return isset( $this->properties[$property] );
1699
	}
1700
1701
	/**
1702
	 * Returns the ID of the bean no matter what the ID field is.
1703
	 *
1704
	 * @return string|null
1705
	 */
1706
	public function getID()
1707
	{
1708
		return ( isset( $this->properties['id'] ) ) ? (string) $this->properties['id'] : NULL;
1709
	}
1710
1711
	/**
1712
	 * Unsets a property of a bean.
1713
	 * Magic method, gets called implicitly when performing the unset() operation
1714
	 * on a bean property.
1715
	 * 
1716
	 * @param  string $property property to unset
1717
	 *
1718
	 * @return void
1719
	 */
1720
	public function __unset( $property )
1721
	{
1722
		$property = $this->beau( $property );
1723
1724
		if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
1725
			$property = substr($property, 1);
1726
		}
1727
1728
		unset( $this->properties[$property] );
1729
1730
		$shadowKey = 'sys.shadow.'.$property;
1731
		if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] );
1732
1733
		//also clear modifiers
1734
		$this->withSql    = '';
1735
		$this->withParams = array();
1736
		$this->aliasName  = NULL;
1737
		$this->fetchType  = NULL;
1738
		$this->noLoad     = FALSE;
1739
		$this->all        = FALSE;
1740
		$this->via        = NULL;
1741
1742
		return;
1743
	}
1744
1745
	/**
1746
	 * Adds WHERE clause conditions to ownList retrieval.
1747
	 * For instance to get the pages that belong to a book you would
1748
	 * issue the following command: $book->ownPage
1749
	 * However, to order these pages by number use:
1750
	 *
1751
	 * $book->with(' ORDER BY `number` ASC ')->ownPage
1752
	 *
1753
	 * the additional SQL snippet will be merged into the final
1754
	 * query.
1755
	 *
1756
	 * @param string $sql SQL to be added to retrieval query.
1757
	 * @param array       $bindings array with parameters to bind to SQL snippet
1758
	 *
1759
	 * @return OODBBean
1760
	 */
1761
	public function with( $sql, $bindings = array() )
1762
	{
1763
		$this->withSql    = $sql;
1764
		$this->withParams = $bindings;
1765
		return $this;
1766
	}
1767
1768
	/**
1769
	 * Just like with(). Except that this method prepends the SQL query snippet
1770
	 * with AND which makes it slightly more comfortable to use a conditional
1771
	 * SQL snippet. For instance to filter an own-list with pages (belonging to
1772
	 * a book) on specific chapters you can use:
1773
	 *
1774
	 * $book->withCondition(' chapter = 3 ')->ownPage
1775
	 *
1776
	 * This will return in the own list only the pages having 'chapter == 3'.
1777
	 *
1778
	 * @param string $sql      SQL to be added to retrieval query (prefixed by AND)
1779
	 * @param array  $bindings array with parameters to bind to SQL snippet
1780
	 *
1781
	 * @return OODBBean
1782
	 */
1783
	public function withCondition( $sql, $bindings = array() )
1784
	{
1785
		$this->withSql    = ' AND ' . $sql;
1786
		$this->withParams = $bindings;
1787
		return $this;
1788
	}
1789
1790
	/**
1791
	 * Tells the bean to (re)load the following list without any
1792
	 * conditions. If you have an ownList or sharedList with a
1793
	 * condition you can use this method to reload the entire list.
1794
	 *
1795
	 * Usage:
1796
	 *
1797
	 * $bean->with( ' LIMIT 3 ' )->ownPage; //Just 3
1798
	 * $bean->all()->ownPage; //Reload all pages
1799
	 *
1800
	 * @return self
1801
	 */
1802
	public function all()
1803
	{
1804
		$this->all = TRUE;
1805
		return $this;
1806
	}
1807
1808
	/**
1809
	 * Tells the bean to only access the list but not load
1810
	 * its contents. Use this if you only want to add something to a list
1811
	 * and you have no interest in retrieving its contents from the database.
1812
	 *
1813
	 * @return self
1814
	 */
1815
	public function noLoad()
1816
	{
1817
		$this->noLoad = TRUE;
1818
		return $this;
1819
	}
1820
1821
	/**
1822
	 * Prepares an own-list to use an alias. This is best explained using
1823
	 * an example. Imagine a project and a person. The project always involves
1824
	 * two persons: a teacher and a student. The person beans have been aliased in this
1825
	 * case, so to the project has a teacher_id pointing to a person, and a student_id
1826
	 * also pointing to a person. Given a project, we obtain the teacher like this:
1827
	 *
1828
	 * $project->fetchAs('person')->teacher;
1829
	 *
1830
	 * Now, if we want all projects of a teacher we cant say:
1831
	 *
1832
	 * $teacher->ownProject
1833
	 *
1834
	 * because the $teacher is a bean of type 'person' and no project has been
1835
	 * assigned to a person. Instead we use the alias() method like this:
1836
	 *
1837
	 * $teacher->alias('teacher')->ownProject
1838
	 *
1839
	 * now we get the projects associated with the person bean aliased as
1840
	 * a teacher.
1841
	 *
1842
	 * @param string $aliasName the alias name to use
1843
	 *
1844
	 * @return OODBBean
1845
	 */
1846
	public function alias( $aliasName )
1847
	{
1848
		$this->aliasName = $this->beau( $aliasName );
1849
1850
		return $this;
1851
	}
1852
1853
	/**
1854
	 * Returns properties of bean as an array.
1855
	 * This method returns the raw internal property list of the
1856
	 * bean. Only use this method for optimization purposes. Otherwise
1857
	 * use the export() method to export bean data to arrays.
1858
	 *
1859
	 * @return array
1860
	 */
1861
	public function getProperties()
1862
	{
1863
		return $this->properties;
1864
	}
1865
1866
	/**
1867
	 * Returns properties of bean as an array.
1868
	 * This method returns the raw internal property list of the
1869
	 * bean. Only use this method for optimization purposes. Otherwise
1870
	 * use the export() method to export bean data to arrays.
1871
	 * This method returns an array with the properties array and
1872
	 * the type (string).
1873
	 *
1874
	 * @return array
1875
	 */
1876
	public function getPropertiesAndType()
1877
	{
1878
		return array( $this->properties, $this->__info['type'] );
1879
	}
1880
1881
	/**
1882
	 * Turns a camelcase property name into an underscored property name.
1883
	 *
1884
	 * Examples:
1885
	 *    oneACLRoute -> one_acl_route
1886
	 *    camelCase -> camel_case
1887
	 *
1888
	 * Also caches the result to improve performance.
1889
	 *
1890
	 * @param string $property
1891
	 *
1892
	 * @return string
1893
	 */
1894
	public function beau( $property )
1895
	{
1896
		static $beautifulColumns = array();
1897
1898
		if ( ctype_lower( $property ) ) return $property;
1899
1900
		if (
1901
			( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )
1902
			|| ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) )
1903
			|| ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) )
1904
		) {
1905
1906
			$property = preg_replace( '/List$/', '', $property );
1907
			return $property;
1908
		}
1909
1910
		if ( !isset( $beautifulColumns[$property] ) ) {
1911
			$beautifulColumns[$property] = AQueryWriter::camelsSnake( $property );
1912
		}
1913
1914
		return $beautifulColumns[$property];
1915
	}
1916
1917
	/**
1918
	 * Clears all modifiers.
1919
	 *
1920
	 * @return self
1921
	 */
1922
	public function clearModifiers()
1923
	{
1924
		$this->withSql    = '';
1925
		$this->withParams = array();
1926
		$this->aliasName  = NULL;
1927
		$this->fetchType  = NULL;
1928
		$this->noLoad     = FALSE;
1929
		$this->all        = FALSE;
1930
		$this->via        = NULL;
1931
		return $this;
1932
	}
1933
1934
	/**
1935
	 * Determines whether a list is opened in exclusive mode or not.
1936
	 * If a list has been opened in exclusive mode this method will return TRUE,
1937
	 * othwerwise it will return FALSE.
1938
	 *
1939
	 * @param string $listName name of the list to check
1940
	 *
1941
	 * @return boolean
1942
	 */
1943
	public function isListInExclusiveMode( $listName )
1944
	{
1945
		$listName = $this->beau( $listName );
1946
1947
		if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) {
1948
			$listName = substr($listName, 1);
1949
		}
1950
1951
		$listName = lcfirst( substr( $listName, 3 ) );
1952
1953
		return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] );
1954
	}
1955
1956
	/**
1957
	 * Magic Getter. Gets the value for a specific property in the bean.
1958
	 * If the property does not exist this getter will make sure no error
1959
	 * occurs. This is because RedBean allows you to query (probe) for
1960
	 * properties. If the property can not be found this method will
1961
	 * return NULL instead.
1962
	 *
1963
	 * @param string $property name of the property you wish to obtain the value of
1964
	 *
1965
	 * @return mixed
1966
	 */
1967
	public function &__get( $property )
1968
	{
1969
		$isEx          = FALSE;
1970
		$isOwn         = FALSE;
1971
		$isShared      = FALSE;
1972
1973 View Code Duplication
		if ( !ctype_lower( $property ) ) {
1974
			$property = $this->beau( $property );
1975
			if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
1976
				$property = substr($property, 1);
1977
				$listName = lcfirst( substr( $property, 3 ) );
1978
				$isEx     = TRUE;
1979
				$isOwn    = TRUE;
1980
				$this->__info['sys.exclusive-'.$listName] = TRUE;
1981
			} elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
1982
				$isOwn    = TRUE;
1983
				$listName = lcfirst( substr( $property, 3 ) );
1984
			} elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
1985
				$isShared = TRUE;
1986
			}
1987
		}
1988
1989
		$fieldLink      = $property . '_id';
1990
		$exists         = isset( $this->properties[$property] );
1991
1992
		//If not exists and no field link and no list, bail out.
1993 View Code Duplication
		if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) {
1994
1995
			$this->withSql    = '';
1996
			$this->withParams = array();
1997
			$this->aliasName  = NULL;
1998
			$this->fetchType  = NULL;
1999
			$this->noLoad     = FALSE;
2000
			$this->all        = FALSE;
2001
			$this->via        = NULL;
2002
2003
			$NULL = NULL;
2004
			return $NULL;
2005
		}
2006
2007
		$hasAlias       = (!is_null($this->aliasName));
2008
		$differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
0 ignored issues
show
Bug introduced by
The variable $listName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2009
								($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2010
		$hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2011
		$hasAll         = (boolean) ($this->all);
2012
2013
		//If exists and no list or exits and list not changed, bail out.
2014 View Code Duplication
		if ( $exists && ((!$isOwn && !$isShared ) ||  (!$hasSQL && !$differentAlias && !$hasAll)) ) {
2015
2016
			$this->withSql    = '';
2017
			$this->withParams = array();
2018
			$this->aliasName  = NULL;
2019
			$this->fetchType  = NULL;
2020
			$this->noLoad     = FALSE;
2021
			$this->all        = FALSE;
2022
			$this->via        = NULL;
2023
			return $this->properties[$property];
2024
		}
2025
2026
2027
		list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2028
2029
		if ( isset( $this->$fieldLink ) ) {
2030
			$this->__info['tainted'] = TRUE;
2031
2032
			if ( isset( $this->__info["sys.parentcache.$property"] ) ) {
2033
				$bean = $this->__info["sys.parentcache.$property"];
2034
			} else {
2035
				if ( isset( self::$aliases[$property] ) ) {
2036
					$type = self::$aliases[$property];
2037
				} elseif ( $this->fetchType ) {
2038
					$type = $this->fetchType;
2039
					$this->fetchType = NULL;
2040
				} else {
2041
					$type = $property;
2042
				}
2043
				$bean = NULL;
2044
				if ( !is_null( $this->properties[$fieldLink] ) ) {
2045
					$bean = $redbean->load( $type, $this->properties[$fieldLink] );
2046
					//If the IDs dont match, we failed to load, so try autoresolv in that case...
2047
					if ( $bean->id !== $this->properties[$fieldLink] && self::$autoResolve ) {
2048
						$type = $this->beanHelper->getToolbox()->getWriter()->inferFetchType( $this->__info['type'], $property );
2049
						if ( !is_null( $type) ) {
2050
							$bean = $redbean->load( $type, $this->properties[$fieldLink] );
2051
							$this->__info["sys.autoresolved.{$property}"] = $type;
2052
						}
2053
					}
2054
				}
2055
			}
2056
2057
			$this->properties[$property] = $bean;
2058
			$this->withSql               = '';
2059
			$this->withParams            = array();
2060
			$this->aliasName             = NULL;
2061
			$this->fetchType             = NULL;
2062
			$this->noLoad                = FALSE;
2063
			$this->all                   = FALSE;
2064
			$this->via                   = NULL;
2065
2066
			return $this->properties[$property];
2067
2068
		}
2069
		//Implicit: elseif ( $isOwn || $isShared ) {
2070
		if ( $this->noLoad ) {
2071
			$beans = array();
2072
		} elseif ( $isOwn ) {
2073
			$beans = $this->getOwnList( $listName, $redbean );
2074
		} else {
2075
			$beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2076
		}
2077
2078
		$this->properties[$property]          = $beans;
2079
		$this->__info["sys.shadow.$property"] = $beans;
2080
		$this->__info['tainted']              = TRUE;
2081
2082
		$this->withSql    = '';
2083
		$this->withParams = array();
2084
		$this->aliasName  = NULL;
2085
		$this->fetchType  = NULL;
2086
		$this->noLoad     = FALSE;
2087
		$this->all        = FALSE;
2088
		$this->via        = NULL;
2089
2090
		return $this->properties[$property];
2091
	}
2092
2093
	/**
2094
	 * Magic Setter. Sets the value for a specific property.
2095
	 * This setter acts as a hook for OODB to mark beans as tainted.
2096
	 * The tainted meta property can be retrieved using getMeta("tainted").
2097
	 * The tainted meta property indicates whether a bean has been modified and
2098
	 * can be used in various caching mechanisms.
2099
	 *
2100
	 * @param string $property name of the property you wish to assign a value to
2101
	 * @param  mixed $value    the value you want to assign
2102
	 *
2103
	 * @return void
2104
	 *
2105
	 * @throws Security
2106
	 */
2107
	public function __set( $property, $value )
2108
	{
2109
		$isEx          = FALSE;
2110
		$isOwn         = FALSE;
2111
		$isShared      = FALSE;
2112
2113 View Code Duplication
		if ( !ctype_lower( $property ) ) {
2114
			$property = $this->beau( $property );
2115
			if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2116
				$property = substr($property, 1);
2117
				$listName = lcfirst( substr( $property, 3 ) );
2118
				$isEx     = TRUE;
2119
				$isOwn    = TRUE;
2120
				$this->__info['sys.exclusive-'.$listName] = TRUE;
2121
			} elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
2122
				$isOwn    = TRUE;
2123
				$listName = lcfirst( substr( $property, 3 ) );
2124
			} elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
2125
				$isShared = TRUE;
2126
			}
2127
		}
2128
2129
		$hasAlias       = (!is_null($this->aliasName));
2130
		$differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
0 ignored issues
show
Bug introduced by
The variable $listName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2131
								($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2132
		$hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2133
		$exists         = isset( $this->properties[$property] );
2134
		$fieldLink      = $property . '_id';
2135
2136
		if ( ($isOwn || $isShared) &&  (!$exists || $hasSQL || $differentAlias) ) {
2137
2138
			if ( !$this->noLoad ) {
2139
				list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2140
				if ( $isOwn ) {
2141
					$beans = $this->getOwnList( $listName, $redbean );
2142
				} else {
2143
					$beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2144
				}
2145
				$this->__info["sys.shadow.$property"] = $beans;
2146
			}
2147
		}
2148
2149
		$this->withSql    = '';
2150
		$this->withParams = array();
2151
		$this->aliasName  = NULL;
2152
		$this->fetchType  = NULL;
2153
		$this->noLoad     = FALSE;
2154
		$this->all        = FALSE;
2155
		$this->via        = NULL;
2156
2157
		$this->__info['tainted'] = TRUE;
2158
		$this->__info['changed'] = TRUE;
2159
2160
		if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) {
2161
			if ( is_null( $value ) || $value === FALSE ) {
2162
2163
				unset( $this->properties[ $property ]);
2164
				$this->properties[ $fieldLink ] = NULL;
2165
2166
				return;
2167
			} else {
2168
				throw new RedException( 'Cannot cast to bean.' );
2169
			}
2170
		}
2171
2172
		if ( $value === FALSE ) {
2173
			$value = '0';
2174
		} elseif ( $value === TRUE ) {
2175
			$value = '1';
2176
		} elseif ( $value instanceof \DateTime ) {
2177
			$value = $value->format( 'Y-m-d H:i:s' );
2178
		}
2179
2180
		$this->properties[$property] = $value;
2181
	}
2182
2183
	/**
2184
	 * Sets a property directly, for internal use only.
2185
	 *
2186
	 * @param string  $property     property
2187
	 * @param mixed   $value        value
2188
	 * @param boolean $updateShadow whether you want to update the shadow
2189
	 * @param boolean $taint        whether you want to mark the bean as tainted
2190
	 *
2191
	 * @return void
2192
	 */
2193
	public function setProperty( $property, $value, $updateShadow = FALSE, $taint = FALSE )
2194
	{
2195
		$this->properties[$property] = $value;
2196
2197
		if ( $updateShadow ) {
2198
			$this->__info['sys.shadow.' . $property] = $value;
2199
		}
2200
2201
		if ( $taint ) {
2202
			$this->__info['tainted'] = TRUE;
2203
			$this->__info['changed'] = TRUE;
2204
		}
2205
	}
2206
2207
	/**
2208
	 * Returns the value of a meta property. A meta property
2209
	 * contains additional information about the bean object that will not
2210
	 * be stored in the database. Meta information is used to instruct
2211
	 * RedBeanPHP as well as other systems how to deal with the bean.
2212
	 * If the property cannot be found this getter will return NULL instead.
2213
	 *
2214
	 * Example:
2215
	 *
2216
	 * $bean->setMeta( 'flush-cache', TRUE );
2217
	 *
2218
	 * RedBeanPHP also stores meta data in beans, this meta data uses
2219
	 * keys prefixed with 'sys.' (system).
2220
	 *
2221
	 * @param string $path    path
2222
	 * @param mixed  $default default value
2223
	 *
2224
	 * @return mixed
2225
	 */
2226
	public function getMeta( $path, $default = NULL )
2227
	{
2228
		return ( isset( $this->__info[$path] ) ) ? $this->__info[$path] : $default;
2229
	}
2230
2231
	/**
2232
	 * Gets and unsets a meta property.
2233
	 * Moves a meta property out of the bean.
2234
	 * This is a short-cut method that can be used instead
2235
	 * of combining a get/unset.
2236
	 *
2237
	 * @param string $path    path
2238
	 * @param mixed  $default default value
0 ignored issues
show
Bug introduced by
There is no parameter named $default. 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...
2239
	 *
2240
	 * @return mixed
2241
	 */
2242
	public function moveMeta( $path, $value = NULL )
2243
	{
2244
		if ( isset( $this->__info[$path] ) ) {
2245
			$value = $this->__info[ $path ];
2246
			unset( $this->__info[ $path ] );
2247
		}
2248
		return $value;
2249
	}
2250
2251
	/**
2252
	 * Stores a value in the specified Meta information property.
2253
	 * The first argument should be the key to store the value under,
2254
	 * the second argument should be the value. It is common to use
2255
	 * a path-like notation for meta data in RedBeanPHP like:
2256
	 * 'my.meta.data', however the dots are purely for readability, the
2257
	 * meta data methods do not store nested structures or hierarchies.
2258
	 *
2259
	 * @param string $path  path / key to store value under
2260
	 * @param mixed  $value value to store in bean (not in database) as meta data
2261
	 *
2262
	 * @return OODBBean
2263
	 */
2264
	public function setMeta( $path, $value )
2265
	{
2266
		$this->__info[$path] = $value;
2267
2268
		return $this;
2269
	}
2270
2271
	/**
2272
	 * Copies the meta information of the specified bean
2273
	 * This is a convenience method to enable you to
2274
	 * exchange meta information easily.
2275
	 *
2276
	 * @param OODBBean $bean
2277
	 *
2278
	 * @return OODBBean
2279
	 */
2280
	public function copyMetaFrom( OODBBean $bean )
2281
	{
2282
		$this->__info = $bean->__info;
2283
2284
		return $this;
2285
	}
2286
2287
	/**
2288
	 * Sends the call to the registered model.
2289
	 *
2290
	 * @param string $method name of the method
2291
	 * @param array  $args   argument list
2292
	 *
2293
	 * @return mixed
2294
	 */
2295
	public function __call( $method, $args )
2296
	{
2297
		if ( !isset( $this->__info['model'] ) ) {
2298
			$model = $this->beanHelper->getModelForBean( $this );
2299
2300
			if ( !$model ) {
2301
				return NULL;
2302
			}
2303
2304
			$this->__info['model'] = $model;
2305
		}
2306
		if ( !method_exists( $this->__info['model'], $method ) ) {
2307
2308
			if ( self::$errorHandlingFUSE === FALSE ) {
2309
				return NULL;
2310
			}
2311
2312
			if ( in_array( $method, array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ), TRUE ) ) {
2313
				return NULL;
2314
			}
2315
2316
			$message = "FUSE: method does not exist in model: $method";
2317
			if ( self::$errorHandlingFUSE === self::C_ERR_LOG ) {
2318
				error_log( $message );
2319
				return NULL;
2320
			} elseif ( self::$errorHandlingFUSE === self::C_ERR_NOTICE ) {
2321
				trigger_error( $message, E_USER_NOTICE );
2322
				return NULL;
2323
			} elseif ( self::$errorHandlingFUSE === self::C_ERR_WARN ) {
2324
				trigger_error( $message, E_USER_WARNING );
2325
				return NULL;
2326
			} elseif ( self::$errorHandlingFUSE === self::C_ERR_EXCEPTION ) {
2327
				throw new \Exception( $message );
2328
			} elseif ( self::$errorHandlingFUSE === self::C_ERR_FUNC ) {
2329
				$func = self::$errorHandler;
2330
				return $func(array(
2331
					'message' => $message,
2332
					'method' => $method,
2333
					'args' => $args,
2334
					'bean' => $this
2335
				));
2336
			}
2337
			trigger_error( $message, E_USER_ERROR );
2338
			return NULL;
2339
		}
2340
2341
		return call_user_func_array( array( $this->__info['model'], $method ), $args );
2342
	}
2343
2344
	/**
2345
	 * Implementation of __toString Method
2346
	 * Routes call to Model. If the model implements a __toString() method this
2347
	 * method will be called and the result will be returned. In case of an
2348
	 * echo-statement this result will be printed. If the model does not
2349
	 * implement a __toString method, this method will return a JSON
2350
	 * representation of the current bean.
2351
	 *
2352
	 * @return string
2353
	 */
2354
	public function __toString()
2355
	{
2356
		$string = $this->__call( '__toString', array() );
2357
2358
		if ( $string === NULL ) {
2359
			return json_encode( $this->properties );
2360
		} else {
2361
			return $string;
2362
		}
2363
	}
2364
2365
	/**
2366
	 * Implementation of Array Access Interface, you can access bean objects
2367
	 * like an array.
2368
	 * Call gets routed to __set.
2369
	 *
2370
	 * @param  mixed $offset offset string
2371
	 * @param  mixed $value  value
2372
	 *
2373
	 * @return void
2374
	 */
2375
	public function offsetSet( $offset, $value )
2376
	{
2377
		$this->__set( $offset, $value );
2378
	}
2379
2380
	/**
2381
	 * Implementation of Array Access Interface, you can access bean objects
2382
	 * like an array.
2383
	 *
2384
	 * Array functions do not reveal x-own-lists and list-alias because
2385
	 * you dont want duplicate entries in foreach-loops.
2386
	 * Also offers a slight performance improvement for array access.
2387
	 *
2388
	 * @param  mixed $offset property
2389
	 *
2390
	 * @return boolean
2391
	 */
2392
	public function offsetExists( $offset )
2393
	{
2394
		return $this->__isset( $offset );
2395
	}
2396
2397
	/**
2398
	 * Implementation of Array Access Interface, you can access bean objects
2399
	 * like an array.
2400
	 * Unsets a value from the array/bean.
2401
	 *
2402
	 * Array functions do not reveal x-own-lists and list-alias because
2403
	 * you dont want duplicate entries in foreach-loops.
2404
	 * Also offers a slight performance improvement for array access.
2405
	 *
2406
	 * @param  mixed $offset property
2407
	 *
2408
	 * @return void
2409
	 */
2410
	public function offsetUnset( $offset )
2411
	{
2412
		$this->__unset( $offset );
2413
	}
2414
2415
	/**
2416
	 * Implementation of Array Access Interface, you can access bean objects
2417
	 * like an array.
2418
	 * Returns value of a property.
2419
	 *
2420
	 * Array functions do not reveal x-own-lists and list-alias because
2421
	 * you dont want duplicate entries in foreach-loops.
2422
	 * Also offers a slight performance improvement for array access.
2423
	 *
2424
	 * @param  mixed $offset property
2425
	 *
2426
	 * @return mixed
2427
	 */
2428
	public function &offsetGet( $offset )
2429
	{
2430
		return $this->__get( $offset );
2431
	}
2432
2433
	/**
2434
	 * Chainable method to cast a certain ID to a bean; for instance:
2435
	 * $person = $club->fetchAs('person')->member;
2436
	 * This will load a bean of type person using member_id as ID.
2437
	 *
2438
	 * @param  string $type preferred fetch type
2439
	 *
2440
	 * @return OODBBean
2441
	 */
2442
	public function fetchAs( $type )
2443
	{
2444
		$this->fetchType = $type;
0 ignored issues
show
Documentation Bug introduced by
It seems like $type of type string is incompatible with the declared type null of property $fetchType.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2445
2446
		return $this;
2447
	}
2448
2449
	/**
2450
	 * For polymorphic bean relations.
2451
	 * Same as fetchAs but uses a column instead of a direct value.
2452
	 *
2453
	 * @param string $column
0 ignored issues
show
Bug introduced by
There is no parameter named $column. 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...
2454
	 *
2455
	 * @return OODBBean
2456
	 */
2457
	public function poly( $field )
2458
	{
2459
		return $this->fetchAs( $this->$field );
2460
	}
2461
2462
	/**
2463
	 * Traverses a bean property with the specified function.
2464
	 * Recursively iterates through the property invoking the
2465
	 * function for each bean along the way passing the bean to it.
2466
	 *
2467
	 * Can be used together with with, withCondition, alias and fetchAs.
2468
	 *
2469
	 * @param string  $property property
2470
	 * @param closure $function function
2471
	 *
2472
	 * @return OODBBean
2473
	 */
2474
	public function traverse( $property, $function, $maxDepth = NULL )
2475
	{
2476
		$this->via = NULL;
2477
		if ( strpos( $property, 'shared' ) !== FALSE ) {
2478
			throw new RedException( 'Traverse only works with (x)own-lists.' );
2479
		}
2480
2481
		if ( !is_null( $maxDepth ) ) {
2482
			if ( !$maxDepth-- ) return $this;
2483
		}
2484
2485
		$oldFetchType = $this->fetchType;
2486
		$oldAliasName = $this->aliasName;
2487
		$oldWith      = $this->withSql;
2488
		$oldBindings  = $this->withParams;
2489
2490
		$beans = $this->$property;
2491
2492
		if ( $beans === NULL ) return $this;
2493
2494
		if ( !is_array( $beans ) ) $beans = array( $beans );
2495
2496
		foreach( $beans as $bean ) {
2497
2498
			$function( $bean );
2499
2500
			$bean->fetchType  = $oldFetchType;
2501
			$bean->aliasName  = $oldAliasName;
2502
			$bean->withSql    = $oldWith;
2503
			$bean->withParams = $oldBindings;
2504
2505
			$bean->traverse( $property, $function, $maxDepth );
2506
		}
2507
2508
		return $this;
2509
	}
2510
2511
	/**
2512
	 * Implementation of\Countable interface. Makes it possible to use
2513
	 * count() function on a bean.
2514
	 *
2515
	 * @return integer
2516
	 */
2517
	public function count()
2518
	{
2519
		return count( $this->properties );
2520
	}
2521
2522
	/**
2523
	 * Checks whether a bean is empty or not.
2524
	 * A bean is empty if it has no other properties than the id field OR
2525
	 * if all the other property are empty().
2526
	 *
2527
	 * @return boolean
2528
	 */
2529
	public function isEmpty()
2530
	{
2531
		$empty = TRUE;
2532
		foreach ( $this->properties as $key => $value ) {
2533
			if ( $key == 'id' ) {
2534
				continue;
2535
			}
2536
			if ( !empty( $value ) ) {
2537
				$empty = FALSE;
2538
			}
2539
		}
2540
2541
		return $empty;
2542
	}
2543
2544
	/**
2545
	 * Chainable setter.
2546
	 *
2547
	 * @param string $property the property of the bean
2548
	 * @param mixed  $value    the value you want to set
2549
	 *
2550
	 * @return OODBBean
2551
	 */
2552
	public function setAttr( $property, $value )
2553
	{
2554
		$this->$property = $value;
2555
2556
		return $this;
2557
	}
2558
2559
	/**
2560
	 * Comfort method.
2561
	 * Unsets all properties in array.
2562
	 *
2563
	 * @param array $properties properties you want to unset.
2564
	 *
2565
	 * @return OODBBean
2566
	 */
2567
	public function unsetAll( $properties )
2568
	{
2569
		foreach ( $properties as $prop ) {
2570
			if ( isset( $this->properties[$prop] ) ) {
2571
				unset( $this->properties[$prop] );
2572
			}
2573
		}
2574
2575
		return $this;
2576
	}
2577
2578
	/**
2579
	 * Returns original (old) value of a property.
2580
	 * You can use this method to see what has changed in a
2581
	 * bean.
2582
	 *
2583
	 * @param string $property name of the property you want the old value of
2584
	 *
2585
	 * @return mixed
2586
	 */
2587
	public function old( $property )
2588
	{
2589
		$old = $this->getMeta( 'sys.orig', array() );
2590
2591
		if ( array_key_exists( $property, $old ) ) {
2592
			return $old[$property];
2593
		}
2594
2595
		return NULL;
2596
	}
2597
2598
	/**
2599
	 * Convenience method.
2600
	 * Returns TRUE if the bean has been changed, or FALSE otherwise.
2601
	 * Same as $bean->getMeta('tainted');
2602
	 * Note that a bean becomes tainted as soon as you retrieve a list from
2603
	 * the bean. This is because the bean lists are arrays and the bean cannot
2604
	 * determine whether you have made modifications to a list so RedBeanPHP
2605
	 * will mark the whole bean as tainted.
2606
	 *
2607
	 * @return boolean
2608
	 */
2609
	public function isTainted()
2610
	{
2611
		return $this->getMeta( 'tainted' );
2612
	}
2613
2614
	/**
2615
	 * Returns TRUE if the value of a certain property of the bean has been changed and
2616
	 * FALSE otherwise.
2617
	 *
2618
	 * Note that this method will return TRUE if applied to a loaded list.
2619
	 * Also note that this method keeps track of the bean's history regardless whether
2620
	 * it has been stored or not. Storing a bean does not undo it's history,
2621
	 * to clean the history of a bean use: clearHistory().
2622
	 *
2623
	 * @param string  $property  name of the property you want the change-status of
2624
	 *
2625
	 * @return boolean
2626
	 */
2627
	public function hasChanged( $property )
2628
	{
2629
		return ( array_key_exists( $property, $this->properties ) ) ?
2630
			$this->old( $property ) != $this->properties[$property] : FALSE;
2631
	}
2632
2633
	/**
2634
	 * Returns TRUE if the specified list exists, has been loaded and has been changed:
2635
	 * beans have been added or deleted. This method will not tell you anything about
2636
	 * the state of the beans in the list.
2637
	 *
2638
	 * @param string $property name of the list to check
2639
	 *
2640
	 * @return boolean
2641
	 */
2642
	public function hasListChanged( $property )
2643
	{
2644
		if ( !array_key_exists( $property, $this->properties ) ) return FALSE;
2645
		$diffAdded = array_diff_assoc( $this->properties[$property], $this->__info['sys.shadow.'.$property] );
2646
		if ( count( $diffAdded ) ) return TRUE;
2647
		$diffMissing = array_diff_assoc( $this->__info['sys.shadow.'.$property], $this->properties[$property] );
2648
		if ( count( $diffMissing ) ) return TRUE;
2649
		return FALSE;
2650
	}
2651
2652
	/**
2653
	 * Clears (syncs) the history of the bean.
2654
	 * Resets all shadow values of the bean to their current value.
2655
	 *
2656
	 * @return self
2657
	 */
2658
	public function clearHistory()
2659
	{
2660
		$this->__info['sys.orig'] = array();
2661
		foreach( $this->properties as $key => $value ) {
2662
			if ( is_scalar($value) ) {
2663
				$this->__info['sys.orig'][$key] = $value;
2664
			} else {
2665
				$this->__info['sys.shadow.'.$key] = $value;
2666
			}
2667
		}
2668
		return $this;
2669
	}
2670
2671
	/**
2672
	 * Creates a N-M relation by linking an intermediate bean.
2673
	 * This method can be used to quickly connect beans using indirect
2674
	 * relations. For instance, given an album and a song you can connect the two
2675
	 * using a track with a number like this:
2676
	 *
2677
	 * Usage:
2678
	 *
2679
	 * $album->link('track', array('number'=>1))->song = $song;
2680
	 *
2681
	 * or:
2682
	 *
2683
	 * $album->link($trackBean)->song = $song;
2684
	 *
2685
	 * What this method does is adding the link bean to the own-list, in this case
2686
	 * ownTrack. If the first argument is a string and the second is an array or
2687
	 * a JSON string then the linking bean gets dispensed on-the-fly as seen in
2688
	 * example #1. After preparing the linking bean, the bean is returned thus
2689
	 * allowing the chained setter: ->song = $song.
2690
	 *
2691
	 * @param string|OODBBean $type          type of bean to dispense or the full bean
0 ignored issues
show
Bug introduced by
There is no parameter named $type. 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...
2692
	 * @param string|array            $qualification JSON string or array (optional)
2693
	 *
2694
	 * @return OODBBean
2695
	 */
2696
	public function link( $typeOrBean, $qualification = array() )
2697
	{
2698
		if ( is_string( $typeOrBean ) ) {
2699
2700
			$typeOrBean = AQueryWriter::camelsSnake( $typeOrBean );
2701
2702
			$bean = $this->beanHelper->getToolBox()->getRedBean()->dispense( $typeOrBean );
2703
2704
			if ( is_string( $qualification ) ) {
2705
				$data = json_decode( $qualification, TRUE );
2706
			} else {
2707
				$data = $qualification;
2708
			}
2709
2710
			foreach ( $data as $key => $value ) {
2711
				$bean->$key = $value;
2712
			}
2713
		} else {
2714
			$bean = $typeOrBean;
2715
		}
2716
2717
		$list = 'own' . ucfirst( $bean->getMeta( 'type' ) );
2718
2719
		array_push( $this->$list, $bean );
2720
2721
		return $bean;
2722
	}
2723
2724
	/**
2725
	 * Returns the same bean freshly loaded from the database.
2726
	 *
2727
	 * @return OODBBean
2728
	 */
2729
	public function fresh()
2730
	{
2731
		return $this->beanHelper->getToolbox()->getRedBean()->load( $this->getMeta( 'type' ), $this->properties['id'] );
2732
	}
2733
2734
	/**
2735
	 * Registers a association renaming globally.
2736
	 *
2737
	 * @param string $via type you wish to use for shared lists
2738
	 *
2739
	 * @return OODBBean
2740
	 */
2741
	public function via( $via )
2742
	{
2743
		$this->via = AQueryWriter::camelsSnake( $via );
2744
2745
		return $this;
2746
	}
2747
2748
	/**
2749
	 * Counts all own beans of type $type.
2750
	 * Also works with alias(), with() and withCondition().
2751
	 *
2752
	 * @param string $type the type of bean you want to count
2753
	 *
2754
	 * @return integer
2755
	 */
2756
	public function countOwn( $type )
2757
	{
2758
		$type = $this->beau( $type );
2759
2760
		if ( $this->aliasName ) {
2761
			$myFieldLink     = $this->aliasName . '_id';
2762
2763
			$this->aliasName = NULL;
2764
		} else {
2765
			$myFieldLink = $this->__info['type'] . '_id';
2766
		}
2767
2768
		$count = 0;
2769
2770
		if ( $this->getID() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getID() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2771
2772
			$firstKey = NULL;
2773 View Code Duplication
			if ( count( $this->withParams ) > 0 ) {
2774
				reset( $this->withParams );
2775
				$firstKey = key( $this->withParams );
2776
			}
2777
2778
			$joinSql = $this->parseJoin( $type );
2779
2780
			if ( !is_numeric( $firstKey ) || $firstKey === NULL ) {
2781
					$bindings           = $this->withParams;
2782
					$bindings[':slot0'] = $this->getID();
2783
					$count              = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
2784
			} else {
2785
					$bindings = array_merge( array( $this->getID() ), $this->withParams );
2786
					$count    = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
2787
			}
2788
2789
		}
2790
2791
		$this->clearModifiers();
2792
		return (int) $count;
2793
	}
2794
2795
	/**
2796
	 * Counts all shared beans of type $type.
2797
	 * Also works with via(), with() and withCondition().
2798
	 *
2799
	 * @param string $type type of bean you wish to count
2800
	 *
2801
	 * @return integer
2802
	 */
2803
	public function countShared( $type )
2804
	{
2805
		$toolbox = $this->beanHelper->getToolbox();
2806
		$redbean = $toolbox->getRedBean();
2807
		$writer  = $toolbox->getWriter();
2808
2809 View Code Duplication
		if ( $this->via ) {
2810
			$oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
2811
2812
			if ( $oldName !== $this->via ) {
2813
				//set the new renaming rule
2814
				$writer->renameAssocTable( $oldName, $this->via );
2815
				$this->via = NULL;
2816
			}
2817
		}
2818
2819
		$type  = $this->beau( $type );
2820
		$count = 0;
2821
2822
		if ( $this->getID() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getID() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2823
			$count = $redbean->getAssociationManager()->relatedCount( $this, $type, $this->withSql, $this->withParams, TRUE );
0 ignored issues
show
Unused Code introduced by
The call to AssociationManager::relatedCount() 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...
2824
		}
2825
2826
		$this->clearModifiers();
2827
		return (integer) $count;
2828
	}
2829
2830
	/**
2831
	 * Iterates through the specified own-list and
2832
	 * fetches all properties (with their type) and
2833
	 * returns the references.
2834
	 * Use this method to quickly load indirectly related
2835
	 * beans in an own-list. Whenever you cannot use a
2836
	 * shared-list this method offers the same convenience
2837
	 * by aggregating the parent beans of all children in
2838
	 * the specified own-list.
2839
	 *
2840
	 * Example:
2841
	 *
2842
	 * $quest->aggr( 'xownQuestTarget', 'target', 'quest' );
2843
	 *
2844
	 * Loads (in batch) and returns references to all
2845
	 * quest beans residing in the $questTarget->target properties
2846
	 * of each element in the xownQuestTargetList.
2847
	 *
2848
	 * @param string $list     the list you wish to process
2849
	 * @param string $property the property to load
2850
	 * @param string $type     the type of bean residing in this property (optional)
2851
	 *
2852
	 * @return array
2853
	 */
2854
	public function &aggr( $list, $property, $type = NULL )
2855
	{
2856
		$this->via = NULL;
2857
		$ids = $beanIndex = $references = array();
2858
2859
		if ( strlen( $list ) < 4 ) throw new RedException('Invalid own-list.');
2860
		if ( strpos( $list, 'own') !== 0 ) throw new RedException('Only own-lists can be aggregated.');
2861
		if ( !ctype_upper( substr( $list, 3, 1 ) ) ) throw new RedException('Invalid own-list.');
2862
2863
		if ( is_null( $type ) ) $type = $property;
2864
2865
		foreach( $this->$list as $bean ) {
2866
			$field = $property . '_id';
2867
			if ( isset( $bean->$field)  ) {
2868
				$ids[] = $bean->$field;
2869
				$beanIndex[$bean->$field] = $bean;
2870
			}
2871
		}
2872
2873
		$beans = $this->beanHelper->getToolBox()->getRedBean()->batch( $type, $ids );
2874
2875
		//now preload the beans as well
2876
		foreach( $beans as $bean ) {
2877
			$beanIndex[$bean->id]->setProperty( $property, $bean );
2878
		}
2879
2880
		foreach( $beanIndex as $indexedBean ) {
2881
			$references[] = $indexedBean->$property;
2882
		}
2883
2884
		return $references;
2885
	}
2886
2887
	/**
2888
	 * Tests whether the database identities of two beans are equal.
2889
	 *
2890
	 * @param OODBBean $bean other bean
2891
	 *
2892
	 * @return boolean
2893
	 */
2894
	public function equals(OODBBean $bean)
2895
	{
2896
		return (bool) (
2897
			   ( (string) $this->properties['id'] === (string) $bean->properties['id'] )
2898
			&& ( (string) $this->__info['type']   === (string) $bean->__info['type']   )
2899
		);
2900
	}
2901
}
2902
} 
2903
2904
namespace RedBeanPHP {
2905
2906
use RedBeanPHP\Observer as Observer;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
2907
2908
/**
2909
 * Observable
2910
 * Base class for Observables
2911
 *
2912
 * @file            RedBeanPHP/Observable.php
2913
 * @author          Gabor de Mooij and the RedBeanPHP community
2914
 * @license         BSD/GPLv2
2915
 *
2916
 * @copyright
2917
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
2918
 * This source file is subject to the BSD/GPLv2 License that is bundled
2919
 * with this source code in the file license.txt.
2920
 */
2921
abstract class Observable { //bracket must be here - otherwise coverage software does not understand.
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
2922
2923
	/**
2924
	 * @var array
2925
	 */
2926
	private $observers = array();
2927
2928
	/**
2929
	 * Implementation of the Observer Pattern.
2930
	 * Adds an event listener to the observable object.
2931
	 * First argument should be the name of the event you wish to listen for.
2932
	 * Second argument should be the object that wants to be notified in case
2933
	 * the event occurs.
2934
	 *
2935
	 * @param string           $eventname event identifier
2936
	 * @param Observer $observer  observer instance
2937
	 *
2938
	 * @return void
2939
	 */
2940
	public function addEventListener( $eventname, Observer $observer )
2941
	{
2942
		if ( !isset( $this->observers[$eventname] ) ) {
2943
			$this->observers[$eventname] = array();
2944
		}
2945
2946
		foreach ( $this->observers[$eventname] as $o ) {
2947
			if ( $o == $observer ) {
2948
				return;
2949
			}
2950
		}
2951
2952
		$this->observers[$eventname][] = $observer;
2953
	}
2954
2955
	/**
2956
	 * Notifies listeners.
2957
	 * Sends the signal $eventname, the event identifier and a message object
2958
	 * to all observers that have been registered to receive notification for
2959
	 * this event. Part of the observer pattern implementation in RedBeanPHP.
2960
	 *
2961
	 * @param string $eventname event you want signal
2962
	 * @param mixed  $info      message object to send along
2963
	 *
2964
	 * @return void
2965
	 */
2966
	public function signal( $eventname, $info )
2967
	{
2968
		if ( !isset( $this->observers[$eventname] ) ) {
2969
			$this->observers[$eventname] = array();
2970
		}
2971
2972
		foreach ( $this->observers[$eventname] as $observer ) {
2973
			$observer->onEvent( $eventname, $info );
2974
		}
2975
	}
2976
}
2977
} 
2978
2979
namespace RedBeanPHP {
2980
2981
/**
2982
 * Observer.
2983
 *
2984
 * Interface for Observer object. Implementation of the
2985
 * observer pattern.
2986
 *
2987
 * @file    RedBeanPHP/Observer.php
2988
 * @author  Gabor de Mooij and the RedBeanPHP community
2989
 * @license BSD/GPLv2
2990
 * @desc    Part of the observer pattern in RedBean
2991
 *
2992
 * @copyright
2993
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
2994
 * This source file is subject to the BSD/GPLv2 License that is bundled
2995
 * with this source code in the file license.txt.
2996
 */
2997
interface Observer
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
2998
{
2999
	/**
3000
	 * An observer object needs to be capable of receiving
3001
	 * notifications. Therefore the observer needs to implement the
3002
	 * onEvent method with two parameters: the event identifier specifying the
3003
	 * current event and a message object (in RedBeanPHP this can also be a bean).
3004
	 * 
3005
	 * @param string $eventname event identifier
3006
	 * @param mixed  $bean      a message sent along with the notification
3007
	 *
3008
	 * @return void
3009
	 */
3010
	public function onEvent( $eventname, $bean );
3011
}
3012
} 
3013
3014
namespace RedBeanPHP {
3015
3016
/**
3017
 * Adapter Interface.
3018
 * Describes the API for a RedBeanPHP Database Adapter.
3019
 * This interface defines the API contract for
3020
 * a RedBeanPHP Database Adapter.  
3021
 *
3022
 * @file    RedBeanPHP/Adapter.php
3023
 * @author  Gabor de Mooij and the RedBeanPHP Community
3024
 * @license BSD/GPLv2
3025
 *
3026
 * @copyright
3027
 * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
3028
 * This source file is subject to the BSD/GPLv2 License that is bundled
3029
 * with this source code in the file license.txt.
3030
 */
3031
interface Adapter
0 ignored issues
show
Coding Style Compatibility introduced by
Each interface must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
3032
{
3033
	/**
3034
	 * Returns the latest SQL statement
3035
	 *
3036
	 * @return string
3037
	 */
3038
	public function getSQL();
3039
3040
	/**
3041
	 * Executes an SQL Statement using an array of values to bind
3042
	 * If $noevent is TRUE then this function will not signal its
3043
	 * observers to notify about the SQL execution; this to prevent
3044
	 * infinite recursion when using observers.
3045
	 *
3046
	 * @param string  $sql      SQL
3047
	 * @param array   $bindings values
3048
	 * @param boolean $noevent  no event firing
3049
	 */
3050
	public function exec( $sql, $bindings = array(), $noevent = FALSE );
3051
3052
	/**
3053
	 * Executes an SQL Query and returns a resultset.
3054
	 * This method returns a multi dimensional resultset similar to getAll
3055
	 * The values array can be used to bind values to the place holders in the
3056
	 * SQL query.
3057
	 *
3058
	 * @param string $sql      SQL
3059
	 * @param array  $bindings values
3060
	 *
3061
	 * @return array
3062
	 */
3063
	public function get( $sql, $bindings = array() );
3064
3065
	/**
3066
	 * Executes an SQL Query and returns a resultset.
3067
	 * This method returns a single row (one array) resultset.
3068
	 * The values array can be used to bind values to the place holders in the
3069
	 * SQL query.
3070
	 *
3071
	 * @param string $sql      SQL
3072
	 * @param array  $bindings values to bind
3073
	 *
3074
	 * @return array
3075
	 */
3076
	public function getRow( $sql, $bindings = array() );
3077
3078
	/**
3079
	 * Executes an SQL Query and returns a resultset.
3080
	 * This method returns a single column (one array) resultset.
3081
	 * The values array can be used to bind values to the place holders in the
3082
	 * SQL query.
3083
	 *
3084
	 * @param string $sql      SQL
3085
	 * @param array  $bindings values to bind
3086
	 *
3087
	 * @return array
3088
	 */
3089
	public function getCol( $sql, $bindings = array() );
3090
3091
	/**
3092
	 * Executes an SQL Query and returns a resultset.
3093
	 * This method returns a single cell, a scalar value as the resultset.
3094
	 * The values array can be used to bind values to the place holders in the
3095
	 * SQL query.
3096
	 *
3097
	 * @param string $sql      SQL
3098
	 * @param array  $bindings values to bind
3099
	 *
3100
	 * @return string
3101
	 */
3102
	public function getCell( $sql, $bindings = array() );
3103
3104
	/**
3105
	 * Executes the SQL query specified in $sql and takes
3106
	 * the first two columns of the resultset. This function transforms the
3107
	 * resultset into an associative array. Values from the the first column will
3108
	 * serve as keys while the values of the second column will be used as values.
3109
	 * The values array can be used to bind values to the place holders in the
3110
	 * SQL query.
3111
	 *
3112
	 * @param string $sql      SQL
3113
	 * @param array  $bindings values to bind
3114
	 *
3115
	 * @return array
3116
	 */
3117
	public function getAssoc( $sql, $bindings = array() );
3118
	
3119
	/**
3120
	 * Executes the SQL query specified in $sql and indexes
3121
	 * the row by the first column.
3122
	 * 
3123
	 * @param string $sql      SQL
3124
	 * @param array  $bindings values to bind
3125
	 *
3126
	 * @return array
3127
	 */
3128
	public function getAssocRow( $sql, $bindings = array() );
3129
3130
	/**
3131
	 * Returns the latest insert ID.
3132
	 *
3133
	 * @return integer
3134
	 */
3135
	public function getInsertID();
3136
3137
	/**
3138
	 * Returns the number of rows that have been
3139
	 * affected by the last update statement.
3140
	 *
3141
	 * @return integer
3142
	 */
3143
	public function getAffectedRows();
3144
3145
	/**
3146
	 * Returns a database agnostic Cursor object.
3147
	 *
3148
	 * @param string $sql      SQL
3149
	 * @param array  $bindings bindings
3150
	 *
3151
	 * @return Cursor
3152
	 */
3153
	public function getCursor( $sql, $bindings = array() );
3154
3155
	/**
3156
	 * Returns the original database resource. This is useful if you want to
3157
	 * perform operations on the driver directly instead of working with the
3158
	 * adapter. RedBean will only access the adapter and never to talk
3159
	 * directly to the driver though.
3160
	 *
3161
	 * @return object
3162
	 */
3163
	public function getDatabase();
3164
3165
	/**
3166
	 * This method is part of the RedBean Transaction Management
3167
	 * mechanisms.
3168
	 * Starts a transaction.
3169
	 *
3170
	 * @return void
3171
	 */
3172
	public function startTransaction();
3173
3174
	/**
3175
	 * This method is part of the RedBean Transaction Management
3176
	 * mechanisms.
3177
	 * Commits the transaction.
3178
	 *
3179
	 * @return void
3180
	 */
3181
	public function commit();
3182
3183
	/**
3184
	 * This method is part of the RedBean Transaction Management
3185
	 * mechanisms.
3186
	 * Rolls back the transaction.
3187
	 *
3188
	 * @return void
3189
	 */
3190
	public function rollback();
3191
3192
	/**
3193
	 * Closes database connection.
3194
	 *
3195
	 * @return void
3196
	 */
3197
	public function close();
3198
}
3199
}
3200
3201
namespace RedBeanPHP\Adapter {
3202
3203
use RedBeanPHP\Observable as Observable;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
3204
use RedBeanPHP\Adapter as Adapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
3205
use RedBeanPHP\Driver as Driver;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
3206
3207
/**
3208
 * DBAdapter (Database Adapter)
3209
 *
3210
 * An adapter class to connect various database systems to RedBean
3211
 * Database Adapter Class. The task of the database adapter class is to
3212
 * communicate with the database driver. You can use all sorts of database
3213
 * drivers with RedBeanPHP. The default database drivers that ships with
3214
 * the RedBeanPHP library is the RPDO driver ( which uses the PHP Data Objects
3215
 * Architecture aka PDO ).
3216
 *
3217
 * @file    RedBeanPHP/Adapter/DBAdapter.php
3218
 * @author  Gabor de Mooij and the RedBeanPHP Community.
3219
 * @license BSD/GPLv2
3220
 *
3221
 * @copyright
3222
 * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP community.
3223
 * This source file is subject to the BSD/GPLv2 License that is bundled
3224
 * with this source code in the file license.txt.
3225
 */
3226
class DBAdapter extends Observable implements Adapter
0 ignored issues
show
Coding Style Compatibility introduced by
Each interface must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
3227
{
3228
	/**
3229
	 * @var Driver
3230
	 */
3231
	private $db = NULL;
3232
3233
	/**
3234
	 * @var string
3235
	 */
3236
	private $sql = '';
3237
3238
	/**
3239
	 * Constructor.
3240
	 *
3241
	 * Creates an instance of the RedBean Adapter Class.
3242
	 * This class provides an interface for RedBean to work
3243
	 * with ADO compatible DB instances.
3244
	 *
3245
	 * @param Driver $database ADO Compatible DB Instance
3246
	 */
3247
	public function __construct( $database )
3248
	{
3249
		$this->db = $database;
3250
	}
3251
3252
	/**
3253
	 * @see Adapter::getSQL
3254
	 */
3255
	public function getSQL()
3256
	{
3257
		return $this->sql;
3258
	}
3259
3260
	/**
3261
	 * @see Adapter::exec
3262
	 */
3263
	public function exec( $sql, $bindings = array(), $noevent = FALSE )
3264
	{
3265
		if ( !$noevent ) {
3266
			$this->sql = $sql;
3267
			$this->signal( 'sql_exec', $this );
3268
		}
3269
3270
		return $this->db->Execute( $sql, $bindings );
3271
	}
3272
3273
	/**
3274
	 * @see Adapter::get
3275
	 */
3276
	public function get( $sql, $bindings = array() )
3277
	{
3278
		$this->sql = $sql;
3279
		$this->signal( 'sql_exec', $this );
3280
3281
		return $this->db->GetAll( $sql, $bindings );
3282
	}
3283
3284
	/**
3285
	 * @see Adapter::getRow
3286
	 */
3287
	public function getRow( $sql, $bindings = array() )
3288
	{
3289
		$this->sql = $sql;
3290
		$this->signal( 'sql_exec', $this );
3291
3292
		return $this->db->GetRow( $sql, $bindings );
3293
	}
3294
3295
	/**
3296
	 * @see Adapter::getCol
3297
	 */
3298
	public function getCol( $sql, $bindings = array() )
3299
	{
3300
		$this->sql = $sql;
3301
		$this->signal( 'sql_exec', $this );
3302
3303
		return $this->db->GetCol( $sql, $bindings );
3304
	}
3305
3306
	/**
3307
	 * @see Adapter::getAssoc
3308
	 */
3309
	public function getAssoc( $sql, $bindings = array() )
3310
	{
3311
		$this->sql = $sql;
3312
3313
		$this->signal( 'sql_exec', $this );
3314
3315
		$rows  = $this->db->GetAll( $sql, $bindings );
3316
3317
		$assoc = array();
3318
		if ( !$rows ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rows of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
3319
			return $assoc;
3320
		}
3321
3322
		foreach ( $rows as $row ) {
3323
			if ( empty( $row ) ) continue;
3324
3325
			if ( count( $row ) > 2 ) {
3326
            $key   = array_shift( $row );
3327
            $value = $row;
3328
        } elseif ( count( $row ) > 1 ) {
3329
				$key   = array_shift( $row );
3330
				$value = array_shift( $row );
3331
			} else {
3332
				$key   = array_shift( $row );
3333
				$value = $key;
3334
			}
3335
3336
			$assoc[$key] = $value;
3337
		}
3338
3339
		return $assoc;
3340
	}
3341
	
3342
	/**
3343
	 * @see Adapter::getAssocRow
3344
	 */
3345
	public function getAssocRow($sql, $bindings = array())
3346
	{
3347
		$this->sql = $sql;
3348
		$this->signal( 'sql_exec', $this );
3349
3350
		return $this->db->GetAssocRow( $sql, $bindings );
3351
	}
3352
3353
	/**
3354
	 * @see Adapter::getCell
3355
	 */
3356
	public function getCell( $sql, $bindings = array(), $noSignal = NULL )
3357
	{
3358
		$this->sql = $sql;
3359
3360
		if ( !$noSignal ) $this->signal( 'sql_exec', $this );
3361
3362
		return $this->db->GetOne( $sql, $bindings );
3363
	}
3364
3365
	/**
3366
	 * @see Adapter::getCursor
3367
	 */
3368
	public function getCursor( $sql, $bindings = array() )
3369
	{
3370
		return $this->db->GetCursor( $sql, $bindings );
3371
	}
3372
3373
	/**
3374
	 * @see Adapter::getInsertID
3375
	 */
3376
	public function getInsertID()
3377
	{
3378
		return $this->db->getInsertID();
3379
	}
3380
3381
	/**
3382
	 * @see Adapter::getAffectedRows
3383
	 */
3384
	public function getAffectedRows()
3385
	{
3386
		return $this->db->Affected_Rows();
3387
	}
3388
3389
	/**
3390
	 * @see Adapter::getDatabase
3391
	 */
3392
	public function getDatabase()
3393
	{
3394
		return $this->db;
3395
	}
3396
3397
	/**
3398
	 * @see Adapter::startTransaction
3399
	 */
3400
	public function startTransaction()
3401
	{
3402
		$this->db->StartTrans();
3403
	}
3404
3405
	/**
3406
	 * @see Adapter::commit
3407
	 */
3408
	public function commit()
3409
	{
3410
		$this->db->CommitTrans();
3411
	}
3412
3413
	/**
3414
	 * @see Adapter::rollback
3415
	 */
3416
	public function rollback()
3417
	{
3418
		$this->db->FailTrans();
3419
	}
3420
3421
	/**
3422
	 * @see Adapter::close.
3423
	 */
3424
	public function close()
3425
	{
3426
		$this->db->close();
3427
	}
3428
}
3429
}
3430
3431
namespace RedBeanPHP {
3432
3433
/**
3434
 * Database Cursor Interface.
3435
 * Represents a simple database cursor.
3436
 * Cursors make it possible to create lightweight BeanCollections.
3437
 *
3438
 * @file    RedBeanPHP/Cursor.php
3439
 * @author  Gabor de Mooij and the RedBeanPHP Community
3440
 * @license BSD/GPLv2
3441
 *
3442
 * @copyright
3443
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
3444
 * This source file is subject to the BSD/GPLv2 License that is bundled
3445
 * with this source code in the file license.txt.
3446
 */
3447
interface Cursor
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
3448
{
3449
	/**
3450
	 * Retrieves the next row from the result set.
3451
	 *
3452
	 * @return array
3453
	 */
3454
	public function getNextItem();
3455
3456
	/**
3457
	 * Closes the database cursor.
3458
	 * Some databases require a cursor to be closed before executing
3459
	 * another statement/opening a new cursor.
3460
	 *
3461
	 * @return void
3462
	 */
3463
	public function close();
3464
}
3465
}
3466
3467
namespace RedBeanPHP\Cursor {
3468
3469
use RedBeanPHP\Cursor as Cursor;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
3470
3471
/**
3472
 * PDO Database Cursor
3473
 * Implementation of PDO Database Cursor.
3474
 * Used by the BeanCollection to fetch one bean at a time.
3475
 *
3476
 * @file    RedBeanPHP/Cursor/PDOCursor.php
3477
 * @author  Gabor de Mooij and the RedBeanPHP Community
3478
 * @license BSD/GPLv2
3479
 *
3480
 * @copyright
3481
 * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
3482
 * This source file is subject to the BSD/GPLv2 License that is bundled
3483
 * with this source code in the file license.txt.
3484
 */
3485
class PDOCursor implements Cursor
0 ignored issues
show
Coding Style Compatibility introduced by
Each interface must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
3486
{
3487
	/**
3488
	 * @var PDOStatement
3489
	 */
3490
	protected $res;
3491
3492
	/**
3493
	 * @var string
3494
	 */
3495
	protected $fetchStyle;
3496
3497
	/**
3498
	 * Constructor, creates a new instance of a PDO Database Cursor.
3499
	 *
3500
	 * @param PDOStatement  $res        the PDO statement
3501
	 * @param string        $fetchStyle fetch style constant to use
3502
	 *
3503
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
3504
	 */
3505
	public function __construct( \PDOStatement $res, $fetchStyle )
3506
	{
3507
		$this->res = $res;
0 ignored issues
show
Documentation Bug introduced by
It seems like $res of type object<PDOStatement> is incompatible with the declared type object<RedBeanPHP\Cursor\PDOStatement> of property $res.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
3508
		$this->fetchStyle = $fetchStyle;
3509
	}
3510
3511
	/**
3512
	 * @see Cursor::getNextItem
3513
	 */
3514
	public function getNextItem()
3515
	{
3516
		return $this->res->fetch();
3517
	}
3518
3519
	/**
3520
	 * @see Cursor::close
3521
	 */
3522
	public function close()
3523
	{
3524
		$this->res->closeCursor();
3525
	}
3526
}
3527
}
3528
3529
namespace RedBeanPHP\Cursor {
3530
3531
use RedBeanPHP\Cursor as Cursor;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
3532
3533
/**
3534
 * NULL Database Cursor
3535
 * Implementation of the NULL Cursor.
3536
 * Used for an empty BeanCollection.
3537
 *
3538
 * @file    RedBeanPHP/Cursor/NULLCursor.php
3539
 * @author  Gabor de Mooij and the RedBeanPHP Community
3540
 * @license BSD/GPLv2
3541
 *
3542
 * @copyright
3543
 * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
3544
 * This source file is subject to the BSD/GPLv2 License that is bundled
3545
 * with this source code in the file license.txt.
3546
 */
3547
class NullCursor implements Cursor
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
3548
{
3549
	/**
3550
	 * @see Cursor::getNextItem
3551
	 */
3552
	public function getNextItem()
3553
	{
3554
		return NULL;
3555
	}
3556
3557
	/**
3558
	 * @see Cursor::close
3559
	 */
3560
	public function close()
3561
	{
3562
		return NULL;
3563
	}
3564
}
3565
}
3566
3567
namespace RedBeanPHP {
3568
3569
use RedBeanPHP\Cursor as Cursor;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
3570
use RedBeanPHP\Repository as Repository;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
3571
3572
/**
3573
 * BeanCollection.
3574
 *
3575
 * The BeanCollection represents a collection of beans and
3576
 * makes it possible to use database cursors. The BeanCollection
3577
 * has a method next() to obtain the first, next and last bean
3578
 * in the collection. The BeanCollection does not implement the array
3579
 * interface nor does it try to act like an array because it cannot go
3580
 * backward or rewind itself.
3581
 *
3582
 * Use the BeanCollection for large datasets where skip/limit is not an
3583
 * option. Keep in mind that ID-marking (querying a start ID) is a decent
3584
 * alternative though.
3585
 *
3586
 * @file    RedBeanPHP/BeanCollection.php
3587
 * @author  Gabor de Mooij and the RedBeanPHP community
3588
 * @license BSD/GPLv2
3589
 *
3590
 * @copyright
3591
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
3592
 * This source file is subject to the BSD/GPLv2 License that is bundled
3593
 * with this source code in the file license.txt.
3594
 */
3595
class BeanCollection
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
3596
{
3597
	/**
3598
	 * @var Cursor
3599
	 */
3600
	protected $cursor = NULL;
3601
3602
	/**
3603
	 * @var Repository
3604
	 */
3605
	protected $repository = NULL;
3606
3607
	/**
3608
	 * @var string
3609
	 */
3610
	protected $type = NULL;
3611
	
3612
	/**
3613
	 * Constructor, creates a new instance of the BeanCollection.
3614
	 * 
3615
	 * @param string     $type       type of beans in this collection
3616
	 * @param Repository $repository repository to use to generate bean objects
3617
	 * @param Cursor     $cursor     cursor object to use
3618
	 *
3619
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
3620
	 */
3621
	public function __construct( $type, Repository $repository, Cursor $cursor )
3622
	{
3623
		$this->type = $type;
3624
		$this->cursor = $cursor;
3625
		$this->repository = $repository;
3626
	}
3627
3628
	/**
3629
	 * Returns the next bean in the collection.
3630
	 * If called the first time, this will return the first bean in the collection.
3631
	 * If there are no more beans left in the collection, this method
3632
	 * will return NULL.
3633
	 *
3634
	 * @return OODBBean|NULL
3635
	 */
3636
	public function next()
3637
	{
3638
		$row = $this->cursor->getNextItem();
3639
		if ( $row ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $row of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
3640
			$beans = $this->repository->convertToBeans( $this->type, array( $row ) );
3641
			$bean = array_shift( $beans );
3642
			return $bean;
3643
		}
3644
		return NULL;
3645
	}
3646
3647
	/**
3648
	 * Closes the underlying cursor (needed for some databases).
3649
	 *
3650
	 * @return void
3651
	 */
3652
	public function close()
3653
	{
3654
		$this->cursor->close();
3655
	}
3656
}
3657
}
3658
3659
namespace RedBeanPHP {
3660
3661
/**
3662
 * QueryWriter
3663
 * Interface for QueryWriters.
3664
 * Describes the API for a QueryWriter.
3665
 *
3666
 * Terminology:
3667
 *
3668
 * - beautified property (a camelCased property, has to be converted first)
3669
 * - beautified type (a camelCased type, has to be converted first)
3670
 * - type (a bean type, corresponds directly to a table)
3671
 * - property (a bean property, corresponds directly to a column)
3672
 * - table (a checked and quoted type, ready for use in a query)
3673
 * - column (a checked and quoted property, ready for use in query)
3674
 * - tableNoQ (same as type, but in context of a database operation)
3675
 * - columnNoQ (same as property, but in context of a database operation)
3676
 *
3677
 * @file    RedBeanPHP/QueryWriter.php
3678
 * @author  Gabor de Mooij and the RedBeanPHP community
3679
 * @license BSD/GPLv2
3680
 *
3681
 * @copyright
3682
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
3683
 * This source file is subject to the BSD/GPLv2 License that is bundled
3684
 * with this source code in the file license.txt.
3685
 */
3686
interface QueryWriter
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
3687
{
3688
	/**
3689
	 * SQL filter constants
3690
	 */
3691
	const C_SQLFILTER_READ = 'r';
3692
	const C_SQLFILTER_WRITE = 'w';
3693
3694
	/**
3695
	 * Query Writer constants.
3696
	 */
3697
	const C_SQLSTATE_NO_SUCH_TABLE                  = 1;
3698
	const C_SQLSTATE_NO_SUCH_COLUMN                 = 2;
3699
	const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3;
3700
3701
	/**
3702
	 * Define data type regions
3703
	 *
3704
	 * 00 - 80: normal data types
3705
	 * 80 - 99: special data types, only scan/code if requested
3706
	 * 99     : specified by user, don't change
3707
	 */
3708
	const C_DATATYPE_RANGE_SPECIAL   = 80;
3709
	const C_DATATYPE_RANGE_SPECIFIED = 99;
3710
3711
	/**
3712
	 * Define GLUE types for use with glueSQLCondition methods.
3713
	 * Determines how to prefix a snippet of SQL before appending it
3714
	 * to other SQL (or integrating it, mixing it otherwise).
3715
	 *
3716
	 * WHERE - glue as WHERE condition
3717
	 * AND   - glue as AND condition
3718
	 */
3719
	const C_GLUE_WHERE = 1;
3720
	const C_GLUE_AND   = 2;
3721
3722
	/**
3723
	 * Writes an SQL Snippet for a JOIN, returns the
3724
	 * SQL snippet string.
3725
	 *
3726
	 * @note A default implementation is available in AQueryWriter
3727
	 * unless a database uses very different SQL this should suffice.
3728
	 *
3729
	 * @param string $type       source type
3730
	 * @param string $targetType target type (type to join)
3731
	 * @param string $leftRight  type of join (possible: 'LEFT', 'RIGHT' or 'INNER').
0 ignored issues
show
Bug introduced by
There is no parameter named $leftRight. 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...
3732
	 *
3733
	 * @return string $joinSQLSnippet
3734
	 */
3735
	public function writeJoin( $type, $targetType, $joinType );
3736
3737
	/**
3738
	 * Glues an SQL snippet to the beginning of a WHERE clause.
3739
	 * This ensures users don't have to add WHERE to their query snippets.
3740
	 *
3741
	 * The snippet gets prefixed with WHERE or AND
3742
	 * if it starts with a condition.
3743
	 *
3744
	 * If the snippet does NOT start with a condition (or this function thinks so)
3745
	 * the snippet is returned as-is.
3746
	 *
3747
	 * The GLUE type determines the prefix:
3748
	 *
3749
	 * - NONE  prefixes with WHERE
3750
	 * - WHERE prefixes with WHERE and replaces AND if snippets starts with AND
3751
	 * - AND   prefixes with AND
3752
	 *
3753
	 * This method will never replace WHERE with AND since a snippet should never
3754
	 * begin with WHERE in the first place. OR is not supported.
3755
	 *
3756
	 * Only a limited set of clauses will be recognized as non-conditions.
3757
	 * For instance beginning a snippet with complex statements like JOIN or UNION
3758
	 * will not work. This is too complex for use in a snippet.
3759
	 *
3760
	 * @note A default implementation is available in AQueryWriter
3761
	 * unless a database uses very different SQL this should suffice.
3762
	 *
3763
	 * @param string  $sql   SQL Snippet
3764
	 * @param integer $glue  the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND)
3765
	 *
3766
	 * @return string
3767
	 */
3768
	public function glueSQLCondition( $sql, $glue = NULL );
3769
3770
	/**
3771
	 * Determines if there is a LIMIT 1 clause in the SQL.
3772
	 * If not, it will add a LIMIT 1. (used for findOne).
3773
	 *
3774
	 * @note A default implementation is available in AQueryWriter
3775
	 * unless a database uses very different SQL this should suffice.
3776
	 *
3777
	 * @param string $sql query to scan and adjust
3778
	 *
3779
	 * @return string
3780
	 */
3781
	public function glueLimitOne( $sql );
3782
3783
	/**
3784
	 * Returns the tables that are in the database.
3785
	 *
3786
	 * @return array
3787
	 */
3788
	public function getTables();
3789
3790
	/**
3791
	 * This method will create a table for the bean.
3792
	 * This methods accepts a type and infers the corresponding table name.
3793
	 *
3794
	 * @param string $type type of bean you want to create a table for
3795
	 *
3796
	 * @return void
3797
	 */
3798
	public function createTable( $type );
3799
3800
	/**
3801
	 * Returns an array containing all the columns of the specified type.
3802
	 * The format of the return array looks like this:
3803
	 * $field => $type where $field is the name of the column and $type
3804
	 * is a database specific description of the datatype.
3805
	 *
3806
	 * This methods accepts a type and infers the corresponding table name.
3807
	 *
3808
	 * @param string $type type of bean you want to obtain a column list of
3809
	 *
3810
	 * @return array
3811
	 */
3812
	public function getColumns( $type );
3813
3814
	/**
3815
	 * Returns the Column Type Code (integer) that corresponds
3816
	 * to the given value type. This method is used to determine the minimum
3817
	 * column type required to represent the given value.
3818
	 *
3819
	 * @param string $value value
3820
	 *
3821
	 * @return integer
3822
	 */
3823
	public function scanType( $value, $alsoScanSpecialForTypes = FALSE );
3824
3825
	/**
3826
	 * This method will add a column to a table.
3827
	 * This methods accepts a type and infers the corresponding table name.
3828
	 *
3829
	 * @param string  $type   name of the table
3830
	 * @param string  $column name of the column
3831
	 * @param integer $field  data type for field
3832
	 *
3833
	 * @return void
3834
	 */
3835
	public function addColumn( $type, $column, $field );
3836
3837
	/**
3838
	 * Returns the Type Code for a Column Description.
3839
	 * Given an SQL column description this method will return the corresponding
3840
	 * code for the writer. If the include specials flag is set it will also
3841
	 * return codes for special columns. Otherwise special columns will be identified
3842
	 * as specified columns.
3843
	 *
3844
	 * @param string  $typedescription description
3845
	 * @param boolean $includeSpecials whether you want to get codes for special columns as well
3846
	 *
3847
	 * @return integer
3848
	 */
3849
	public function code( $typedescription, $includeSpecials = FALSE );
3850
3851
	/**
3852
	 * This method will widen the column to the specified data type.
3853
	 * This methods accepts a type and infers the corresponding table name.
3854
	 *
3855
	 * @param string  $type       type / table that needs to be adjusted
3856
	 * @param string  $column     column that needs to be altered
3857
	 * @param integer $datatype   target data type
3858
	 *
3859
	 * @return void
3860
	 */
3861
	public function widenColumn( $type, $column, $datatype );
3862
3863
	/**
3864
	 * Selects records from the database.
3865
	 * This methods selects the records from the database that match the specified
3866
	 * type, conditions (optional) and additional SQL snippet (optional).
3867
	 *
3868
	 * @param string $type       name of the table you want to query
3869
	 * @param array  $conditions criteria ( $column => array( $values ) )
3870
	 * @param string $addSQL     additional SQL snippet
0 ignored issues
show
Documentation introduced by
There is no parameter named $addSQL. Did you maybe mean $addSql?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
3871
	 * @param array  $bindings   bindings for SQL snippet
3872
	 *
3873
	 * @return array
3874
	 */
3875
	public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
3876
3877
	/**
3878
	 * Selects records from the database and returns a cursor.
3879
	 * This methods selects the records from the database that match the specified
3880
	 * type, conditions (optional) and additional SQL snippet (optional).
3881
	 *
3882
	 * @param string $type       name of the table you want to query
3883
	 * @param array  $conditions criteria ( $column => array( $values ) )
0 ignored issues
show
Bug introduced by
There is no parameter named $conditions. 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...
3884
	 * @param string $addSQL     additional SQL snippet
0 ignored issues
show
Documentation introduced by
There is no parameter named $addSQL. Did you maybe mean $addSql?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
3885
	 * @param array  $bindings   bindings for SQL snippet
3886
	 *
3887
	 * @return Cursor
3888
	 */
3889
	public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() );
3890
3891
	/**
3892
	 * Returns records through an intermediate type. This method is used to obtain records using a link table and
3893
	 * allows the SQL snippets to reference columns in the link table for additional filtering or ordering.
3894
	 *
3895
	 * @param string $sourceType source type, the reference type you want to use to fetch related items on the other side
3896
	 * @param string $destType   destination type, the target type you want to get beans of
3897
	 * @param mixed  $linkID     ID to use for the link table
3898
	 * @param string $addSql     Additional SQL snippet
3899
	 * @param array  $bindings   Bindings for SQL snippet
3900
	 *
3901
	 * @return array
3902
	 */
3903
	public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() );
3904
3905
	/**
3906
	 * Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation.
3907
	 *
3908
	 * @param string $sourceType source type, the first part of the link you're looking for
3909
	 * @param string $destType   destination type, the second part of the link you're looking for
3910
	 * @param string $sourceID   ID for the source
3911
	 * @param string $destID     ID for the destination
3912
	 *
3913
	 * @return array|null
3914
	 */
3915
	public function queryRecordLink( $sourceType, $destType, $sourceID, $destID );
3916
3917
	/**
3918
	 * Counts the number of records in the database that match the
3919
	 * conditions and additional SQL.
3920
	 *
3921
	 * @param string $type       name of the table you want to query
3922
	 * @param array  $conditions criteria ( $column => array( $values ) )
3923
	 * @param string $addSQL     additional SQL snippet
0 ignored issues
show
Documentation introduced by
There is no parameter named $addSQL. Did you maybe mean $addSql?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
3924
	 * @param array  $bindings   bindings for SQL snippet
3925
	 *
3926
	 * @return integer
3927
	 */
3928
	public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
3929
3930
	/**
3931
	 * Returns the number of records linked through $linkType and satisfying the SQL in $addSQL/$bindings.
3932
	 *
3933
	 * @param string $sourceType source type
3934
	 * @param string $targetType the thing you want to count
3935
	 * @param mixed  $linkID     the of the source type
3936
	 * @param string $addSQL     additional SQL snippet
3937
	 * @param array  $bindings   bindings for SQL snippet
3938
	 *
3939
	 * @return integer
3940
	 */
3941
	public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() );
3942
3943
	/**
3944
	 * Returns all rows of specified type that have been tagged with one of the
3945
	 * strings in the specified tag list array.
3946
	 *
3947
	 * Note that the additional SQL snippet can only be used for pagination,
3948
	 * the SQL snippet will be appended to the end of the query.
3949
	 *
3950
	 * @param string  $type     the bean type you want to query
3951
	 * @param array   $tagList  an array of strings, each string containing a tag title
3952
	 * @param boolean $all      if TRUE only return records that have been associated with ALL the tags in the list
3953
	 * @param string  $addSql   addition SQL snippet, for pagination
3954
	 * @param array   $bindings parameter bindings for additional SQL snippet
3955
	 *
3956
	 * @return array
3957
	 */
3958
	public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() );
3959
3960
	/**
3961
	 * This method should update (or insert a record), it takes
3962
	 * a table name, a list of update values ( $field => $value ) and an
3963
	 * primary key ID (optional). If no primary key ID is provided, an
3964
	 * INSERT will take place.
3965
	 * Returns the new ID.
3966
	 * This methods accepts a type and infers the corresponding table name.
3967
	 *
3968
	 * @param string  $type            name of the table to update
3969
	 * @param array   $updatevalues    list of update values
3970
	 * @param integer $id              optional primary key ID value
3971
	 *
3972
	 * @return integer
3973
	 */
3974
	public function updateRecord( $type, $updatevalues, $id = NULL );
3975
3976
	/**
3977
	 * Deletes records from the database.
3978
	 * @note $addSql is always prefixed with ' WHERE ' or ' AND .'
3979
	 *
3980
	 * @param string $type       name of the table you want to query
3981
	 * @param array  $conditions criteria ( $column => array( $values ) )
3982
	 * @param string $sql        additional SQL
0 ignored issues
show
Bug introduced by
There is no parameter named $sql. 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...
3983
	 * @param array  $bindings   bindings
3984
	 *
3985
	 * @return void
3986
	 */
3987
	public function deleteRecord( $type, $conditions = array(), $addSql = '', $bindings = array() );
3988
3989
	/**
3990
	 * Deletes all links between $sourceType and $destType in an N-M relation.
3991
	 *
3992
	 * @param string $sourceType source type
3993
	 * @param string $destType   destination type
3994
	 * @param string $sourceID   source ID
3995
	 *
3996
	 * @return void
3997
	 */
3998
	public function deleteRelations( $sourceType, $destType, $sourceID );
3999
4000
	/**
4001
	 * @see QueryWriter::addUniqueConstaint
4002
	 */
4003
	public function addUniqueIndex( $type, $columns );
4004
4005
	/**
4006
	 * This method will add a UNIQUE constraint index to a table on columns $columns.
4007
	 * This methods accepts a type and infers the corresponding table name.
4008
	 *
4009
	 * @param string $type               type
4010
	 * @param array  $columnsPartOfIndex columns to include in index
0 ignored issues
show
Documentation introduced by
There is no parameter named $columnsPartOfIndex. Did you maybe mean $columns?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
4011
	 *
4012
	 * @return void
4013
	 */
4014
	public function addUniqueConstraint( $type, $columns );
4015
4016
	/**
4017
	 * This method will check whether the SQL state is in the list of specified states
4018
	 * and returns TRUE if it does appear in this list or FALSE if it
4019
	 * does not. The purpose of this method is to translate the database specific state to
4020
	 * a one of the constants defined in this class and then check whether it is in the list
4021
	 * of standard states provided.
4022
	 *
4023
	 * @param string $state sql state
4024
	 * @param array  $list  list
4025
	 *
4026
	 * @return boolean
4027
	 */
4028
	public function sqlStateIn( $state, $list );
4029
4030
	/**
4031
	 * This method will remove all beans of a certain type.
4032
	 * This methods accepts a type and infers the corresponding table name.
4033
	 *
4034
	 * @param  string $type bean type
4035
	 *
4036
	 * @return void
4037
	 */
4038
	public function wipe( $type );
4039
4040
	/**
4041
	 * This method will add a foreign key from type and field to
4042
	 * target type and target field.
4043
	 * The foreign key is created without an action. On delete/update
4044
	 * no action will be triggered. The FK is only used to allow database
4045
	 * tools to generate pretty diagrams and to make it easy to add actions
4046
	 * later on.
4047
	 * This methods accepts a type and infers the corresponding table name.
4048
	 *
4049
	 *
4050
	 * @param  string $type           type that will have a foreign key field
4051
	 * @param  string $targetType     points to this type
4052
	 * @param  string $property       field that contains the foreign key value
4053
	 * @param  string $targetProperty field where the fk points to
4054
	 * @param  string $isDep          whether target is dependent and should cascade on update/delete
4055
	 *
4056
	 * @return void
4057
	 */
4058
	public function addFK( $type, $targetType, $property, $targetProperty, $isDep = false );
4059
4060
	/**
4061
	 * This method will add an index to a type and field with name
4062
	 * $name.
4063
	 * This methods accepts a type and infers the corresponding table name.
4064
	 *
4065
	 * @param string $type     type to add index to
4066
	 * @param string $name     name of the new index
4067
	 * @param string $property field to index
4068
	 *
4069
	 * @return void
4070
	 */
4071
	public function addIndex( $type, $name, $property );
4072
4073
	/**
4074
	 * Checks and filters a database structure element like a table of column
4075
	 * for safe use in a query. A database structure has to conform to the
4076
	 * RedBeanPHP DB security policy which basically means only alphanumeric
4077
	 * symbols are allowed. This security policy is more strict than conventional
4078
	 * SQL policies and does therefore not require database specific escaping rules.
4079
	 *
4080
	 * @param string  $databaseStructure name of the column/table to check
4081
	 * @param boolean $noQuotes          TRUE to NOT put backticks or quotes around the string
0 ignored issues
show
Bug introduced by
There is no parameter named $noQuotes. 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...
4082
	 *
4083
	 * @return string
4084
	 */
4085
	public function esc( $databaseStructure, $dontQuote = FALSE );
4086
4087
	/**
4088
	 * Removes all tables and views from the database.
4089
	 *
4090
	 * @return void
4091
	 */
4092
	public function wipeAll();
4093
4094
	/**
4095
	 * Renames an association. For instance if you would like to refer to
4096
	 * album_song as: track you can specify this by calling this method like:
4097
	 *
4098
	 * renameAssociation('album_song','track')
4099
	 *
4100
	 * This allows:
4101
	 *
4102
	 * $album->sharedSong
4103
	 *
4104
	 * to add/retrieve beans from track instead of album_song.
4105
	 * Also works for exportAll().
4106
	 *
4107
	 * This method also accepts a single associative array as
4108
	 * its first argument.
4109
	 *
4110
	 * @param string|array $fromType
4111
	 * @param string       $toType (optional)
4112
	 *
4113
	 * @return void
4114
	 */
4115
	public function renameAssocTable( $fromType, $toType = NULL );
4116
4117
	/**
4118
	 * Returns the format for link tables.
4119
	 * Given an array containing two type names this method returns the
4120
	 * name of the link table to be used to store and retrieve
4121
	 * association records. For instance, given two types: person and
4122
	 * project, the corresponding link table might be: 'person_project'.
4123
	 *
4124
	 * @param  array $types two types array($type1, $type2)
4125
	 *
4126
	 * @return string
4127
	 */
4128
	public function getAssocTable( $types );
4129
4130
	/**
4131
	 * Given a bean type and a property, this method
4132
	 * tries to infer the fetch type using the foreign key
4133
	 * definitions in the database. 
4134
	 * For instance: project, student -> person.
4135
	 * If no fetchType can be inferred, this method will return NULL.
4136
	 *
4137
	 * @note QueryWriters do not have to implement this method,
4138
	 * it's optional. A default version is available in AQueryWriter.
4139
	 *
4140
	 * @param $type     the source type to fetch a target type for
4141
	 * @param $property the property to fetch the type of
4142
	 *
4143
	 * @return string|NULL
4144
	 */
4145
	public function inferFetchType( $type, $property );
4146
}
4147
}
4148
4149
namespace RedBeanPHP\QueryWriter {
4150
4151
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
4152
use RedBeanPHP\RedException as RedException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
4153
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
4154
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
4155
use RedBeanPHP\RedException\SQL as SQLException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
4156
4157
/**
4158
 * RedBeanPHP Abstract Query Writer.
4159
 * Represents an abstract Database to RedBean
4160
 * To write a driver for a different database for RedBean
4161
 * Contains a number of functions all implementors can
4162
 * inherit or override.
4163
 *
4164
 * @file    RedBeanPHP/QueryWriter/AQueryWriter.php
4165
 * @author  Gabor de Mooij and the RedBeanPHP Community
4166
 * @license BSD/GPLv2
4167
 *
4168
 * @copyright
4169
 * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
4170
 * This source file is subject to the BSD/GPLv2 License that is bundled
4171
 * with this source code in the file license.txt.
4172
 */
4173
abstract class AQueryWriter
0 ignored issues
show
Coding Style Compatibility introduced by
Each interface must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
4174
{
4175
	/**
4176
	 * @var array
4177
	 */
4178
	private static $sqlFilters = array();
4179
4180
	/**
4181
	 * @var boolean
4182
	 */
4183
	private static $flagSQLFilterSafeMode = false;
4184
4185
	/**
4186
	 * @var boolean
4187
	 */
4188
	private static $flagNarrowFieldMode = true;
4189
4190
	/**
4191
	 * @var array
4192
	 */
4193
	public static $renames = array();
4194
4195
	/**
4196
	 * @var DBAdapter
4197
	 */
4198
	protected $adapter;
4199
4200
	/**
4201
	 * @var string
4202
	 */
4203
	protected $defaultValue = 'NULL';
4204
4205
	/**
4206
	 * @var string
4207
	 */
4208
	protected $quoteCharacter = '';
4209
4210
	/**
4211
	 * @var boolean
4212
	 */
4213
	protected $flagUseCache = TRUE;
4214
4215
	/**
4216
	 * @var array
4217
	 */
4218
	protected $cache = array();
4219
4220
	/**
4221
	 * @var integer
4222
	 */
4223
	protected $maxCacheSizePerType = 20;
4224
4225
	/**
4226
	 * @var array
4227
	 */
4228
	public $typeno_sqltype = array();
4229
4230
	/**
4231
	 * Checks whether a number can be treated like an int.
4232
	 *
4233
	 * @param  string $value string representation of a certain value
4234
	 *
4235
	 * @return boolean
4236
	 */
4237
	public static function canBeTreatedAsInt( $value )
4238
	{
4239
		return (bool) ( strval( $value ) === strval( intval( $value ) ) );
4240
	}
4241
4242
	/**
4243
	 * @see QueryWriter::getAssocTableFormat
4244
	 */
4245
	public static function getAssocTableFormat( $types )
4246
	{
4247
		sort( $types );
4248
4249
		$assoc = implode( '_', $types );
4250
4251
		return ( isset( self::$renames[$assoc] ) ) ? self::$renames[$assoc] : $assoc;
4252
	}
4253
4254
	/**
4255
	 * @see QueryWriter::renameAssociation
4256
	 */
4257
	public static function renameAssociation( $from, $to = NULL )
4258
	{
4259
		if ( is_array( $from ) ) {
4260
			foreach ( $from as $key => $value ) self::$renames[$key] = $value;
4261
4262
			return;
4263
		}
4264
4265
		self::$renames[$from] = $to;
4266
	}
4267
4268
	/**
4269
	 * Globally available service method for RedBeanPHP.
4270
	 * Converts a camel cased string to a snake cased string.
4271
	 *
4272
	 * @param string $camel a camelCased string
4273
	 *
4274
	 * @return string
4275
	 */
4276
	public static function camelsSnake( $camel )
4277
	{
4278
		return strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $camel ) );
4279
	}
4280
4281
	/**
4282
	 * Clears renames.
4283
	 *
4284
	 * @return void
4285
	 */
4286
	public static function clearRenames()
4287
	{
4288
		self::$renames = array();
4289
	}
4290
4291
	/**
4292
	 * Toggles 'Narrow Field Mode'.
4293
	 * In Narrow Field mode the queryRecord method will
4294
	 * narrow its selection field to
4295
	 *
4296
	 * SELECT {table}.*
4297
	 *
4298
	 * instead of
4299
	 *
4300
	 * SELECT *
4301
	 *
4302
	 * This is a better way of querying because it allows
4303
	 * more flexibility (for instance joins). However if you need
4304
	 * the wide selector for backward compatibility; use this method
4305
	 * to turn OFF Narrow Field Mode by passing FALSE.
4306
	 *
4307
	 * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field
4308
	 *
4309
	 * @return void
4310
	 */
4311
	public static function setNarrowFieldMode( $narrowField )
4312
	{
4313
		self::$flagNarrowFieldMode = (boolean) $narrowField;
4314
	}
4315
4316
	/**
4317
	 * Sets SQL filters.
4318
	 * This is a lowlevel method to set the SQL filter array.
4319
	 * The format of this array is:
4320
	 *
4321
	 * array(
4322
	 * 		'<MODE, i.e. 'r' for read, 'w' for write>' => array(
4323
	 * 			'<TABLE NAME>' => array(
4324
	 * 				'<COLUMN NAME>' => '<SQL>'
4325
	 * 			)
4326
	 * 		)
4327
	 * )
4328
	 *
4329
	 * Example:
4330
	 *
4331
	 * array(
4332
	 * QueryWriter::C_SQLFILTER_READ => array(
4333
	 * 	'book' => array(
4334
	 * 		'title' => ' LOWER(book.title) '
4335
	 * 	)
4336
	 * )
4337
	 *
4338
	 * Note that you can use constants instead of magical chars
4339
	 * as keys for the uppermost array.
4340
	 * This is a lowlevel method. For a more friendly method
4341
	 * please take a look at the facade: R::bindFunc().
4342
	 *
4343
	 * @param array
4344
	 */
4345
	public static function setSQLFilters( $sqlFilters, $safeMode = false )
4346
	{
4347
		self::$flagSQLFilterSafeMode = (boolean) $safeMode;
4348
		self::$sqlFilters = $sqlFilters;
4349
	}
4350
4351
	/**
4352
	 * Returns current SQL Filters.
4353
	 * This method returns the raw SQL filter array.
4354
	 * This is a lowlevel method. For a more friendly method
4355
	 * please take a look at the facade: R::bindFunc().
4356
	 *
4357
	 * @return array
4358
	 */
4359
	public static function getSQLFilters()
4360
	{
4361
		return self::$sqlFilters;
4362
	}
4363
4364
	/**
4365
	 * Returns a cache key for the cache values passed.
4366
	 * This method returns a fingerprint string to be used as a key to store
4367
	 * data in the writer cache.
4368
	 *
4369
	 * @param array $keyValues key-value to generate key for
4370
	 *
4371
	 * @return string
4372
	 */
4373
	private function getCacheKey( $keyValues )
4374
	{
4375
		return json_encode( $keyValues );
4376
	}
4377
4378
	/**
4379
	 * Returns the values associated with the provided cache tag and key.
4380
	 *
4381
	 * @param string $cacheTag cache tag to use for lookup
4382
	 * @param string $key      key to use for lookup
4383
	 *
4384
	 * @return mixed
4385
	 */
4386
	private function getCached( $cacheTag, $key )
4387
	{
4388
		$sql = $this->adapter->getSQL();
4389
4390
		if ($this->updateCache()) {
4391
			if ( isset( $this->cache[$cacheTag][$key] ) ) {
4392
				return $this->cache[$cacheTag][$key];
4393
			}
4394
		}
4395
4396
		return NULL;
4397
	}
4398
4399
	/**
4400
	 * Checks if the previous query had a keep-cache tag.
4401
	 * If so, the cache will persist, otherwise the cache will be flushed.
4402
	 *
4403
	 * Returns TRUE if the cache will remain and FALSE if a flush has
4404
	 * been performed.
4405
	 *
4406
	 * @return boolean
4407
	 */
4408
	private function updateCache()
4409
	{
4410
		$sql = $this->adapter->getSQL();
4411
		if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) {
4412
			// If SQL has been taken place outside of this method then something else then
4413
			// a select query might have happened! (or instruct to keep cache)
4414
			$this->cache = array();
4415
			return FALSE;
4416
		}
4417
		return TRUE;
4418
	}
4419
4420
	/**
4421
	 * Stores data from the writer in the cache under a specific key and cache tag.
4422
	 * A cache tag is used to make sure the cache remains consistent. In most cases the cache tag
4423
	 * will be the bean type, this makes sure queries associated with a certain reference type will
4424
	 * never contain conflicting data.
4425
	 * Why not use the cache tag as a key? Well
4426
	 * we need to make sure the cache contents fits the key (and key is based on the cache values).
4427
	 * Otherwise it would be possible to store two different result sets under the same key (the cache tag).
4428
	 *
4429
	 * In previous versions you could only store one key-entry, I have changed this to
4430
	 * improve caching efficiency (issue #400).
4431
	 *
4432
	 * @param string $cacheTag cache tag (secondary key)
4433
	 * @param string $key      key
4434
	 * @param array  $values   content to be stored
4435
	 *
4436
	 * @return void
4437
	 */
4438
	private function putResultInCache( $cacheTag, $key, $values )
4439
	{
4440
		if ( isset( $this->cache[$cacheTag] ) ) {
4441
			if ( count( $this->cache[$cacheTag] ) > $this->maxCacheSizePerType ) array_shift( $this->cache[$cacheTag] );
4442
		} else {
4443
			$this->cache[$cacheTag] = array();
4444
		}
4445
4446
		$this->cache[$cacheTag][$key] = $values;
4447
	}
4448
4449
	/**
4450
	 * Creates an SQL snippet from a list of conditions of format:
4451
	 *
4452
	 * array(
4453
	 *    key => array(
4454
	 *           value1, value2, value3 ....
4455
	 *        )
4456
	 * )
4457
	 *
4458
	 * @param array  $conditions list of conditions
4459
	 * @param array  $bindings   parameter bindings for SQL snippet
4460
	 * @param string $addSql     SQL snippet
4461
	 *
4462
	 * @return string
4463
	 */
4464
	private function makeSQLFromConditions( $conditions, &$bindings, $addSql = '' )
4465
	{
4466
		reset( $bindings );
4467
		$firstKey       = key( $bindings );
4468
		$paramTypeIsNum = ( is_numeric( $firstKey ) );
4469
		$counter        = 0;
4470
4471
		$sqlConditions = array();
4472
		foreach ( $conditions as $column => $values ) {
4473
			if ( !count( $values ) ) continue;
4474
4475
			$sql = $this->esc( $column );
4476
			$sql .= ' IN ( ';
4477
4478
			if ( !is_array( $values ) ) $values = array( $values );
4479
4480
			// If it's safe to skip bindings, do so...
4481
			if ( ctype_digit( implode( '', $values ) ) ) {
4482
				$sql .= implode( ',', $values ) . ' ) ';
4483
4484
				// only numeric, cant do much harm
4485
				$sqlConditions[] = $sql;
4486
			} else {
4487
4488
				if ( $paramTypeIsNum ) {
4489
					$sql .= implode( ',', array_fill( 0, count( $values ), '?' ) ) . ' ) ';
4490
4491
					array_unshift($sqlConditions, $sql);
4492
4493
					foreach ( $values as $k => $v ) {
4494
						$values[$k] = strval( $v );
4495
4496
						array_unshift( $bindings, $v );
4497
					}
4498
				} else {
4499
4500
					$slots = array();
4501
4502
					foreach( $values as $k => $v ) {
4503
						$slot            = ':slot'.$counter++;
4504
						$slots[]         = $slot;
4505
						$bindings[$slot] = strval( $v );
4506
					}
4507
4508
					$sql .= implode( ',', $slots ).' ) ';
4509
					$sqlConditions[] = $sql;
4510
				}
4511
			}
4512
		}
4513
4514
		$sql = '';
4515
		if ( is_array( $sqlConditions ) && count( $sqlConditions ) > 0 ) {
4516
			$sql = implode( ' AND ', $sqlConditions );
4517
			$sql = " WHERE ( $sql ) ";
4518
4519
			if ( $addSql ) $sql .= $addSql;
4520
		} elseif ( $addSql ) {
4521
			$sql = $addSql;
4522
		}
4523
4524
		return $sql;
4525
	}
4526
4527
	/**
4528
	 * Returns the table names and column names for a relational query.
4529
	 *
4530
	 * @param string  $sourceType type of the source bean
4531
	 * @param string  $destType   type of the bean you want to obtain using the relation
4532
	 * @param boolean $noQuote    TRUE if you want to omit quotes
4533
	 *
4534
	 * @return array
4535
	 */
4536
	private function getRelationalTablesAndColumns( $sourceType, $destType, $noQuote = FALSE )
4537
	{
4538
		$linkTable   = $this->esc( $this->getAssocTable( array( $sourceType, $destType ) ), $noQuote );
4539
		$sourceCol   = $this->esc( $sourceType . '_id', $noQuote );
4540
4541
		if ( $sourceType === $destType ) {
4542
			$destCol = $this->esc( $destType . '2_id', $noQuote );
4543
		} else {
4544
			$destCol = $this->esc( $destType . '_id', $noQuote );
4545
		}
4546
4547
		$sourceTable = $this->esc( $sourceType, $noQuote );
4548
		$destTable   = $this->esc( $destType, $noQuote );
4549
4550
		return array( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol );
4551
	}
4552
4553
	/**
4554
	 * Given a type and a property name this method
4555
	 * returns the foreign key map section associated with this pair.
4556
	 *
4557
	 * @param string $type     name of the type
4558
	 * @param string $property name of the property
4559
	 *
4560
	 * @return array|NULL
4561
	 */
4562
	protected function getForeignKeyForTypeProperty( $type, $property )
4563
	{
4564
		$property = $this->esc( $property, TRUE );
4565
4566
		try {
4567
			$map = $this->getKeyMapForType( $type );
4568
		} catch ( SQLException $e ) {
4569
			return NULL;
4570
		}
4571
4572
		foreach( $map as $key ) {
4573
			if ( $key['from'] === $property ) return $key;
4574
		}
4575
		return NULL;
4576
	}
4577
4578
	/**
4579
	 * Returns the foreign key map (FKM) for a type.
4580
	 * A foreign key map describes the foreign keys in a table.
4581
	 * A FKM always has the same structure:
4582
	 *
4583
	 * array(
4584
	 * 	'name'      => <name of the foreign key>
4585
	 *    'from'      => <name of the column on the source table>
4586
	 *    'table'     => <name of the target table>
4587
	 *    'to'        => <name of the target column> (most of the time 'id')
4588
	 *    'on_update' => <update rule: 'SET NULL','CASCADE' or 'RESTRICT'>
4589
	 *    'on_delete' => <delete rule: 'SET NULL','CASCADE' or 'RESTRICT'>
4590
	 * )
4591
	 *
4592
	 * @note the keys in the result array are FKDLs, i.e. descriptive unique
4593
	 * keys per source table. Also see: AQueryWriter::makeFKLabel for details.
4594
	 *
4595
	 * @param string $type the bean type you wish to obtain a key map of
4596
	 *
4597
	 * @return array
4598
	 */
4599
	protected function getKeyMapForType( $type )
4600
	{
4601
		return array();
4602
	}
4603
4604
	/**
4605
	 * This method makes a key for a foreign key description array.
4606
	 * This key is a readable string unique for every source table.
4607
	 * This uniform key is called the FKDL Foreign Key Description Label.
4608
	 * Note that the source table is not part of the FKDL because
4609
	 * this key is supposed to be 'per source table'. If you wish to
4610
	 * include a source table, prefix the key with 'on_table_<SOURCE>_'.
4611
	 *
4612
	 * @param string $from  the column of the key in the source table
4613
	 * @param string $type  the type (table) where the key points to
4614
	 * @param string $to    the target column of the foreign key (mostly just 'id')
4615
	 *
4616
	 * @return string
4617
	 */
4618
	protected function makeFKLabel($from, $type, $to)
4619
	{
4620
		return "from_{$from}_to_table_{$type}_col_{$to}";
4621
	}
4622
4623
	/**
4624
	 * Returns an SQL Filter snippet for reading.
4625
	 *
4626
	 * @param string $type type of bean
4627
	 *
4628
	 * @return string
4629
	 */
4630
	protected function getSQLFilterSnippet( $type )
4631
	{
4632
		$existingCols = array();
4633
		if (self::$flagSQLFilterSafeMode) {
4634
			$existingCols = $this->getColumns( $type );
4635
		}
4636
4637
		$sqlFilters = array();
4638
		if ( isset( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] ) ) {
4639
			foreach( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] as $property => $sqlFilter ) {
4640
				if ( !self::$flagSQLFilterSafeMode || isset( $existingCols[$property] ) ) {
4641
					$sqlFilters[] = $sqlFilter.' AS '.$property.' ';
4642
				}
4643
			}
4644
		}
4645
		$sqlFilterStr = ( count($sqlFilters) ) ? ( ','.implode( ',', $sqlFilters ) ) : '';
4646
		return $sqlFilterStr;
4647
	}
4648
4649
	/**
4650
	 * Generates a list of parameters (slots) for an SQL snippet.
4651
	 * This method calculates the correct number of slots to insert in the
4652
	 * SQL snippet and determines the correct type of slot. If the bindings
4653
	 * array contains named parameters this method will return named ones and
4654
	 * update the keys in the value list accordingly (that's why we use the &).
4655
	 *
4656
	 * If you pass an offset the bindings will be re-added to the value list.
4657
	 * Some databases cant handle duplicate parameter names in queries.
4658
	 *
4659
	 * @param array   &$valueList     list of values to generate slots for (gets modified if needed)
4660
	 * @param array   $otherBindings  list of additional bindings
4661
	 * @param integer $offset         start counter at...
4662
	 *
4663
	 * @return string
4664
	 */
4665
	protected function getParametersForInClause( &$valueList, $otherBindings, $offset = 0 )
4666
	{
4667
		if ( is_array( $otherBindings ) && count( $otherBindings ) > 0 ) {
4668
			reset( $otherBindings );
4669
4670
			$key = key( $otherBindings );
4671
4672
			if ( !is_numeric($key) ) {
4673
				$filler  = array();
4674
				$newList = (!$offset) ? array() : $valueList;
4675
				$counter = $offset;
4676
4677
				foreach( $valueList as $value ) {
4678
					$slot           = ':slot' . ( $counter++ );
4679
					$filler[]       = $slot;
4680
					$newList[$slot] = $value;
4681
				}
4682
4683
				// Change the keys!
4684
				$valueList = $newList;
4685
4686
				return implode( ',', $filler );
4687
			}
4688
		}
4689
4690
		return implode( ',', array_fill( 0, count( $valueList ), '?' ) );
4691
	}
4692
4693
	/**
4694
	 * Adds a data type to the list of data types.
4695
	 * Use this method to add a new column type definition to the writer.
4696
	 * Used for UUID support.
4697
	 *
4698
	 * @param integer $dataTypeID    magic number constant assigned to this data type
4699
	 * @param string  $SQLDefinition SQL column definition (i.e. INT(11))
4700
	 *
4701
	 * @return self
4702
	 */
4703
	protected function addDataType( $dataTypeID, $SQLDefinition )
4704
	{
4705
		$this->typeno_sqltype[ $dataTypeID ] = $SQLDefinition;
4706
		$this->sqltype_typeno[ $SQLDefinition ] = $dataTypeID;
0 ignored issues
show
Bug introduced by
The property sqltype_typeno does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
4707
		return $this;
4708
	}
4709
4710
	/**
4711
	 * Returns the sql that should follow an insert statement.
4712
	 *
4713
	 * @param string $table name
4714
	 *
4715
	 * @return string
4716
	 */
4717
	protected function getInsertSuffix( $table )
0 ignored issues
show
Unused Code introduced by
The parameter $table 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...
4718
	{
4719
		return '';
4720
	}
4721
4722
	/**
4723
	 * Checks whether a value starts with zeros. In this case
4724
	 * the value should probably be stored using a text datatype instead of a
4725
	 * numerical type in order to preserve the zeros.
4726
	 *
4727
	 * @param string $value value to be checked.
4728
	 *
4729
	 * @return boolean
4730
	 */
4731
	protected function startsWithZeros( $value )
4732
	{
4733
		$value = strval( $value );
4734
4735
		if ( strlen( $value ) > 1 && strpos( $value, '0' ) === 0 && strpos( $value, '0.' ) !== 0 ) {
4736
			return TRUE;
4737
		} else {
4738
			return FALSE;
4739
		}
4740
	}
4741
4742
	/**
4743
	 * Inserts a record into the database using a series of insert columns
4744
	 * and corresponding insertvalues. Returns the insert id.
4745
	 *
4746
	 * @param string $table         table to perform query on
0 ignored issues
show
Bug introduced by
There is no parameter named $table. 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...
4747
	 * @param array  $insertcolumns columns to be inserted
4748
	 * @param array  $insertvalues  values to be inserted
4749
	 *
4750
	 * @return integer
4751
	 */
4752
	protected function insertRecord( $type, $insertcolumns, $insertvalues )
4753
	{
4754
		$default = $this->defaultValue;
4755
		$suffix  = $this->getInsertSuffix( $type );
4756
		$table   = $this->esc( $type );
4757
4758
		if ( count( $insertvalues ) > 0 && is_array( $insertvalues[0] ) && count( $insertvalues[0] ) > 0 ) {
4759
4760
			$insertSlots = array();
4761
			foreach ( $insertcolumns as $k => $v ) {
4762
				$insertcolumns[$k] = $this->esc( $v );
4763
4764
				if (isset(self::$sqlFilters['w'][$type][$v])) {
4765
					$insertSlots[] = self::$sqlFilters['w'][$type][$v];
4766
				} else {
4767
					$insertSlots[] = '?';
4768
				}
4769
			}
4770
4771
			$insertSQL = "INSERT INTO $table ( id, " . implode( ',', $insertcolumns ) . " ) VALUES
4772
			( $default, " . implode( ',', $insertSlots ) . " ) $suffix";
4773
4774
			$ids = array();
4775
			foreach ( $insertvalues as $i => $insertvalue ) {
4776
				$ids[] = $this->adapter->getCell( $insertSQL, $insertvalue, $i );
4777
			}
4778
4779
			$result = count( $ids ) === 1 ? array_pop( $ids ) : $ids;
4780
		} else {
4781
			$result = $this->adapter->getCell( "INSERT INTO $table (id) VALUES($default) $suffix" );
4782
		}
4783
4784
		if ( $suffix ) return $result;
4785
4786
		$last_id = $this->adapter->getInsertID();
4787
4788
		return $last_id;
4789
	}
4790
4791
	/**
4792
	 * Checks table name or column name.
4793
	 *
4794
	 * @param string $table table string
0 ignored issues
show
Bug introduced by
There is no parameter named $table. 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...
4795
	 *
4796
	 * @return string
4797
	 *
4798
	 * @throws Security
4799
	 */
4800
	protected function check( $struct )
4801
	{
4802
		if ( !is_string( $struct ) || !preg_match( '/^[a-zA-Z0-9_]+$/', $struct ) ) {
4803
			throw new RedException( 'Identifier does not conform to RedBeanPHP security policies.' );
4804
		}
4805
4806
		return $struct;
4807
	}
4808
4809
	/**
4810
	 * Checks whether the specified type (i.e. table) already exists in the database.
4811
	 * Not part of the Object Database interface!
4812
	 *
4813
	 * @param string $table table name
4814
	 *
4815
	 * @return boolean
4816
	 */
4817
	public function tableExists( $table )
4818
	{
4819
		$tables = $this->getTables();
4820
4821
		return in_array( $table, $tables );
4822
	}
4823
4824
	/**
4825
	 * @see QueryWriter::glueSQLCondition
4826
	 */
4827
	public function glueSQLCondition( $sql, $glue = NULL )
4828
	{
4829
		static $snippetCache = array();
4830
4831
		if ( trim( $sql ) === '' ) {
4832
			return $sql;
4833
		}
4834
4835
		$key = $glue . '|' . $sql;
4836
4837
		if ( isset( $snippetCache[$key] ) ) {
4838
			return $snippetCache[$key];
4839
		}
4840
4841
		$lsql = ltrim( $sql );
4842
4843
		if ( preg_match( '/^(INNER|LEFT|RIGHT|JOIN|AND|OR|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) {
4844
			if ( $glue === QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) {
4845
				$snippetCache[$key] = ' WHERE ' . substr( $lsql, 3 );
4846
			} else {
4847
				$snippetCache[$key] = $sql;
4848
			}
4849
		} else {
4850
			$snippetCache[$key] = ( ( $glue === QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql;
4851
		}
4852
4853
		return $snippetCache[$key];
4854
	}
4855
4856
	/**
4857
	 * @see QueryWriter::glueLimitOne
4858
	 */
4859
	public function glueLimitOne( $sql = '')
4860
	{
4861
		return ( strpos( $sql, 'LIMIT' ) === FALSE ) ? ( $sql . ' LIMIT 1 ' ) : $sql;
4862
	}
4863
4864
	/**
4865
	 * @see QueryWriter::esc
4866
	 */
4867
	public function esc( $dbStructure, $dontQuote = FALSE )
4868
	{
4869
		$this->check( $dbStructure );
4870
4871
		return ( $dontQuote ) ? $dbStructure : $this->quoteCharacter . $dbStructure . $this->quoteCharacter;
4872
	}
4873
4874
	/**
4875
	 * @see QueryWriter::addColumn
4876
	 */
4877 View Code Duplication
	public function addColumn( $type, $column, $field )
4878
	{
4879
		$table  = $type;
4880
		$type   = $field;
4881
		$table  = $this->esc( $table );
4882
		$column = $this->esc( $column );
4883
4884
		$type = ( isset( $this->typeno_sqltype[$type] ) ) ? $this->typeno_sqltype[$type] : '';
4885
4886
		$this->adapter->exec( "ALTER TABLE $table ADD $column $type " );
4887
	}
4888
4889
	/**
4890
	 * @see QueryWriter::updateRecord
4891
	 */
4892
	public function updateRecord( $type, $updatevalues, $id = NULL )
4893
	{
4894
		$table = $type;
4895
4896
		if ( !$id ) {
4897
			$insertcolumns = $insertvalues = array();
4898
4899
			foreach ( $updatevalues as $pair ) {
4900
				$insertcolumns[] = $pair['property'];
4901
				$insertvalues[]  = $pair['value'];
4902
			}
4903
4904
			//Otherwise psql returns string while MySQL/SQLite return numeric causing problems with additions (array_diff)
4905
			return (string) $this->insertRecord( $table, $insertcolumns, array( $insertvalues ) );
4906
		}
4907
4908
		if ( $id && !count( $updatevalues ) ) {
4909
			return $id;
4910
		}
4911
4912
		$table = $this->esc( $table );
4913
		$sql   = "UPDATE $table SET ";
4914
4915
		$p = $v = array();
4916
4917
		foreach ( $updatevalues as $uv ) {
4918
4919
			if ( isset( self::$sqlFilters['w'][$type][$uv['property']] ) ) {
4920
				$p[] = " {$this->esc( $uv["property"] )} = ". self::$sqlFilters['w'][$type][$uv['property']];
4921
			} else {
4922
				$p[] = " {$this->esc( $uv["property"] )} = ? ";
4923
			}
4924
4925
			$v[] = $uv['value'];
4926
		}
4927
4928
		$sql .= implode( ',', $p ) . ' WHERE id = ? ';
4929
4930
		$v[] = $id;
4931
4932
		$this->adapter->exec( $sql, $v );
4933
4934
		return $id;
4935
	}
4936
4937
	/**
4938
	 * @see QueryWriter::writeJoin
4939
	 */
4940
	public function writeJoin( $type, $targetType, $leftRight = 'LEFT' )
4941
	{
4942
		if ( $leftRight !== 'LEFT' && $leftRight !== 'RIGHT' && $leftRight !== 'INNER' )
4943
			throw new RedException( 'Invalid JOIN.' );
4944
4945
		$table = $this->esc( $type );
4946
		$targetTable = $this->esc( $targetType );
4947
		$field = $this->esc( $targetType, TRUE );
4948
		return " {$leftRight} JOIN {$targetTable} ON {$targetTable}.id = {$table}.{$field}_id ";
4949
	}
4950
4951
	/**
4952
	 * @see QueryWriter::queryRecord
4953
	 */
4954
	public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
4955
	{
4956
		$addSql = $this->glueSQLCondition( $addSql, ( count($conditions) > 0) ? QueryWriter::C_GLUE_AND : NULL );
4957
4958
		$key = NULL;
4959
		if ( $this->flagUseCache ) {
4960
			$key = $this->getCacheKey( array( $conditions, $addSql, $bindings, 'select' ) );
4961
4962
			if ( $cached = $this->getCached( $type, $key ) ) {
4963
				return $cached;
4964
			}
4965
		}
4966
4967
		$table = $this->esc( $type );
4968
4969
		$sqlFilterStr = '';
4970
		if ( count( self::$sqlFilters ) ) {
4971
			$sqlFilterStr = $this->getSQLFilterSnippet( $type );
4972
		}
4973
4974
		$sql   = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
4975
4976
		$fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*';
4977
		$sql   = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} -- keep-cache";
4978
4979
		$rows  = $this->adapter->get( $sql, $bindings );
4980
4981
4982
		if ( $this->flagUseCache && $key ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $key of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
4983
			$this->putResultInCache( $type, $key, $rows );
4984
		}
4985
4986
		return $rows;
4987
	}
4988
4989
	/**
4990
	 * @see QueryWriter::queryRecordWithCursor
4991
	 */
4992
	public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() )
4993
	{
4994
		$sql = $this->glueSQLCondition( $addSql, NULL );
4995
		$table = $this->esc( $type );
4996
		$sql   = "SELECT {$table}.* FROM {$table} {$sql}";
4997
		return $this->adapter->getCursor( $sql, $bindings );
4998
	}
4999
5000
	/**
5001
	 * @see QueryWriter::queryRecordRelated
5002
	 */
5003
	public function queryRecordRelated( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() )
5004
	{
5005
		$addSql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE );
5006
5007
		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
0 ignored issues
show
Unused Code introduced by
The assignment to $sourceTable is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
5008
5009
		$key = $this->getCacheKey( array( $sourceType, $destType, implode( ',', $linkIDs ), $addSql, $bindings ) );
5010
5011
		if ( $this->flagUseCache && $cached = $this->getCached( $destType, $key ) ) {
5012
			return $cached;
5013
		}
5014
5015
		$inClause = $this->getParametersForInClause( $linkIDs, $bindings );
5016
5017
		$sqlFilterStr = '';
5018
		if ( count( self::$sqlFilters ) ) {
5019
			$sqlFilterStr = $this->getSQLFilterSnippet( $destType );
5020
		}
5021
5022
		if ( $sourceType === $destType ) {
5023
			$inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases
5024
			$sql = "
5025
			SELECT
5026
				{$destTable}.* {$sqlFilterStr} ,
5027
				COALESCE(
5028
				NULLIF({$linkTable}.{$sourceCol}, {$destTable}.id),
5029
				NULLIF({$linkTable}.{$destCol}, {$destTable}.id)) AS linked_by
5030
			FROM {$linkTable}
5031
			INNER JOIN {$destTable} ON
5032
			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) ) OR
5033
			( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} IN ($inClause2) )
5034
			{$addSql}
5035
			-- keep-cache";
5036
5037
			$linkIDs = array_merge( $linkIDs, $linkIDs );
5038
		} else {
5039
			$sql = "
5040
			SELECT
5041
				{$destTable}.* {$sqlFilterStr},
5042
				{$linkTable}.{$sourceCol} AS linked_by
5043
			FROM {$linkTable}
5044
			INNER JOIN {$destTable} ON
5045
			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) )
5046
			{$addSql}
5047
			-- keep-cache";
5048
		}
5049
5050
		$bindings = array_merge( $linkIDs, $bindings );
5051
5052
		$rows = $this->adapter->get( $sql, $bindings );
5053
5054
		$this->putResultInCache( $destType, $key, $rows );
5055
5056
		return $rows;
5057
	}
5058
5059
	/**
5060
	 * @see QueryWriter::queryRecordLink
5061
	 */
5062
	public function queryRecordLink( $sourceType, $destType, $sourceID, $destID )
5063
	{
5064
		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
5065
5066
		$key = $this->getCacheKey( array( $sourceType, $destType, $sourceID, $destID ) );
5067
5068
		if ( $this->flagUseCache && $cached = $this->getCached( $linkTable, $key ) ) {
5069
			return $cached;
5070
		}
5071
5072
		$sqlFilterStr = '';
5073
		if ( count( self::$sqlFilters ) ) {
5074
			$sqlFilterStr = $this->getSQLFilterSnippet( $destType );
5075
		}
5076
5077
		if ( $sourceTable === $destTable ) {
5078
			$sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
5079
				WHERE ( {$sourceCol} = ? AND {$destCol} = ? ) OR
5080
				 ( {$destCol} = ? AND {$sourceCol} = ? ) -- keep-cache";
5081
			$row = $this->adapter->getRow( $sql, array( $sourceID, $destID, $sourceID, $destID ) );
5082
		} else {
5083
			$sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
5084
				WHERE {$sourceCol} = ? AND {$destCol} = ? -- keep-cache";
5085
			$row = $this->adapter->getRow( $sql, array( $sourceID, $destID ) );
5086
		}
5087
5088
		$this->putResultInCache( $linkTable, $key, $row );
5089
5090
		return $row;
5091
	}
5092
5093
	/**
5094
	 * @see QueryWriter::queryTagged
5095
	 */
5096
	public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() )
5097
	{
5098
		$assocType = $this->getAssocTable( array( $type, 'tag' ) );
5099
		$assocTable = $this->esc( $assocType );
5100
		$assocField = $type . '_id';
5101
		$table = $this->esc( $type );
5102
		$slots = implode( ',', array_fill( 0, count( $tagList ), '?' ) );
5103
		$score = ( $all ) ? count( $tagList ) : 1;
5104
5105
		$sql = "
5106
			SELECT {$table}.*, count({$table}.id) FROM {$table}
5107
			INNER JOIN {$assocTable} ON {$assocField} = {$table}.id
5108
			INNER JOIN tag ON {$assocTable}.tag_id = tag.id
5109
			WHERE tag.title IN ({$slots})
5110
			GROUP BY {$table}.id
5111
			HAVING count({$table}.id) >= ?
5112
			{$addSql}
5113
		";
5114
5115
		$bindings = array_merge( $tagList, array( $score ), $bindings );
5116
		$rows = $this->adapter->get( $sql, $bindings );
5117
		return $rows;
5118
	}
5119
5120
	/**
5121
	 * @see QueryWriter::queryRecordCount
5122
	 */
5123 View Code Duplication
	public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
5124
	{
5125
		$addSql = $this->glueSQLCondition( $addSql );
5126
5127
		$table  = $this->esc( $type );
5128
5129
		$this->updateCache(); //check if cache chain has been broken
5130
5131
		$sql    = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
5132
		$sql    = "SELECT COUNT(*) FROM {$table} {$sql} -- keep-cache";
5133
5134
		return (int) $this->adapter->getCell( $sql, $bindings );
5135
	}
5136
5137
	/**
5138
	 * @see QueryWriter::queryRecordCountRelated
5139
	 */
5140
	public function queryRecordCountRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() )
5141
	{
5142
		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
0 ignored issues
show
Unused Code introduced by
The assignment to $sourceTable is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
5143
5144
		$this->updateCache(); //check if cache chain has been broken
5145
5146
		if ( $sourceType === $destType ) {
5147
			$sql = "
5148
			SELECT COUNT(*) FROM {$linkTable}
5149
			INNER JOIN {$destTable} ON
5150
			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) OR
5151
			( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} = ? )
5152
			{$addSql}
5153
			-- keep-cache";
5154
5155
			$bindings = array_merge( array( $linkID, $linkID ), $bindings );
5156
		} else {
5157
			$sql = "
5158
			SELECT COUNT(*) FROM {$linkTable}
5159
			INNER JOIN {$destTable} ON
5160
			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? )
5161
			{$addSql}
5162
			-- keep-cache";
5163
5164
			$bindings = array_merge( array( $linkID ), $bindings );
5165
		}
5166
5167
		return (int) $this->adapter->getCell( $sql, $bindings );
5168
	}
5169
5170
	/**
5171
	 * @see QueryWriter::deleteRecord
5172
	 */
5173 View Code Duplication
	public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
5174
	{
5175
		$addSql = $this->glueSQLCondition( $addSql );
5176
5177
		$table  = $this->esc( $type );
5178
5179
		$sql    = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
5180
		$sql    = "DELETE FROM {$table} {$sql}";
5181
5182
		$this->adapter->exec( $sql, $bindings );
5183
	}
5184
5185
	/**
5186
	 * @see QueryWriter::deleteRelations
5187
	 */
5188
	public function deleteRelations( $sourceType, $destType, $sourceID )
5189
	{
5190
		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
5191
5192
		if ( $sourceTable === $destTable ) {
5193
			$sql = "DELETE FROM {$linkTable}
5194
				WHERE ( {$sourceCol} = ? ) OR
5195
				( {$destCol} = ?  )
5196
			";
5197
5198
			$this->adapter->exec( $sql, array( $sourceID, $sourceID ) );
5199
		} else {
5200
			$sql = "DELETE FROM {$linkTable}
5201
				WHERE {$sourceCol} = ? ";
5202
5203
			$this->adapter->exec( $sql, array( $sourceID ) );
5204
		}
5205
	}
5206
5207
	/**
5208
	 * @see QueryWriter::widenColumn
5209
	 */
5210
	public function widenColumn( $type, $property, $dataType )
5211
	{
5212
		if ( !isset($this->typeno_sqltype[$dataType]) ) return FALSE;
5213
5214
		$table   = $this->esc( $type );
5215
		$column  = $this->esc( $property );
5216
5217
		$newType = $this->typeno_sqltype[$dataType];
5218
5219
		$this->adapter->exec( "ALTER TABLE $table CHANGE $column $column $newType " );
5220
5221
		return TRUE;
5222
	}
5223
5224
	/**
5225
	 * @see QueryWriter::wipe
5226
	 */
5227
	public function wipe( $type )
5228
	{
5229
		$table = $this->esc( $type );
5230
5231
		$this->adapter->exec( "TRUNCATE $table " );
5232
	}
5233
5234
	/**
5235
	 * @see QueryWriter::renameAssocTable
5236
	 */
5237
	public function renameAssocTable( $from, $to = NULL )
5238
	{
5239
		self::renameAssociation( $from, $to );
5240
	}
5241
5242
	/**
5243
	 * @see QueryWriter::getAssocTable
5244
	 */
5245
	public function getAssocTable( $types )
5246
	{
5247
		return self::getAssocTableFormat( $types );
5248
	}
5249
5250
	/**
5251
	 * Turns caching on or off. Default: off.
5252
	 * If caching is turned on retrieval queries fired after eachother will
5253
	 * use a result row cache.
5254
	 *
5255
	 * @param boolean
5256
	 */
5257
	public function setUseCache( $yesNo )
5258
	{
5259
		$this->flushCache();
5260
5261
		$this->flagUseCache = (bool) $yesNo;
5262
	}
5263
5264
	/**
5265
	 * Flushes the Query Writer Cache.
5266
	 * Clears the internal query cache array and returns its overall
5267
	 * size.
5268
	 *
5269
	 * @return integer
5270
	 */
5271
	public function flushCache( $newMaxCacheSizePerType = NULL )
5272
	{
5273
		if ( !is_null( $newMaxCacheSizePerType ) && $newMaxCacheSizePerType > 0 ) {
5274
			$this->maxCacheSizePerType = $newMaxCacheSizePerType;
5275
		}
5276
		$count = count( $this->cache, COUNT_RECURSIVE );
5277
		$this->cache = array();
5278
		return $count;
5279
	}
5280
5281
	/**
5282
	 * @deprecated Use esc() instead.
5283
	 *
5284
	 * @param string  $column   column to be escaped
5285
	 * @param boolean $noQuotes omit quotes
5286
	 *
5287
	 * @return string
5288
	 */
5289
	public function safeColumn( $column, $noQuotes = FALSE )
5290
	{
5291
		return $this->esc( $column, $noQuotes );
5292
	}
5293
5294
	/**
5295
	 * @deprecated Use esc() instead.
5296
	 *
5297
	 * @param string  $table    table to be escaped
5298
	 * @param boolean $noQuotes omit quotes
5299
	 *
5300
	 * @return string
5301
	 */
5302
	public function safeTable( $table, $noQuotes = FALSE )
5303
	{
5304
		return $this->esc( $table, $noQuotes );
5305
	}
5306
5307
	/**
5308
	 * @see QueryWriter::inferFetchType
5309
	 */
5310
	public function inferFetchType( $type, $property )
5311
	{
5312
		$type = $this->esc( $type, TRUE );
5313
		$field = $this->esc( $property, TRUE ) . '_id';
5314
		$keys = $this->getKeyMapForType( $type );
5315
5316
		foreach( $keys as $key ) {
5317
			if (
5318
				$key['from'] === $field
5319
			) return $key['table'];
5320
		}
5321
		return NULL;
5322
	}
5323
5324
	/**
5325
	 * @see QueryWriter::addUniqueConstraint
5326
	 */
5327
	public function addUniqueIndex( $type, $properties )
5328
	{
5329
		return $this->addUniqueConstraint( $type, $properties );
5330
	}
5331
}
5332
}
5333
5334
namespace RedBeanPHP\QueryWriter {
5335
5336
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5337
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5338
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5339
use RedBeanPHP\Adapter as Adapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5340
use RedBeanPHP\RedException\SQL as SQLException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5341
5342
/**
5343
 * RedBeanPHP MySQLWriter.
5344
 * This is a QueryWriter class for RedBeanPHP.
5345
 * This QueryWriter provides support for the MySQL/MariaDB database platform.
5346
 *
5347
 * @file    RedBeanPHP/QueryWriter/MySQL.php
5348
 * @author  Gabor de Mooij and the RedBeanPHP Community
5349
 * @license BSD/GPLv2
5350
 *
5351
 * @copyright
5352
 * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
5353
 * This source file is subject to the BSD/GPLv2 License that is bundled
5354
 * with this source code in the file license.txt.
5355
 */
5356
class MySQL extends AQueryWriter implements QueryWriter
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
5357
{
5358
	/**
5359
	 * Data types
5360
	 */
5361
	const C_DATATYPE_BOOL             = 0;
5362
	const C_DATATYPE_UINT32           = 2;
5363
	const C_DATATYPE_DOUBLE           = 3;
5364
	const C_DATATYPE_TEXT7            = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible
5365
	const C_DATATYPE_TEXT8            = 5;
5366
	const C_DATATYPE_TEXT16           = 6;
5367
	const C_DATATYPE_TEXT32           = 7;
5368
	const C_DATATYPE_SPECIAL_DATE     = 80;
5369
	const C_DATATYPE_SPECIAL_DATETIME = 81;
5370
	const C_DATATYPE_SPECIAL_POINT    = 90;
5371
	const C_DATATYPE_SPECIAL_LINESTRING = 91;
5372
	const C_DATATYPE_SPECIAL_POLYGON    = 92;
5373
5374
	const C_DATATYPE_SPECIFIED        = 99;
5375
5376
	/**
5377
	 * @var DBAdapter
5378
	 */
5379
	protected $adapter;
5380
5381
	/**
5382
	 * @var string
5383
	 */
5384
	protected $quoteCharacter = '`';
5385
5386
	/**
5387
	 * @see AQueryWriter::getKeyMapForType
5388
	 */
5389 View Code Duplication
	protected function getKeyMapForType( $type )
5390
	{
5391
		$table = $this->esc( $type, TRUE );
5392
		$keys = $this->adapter->get('
5393
			SELECT
5394
				information_schema.key_column_usage.constraint_name AS `name`,
5395
				information_schema.key_column_usage.referenced_table_name AS `table`,
5396
				information_schema.key_column_usage.column_name AS `from`,
5397
				information_schema.key_column_usage.referenced_column_name AS `to`,
5398
				information_schema.referential_constraints.update_rule AS `on_update`,
5399
				information_schema.referential_constraints.delete_rule AS `on_delete`
5400
				FROM information_schema.key_column_usage
5401
				INNER JOIN information_schema.referential_constraints
5402
					ON (
5403
						information_schema.referential_constraints.constraint_name = information_schema.key_column_usage.constraint_name
5404
						AND information_schema.referential_constraints.constraint_schema = information_schema.key_column_usage.constraint_schema
5405
						AND information_schema.referential_constraints.constraint_catalog = information_schema.key_column_usage.constraint_catalog
5406
					)
5407
			WHERE
5408
				information_schema.key_column_usage.table_schema IN ( SELECT DATABASE() )
5409
				AND information_schema.key_column_usage.table_name = ?
5410
				AND information_schema.key_column_usage.constraint_name != \'PRIMARY\'
5411
				AND information_schema.key_column_usage.referenced_table_name IS NOT NULL
5412
		', array($table));
5413
		$keyInfoList = array();
5414
		foreach ( $keys as $k ) {
5415
			$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
5416
			$keyInfoList[$label] = array(
5417
				'name'          => $k['name'],
5418
				'from'          => $k['from'],
5419
				'table'         => $k['table'],
5420
				'to'            => $k['to'],
5421
				'on_update'     => $k['on_update'],
5422
				'on_delete'     => $k['on_delete']
5423
			);
5424
		}
5425
		return $keyInfoList;
5426
	}
5427
5428
	/**
5429
	 * Constructor
5430
	 *
5431
	 * @param Adapter $adapter Database Adapter
5432
	 */
5433
	public function __construct( Adapter $adapter )
5434
	{
5435
		$this->typeno_sqltype = array(
5436
			MySQL::C_DATATYPE_BOOL             => ' TINYINT(1) UNSIGNED ',
5437
			MySQL::C_DATATYPE_UINT32           => ' INT(11) UNSIGNED ',
5438
			MySQL::C_DATATYPE_DOUBLE           => ' DOUBLE ',
5439
			MySQL::C_DATATYPE_TEXT7            => ' VARCHAR(191) ',
5440
			MYSQL::C_DATATYPE_TEXT8	           => ' VARCHAR(255) ',
5441
			MySQL::C_DATATYPE_TEXT16           => ' TEXT ',
5442
			MySQL::C_DATATYPE_TEXT32           => ' LONGTEXT ',
5443
			MySQL::C_DATATYPE_SPECIAL_DATE     => ' DATE ',
5444
			MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
5445
			MySQL::C_DATATYPE_SPECIAL_POINT    => ' POINT ',
5446
			MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ',
5447
			MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ',
5448
		);
5449
5450
		$this->sqltype_typeno = array();
0 ignored issues
show
Bug introduced by
The property sqltype_typeno does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
5451
5452
		foreach ( $this->typeno_sqltype as $k => $v ) {
5453
			$this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
5454
		}
5455
5456
		$this->adapter = $adapter;
0 ignored issues
show
Documentation Bug introduced by
$adapter is of type object<RedBeanPHP\Adapter>, but the property $adapter was declared to be of type object<RedBeanPHP\Adapter\DBAdapter>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
5457
5458
		$this->encoding = $this->adapter->getDatabase()->getMysqlEncoding();
0 ignored issues
show
Bug introduced by
The property encoding does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
5459
	}
5460
5461
	/**
5462
	 * This method returns the datatype to be used for primary key IDS and
5463
	 * foreign keys. Returns one if the data type constants.
5464
	 *
5465
	 * @return integer $const data type to be used for IDS.
5466
	 */
5467
	public function getTypeForID()
5468
	{
5469
		return self::C_DATATYPE_UINT32;
5470
	}
5471
5472
	/**
5473
	 * @see QueryWriter::getTables
5474
	 */
5475
	public function getTables()
5476
	{
5477
		return $this->adapter->getCol( 'show tables' );
5478
	}
5479
5480
	/**
5481
	 * @see QueryWriter::createTable
5482
	 */
5483
	public function createTable( $table )
5484
	{
5485
		$table = $this->esc( $table );
5486
5487
		$encoding = $this->adapter->getDatabase()->getMysqlEncoding();
5488
		$sql   = "CREATE TABLE $table (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET={$encoding} COLLATE={$encoding}_unicode_ci ";
5489
5490
		$this->adapter->exec( $sql );
5491
	}
5492
5493
	/**
5494
	 * @see QueryWriter::getColumns
5495
	 */
5496
	public function getColumns( $table )
5497
	{
5498
		$columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) );
5499
5500
		$columns = array();
5501
		foreach ( $columnsRaw as $r ) {
5502
			$columns[$r['Field']] = $r['Type'];
5503
		}
5504
5505
		return $columns;
5506
	}
5507
5508
	/**
5509
	 * @see QueryWriter::scanType
5510
	 */
5511
	public function scanType( $value, $flagSpecial = FALSE )
5512
	{
5513
		$this->svalue = $value;
0 ignored issues
show
Bug introduced by
The property svalue does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
5514
5515
		if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL;
5516
		if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7;
5517
5518
		if ( $flagSpecial ) {
5519
			if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
5520
				return MySQL::C_DATATYPE_SPECIAL_DATE;
5521
			}
5522
			if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
5523
				return MySQL::C_DATATYPE_SPECIAL_DATETIME;
5524
			}
5525
			if ( preg_match( '/^POINT\(/', $value ) ) {
5526
				return MySQL::C_DATATYPE_SPECIAL_POINT;
5527
			}
5528
			if ( preg_match( '/^LINESTRING\(/', $value ) ) {
5529
				return MySQL::C_DATATYPE_SPECIAL_LINESTRING;
5530
			}
5531
			if ( preg_match( '/^POLYGON\(/', $value ) ) {
5532
				return MySQL::C_DATATYPE_SPECIAL_POLYGON;
5533
			}
5534
		}
5535
5536
		//setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?).
5537
		if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) {
5538
			return MySQL::C_DATATYPE_BOOL;
5539
		}
5540
5541
		if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
5542
5543
		if ( !$this->startsWithZeros( $value ) ) {
5544
5545
			if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) {
5546
				return MySQL::C_DATATYPE_UINT32;
5547
			}
5548
5549
			if ( is_numeric( $value ) ) {
5550
				return MySQL::C_DATATYPE_DOUBLE;
5551
			}
5552
		}
5553
5554
		if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) {
5555
			return MySQL::C_DATATYPE_TEXT7;
5556
		}
5557
5558
		if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) {
5559
			return MySQL::C_DATATYPE_TEXT8;
5560
		}
5561
5562
		if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) {
5563
			return MySQL::C_DATATYPE_TEXT16;
5564
		}
5565
5566
		return MySQL::C_DATATYPE_TEXT32;
5567
	}
5568
5569
	/**
5570
	 * @see QueryWriter::code
5571
	 */
5572 View Code Duplication
	public function code( $typedescription, $includeSpecials = FALSE )
5573
	{
5574
		if ( isset( $this->sqltype_typeno[$typedescription] ) ) {
5575
			$r = $this->sqltype_typeno[$typedescription];
5576
		} else {
5577
			$r = self::C_DATATYPE_SPECIFIED;
5578
		}
5579
5580
		if ( $includeSpecials ) {
5581
			return $r;
5582
		}
5583
5584
		if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
5585
			return self::C_DATATYPE_SPECIFIED;
5586
		}
5587
5588
		return $r;
5589
	}
5590
5591
	/**
5592
	 * @see QueryWriter::addUniqueIndex
5593
	 */
5594 View Code Duplication
	public function addUniqueConstraint( $type, $properties )
5595
	{
5596
		$tableNoQ = $this->esc( $type, TRUE );
5597
		$columns = array();
5598
		foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
5599
		$table = $this->esc( $type );
5600
		sort( $columns ); // Else we get multiple indexes due to order-effects
5601
		$name = 'UQ_' . sha1( implode( ',', $columns ) );
5602
		try {
5603
			$sql = "ALTER TABLE $table
5604
						 ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")";
5605
			$this->adapter->exec( $sql );
5606
		} catch ( SQLException $e ) {
5607
			//do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways!
5608
			return FALSE;
5609
		}
5610
		return TRUE;
5611
	}
5612
5613
	/**
5614
	 * @see QueryWriter::addIndex
5615
	 */
5616 View Code Duplication
	public function addIndex( $type, $name, $property )
5617
	{
5618
		try {
5619
			$table  = $this->esc( $type );
5620
			$name   = preg_replace( '/\W/', '', $name );
5621
			$column = $this->esc( $property );
5622
			$this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
5623
			return TRUE;
5624
		} catch ( SQLException $e ) {
5625
			return FALSE;
5626
		}
5627
	}
5628
5629
	/**
5630
	 * @see QueryWriter::addFK
5631
	 */
5632
	public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
5633
	{
5634
		$table = $this->esc( $type );
5635
		$targetTable = $this->esc( $targetType );
5636
		$targetTableNoQ = $this->esc( $targetType, TRUE );
5637
		$field = $this->esc( $property );
5638
		$fieldNoQ = $this->esc( $property, TRUE );
5639
		$targetField = $this->esc( $targetProperty );
5640
		$targetFieldNoQ = $this->esc( $targetProperty, TRUE );
5641
		$tableNoQ = $this->esc( $type, TRUE );
5642
		$fieldNoQ = $this->esc( $property, TRUE );
5643
		if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
5644
5645
		//Widen the column if it's incapable of representing a foreign key (at least INT).
5646
		$columns = $this->getColumns( $tableNoQ );
5647
		$idType = $this->getTypeForID();
5648
		if ( $this->code( $columns[$fieldNoQ] ) !==  $idType ) {
5649
			$this->widenColumn( $type, $property, $idType );
5650
		}
5651
5652
		$fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ);
5653
		$cName = 'c_'.$fkName;
5654
		try {
5655
			$this->adapter->exec( "
5656
				ALTER TABLE {$table}
5657
				ADD CONSTRAINT $cName
5658
				FOREIGN KEY $fkName ( {$fieldNoQ} ) REFERENCES {$targetTableNoQ}
5659
				({$targetFieldNoQ}) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';');
5660
		} catch ( SQLException $e ) {
5661
			// Failure of fk-constraints is not a problem
5662
		}
5663
	}
5664
5665
	/**
5666
	 * @see QueryWriter::sqlStateIn
5667
	 */
5668
	public function sqlStateIn( $state, $list )
5669
	{
5670
		$stateMap = array(
5671
			'42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
5672
			'42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
5673
			'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
5674
		);
5675
5676
		return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
5677
	}
5678
5679
	/**
5680
	 * @see QueryWriter::wipeAll
5681
	 */
5682 View Code Duplication
	public function wipeAll()
5683
	{
5684
		$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' );
5685
5686
		foreach ( $this->getTables() as $t ) {
5687
			try {
5688
				$this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
5689
			} catch ( SQLException $e ) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
5690
			}
5691
5692
			try {
5693
				$this->adapter->exec( "DROP VIEW IF EXISTS `$t`" );
5694
			} catch ( SQLException $e ) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
5695
			}
5696
		}
5697
5698
		$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' );
5699
	}
5700
}
5701
} 
5702
5703
namespace RedBeanPHP\QueryWriter {
5704
5705
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5706
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5707
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5708
use RedBeanPHP\Adapter as Adapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5709
use RedBeanPHP\RedException\SQL as SQLException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
5710
5711
/**
5712
 * RedBeanPHP SQLiteWriter with support for SQLite types
5713
 * This is a QueryWriter class for RedBeanPHP.
5714
 * This QueryWriter provides support for the SQLite database platform.
5715
 *
5716
 * @file    RedBeanPHP/QueryWriter/SQLiteT.php
5717
 * @author  Gabor de Mooij and the RedBeanPHP Community
5718
 * @license BSD/GPLv2
5719
 *
5720
 * @copyright
5721
 * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
5722
 * This source file is subject to the BSD/GPLv2 License that is bundled
5723
 * with this source code in the file license.txt.
5724
 */
5725
class SQLiteT extends AQueryWriter implements QueryWriter
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
5726
{
5727
	/**
5728
	 * Data types
5729
	 */
5730
	const C_DATATYPE_INTEGER   = 0;
5731
	const C_DATATYPE_NUMERIC   = 1;
5732
	const C_DATATYPE_TEXT      = 2;
5733
	const C_DATATYPE_SPECIFIED = 99;
5734
5735
	/**
5736
	 * @var DBAdapter
5737
	 */
5738
	protected $adapter;
5739
5740
	/**
5741
	 * @var string
5742
	 */
5743
	protected $quoteCharacter = '`';
5744
5745
	/**
5746
	 * Gets all information about a table (from a type).
5747
	 *
5748
	 * Format:
5749
	 * array(
5750
	 *    name => name of the table
5751
	 *    columns => array( name => datatype )
5752
	 *    indexes => array() raw index information rows from PRAGMA query
5753
	 *    keys => array() raw key information rows from PRAGMA query
5754
	 * )
5755
	 *
5756
	 * @param string $type type you want to get info of
5757
	 *
5758
	 * @return array $info
5759
	 */
5760
	protected function getTable( $type )
5761
	{
5762
		$tableName = $this->esc( $type, TRUE );
5763
		$columns   = $this->getColumns( $type );
5764
		$indexes   = $this->getIndexes( $type );
5765
		$keys      = $this->getKeyMapForType( $type );
5766
5767
		$table = array(
5768
			'columns' => $columns,
5769
			'indexes' => $indexes,
5770
			'keys' => $keys,
5771
			'name' => $tableName
5772
		);
5773
5774
		$this->tableArchive[$tableName] = $table;
0 ignored issues
show
Bug introduced by
The property tableArchive does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
5775
5776
		return $table;
5777
	}
5778
5779
	/**
5780
	 * Puts a table. Updates the table structure.
5781
	 * In SQLite we can't change columns, drop columns, change or add foreign keys so we
5782
	 * have a table-rebuild function. You simply load your table with getTable(), modify it and
5783
	 * then store it with putTable()...
5784
	 *
5785
	 * @param array $tableMap information array
5786
	 */
5787
	protected function putTable( $tableMap )
5788
	{
5789
		$table = $tableMap['name'];
5790
		$q     = array();
5791
		$q[]   = "DROP TABLE IF EXISTS tmp_backup;";
5792
5793
		$oldColumnNames = array_keys( $this->getColumns( $table ) );
5794
5795
		foreach ( $oldColumnNames as $k => $v ) $oldColumnNames[$k] = "`$v`";
5796
5797
		$q[] = "CREATE TEMPORARY TABLE tmp_backup(" . implode( ",", $oldColumnNames ) . ");";
5798
		$q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
5799
		$q[] = "PRAGMA foreign_keys = 0 ";
5800
		$q[] = "DROP TABLE `$table`;";
5801
5802
		$newTableDefStr = '';
5803
		foreach ( $tableMap['columns'] as $column => $type ) {
5804
			if ( $column != 'id' ) {
5805
				$newTableDefStr .= ",`$column` $type";
5806
			}
5807
		}
5808
5809
		$fkDef = '';
5810
		foreach ( $tableMap['keys'] as $key ) {
5811
			$fkDef .= ", FOREIGN KEY(`{$key['from']}`)
5812
						 REFERENCES `{$key['table']}`(`{$key['to']}`)
5813
						 ON DELETE {$key['on_delete']} ON UPDATE {$key['on_update']}";
5814
		}
5815
5816
		$q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT  $newTableDefStr  $fkDef );";
5817
5818
		foreach ( $tableMap['indexes'] as $name => $index ) {
5819
			if ( strpos( $name, 'UQ_' ) === 0 ) {
5820
				$cols = explode( '__', substr( $name, strlen( 'UQ_' . $table ) ) );
5821
				foreach ( $cols as $k => $v ) $cols[$k] = "`$v`";
5822
				$q[] = "CREATE UNIQUE INDEX $name ON `$table` (" . implode( ',', $cols ) . ")";
5823
			} else $q[] = "CREATE INDEX $name ON `$table` ({$index['name']}) ";
5824
		}
5825
5826
		$q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
5827
		$q[] = "DROP TABLE tmp_backup;";
5828
		$q[] = "PRAGMA foreign_keys = 1 ";
5829
5830
		foreach ( $q as $sq ) $this->adapter->exec( $sq );
5831
	}
5832
5833
	/**
5834
	 * Returns the indexes for type $type.
5835
	 *
5836
	 * @param string $type
5837
	 *
5838
	 * @return array $indexInfo index information
5839
	 */
5840
	protected function getIndexes( $type )
5841
	{
5842
		$table   = $this->esc( $type, TRUE );
5843
		$indexes = $this->adapter->get( "PRAGMA index_list('$table')" );
5844
5845
		$indexInfoList = array();
5846
		foreach ( $indexes as $i ) {
5847
			$indexInfoList[$i['name']] = $this->adapter->getRow( "PRAGMA index_info('{$i['name']}') " );
5848
5849
			$indexInfoList[$i['name']]['unique'] = $i['unique'];
5850
		}
5851
5852
		return $indexInfoList;
5853
	}
5854
5855
	/**
5856
	 * Adds a foreign key to a type
5857
	 *
5858
	 * @param  string  $type        type you want to modify table of
5859
	 * @param  string  $targetType  target type
5860
	 * @param  string  $field       field of the type that needs to get the fk
0 ignored issues
show
Bug introduced by
There is no parameter named $field. 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...
5861
	 * @param  string  $targetField field where the fk needs to point to
0 ignored issues
show
Bug introduced by
There is no parameter named $targetField. 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...
5862
	 * @param  integer $buildopt    0 = NO ACTION, 1 = ON DELETE CASCADE
0 ignored issues
show
Bug introduced by
There is no parameter named $buildopt. 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...
5863
	 *
5864
	 * @return boolean $didIt
5865
	 *
5866
	 * @note: cant put this in try-catch because that can hide the fact
5867
	 *      that database has been damaged.
5868
	 */
5869
	protected function buildFK( $type, $targetType, $property, $targetProperty, $constraint = FALSE )
5870
	{
5871
		$table           = $this->esc( $type, TRUE );
5872
		$targetTable     = $this->esc( $targetType, TRUE );
5873
		$column          = $this->esc( $property, TRUE );
5874
		$targetColumn    = $this->esc( $targetProperty, TRUE );
5875
5876
		$tables = $this->getTables();
5877
		if ( !in_array( $targetTable, $tables ) ) return FALSE;
5878
5879
		if ( !is_null( $this->getForeignKeyForTypeProperty( $table, $column ) ) ) return FALSE;
5880
		$t = $this->getTable( $table );
5881
		$consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' );
5882
		$label   = 'from_' . $column . '_to_table_' . $targetTable . '_col_' . $targetColumn;
5883
		$t['keys'][$label] = array(
5884
			'table'     => $targetTable,
5885
			'from'      => $column,
5886
			'to'        => $targetColumn,
5887
			'on_update' => $consSQL,
5888
			'on_delete' => $consSQL
5889
		);
5890
		$this->putTable( $t );
5891
		return TRUE;
5892
	}
5893
5894
	/**
5895
	 * @see AQueryWriter::getKeyMapForType
5896
	 */
5897 View Code Duplication
	protected function getKeyMapForType( $type )
5898
	{
5899
		$table = $this->esc( $type, TRUE );
5900
		$keys  = $this->adapter->get( "PRAGMA foreign_key_list('$table')" );
5901
		$keyInfoList = array();
5902
		foreach ( $keys as $k ) {
5903
			$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
5904
			$keyInfoList[$label] = array(
5905
				'name'          => $label,
5906
				'from'          => $k['from'],
5907
				'table'         => $k['table'],
5908
				'to'            => $k['to'],
5909
				'on_update'     => $k['on_update'],
5910
				'on_delete'     => $k['on_delete']
5911
			);
5912
		}
5913
		return $keyInfoList;
5914
	}
5915
5916
	/**
5917
	 * Constructor
5918
	 *
5919
	 * @param Adapter $adapter Database Adapter
5920
	 */
5921
	public function __construct( Adapter $adapter )
5922
	{
5923
		$this->typeno_sqltype = array(
5924
			SQLiteT::C_DATATYPE_INTEGER => 'INTEGER',
5925
			SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC',
5926
			SQLiteT::C_DATATYPE_TEXT    => 'TEXT',
5927
		);
5928
5929
		$this->sqltype_typeno = array();
0 ignored issues
show
Bug introduced by
The property sqltype_typeno does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
5930
5931
		foreach ( $this->typeno_sqltype as $k => $v ) {
5932
			$this->sqltype_typeno[$v] = $k;
5933
		}
5934
5935
		$this->adapter = $adapter;
0 ignored issues
show
Documentation Bug introduced by
$adapter is of type object<RedBeanPHP\Adapter>, but the property $adapter was declared to be of type object<RedBeanPHP\Adapter\DBAdapter>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
5936
	}
5937
5938
	/**
5939
	 * This method returns the datatype to be used for primary key IDS and
5940
	 * foreign keys. Returns one if the data type constants.
5941
	 *
5942
	 * @return integer $const data type to be used for IDS.
5943
	 */
5944
	public function getTypeForID()
5945
	{
5946
		return self::C_DATATYPE_INTEGER;
5947
	}
5948
5949
	/**
5950
	 * @see QueryWriter::scanType
5951
	 */
5952
	public function scanType( $value, $flagSpecial = FALSE )
5953
	{
5954
		$this->svalue = $value;
0 ignored issues
show
Bug introduced by
The property svalue does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
5955
5956
		if ( $value === NULL ) return self::C_DATATYPE_INTEGER;
5957
		if ( $value === INF ) return self::C_DATATYPE_TEXT;
5958
5959
		if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
5960
5961
		if ( $value === TRUE || $value === FALSE )  return self::C_DATATYPE_INTEGER;
5962
		
5963
		if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER;
5964
5965
		if ( ( is_numeric( $value ) && $value < 2147483648 && $value > -2147483648)
5966
			|| preg_match( '/\d{4}\-\d\d\-\d\d/', $value )
5967
			|| preg_match( '/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', $value )
5968
		) {
5969
			return self::C_DATATYPE_NUMERIC;
5970
		}
5971
5972
		return self::C_DATATYPE_TEXT;
5973
	}
5974
5975
	/**
5976
	 * @see QueryWriter::addColumn
5977
	 */
5978
	public function addColumn( $table, $column, $type )
5979
	{
5980
		$column = $this->check( $column );
5981
		$table  = $this->check( $table );
5982
		$type   = $this->typeno_sqltype[$type];
5983
5984
		$this->adapter->exec( "ALTER TABLE `$table` ADD `$column` $type " );
5985
	}
5986
5987
	/**
5988
	 * @see QueryWriter::code
5989
	 */
5990
	public function code( $typedescription, $includeSpecials = FALSE )
5991
	{
5992
		$r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99 );
5993
		
5994
		return $r;
5995
	}
5996
5997
	/**
5998
	 * @see QueryWriter::widenColumn
5999
	 */
6000
	public function widenColumn( $type, $column, $datatype )
6001
	{
6002
		$t = $this->getTable( $type );
6003
6004
		$t['columns'][$column] = $this->typeno_sqltype[$datatype];
6005
6006
		$this->putTable( $t );
6007
	}
6008
6009
	/**
6010
	 * @see QueryWriter::getTables();
6011
	 */
6012
	public function getTables()
6013
	{
6014
		return $this->adapter->getCol( "SELECT name FROM sqlite_master
6015
			WHERE type='table' AND name!='sqlite_sequence';" );
6016
	}
6017
6018
	/**
6019
	 * @see QueryWriter::createTable
6020
	 */
6021
	public function createTable( $table )
6022
	{
6023
		$table = $this->esc( $table );
6024
6025
		$sql   = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) ";
6026
6027
		$this->adapter->exec( $sql );
6028
	}
6029
6030
	/**
6031
	 * @see QueryWriter::getColumns
6032
	 */
6033 View Code Duplication
	public function getColumns( $table )
6034
	{
6035
		$table      = $this->esc( $table, TRUE );
6036
6037
		$columnsRaw = $this->adapter->get( "PRAGMA table_info('$table')" );
6038
6039
		$columns    = array();
6040
		foreach ( $columnsRaw as $r ) $columns[$r['name']] = $r['type'];
6041
6042
		return $columns;
6043
	}
6044
6045
	/**
6046
	 * @see QueryWriter::addUniqueIndex
6047
	 */
6048
	public function addUniqueConstraint( $type, $properties )
6049
	{
6050
		$tableNoQ = $this->esc( $type, TRUE );
6051
		$name  = 'UQ_' . $this->esc( $type, TRUE ) . implode( '__', $properties );
6052
		$t     = $this->getTable( $type );
6053
		$t['indexes'][$name] = array( 'name' => $name );
6054
		try {
6055
			$this->putTable( $t );
6056
		} catch( SQLException $e ) {
6057
			return FALSE;
6058
		}
6059
		return TRUE;
6060
	}
6061
6062
	/**
6063
	 * @see QueryWriter::sqlStateIn
6064
	 */
6065 View Code Duplication
	public function sqlStateIn( $state, $list )
6066
	{
6067
		$stateMap = array(
6068
			'HY000' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
6069
			'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
6070
		);
6071
6072
		return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
6073
	}
6074
6075
	/**
6076
	 * @see QueryWriter::addIndex
6077
	 */
6078
	public function addIndex( $type, $name, $column )
6079
	{
6080
		$columns = $this->getColumns( $type );
6081
		if ( !isset( $columns[$column] ) ) return FALSE;
6082
6083
		$table  = $this->esc( $type );
6084
		$name   = preg_replace( '/\W/', '', $name );
6085
		$column = $this->esc( $column, TRUE );
6086
6087
		try {
6088
			$t = $this->getTable( $type );
6089
			$t['indexes'][$name] = array( 'name' => $column );
6090
			$this->putTable( $t );
6091
			return TRUE;
6092
		} catch( SQLException $exception ) {
6093
			return FALSE;
6094
		}
6095
	}
6096
6097
	/**
6098
	 * @see QueryWriter::wipe
6099
	 */
6100
	public function wipe( $type )
6101
	{
6102
		$table = $this->esc( $type );
6103
		
6104
		$this->adapter->exec( "DELETE FROM $table " );
6105
	}
6106
6107
	/**
6108
	 * @see QueryWriter::addFK
6109
	 */
6110
	public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
6111
	{
6112
		return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDep );
6113
	}
6114
6115
	/**
6116
	 * @see QueryWriter::wipeAll
6117
	 */
6118 View Code Duplication
	public function wipeAll()
6119
	{
6120
		$this->adapter->exec( 'PRAGMA foreign_keys = 0 ' );
6121
6122
		foreach ( $this->getTables() as $t ) {
6123
			try {
6124
				$this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
6125
			} catch ( SQLException $e ) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
6126
			}
6127
6128
			try {
6129
				$this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
6130
			} catch ( SQLException $e ) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
6131
			}
6132
		}
6133
6134
		$this->adapter->exec( 'PRAGMA foreign_keys = 1 ' );
6135
	}
6136
}
6137
}
6138
6139
namespace RedBeanPHP\QueryWriter {
6140
6141
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6142
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6143
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6144
use RedBeanPHP\Adapter as Adapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6145
use RedBeanPHP\RedException\SQL as SQLException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6146
6147
/**
6148
 * RedBeanPHP PostgreSQL Query Writer.
6149
 * This is a QueryWriter class for RedBeanPHP.
6150
 * This QueryWriter provides support for the PostgreSQL database platform.
6151
 *
6152
 * @file    RedBeanPHP/QueryWriter/PostgreSQL.php
6153
 * @author  Gabor de Mooij and the RedBeanPHP Community
6154
 * @license BSD/GPLv2
6155
 *
6156
 * @copyright
6157
 * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
6158
 * This source file is subject to the BSD/GPLv2 License that is bundled
6159
 * with this source code in the file license.txt.
6160
 */
6161
class PostgreSQL extends AQueryWriter implements QueryWriter
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
6162
{
6163
	/**
6164
	 * Data types
6165
	 */
6166
	const C_DATATYPE_INTEGER          = 0;
6167
	const C_DATATYPE_DOUBLE           = 1;
6168
	const C_DATATYPE_TEXT             = 3;
6169
	const C_DATATYPE_SPECIAL_DATE     = 80;
6170
	const C_DATATYPE_SPECIAL_DATETIME = 81;
6171
	const C_DATATYPE_SPECIAL_POINT    = 90;
6172
	const C_DATATYPE_SPECIAL_LSEG     = 91;
6173
	const C_DATATYPE_SPECIAL_CIRCLE   = 92;
6174
	const C_DATATYPE_SPECIAL_MONEY    = 93;
6175
	const C_DATATYPE_SPECIAL_POLYGON  = 94;
6176
	const C_DATATYPE_SPECIFIED        = 99;
6177
6178
	/**
6179
	 * @var DBAdapter
6180
	 */
6181
	protected $adapter;
6182
6183
	/**
6184
	 * @var string
6185
	 */
6186
	protected $quoteCharacter = '"';
6187
6188
	/**
6189
	 * @var string
6190
	 */
6191
	protected $defaultValue = 'DEFAULT';
6192
6193
	/**
6194
	 * Returns the insert suffix SQL Snippet
6195
	 *
6196
	 * @param string $table table
6197
	 *
6198
	 * @return  string $sql SQL Snippet
6199
	 */
6200
	protected function getInsertSuffix( $table )
6201
	{
6202
		return 'RETURNING id ';
6203
	}
6204
6205
	/**
6206
	 * @see AQueryWriter::getKeyMapForType
6207
	 */
6208 View Code Duplication
	protected function getKeyMapForType( $type )
6209
	{
6210
		$table = $this->esc( $type, TRUE );
6211
		$keys = $this->adapter->get( '
6212
			SELECT
6213
			information_schema.key_column_usage.constraint_name AS "name",
6214
			information_schema.key_column_usage.column_name AS "from",
6215
			information_schema.constraint_table_usage.table_name AS "table",
6216
			information_schema.constraint_column_usage.column_name AS "to",
6217
			information_schema.referential_constraints.update_rule AS "on_update",
6218
			information_schema.referential_constraints.delete_rule AS "on_delete"
6219
				FROM information_schema.key_column_usage
6220
			INNER JOIN information_schema.constraint_table_usage
6221
				ON (
6222
					information_schema.key_column_usage.constraint_name = information_schema.constraint_table_usage.constraint_name
6223
					AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_table_usage.constraint_schema
6224
					AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_table_usage.constraint_catalog
6225
				)
6226
			INNER JOIN information_schema.constraint_column_usage
6227
				ON (
6228
					information_schema.key_column_usage.constraint_name = information_schema.constraint_column_usage.constraint_name
6229
					AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_column_usage.constraint_schema
6230
					AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_column_usage.constraint_catalog
6231
				)
6232
			INNER JOIN information_schema.referential_constraints
6233
				ON (
6234
					information_schema.key_column_usage.constraint_name = information_schema.referential_constraints.constraint_name
6235
					AND information_schema.key_column_usage.constraint_schema = information_schema.referential_constraints.constraint_schema
6236
					AND information_schema.key_column_usage.constraint_catalog = information_schema.referential_constraints.constraint_catalog
6237
				)
6238
			WHERE
6239
				information_schema.key_column_usage.table_catalog = current_database()
6240
				AND information_schema.key_column_usage.table_schema = ANY( current_schemas( FALSE ) )
6241
				AND information_schema.key_column_usage.table_name = ?
6242
		', array( $type ) );
6243
		$keyInfoList = array();
6244
		foreach ( $keys as $k ) {
6245
			$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
6246
			$keyInfoList[$label] = array(
6247
				'name'          => $k['name'],
6248
				'from'          => $k['from'],
6249
				'table'         => $k['table'],
6250
				'to'            => $k['to'],
6251
				'on_update'     => $k['on_update'],
6252
				'on_delete'     => $k['on_delete']
6253
			);
6254
		}
6255
		return $keyInfoList;
6256
	}
6257
6258
	/**
6259
	 * Constructor
6260
	 *
6261
	 * @param Adapter $adapter Database Adapter
6262
	 */
6263
	public function __construct( Adapter $adapter )
6264
	{
6265
		$this->typeno_sqltype = array(
6266
			self::C_DATATYPE_INTEGER          => ' integer ',
6267
			self::C_DATATYPE_DOUBLE           => ' double precision ',
6268
			self::C_DATATYPE_TEXT             => ' text ',
6269
			self::C_DATATYPE_SPECIAL_DATE     => ' date ',
6270
			self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ',
6271
			self::C_DATATYPE_SPECIAL_POINT    => ' point ',
6272
			self::C_DATATYPE_SPECIAL_LSEG     => ' lseg ',
6273
			self::C_DATATYPE_SPECIAL_CIRCLE   => ' circle ',
6274
			self::C_DATATYPE_SPECIAL_MONEY    => ' money ',
6275
			self::C_DATATYPE_SPECIAL_POLYGON  => ' polygon ',
6276
		);
6277
6278
		$this->sqltype_typeno = array();
0 ignored issues
show
Bug introduced by
The property sqltype_typeno does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
6279
6280
		foreach ( $this->typeno_sqltype as $k => $v ) {
6281
			$this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
6282
		}
6283
6284
		$this->adapter = $adapter;
0 ignored issues
show
Documentation Bug introduced by
$adapter is of type object<RedBeanPHP\Adapter>, but the property $adapter was declared to be of type object<RedBeanPHP\Adapter\DBAdapter>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
6285
	}
6286
6287
	/**
6288
	 * This method returns the datatype to be used for primary key IDS and
6289
	 * foreign keys. Returns one if the data type constants.
6290
	 *
6291
	 * @return integer $const data type to be used for IDS.
6292
	 */
6293
	public function getTypeForID()
6294
	{
6295
		return self::C_DATATYPE_INTEGER;
6296
	}
6297
6298
	/**
6299
	 * @see QueryWriter::getTables
6300
	 */
6301
	public function getTables()
6302
	{
6303
		return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' );
6304
	}
6305
6306
	/**
6307
	 * @see QueryWriter::createTable
6308
	 */
6309
	public function createTable( $table )
6310
	{
6311
		$table = $this->esc( $table );
6312
6313
		$this->adapter->exec( " CREATE TABLE $table (id SERIAL PRIMARY KEY); " );
6314
	}
6315
6316
	/**
6317
	 * @see QueryWriter::getColumns
6318
	 */
6319 View Code Duplication
	public function getColumns( $table )
6320
	{
6321
		$table      = $this->esc( $table, TRUE );
6322
6323
		$columnsRaw = $this->adapter->get( "SELECT column_name, data_type FROM information_schema.columns WHERE table_name='$table'" );
6324
6325
		$columns = array();
6326
		foreach ( $columnsRaw as $r ) {
6327
			$columns[$r['column_name']] = $r['data_type'];
6328
		}
6329
6330
		return $columns;
6331
	}
6332
6333
	/**
6334
	 * @see QueryWriter::scanType
6335
	 */
6336
	public function scanType( $value, $flagSpecial = FALSE )
6337
	{
6338
		$this->svalue = $value;
0 ignored issues
show
Bug introduced by
The property svalue does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
6339
6340
		if ( $value === INF ) return self::C_DATATYPE_TEXT;
6341
6342
		if ( $flagSpecial && $value ) {
6343
			if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
6344
				return PostgreSQL::C_DATATYPE_SPECIAL_DATE;
6345
			}
6346
6347
			if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) {
6348
				return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME;
6349
			}
6350
6351
			if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) {
6352
				return PostgreSQL::C_DATATYPE_SPECIAL_POINT;
6353
			}
6354
6355
			if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) {
6356
				return PostgreSQL::C_DATATYPE_SPECIAL_LSEG;
6357
			}
6358
6359
			if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) {
6360
				return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE;
6361
			}
6362
6363
			if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) {
6364
				return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON;
6365
			}
6366
6367
			if ( preg_match( '/^\-?(\$|€|¥|£)[\d,\.]+$/', $value ) ) {
6368
				return PostgreSQL::C_DATATYPE_SPECIAL_MONEY;
6369
			}
6370
		}
6371
6372
		if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
6373
6374
		if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
6375
		
6376
		if ( $value === FALSE || $value === TRUE || $value === NULL || ( is_numeric( $value )
6377
				&& AQueryWriter::canBeTreatedAsInt( $value )
6378
				&& $value < 2147483648
6379
				&& $value > -2147483648 )
6380
		) {
6381
			return self::C_DATATYPE_INTEGER;
6382
		} elseif ( is_numeric( $value ) ) {
6383
			return self::C_DATATYPE_DOUBLE;
6384
		} else {
6385
			return self::C_DATATYPE_TEXT;
6386
		}
6387
	}
6388
6389
	/**
6390
	 * @see QueryWriter::code
6391
	 */
6392 View Code Duplication
	public function code( $typedescription, $includeSpecials = FALSE )
6393
	{
6394
		$r = ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99;
6395
6396
		if ( $includeSpecials ) return $r;
6397
6398
		if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
6399
			return self::C_DATATYPE_SPECIFIED;
6400
		}
6401
6402
		return $r;
6403
	}
6404
6405
	/**
6406
	 * @see QueryWriter::widenColumn
6407
	 */
6408 View Code Duplication
	public function widenColumn( $type, $column, $datatype )
6409
	{
6410
		$table   = $type;
6411
		$type    = $datatype;
6412
6413
		$table   = $this->esc( $table );
6414
		$column  = $this->esc( $column );
6415
6416
		$newtype = $this->typeno_sqltype[$type];
6417
6418
		$this->adapter->exec( "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype " );
6419
	}
6420
6421
	/**
6422
	 * @see QueryWriter::addUniqueIndex
6423
	 */
6424 View Code Duplication
	public function addUniqueConstraint( $type, $properties )
6425
	{
6426
		$tableNoQ = $this->esc( $type, TRUE );
6427
		$columns = array();
6428
		foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
6429
		$table = $this->esc( $type );
6430
		sort( $columns ); //else we get multiple indexes due to order-effects
6431
		$name = "UQ_" . sha1( $table . implode( ',', $columns ) );
6432
		$sql = "ALTER TABLE {$table}
6433
                ADD CONSTRAINT $name UNIQUE (" . implode( ',', $columns ) . ")";
6434
		try {
6435
			$this->adapter->exec( $sql );
6436
		} catch( SQLException $e ) {
6437
			return FALSE;
6438
		}
6439
		return TRUE;
6440
	}
6441
6442
	/**
6443
	 * @see QueryWriter::sqlStateIn
6444
	 */
6445 View Code Duplication
	public function sqlStateIn( $state, $list )
6446
	{
6447
		$stateMap = array(
6448
			'42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
6449
			'42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
6450
			'23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
6451
		);
6452
6453
		return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
6454
	}
6455
6456
	/**
6457
	 * @see QueryWriter::addIndex
6458
	 */
6459 View Code Duplication
	public function addIndex( $type, $name, $property )
6460
	{
6461
		$table  = $this->esc( $type );
6462
		$name   = preg_replace( '/\W/', '', $name );
6463
		$column = $this->esc( $property );
6464
6465
		try {
6466
			$this->adapter->exec( "CREATE INDEX {$name} ON $table ({$column}) " );
6467
			return TRUE;
6468
		} catch ( SQLException $e ) {
6469
			return FALSE;
6470
		}
6471
	}
6472
6473
	/**
6474
	 * @see QueryWriter::addFK
6475
	 */
6476
	public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
6477
	{
6478
		$table = $this->esc( $type );
6479
		$targetTable = $this->esc( $targetType );
6480
		$field = $this->esc( $property );
6481
		$targetField = $this->esc( $targetProperty );
6482
		$tableNoQ = $this->esc( $type, TRUE );
6483
		$fieldNoQ = $this->esc( $property, TRUE );
6484
		if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
6485
		try{
6486
			$delRule = ( $isDep ? 'CASCADE' : 'SET NULL' );
6487
			$this->adapter->exec( "ALTER TABLE {$table}
6488
				ADD FOREIGN KEY ( {$field} ) REFERENCES  {$targetTable}
6489
				({$targetField}) ON DELETE {$delRule} ON UPDATE {$delRule} DEFERRABLE ;" );
6490
			return TRUE;
6491
		} catch ( SQLException $e ) {
6492
			return FALSE;
6493
		}
6494
	}
6495
6496
	/**
6497
	 * @see QueryWriter::wipeAll
6498
	 */
6499
	public function wipeAll()
6500
	{
6501
		$this->adapter->exec( 'SET CONSTRAINTS ALL DEFERRED' );
6502
6503
		foreach ( $this->getTables() as $t ) {
6504
			$t = $this->esc( $t );
6505
6506
			$this->adapter->exec( "DROP TABLE IF EXISTS $t CASCADE " );
6507
		}
6508
6509
		$this->adapter->exec( 'SET CONSTRAINTS ALL IMMEDIATE' );
6510
	}
6511
}
6512
} 
6513
6514
namespace RedBeanPHP {
6515
6516
/**
6517
 * RedBean\Exception Base.
6518
 * Represents the base class for RedBeanPHP\Exceptions.
6519
 *
6520
 * @file    RedBeanPHP/Exception.php
6521
 * @author  Gabor de Mooij and the RedBeanPHP Community
6522
 * @license BSD/GPLv2
6523
 *
6524
 * @copyright
6525
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
6526
 * This source file is subject to the BSD/GPLv2 License that is bundled
6527
 * with this source code in the file license.txt.
6528
 */
6529
class RedException extends \Exception
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
6530
{
6531
}
6532
}
6533
6534
namespace RedBeanPHP\RedException {
6535
6536
use RedBeanPHP\RedException as RedException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6537
6538
/**
6539
 * SQL Exception.
6540
 * Represents a generic database exception independent of the underlying driver.
6541
 *
6542
 * @file       RedBeanPHP/RedException/SQL.php
6543
 * @author     Gabor de Mooij and the RedBeanPHP Community
6544
 * @license    BSD/GPLv2
6545
 *
6546
 * @copyright
6547
 * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
6548
 * This source file is subject to the BSD/GPLv2 License that is bundled
6549
 * with this source code in the file license.txt.
6550
 */
6551
class SQL extends RedException
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
6552
{
6553
	/**
6554
	 * @var string
6555
	 */
6556
	private $sqlState;
6557
6558
	/**
6559
	 * Returns an ANSI-92 compliant SQL state.
6560
	 *
6561
	 * @return string $state ANSI state code
6562
	 */
6563
	public function getSQLState()
6564
	{
6565
		return $this->sqlState;
6566
	}
6567
6568
	/**
6569
	 * @todo parse state to verify valid ANSI92!
6570
	 *       Stores ANSI-92 compliant SQL state.
6571
	 *
6572
	 * @param string $sqlState code
6573
	 *
6574
	 * @return void
6575
	 */
6576
	public function setSQLState( $sqlState )
6577
	{
6578
		$this->sqlState = $sqlState;
6579
	}
6580
6581
	/**
6582
	 * To String prints both code and SQL state.
6583
	 *
6584
	 * @return string $message prints this exception instance as a string
6585
	 */
6586
	public function __toString()
6587
	{
6588
		return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n".
6589
				'trace: ' . $this->getTraceAsString();
6590
	}
6591
}
6592
}
6593
6594
namespace RedBeanPHP {
6595
6596
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6597
use RedBeanPHP\Observable as Observable;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6598
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6599
use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6600
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6601
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6602
use RedBeanPHP\SimpleModel as SimpleModel;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6603
use RedBeanPHP\BeanHelper as BeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6604
use RedBeanPHP\RedException\SQL as SQLException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6605
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6606
use RedBeanPHP\OODB as OODB;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6607
use RedBeanPHP\Cursor as Cursor;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6608
use RedBeanPHP\Cursor\NullCursor as NullCursor;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
6609
6610
/**
6611
 * Abstract Repository.
6612
 *
6613
 * OODB manages two repositories, a fluid one that
6614
 * adjust the database schema on-the-fly to accomodate for
6615
 * new bean types (tables) and new properties (columns) and
6616
 * a frozen one for use in a production environment. OODB
6617
 * allows you to swap the repository instances using the freeze()
6618
 * method.
6619
 *
6620
 * @file    RedBeanPHP/Repository.php
6621
 * @author  Gabor de Mooij and the RedBeanPHP community
6622
 * @license BSD/GPLv2
6623
 * 
6624
 * @copyright
6625
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
6626
 * This source file is subject to the BSD/GPLv2 License that is bundled
6627
 * with this source code in the file license.txt.
6628
 */
6629
abstract class Repository
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
6630
{
6631
	/**
6632
	 * @var array
6633
	 */
6634
	protected $stash = NULL;
6635
6636
	/*
6637
	 * @var integer
6638
	 */
6639
	protected $nesting = 0;
6640
6641
	/**
6642
	 * @var DBAdapter
6643
	 */
6644
	protected $writer;
6645
6646
	/**
6647
	 * Stores a bean and its lists in one run.
6648
	 *
6649
	 * @param OODBBean $bean
6650
	 *
6651
	 * @return void
6652
	 */
6653
	protected function storeBeanWithLists( OODBBean $bean )
6654
	{
6655
		$sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups
6656
		foreach ( $bean as $property => $value ) {
6657
			$value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value;
6658
			if ( $value instanceof OODBBean ) {
6659
				$this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value );
6660
				$bean->setMeta("sys.typeof.{$property}", $value->getMeta('type'));
6661
			} elseif ( is_array( $value ) ) {
6662
				$originals = $bean->moveMeta( 'sys.shadow.' . $property, array() );
6663
				if ( strpos( $property, 'own' ) === 0 ) {
6664
					list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue );
6665
					$listName = lcfirst( substr( $property, 3 ) );
6666
					if ($bean->moveMeta( 'sys.exclusive-'.  $listName ) ) {
6667
						OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE );
6668
						OODBBean::setMetaAll( $ownAdditions, 'sys.buildcommand.fkdependson', $bean->getMeta( 'type' ) );
6669
					}
6670
					unset( $bean->$property );
6671
				} elseif ( strpos( $property, 'shared' ) === 0 ) {
6672
					list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue );
6673
					unset( $bean->$property );
6674
				}
6675
			}
6676
		}
6677
		$this->storeBean( $bean );
0 ignored issues
show
Bug introduced by
The method storeBean() does not exist on RedBeanPHP\Repository. Did you maybe mean storeBeanWithLists()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
6678
		$this->processTrashcan( $bean, $ownTrashcan );
6679
		$this->processAdditions( $bean, $ownAdditions );
6680
		$this->processResidue( $ownresidue );
6681
		$this->processSharedTrashcan( $bean, $sharedTrashcan );
6682
		$this->processSharedAdditions( $bean, $sharedAdditions );
6683
		$this->processSharedResidue( $bean, $sharedresidue );
6684
	}
6685
6686
	/**
6687
	 * Process groups. Internal function. Processes different kind of groups for
6688
	 * storage function. Given a list of original beans and a list of current beans,
6689
	 * this function calculates which beans remain in the list (residue), which
6690
	 * have been deleted (are in the trashcan) and which beans have been added
6691
	 * (additions).
6692
	 *
6693
	 * @param  array $originals originals
6694
	 * @param  array $current   the current beans
6695
	 * @param  array $additions beans that have been added
6696
	 * @param  array $trashcan  beans that have been deleted
6697
	 * @param  array $residue   beans that have been left untouched
6698
	 *
6699
	 * @return array
6700
	 */
6701
	protected function processGroups( $originals, $current, $additions, $trashcan, $residue )
6702
	{
6703
		return array(
6704
			array_merge( $additions, array_diff( $current, $originals ) ),
6705
			array_merge( $trashcan, array_diff( $originals, $current ) ),
6706
			array_merge( $residue, array_intersect( $current, $originals ) )
6707
		);
6708
	}
6709
6710
	/**
6711
	 * Processes an embedded bean.
6712
	 *
6713
	 * @param OODBBean|SimpleModel $embeddedBean the bean or model
6714
	 *
6715
	 * @return integer
6716
	 */
6717
	protected function prepareEmbeddedBean( $embeddedBean )
6718
	{
6719
		if ( !$embeddedBean->id || $embeddedBean->getMeta( 'tainted' ) ) {
0 ignored issues
show
Bug introduced by
The method getMeta does only exist in RedBeanPHP\OODBBean, but not in RedBeanPHP\SimpleModel.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
6720
			$this->store( $embeddedBean );
6721
		}
6722
6723
		return $embeddedBean->id;
6724
	}
6725
6726
	/**
6727
	 * Processes a list of beans from a bean. A bean may contain lists. This
6728
	 * method handles shared addition lists; i.e. the $bean->sharedObject properties.
6729
	 *
6730
	 * @param OODBBean $bean             the bean
6731
	 * @param array            $sharedAdditions  list with shared additions
6732
	 *
6733
	 * @return void
6734
	 *
6735
	 * @throws Security
6736
	 */
6737
	protected function processSharedAdditions( $bean, $sharedAdditions )
6738
	{
6739
		foreach ( $sharedAdditions as $addition ) {
6740
			if ( $addition instanceof OODBBean ) {
6741
				$this->oodb->getAssociationManager()->associate( $addition, $bean );
0 ignored issues
show
Bug introduced by
The property oodb does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
6742
			} else {
6743
				throw new RedException( 'Array may only contain OODBBeans' );
6744
			}
6745
		}
6746
	}
6747
6748
	/**
6749
	 * Processes a list of beans from a bean. A bean may contain lists. This
6750
	 * method handles own lists; i.e. the $bean->ownObject properties.
6751
	 * A residue is a bean in an own-list that stays where it is. This method
6752
	 * checks if there have been any modification to this bean, in that case
6753
	 * the bean is stored once again, otherwise the bean will be left untouched.
6754
	 *
6755
	 * @param OODBBean $bean       the bean
0 ignored issues
show
Bug introduced by
There is no parameter named $bean. 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...
6756
	 * @param array            $ownresidue list
6757
	 *
6758
	 * @return void
6759
	 */
6760
	protected function processResidue( $ownresidue )
6761
	{
6762
		foreach ( $ownresidue as $residue ) {
6763
			if ( $residue->getMeta( 'tainted' ) ) {
6764
				$this->store( $residue );
6765
			}
6766
		}
6767
	}
6768
6769
	/**
6770
	 * Processes a list of beans from a bean. A bean may contain lists. This
6771
	 * method handles own lists; i.e. the $bean->ownObject properties.
6772
	 * A trash can bean is a bean in an own-list that has been removed
6773
	 * (when checked with the shadow). This method
6774
	 * checks if the bean is also in the dependency list. If it is the bean will be removed.
6775
	 * If not, the connection between the bean and the owner bean will be broken by
6776
	 * setting the ID to NULL.
6777
	 *
6778
	 * @param OODBBean $bean        the bean
6779
	 * @param array            $ownTrashcan list
6780
	 *
6781
	 * @return void
6782
	 */
6783
	protected function processTrashcan( $bean, $ownTrashcan )
6784
	{
6785
6786
		foreach ( $ownTrashcan as $trash ) {
6787
6788
			$myFieldLink = $bean->getMeta( 'type' ) . '_id';
6789
			$alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) );
6790
			if ( $alias ) $myFieldLink = $alias . '_id';
6791
6792
			if ( $trash->getMeta( 'sys.garbage' ) === true ) {
6793
				$this->trash( $trash );
6794
			} else {
6795
				$trash->$myFieldLink = NULL;
6796
				$this->store( $trash );
6797
			}
6798
		}
6799
	}
6800
6801
	/**
6802
	 * Unassociates the list items in the trashcan.
6803
	 *
6804
	 * @param OODBBean $bean           bean
6805
	 * @param array            $sharedTrashcan list
6806
	 *
6807
	 * @return void
6808
	 */
6809
	protected function processSharedTrashcan( $bean, $sharedTrashcan )
6810
	{
6811
		foreach ( $sharedTrashcan as $trash ) {
6812
			$this->oodb->getAssociationManager()->unassociate( $trash, $bean );
6813
		}
6814
	}
6815
6816
	/**
6817
	 * Stores all the beans in the residue group.
6818
	 *
6819
	 * @param OODBBean $bean          bean
6820
	 * @param array            $sharedresidue list
6821
	 *
6822
	 * @return void
6823
	 */
6824
	protected function processSharedResidue( $bean, $sharedresidue )
0 ignored issues
show
Unused Code introduced by
The parameter $bean 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...
6825
	{
6826
		foreach ( $sharedresidue as $residue ) {
6827
			$this->store( $residue );
6828
		}
6829
	}
6830
6831
	/**
6832
	 * Determines whether the bean has 'loaded lists' or
6833
	 * 'loaded embedded beans' that need to be processed
6834
	 * by the store() method.
6835
	 *
6836
	 * @param OODBBean $bean bean to be examined
6837
	 *
6838
	 * @return boolean
6839
	 */
6840
	protected function hasListsOrObjects( OODBBean $bean )
6841
	{
6842
		$processLists = FALSE;
6843
		foreach ( $bean as $value ) {
6844
			if ( is_array( $value ) || is_object( $value ) ) {
6845
				$processLists = TRUE;
6846
				break;
6847
			}
6848
		}
6849
6850
		return $processLists;
6851
	}
6852
6853
6854
	/**
6855
	 * Converts an embedded bean to an ID, removed the bean property and
6856
	 * stores the bean in the embedded beans array.
6857
	 *
6858
	 * @param array            $embeddedBeans destination array for embedded bean
6859
	 * @param OODBBean $bean          target bean
6860
	 * @param string           $property      property that contains the embedded bean
6861
	 * @param OODBBean $value         embedded bean itself
6862
	 */
6863
	protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value )
6864
	{
6865
		$linkField        = $property . '_id';
6866
		$id = $this->prepareEmbeddedBean( $value );
6867
		if ($bean->$linkField != $id) $bean->$linkField = $id;
6868
		$bean->setMeta( 'cast.' . $linkField, 'id' );
6869
		$embeddedBeans[$linkField] = $value;
6870
		unset( $bean->$property );
6871
	}
6872
6873
6874
	/**
6875
	 * Constructor, requires a query writer.
6876
	 *
6877
	 * @param QueryWriter $writer writer
6878
	 */
6879
	public function __construct( OODB $oodb, QueryWriter $writer )
6880
	{
6881
		$this->writer = $writer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $writer of type object<RedBeanPHP\QueryWriter> is incompatible with the declared type object<RedBeanPHP\Adapter\DBAdapter> of property $writer.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
6882
		$this->oodb = $oodb;
6883
	}
6884
6885
	/**
6886
	 * Checks whether a OODBBean bean is valid.
6887
	 * If the type is not valid or the ID is not valid it will
6888
	 * throw an exception: Security.
6889
	 *
6890
	 * @param OODBBean $bean the bean that needs to be checked
6891
	 *
6892
	 * @return void
6893
	 *
6894
	 * @throws Security $exception
6895
	 */
6896
	public function check( OODBBean $bean )
6897
	{
6898
		//Is all meta information present?
6899
		if ( !isset( $bean->id ) ) {
6900
			throw new RedException( 'Bean has incomplete Meta Information id ' );
6901
		}
6902
		if ( !( $bean->getMeta( 'type' ) ) ) {
6903
			throw new RedException( 'Bean has incomplete Meta Information II' );
6904
		}
6905
		//Pattern of allowed characters
6906
		$pattern = '/[^a-z0-9_]/i';
6907
		//Does the type contain invalid characters?
6908
		if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) {
6909
			throw new RedException( 'Bean Type is invalid' );
6910
		}
6911
		//Are the properties and values valid?
6912
		foreach ( $bean as $prop => $value ) {
6913
			if (
6914
				is_array( $value )
6915
				|| ( is_object( $value ) )
6916
			) {
6917
				throw new RedException( "Invalid Bean value: property $prop" );
6918
			} else if (
6919
				strlen( $prop ) < 1
6920
				|| preg_match( $pattern, $prop )
6921
			) {
6922
				throw new RedException( "Invalid Bean property: property $prop" );
6923
			}
6924
		}
6925
	}
6926
6927
	/**
6928
	 * Searches the database for a bean that matches conditions $conditions and sql $addSQL
6929
	 * and returns an array containing all the beans that have been found.
6930
	 *
6931
	 * Conditions need to take form:
6932
	 *
6933
	 * array(
6934
	 *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
6935
	 *    'PROPERTY' => array( POSSIBLE VALUES... )
6936
	 * );
6937
	 *
6938
	 * All conditions are glued together using the AND-operator, while all value lists
6939
	 * are glued using IN-operators thus acting as OR-conditions.
6940
	 *
6941
	 * Note that you can use property names; the columns will be extracted using the
6942
	 * appropriate bean formatter.
6943
	 *
6944
	 * @param string $type       type of beans you are looking for
6945
	 * @param array  $conditions list of conditions
6946
	 * @param string $addSQL     SQL to be used in query
0 ignored issues
show
Bug introduced by
There is no parameter named $addSQL. 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...
6947
	 * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
6948
	 *
6949
	 * @return array
6950
	 *
6951
	 */
6952
	public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
6953
	{
6954
		//for backward compatibility, allow mismatch arguments:
6955
		if ( is_array( $sql ) ) {
6956
			if ( isset( $sql[1] ) ) {
6957
				$bindings = $sql[1];
6958
			}
6959
			$sql = $sql[0];
6960
		}
6961
		try {
6962
			$beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) );
0 ignored issues
show
Bug introduced by
The method queryRecord() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
6963
6964
			return $beans;
6965
		} catch ( SQLException $exception ) {
6966
			$this->handleException( $exception );
6967
		}
6968
6969
		return array();
6970
	}
6971
6972
	/**
6973
	 * Finds a BeanCollection.
6974
	 *
6975
	 * @param string $type     type of beans you are looking for
6976
	 * @param string $sql      SQL to be used in query
6977
	 * @param array  $bindings whether you prefer to use a WHERE clause or not (TRUE = not)
6978
	 *
6979
	 * @return BeanCollection
6980
	 */
6981
	public function findCollection( $type, $sql, $bindings = array() )
6982
	{
6983
		try {
6984
			$cursor = $this->writer->queryRecordWithCursor( $type, $sql, $bindings );
0 ignored issues
show
Bug introduced by
The method queryRecordWithCursor() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
6985
			return new BeanCollection( $type, $this, $cursor );
6986
		} catch ( SQLException $exception ) {
6987
			$this->handleException( $exception );
6988
		}
6989
		return new BeanCollection( $type, $this, new NullCursor );
6990
	}
6991
6992
	/**
6993
	 * Stores a bean in the database. This method takes a
6994
	 * OODBBean Bean Object $bean and stores it
6995
	 * in the database. If the database schema is not compatible
6996
	 * with this bean and RedBean runs in fluid mode the schema
6997
	 * will be altered to store the bean correctly.
6998
	 * If the database schema is not compatible with this bean and
6999
	 * RedBean runs in frozen mode it will throw an exception.
7000
	 * This function returns the primary key ID of the inserted
7001
	 * bean.
7002
	 *
7003
	 * The return value is an integer if possible. If it is not possible to
7004
	 * represent the value as an integer a string will be returned. We use
7005
	 * explicit casts instead of functions to preserve performance
7006
	 * (0.13 vs 0.28 for 10000 iterations on Core i3).
7007
	 *
7008
	 * @param OODBBean|SimpleModel $bean bean to store
7009
	 *
7010
	 * @return integer|string
7011
	 */
7012
	public function store( $bean )
7013
	{
7014
		$processLists = $this->hasListsOrObjects( $bean );
0 ignored issues
show
Bug introduced by
It seems like $bean defined by parameter $bean on line 7012 can also be of type object<RedBeanPHP\SimpleModel>; however, RedBeanPHP\Repository::hasListsOrObjects() does only seem to accept object<RedBeanPHP\OODBBean>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
7015
		if ( !$processLists && !$bean->getMeta( 'tainted' ) ) {
0 ignored issues
show
Bug introduced by
The method getMeta does only exist in RedBeanPHP\OODBBean, but not in RedBeanPHP\SimpleModel.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
7016
			return $bean->getID(); //bail out!
0 ignored issues
show
Bug introduced by
The method getID does only exist in RedBeanPHP\OODBBean, but not in RedBeanPHP\SimpleModel.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
7017
		}
7018
		$this->oodb->signal( 'update', $bean );
7019
		$processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model!
0 ignored issues
show
Bug introduced by
It seems like $bean defined by parameter $bean on line 7012 can also be of type object<RedBeanPHP\SimpleModel>; however, RedBeanPHP\Repository::hasListsOrObjects() does only seem to accept object<RedBeanPHP\OODBBean>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
7020
		if ( $processLists ) {
7021
			$this->storeBeanWithLists( $bean );
0 ignored issues
show
Bug introduced by
It seems like $bean defined by parameter $bean on line 7012 can also be of type object<RedBeanPHP\SimpleModel>; however, RedBeanPHP\Repository::storeBeanWithLists() does only seem to accept object<RedBeanPHP\OODBBean>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
7022
		} else {
7023
			$this->storeBean( $bean );
0 ignored issues
show
Bug introduced by
The method storeBean() does not exist on RedBeanPHP\Repository. Did you maybe mean storeBeanWithLists()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
7024
		}
7025
		$this->oodb->signal( 'after_update', $bean );
7026
7027
		return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id;
7028
	}
7029
7030
	/**
7031
	 * Returns an array of beans. Pass a type and a series of ids and
7032
	 * this method will bring you the corresponding beans.
7033
	 *
7034
	 * important note: Because this method loads beans using the load()
7035
	 * function (but faster) it will return empty beans with ID 0 for
7036
	 * every bean that could not be located. The resulting beans will have the
7037
	 * passed IDs as their keys.
7038
	 *
7039
	 * @param string $type type of beans
7040
	 * @param array  $ids  ids to load
7041
	 *
7042
	 * @return array
7043
	 */
7044
	public function batch( $type, $ids )
7045
	{
7046
		if ( !$ids ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ids of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
7047
			return array();
7048
		}
7049
		$collection = array();
7050
		try {
7051
			$rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) );
0 ignored issues
show
Bug introduced by
The method queryRecord() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7052
		} catch ( SQLException $e ) {
7053
			$this->handleException( $e );
7054
			$rows = FALSE;
7055
		}
7056
		$this->stash[$this->nesting] = array();
7057
		if ( !$rows ) {
7058
			return array();
7059
		}
7060
		foreach ( $rows as $row ) {
7061
			$this->stash[$this->nesting][$row['id']] = $row;
7062
		}
7063
		foreach ( $ids as $id ) {
7064
			$collection[$id] = $this->load( $type, $id );
7065
		}
7066
		$this->stash[$this->nesting] = NULL;
7067
7068
		return $collection;
7069
	}
7070
7071
	/**
7072
	 * This is a convenience method; it converts database rows
7073
	 * (arrays) into beans. Given a type and a set of rows this method
7074
	 * will return an array of beans of the specified type loaded with
7075
	 * the data fields provided by the result set from the database.
7076
	 *
7077
	 * @param string $type type of beans you would like to have
7078
	 * @param array  $rows rows from the database result
7079
	 *
7080
	 * @return array
7081
	 */
7082
	public function convertToBeans( $type, $rows )
7083
	{
7084
		$collection                  = array();
7085
		$this->stash[$this->nesting] = array();
7086
		foreach ( $rows as $row ) {
7087
			$id                               = $row['id'];
7088
			$this->stash[$this->nesting][$id] = $row;
7089
			$collection[$id]                  = $this->load( $type, $id );
7090
		}
7091
		$this->stash[$this->nesting] = NULL;
7092
7093
		return $collection;
7094
	}
7095
7096
	/**
7097
	 * Counts the number of beans of type $type.
7098
	 * This method accepts a second argument to modify the count-query.
7099
	 * A third argument can be used to provide bindings for the SQL snippet.
7100
	 *
7101
	 * @param string $type     type of bean we are looking for
7102
	 * @param string $addSQL   additional SQL snippet
7103
	 * @param array  $bindings parameters to bind to SQL
7104
	 *
7105
	 * @return integer
7106
	 *
7107
	 * @throws SQLException
7108
	 */
7109
	public function count( $type, $addSQL = '', $bindings = array() )
7110
	{
7111
		$type = AQueryWriter::camelsSnake( $type );
7112
		if ( count( explode( '_', $type ) ) > 2 ) {
7113
			throw new RedException( 'Invalid type for count.' );
7114
		}
7115
7116
		try {
7117
			return (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings );
0 ignored issues
show
Bug introduced by
The method queryRecordCount() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7118
		} catch ( SQLException $exception ) {
7119
			if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array(
0 ignored issues
show
Bug introduced by
The method sqlStateIn() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7120
				 QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
7121
				 QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) ) {
7122
				throw $exception;
7123
			}
7124
		}
7125
7126
		return 0;
7127
	}
7128
7129
	/**
7130
	 * Removes a bean from the database.
7131
	 * This function will remove the specified OODBBean
7132
	 * Bean Object from the database.
7133
	 *
7134
	 * @param OODBBean|SimpleModel $bean bean you want to remove from database
7135
	 *
7136
	 * @return void
7137
	 *
7138
	 * @throws SQLException
7139
	 */
7140
	public function trash( $bean )
7141
	{
7142
		$this->oodb->signal( 'delete', $bean );
7143
		foreach ( $bean as $property => $value ) {
0 ignored issues
show
Bug introduced by
The expression $bean of type object<RedBeanPHP\OODBBe...RedBeanPHP\SimpleModel> 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...
7144
			if ( $value instanceof OODBBean ) {
7145
				unset( $bean->$property );
7146
			}
7147
			if ( is_array( $value ) ) {
7148
				if ( strpos( $property, 'own' ) === 0 ) {
7149
					unset( $bean->$property );
7150
				} elseif ( strpos( $property, 'shared' ) === 0 ) {
7151
					unset( $bean->$property );
7152
				}
7153
			}
7154
		}
7155
		try {
7156
			$this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL );
0 ignored issues
show
Bug introduced by
The method getMeta does only exist in RedBeanPHP\OODBBean, but not in RedBeanPHP\SimpleModel.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Bug introduced by
The method deleteRecord() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7157
		} catch ( SQLException $exception ) {
7158
			$this->handleException( $exception );
7159
		}
7160
		$bean->id = 0;
7161
		$this->oodb->signal( 'after_delete', $bean );
7162
	}
7163
7164
	/**
7165
	 * Checks whether the specified table already exists in the database.
7166
	 * Not part of the Object Database interface!
7167
	 *
7168
	 * @deprecated Use AQueryWriter::typeExists() instead.
7169
	 *
7170
	 * @param string $table table name
7171
	 *
7172
	 * @return boolean
7173
	 */
7174
	public function tableExists( $table )
7175
	{
7176
		return $this->writer->tableExists( $table );
0 ignored issues
show
Bug introduced by
The method tableExists() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7177
	}
7178
7179
	/**
7180
	 * Trash all beans of a given type. Wipes an entire type of bean.
7181
	 *
7182
	 * @param string $type type of bean you wish to delete all instances of
7183
	 *
7184
	 * @return boolean
7185
	 *
7186
	 * @throws SQLException
7187
	 */
7188
	public function wipe( $type )
7189
	{
7190
		try {
7191
			$this->writer->wipe( $type );
0 ignored issues
show
Bug introduced by
The method wipe() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7192
7193
			return TRUE;
7194
		} catch ( SQLException $exception ) {
7195
			if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) {
0 ignored issues
show
Bug introduced by
The method sqlStateIn() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7196
				throw $exception;
7197
			}
7198
7199
			return FALSE;
7200
		}
7201
	}
7202
}
7203
}
7204
7205
namespace RedBeanPHP\Repository {
7206
7207
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7208
use RedBeanPHP\Observable as Observable;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7209
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7210
use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7211
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7212
use RedBeanPHP\RedException as RedException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7213
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7214
use RedBeanPHP\SimpleModel as SimpleModel;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7215
use RedBeanPHP\BeanHelper as BeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7216
use RedBeanPHP\RedException\SQL as SQLException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7217
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7218
use RedBeanPHP\Repository as Repository;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7219
7220
/**
7221
 * Fluid Repository.
7222
 * OODB manages two repositories, a fluid one that
7223
 * adjust the database schema on-the-fly to accomodate for
7224
 * new bean types (tables) and new properties (columns) and
7225
 * a frozen one for use in a production environment. OODB
7226
 * allows you to swap the repository instances using the freeze()
7227
 * method.
7228
 *
7229
 * @file    RedBeanPHP/Repository/Fluid.php
7230
 * @author  Gabor de Mooij and the RedBeanPHP community
7231
 * @license BSD/GPLv2
7232
 *
7233
 * @copyright
7234
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
7235
 * This source file is subject to the BSD/GPLv2 License that is bundled
7236
 * with this source code in the file license.txt.
7237
 */
7238
class Fluid extends Repository
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
7239
{
7240
	/**
7241
	 * Figures out the desired type given the cast string ID.
7242
	 *
7243
	 * @param string $cast cast identifier
7244
	 *
7245
	 * @return integer
7246
	 *
7247
	 * @throws Security
7248
	 */
7249
	private function getTypeFromCast( $cast )
7250
	{
7251
		if ( $cast == 'string' ) {
7252
			$typeno = $this->writer->scanType( 'STRING' );
0 ignored issues
show
Bug introduced by
The method scanType() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7253
		} elseif ( $cast == 'id' ) {
7254
			$typeno = $this->writer->getTypeForID();
0 ignored issues
show
Bug introduced by
The method getTypeForID() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7255
		} elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) {
0 ignored issues
show
Bug introduced by
The property sqltype_typeno does not seem to exist in RedBeanPHP\Adapter\DBAdapter.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
7256
			$typeno = $this->writer->sqltype_typeno[$cast];
7257
		} else {
7258
			throw new RedException( 'Invalid Cast' );
7259
		}
7260
7261
		return $typeno;
7262
	}
7263
7264
	/**
7265
	 * Orders the Query Writer to create a table if it does not exist already and
7266
	 * adds a note in the build report about the creation.
7267
	 *
7268
	 * @param OODBBean $bean  bean to update report of
7269
	 * @param string           $table table to check and create if not exists
7270
	 *
7271
	 * @return void
7272
	 */
7273
	private function createTableIfNotExists( OODBBean $bean, $table )
7274
	{
7275
		//Does table exist? If not, create
7276
		if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) {
0 ignored issues
show
Bug introduced by
The method esc() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Deprecated Code introduced by
The method RedBeanPHP\Repository::tableExists() has been deprecated with message: Use AQueryWriter::typeExists() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
7277
			$this->writer->createTable( $table );
0 ignored issues
show
Bug introduced by
The method createTable() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7278
			$bean->setMeta( 'buildreport.flags.created', TRUE );
7279
		}
7280
	}
7281
7282
	/**
7283
	 * Modifies the table to fit the bean data.
7284
	 * Given a property and a value and the bean, this method will
7285
	 * adjust the table structure to fit the requirements of the property and value.
7286
	 * This may include adding a new column or widening an existing column to hold a larger
7287
	 * or different kind of value. This method employs the writer to adjust the table
7288
	 * structure in the database. Schema updates are recorded in meta properties of the bean.
7289
	 *
7290
	 * This method will also apply indexes, unique constraints and foreign keys.
7291
	 *
7292
	 * @param OODBBean $bean     bean to get cast data from and store meta in
7293
	 * @param string   $property property to store
7294
	 * @param mixed    $value    value to store
7295
	 *
7296
	 * @return void
7297
	 */
7298
	private function modifySchema( OODBBean $bean, $property, $value )
7299
	{
7300
		$doFKStuff = FALSE;
7301
		$table   = $bean->getMeta( 'type' );
7302
		$columns = $this->writer->getColumns( $table );
0 ignored issues
show
Bug introduced by
The method getColumns() does not exist on RedBeanPHP\Adapter\DBAdapter. Did you maybe mean getCol()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
7303
		$columnNoQ = $this->writer->esc( $property, TRUE );
0 ignored issues
show
Bug introduced by
The method esc() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7304
		if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) {
0 ignored issues
show
Bug introduced by
The property oodb does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
7305
			if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types
7306
				$cast   = $bean->getMeta( "cast.$property" );
7307
				$typeno = $this->getTypeFromCast( $cast );
7308
			} else {
7309
				$cast   = FALSE;
7310
				$typeno = $this->writer->scanType( $value, TRUE );
0 ignored issues
show
Bug introduced by
The method scanType() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7311
			}
7312
			if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ?
0 ignored issues
show
Bug introduced by
The method esc() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7313
				if ( !$cast ) { //rescan without taking into account special types >80
7314
					$typeno = $this->writer->scanType( $value, FALSE );
0 ignored issues
show
Bug introduced by
The method scanType() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7315
				}
7316
				$sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] );
0 ignored issues
show
Bug introduced by
The method esc() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method code() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7317
				if ( $typeno > $sqlt ) { //no, we have to widen the database column type
7318
					$this->writer->widenColumn( $table, $property, $typeno );
0 ignored issues
show
Bug introduced by
The method widenColumn() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7319
					$bean->setMeta( 'buildreport.flags.widen', TRUE );
7320
					$doFKStuff = TRUE;
7321
				}
7322
			} else {
7323
				$this->writer->addColumn( $table, $property, $typeno );
0 ignored issues
show
Bug introduced by
The method addColumn() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7324
				$bean->setMeta( 'buildreport.flags.addcolumn', TRUE );
7325
				$doFKStuff = TRUE;
7326
			}
7327
			if ($doFKStuff) {
7328
				if (strrpos($columnNoQ, '_id')===(strlen($columnNoQ)-3)) {
7329
					$destinationColumnNoQ = substr($columnNoQ, 0, strlen($columnNoQ)-3);
7330
					$indexName = "index_foreignkey_{$table}_{$destinationColumnNoQ}";
7331
					$this->writer->addIndex($table, $indexName, $columnNoQ);
0 ignored issues
show
Bug introduced by
The method addIndex() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7332
					$typeof = $bean->getMeta("sys.typeof.{$destinationColumnNoQ}", $destinationColumnNoQ);
7333
					$isLink = $bean->getMeta( 'sys.buildcommand.unique', FALSE );
7334
					//Make FK CASCADING if part of exclusive list (dependson=typeof) or if link bean
7335
					$isDep = ( $bean->moveMeta( 'sys.buildcommand.fkdependson' ) === $typeof || is_array( $isLink ) );
7336
					$result = $this->writer->addFK( $table, $typeof, $columnNoQ, 'id', $isDep );
0 ignored issues
show
Bug introduced by
The method addFK() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7337
					//If this is a link bean and all unique columns have been added already, then apply unique constraint
7338
					if ( is_array( $isLink ) && !count( array_diff( $isLink, array_keys( $this->writer->getColumns( $table ) ) ) ) ) {
0 ignored issues
show
Bug introduced by
The method getColumns() does not exist on RedBeanPHP\Adapter\DBAdapter. Did you maybe mean getCol()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
7339
						$this->writer->addUniqueConstraint( $table, $bean->moveMeta('sys.buildcommand.unique') );
0 ignored issues
show
Bug introduced by
The method addUniqueConstraint() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7340
						$bean->setMeta("sys.typeof.{$destinationColumnNoQ}", NULL);
7341
					}
7342
				}
7343
			}
7344
		}
7345
	}
7346
7347
	/**
7348
	 * Part of the store() functionality.
7349
	 * Handles all new additions after the bean has been saved.
7350
	 * Stores addition bean in own-list, extracts the id and
7351
	 * adds a foreign key. Also adds a constraint in case the type is
7352
	 * in the dependent list.
7353
	 *
7354
	 * @param OODBBean $bean         bean
7355
	 * @param array            $ownAdditions list of addition beans in own-list
7356
	 *
7357
	 * @return void
7358
	 *
7359
	 * @throws Security
7360
	 */
7361
	protected function processAdditions( $bean, $ownAdditions )
7362
	{
7363
		$beanType = $bean->getMeta( 'type' );
7364
7365
		foreach ( $ownAdditions as $addition ) {
7366
			if ( $addition instanceof OODBBean ) {
7367
7368
				$myFieldLink = $beanType . '_id';
7369
				$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
7370
				if ( $alias ) $myFieldLink = $alias . '_id';
7371
7372
				$addition->$myFieldLink = $bean->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
7373
				$addition->setMeta( 'cast.' . $myFieldLink, 'id' );
7374
7375
				if ($alias) {
7376
					$addition->setMeta( "sys.typeof.{$alias}", $beanType );
7377
				} else {
7378
					$addition->setMeta( "sys.typeof.{$beanType}", $beanType );
7379
				}
7380
7381
				$this->store( $addition );
7382
			} else {
7383
				throw new RedException( 'Array may only contain OODBBeans' );
7384
			}
7385
		}
7386
	}
7387
7388
	/**
7389
	 * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
7390
	 * method. When all lists and embedded beans (parent objects) have been processed and
7391
	 * removed from the original bean the bean is passed to this method to be stored
7392
	 * in the database.
7393
	 *
7394
	 * @param OODBBean $bean the clean bean
7395
	 *
7396
	 * @return void
7397
	 */
7398
	protected function storeBean( OODBBean $bean )
7399
	{
7400
		if ( $bean->getMeta( 'changed' ) ) {
7401
			$this->check( $bean );
7402
			$table = $bean->getMeta( 'type' );
7403
			$this->createTableIfNotExists( $bean, $table );
7404
7405
			$updateValues = array();
7406
			foreach ( $bean as $property => $value ) {
7407
				if ( $property !== 'id' ) {
7408
					$this->modifySchema( $bean, $property, $value );
7409
				}
7410
				if ( $property !== 'id' ) {
7411
					$updateValues[] = array( 'property' => $property, 'value' => $value );
7412
				}
7413
			}
7414
7415
			$bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id );
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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 id does not exist on object<RedBeanPHP\OODBBean>. 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...
Bug introduced by
The method updateRecord() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7416
			$bean->setMeta( 'changed', FALSE );
7417
		}
7418
		$bean->setMeta( 'tainted', FALSE );
7419
	}
7420
7421
	/**
7422
	 * Handles\Exceptions. Suppresses exceptions caused by missing structures.
7423
	 *
7424
	 * @param\Exception $exception exception
7425
	 *
7426
	 * @return void
7427
	 *
7428
	 * @throws\Exception
7429
	 */
7430 View Code Duplication
	protected function handleException( \Exception $exception )
7431
	{
7432
		if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getSQLState() does only exist in the following sub-classes of Exception: Doctrine\DBAL\Driver\AbstractDriverException, Doctrine\DBAL\Driver\Mysqli\MysqliException, Doctrine\DBAL\Driver\OCI8\OCI8Exception, Doctrine\DBAL\Driver\PDOException, Doctrine\DBAL\Driver\SQL...re\SQLAnywhereException, Doctrine\DBAL\Exception\ConnectionException, Doctrine\DBAL\Exception\...raintViolationException, Doctrine\DBAL\Exception\...seObjectExistsException, Doctrine\DBAL\Exception\...ObjectNotFoundException, Doctrine\DBAL\Exception\DriverException, Doctrine\DBAL\Exception\...raintViolationException, Doctrine\DBAL\Exception\InvalidFieldNameException, Doctrine\DBAL\Exception\...niqueFieldNameException, Doctrine\DBAL\Exception\...raintViolationException, Doctrine\DBAL\Exception\ReadOnlyException, Doctrine\DBAL\Exception\ServerException, Doctrine\DBAL\Exception\SyntaxErrorException, Doctrine\DBAL\Exception\TableExistsException, Doctrine\DBAL\Exception\TableNotFoundException, Doctrine\DBAL\Exception\...raintViolationException, RedBeanPHP\RedException\SQL. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
Bug introduced by
The method sqlStateIn() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7433
			array(
7434
				QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
7435
				QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) )
7436
		) {
7437
			throw $exception;
7438
		}
7439
	}
7440
7441
	/**
7442
	 * Dispenses a new bean (a OODBBean Bean Object)
7443
	 * of the specified type. Always
7444
	 * use this function to get an empty bean object. Never
7445
	 * instantiate a OODBBean yourself because it needs
7446
	 * to be configured before you can use it with RedBean. This
7447
	 * function applies the appropriate initialization /
7448
	 * configuration for you.
7449
	 *
7450
	 * @param string  $type              type of bean you want to dispense
7451
	 * @param string  $number            number of beans you would like to get
7452
	 * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
7453
	 *
7454
	 * @return OODBBean
7455
	 */
7456 View Code Duplication
	public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
7457
	{
7458
		$OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
7459
		$beans = array();
7460
		for ( $i = 0; $i < $number; $i++ ) {
7461
			$bean = new $OODBBEAN;
7462
			$bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
7463
			$this->check( $bean );
7464
			$this->oodb->signal( 'dispense', $bean );
7465
			$beans[] = $bean;
7466
		}
7467
7468
		return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
7469
	}
7470
7471
	/**
7472
	 * Loads a bean from the object database.
7473
	 * It searches for a OODBBean Bean Object in the
7474
	 * database. It does not matter how this bean has been stored.
7475
	 * RedBean uses the primary key ID $id and the string $type
7476
	 * to find the bean. The $type specifies what kind of bean you
7477
	 * are looking for; this is the same type as used with the
7478
	 * dispense() function. If RedBean finds the bean it will return
7479
	 * the OODB Bean object; if it cannot find the bean
7480
	 * RedBean will return a new bean of type $type and with
7481
	 * primary key ID 0. In the latter case it acts basically the
7482
	 * same as dispense().
7483
	 *
7484
	 * Important note:
7485
	 * If the bean cannot be found in the database a new bean of
7486
	 * the specified type will be generated and returned.
7487
	 *
7488
	 * @param string  $type type of bean you want to load
7489
	 * @param integer $id   ID of the bean you want to load
7490
	 *
7491
	 * @throws SQL
7492
	 *
7493
	 * @return OODBBean
7494
	 *
7495
	 */
7496 View Code Duplication
	public function load( $type, $id )
7497
	{
7498
		$bean = $this->dispense( $type );
7499
		if ( isset( $this->stash[$this->nesting][$id] ) ) {
7500
			$row = $this->stash[$this->nesting][$id];
7501
		} else {
7502
			try {
7503
				$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
0 ignored issues
show
Bug introduced by
The method queryRecord() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7504
			} catch ( SQLException $exception ) {
7505
				if ( $this->writer->sqlStateIn( $exception->getSQLState(),
0 ignored issues
show
Bug introduced by
The method sqlStateIn() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7506
					array(
7507
						QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
7508
						QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
7509
				)
7510
				) {
7511
					$rows = 0;
7512
7513
				}
7514
			}
7515
			if ( empty( $rows ) ) {
7516
				return $bean;
7517
			}
7518
			$row = array_pop( $rows );
7519
		}
7520
		$bean->importRow( $row );
7521
		$this->nesting++;
7522
		$this->oodb->signal( 'open', $bean );
7523
		$this->nesting--;
7524
7525
		return $bean->setMeta( 'tainted', FALSE );
7526
	}
7527
}
7528
}
7529
7530
namespace RedBeanPHP\Repository {
7531
7532
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7533
use RedBeanPHP\Observable as Observable;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7534
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7535
use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7536
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7537
use RedBeanPHP\RedException as RedException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7538
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7539
use RedBeanPHP\SimpleModel as SimpleModel;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7540
use RedBeanPHP\BeanHelper as BeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7541
use RedBeanPHP\RedException\SQL as SQLException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7542
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7543
use RedBeanPHP\Repository as Repository;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7544
7545
/**
7546
 * Frozen Repository.
7547
 * OODB manages two repositories, a fluid one that
7548
 * adjust the database schema on-the-fly to accomodate for
7549
 * new bean types (tables) and new properties (columns) and
7550
 * a frozen one for use in a production environment. OODB
7551
 * allows you to swap the repository instances using the freeze()
7552
 * method.
7553
 *
7554
 * @file    RedBeanPHP/Repository/Frozen.php
7555
 * @author  Gabor de Mooij and the RedBeanPHP community
7556
 * @license BSD/GPLv2
7557
 *
7558
 * @copyright
7559
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
7560
 * This source file is subject to the BSD/GPLv2 License that is bundled
7561
 * with this source code in the file license.txt.
7562
 */
7563
class Frozen extends Repository
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
7564
{
7565
	/**
7566
	 * Handles\Exceptions. Suppresses exceptions caused by missing structures.
7567
	 *
7568
	 * @param \Exception $exception exception
7569
	 *
7570
	 * @return void
7571
	 *
7572
	 * @throws \Exception
7573
	 */
7574
	protected function handleException( \Exception $exception )
7575
	{
7576
		throw $exception;
7577
	}
7578
7579
	/**
7580
	 * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
7581
	 * method. When all lists and embedded beans (parent objects) have been processed and
7582
	 * removed from the original bean the bean is passed to this method to be stored
7583
	 * in the database.
7584
	 *
7585
	 * @param OODBBean $bean the clean bean
7586
	 *
7587
	 * @return void
7588
	 */
7589
	protected function storeBean( OODBBean $bean )
7590
	{
7591
		if ( $bean->getMeta( 'changed' ) ) {
7592
7593
			list( $properties, $table ) = $bean->getPropertiesAndType();
7594
			$id = $properties['id'];
7595
			unset($properties['id']);
7596
			$updateValues = array();
7597
			$k1 = 'property';
7598
			$k2 = 'value';
7599
			foreach( $properties as $key => $value ) {
7600
				$updateValues[] = array( $k1 => $key, $k2 => $value );
7601
			}
7602
			$bean->id = $this->writer->updateRecord( $table, $updateValues, $id );
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
Bug introduced by
The method updateRecord() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7603
			$bean->setMeta( 'changed', FALSE );
7604
		}
7605
		$bean->setMeta( 'tainted', FALSE );
7606
	}
7607
7608
	/**
7609
	 * Part of the store() functionality.
7610
	 * Handles all new additions after the bean has been saved.
7611
	 * Stores addition bean in own-list, extracts the id and
7612
	 * adds a foreign key. Also adds a constraint in case the type is
7613
	 * in the dependent list.
7614
	 *
7615
	 * @param OODBBean $bean         bean
7616
	 * @param array            $ownAdditions list of addition beans in own-list
7617
	 *
7618
	 * @return void
7619
	 *
7620
	 * @throws Security
7621
	 */
7622
	protected function processAdditions( $bean, $ownAdditions )
7623
	{
7624
		$beanType = $bean->getMeta( 'type' );
7625
7626
		$cachedIndex = array();
7627
		foreach ( $ownAdditions as $addition ) {
7628
			if ( $addition instanceof OODBBean ) {
7629
7630
				$myFieldLink = $beanType . '_id';
7631
				$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
7632
				if ( $alias ) $myFieldLink = $alias . '_id';
7633
7634
				$addition->$myFieldLink = $bean->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
7635
				$addition->setMeta( 'cast.' . $myFieldLink, 'id' );
7636
				$this->store( $addition );
7637
7638
			} else {
7639
				throw new RedException( 'Array may only contain OODBBeans' );
7640
			}
7641
		}
7642
	}
7643
7644
	/**
7645
	 * Dispenses a new bean (a OODBBean Bean Object)
7646
	 * of the specified type. Always
7647
	 * use this function to get an empty bean object. Never
7648
	 * instantiate a OODBBean yourself because it needs
7649
	 * to be configured before you can use it with RedBean. This
7650
	 * function applies the appropriate initialization /
7651
	 * configuration for you.
7652
	 *
7653
	 * @param string  $type              type of bean you want to dispense
7654
	 * @param string  $number            number of beans you would like to get
7655
	 * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
7656
	 *
7657
	 * @return OODBBean
7658
	 */
7659 View Code Duplication
	public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
7660
	{
7661
		$OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
7662
		$beans = array();
7663
		for ( $i = 0; $i < $number; $i++ ) {
7664
			$bean = new $OODBBEAN;
7665
			$bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
0 ignored issues
show
Bug introduced by
The property oodb does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
7666
			$this->oodb->signal( 'dispense', $bean );
7667
			$beans[] = $bean;
7668
		}
7669
7670
		return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
7671
	}
7672
7673
	/**
7674
	 * Loads a bean from the object database.
7675
	 * It searches for a OODBBean Bean Object in the
7676
	 * database. It does not matter how this bean has been stored.
7677
	 * RedBean uses the primary key ID $id and the string $type
7678
	 * to find the bean. The $type specifies what kind of bean you
7679
	 * are looking for; this is the same type as used with the
7680
	 * dispense() function. If RedBean finds the bean it will return
7681
	 * the OODB Bean object; if it cannot find the bean
7682
	 * RedBean will return a new bean of type $type and with
7683
	 * primary key ID 0. In the latter case it acts basically the
7684
	 * same as dispense().
7685
	 *
7686
	 * Important note:
7687
	 * If the bean cannot be found in the database a new bean of
7688
	 * the specified type will be generated and returned.
7689
	 *
7690
	 * @param string  $type type of bean you want to load
7691
	 * @param integer $id   ID of the bean you want to load
7692
	 *
7693
	 * @throws SQL
7694
	 *
7695
	 * @return OODBBean
7696
	 *
7697
	 */
7698 View Code Duplication
	public function load( $type, $id )
7699
	{
7700
		$bean = $this->dispense( $type );
7701
		if ( isset( $this->stash[$this->nesting][$id] ) ) {
7702
			$row = $this->stash[$this->nesting][$id];
7703
		} else {
7704
			try {
7705
				$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
0 ignored issues
show
Bug introduced by
The method queryRecord() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7706
			} catch ( SQLException $exception ) {
7707
				if ( $this->writer->sqlStateIn( $exception->getSQLState(),
0 ignored issues
show
Bug introduced by
The method sqlStateIn() does not seem to exist on object<RedBeanPHP\Adapter\DBAdapter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
7708
					array(
7709
						QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
7710
						QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
7711
				)
7712
				) {
7713
					throw $exception; //only throw if frozen
7714
				}
7715
			}
7716
			if ( empty( $rows ) ) {
7717
				return $bean;
7718
			}
7719
			$row = array_pop( $rows );
7720
		}
7721
		$bean->importRow( $row );
7722
		$this->nesting++;
7723
		$this->oodb->signal( 'open', $bean );
7724
		$this->nesting--;
7725
7726
		return $bean->setMeta( 'tainted', FALSE );
7727
	}
7728
}
7729
}
7730
7731
namespace RedBeanPHP {
7732
7733
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7734
use RedBeanPHP\Observable as Observable;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7735
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7736
use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7737
use RedBeanPHP\AssociationManager as AssociationManager;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7738
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7739
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7740
use RedBeanPHP\SimpleModel as SimpleModel;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7741
use RedBeanPHP\BeanHelper as BeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7742
use RedBeanPHP\RedException\SQL as SQL;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7743
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7744
use RedBeanPHP\Repository as Repository;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7745
use RedBeanPHP\Repository\Fluid as FluidRepo;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7746
use RedBeanPHP\Repository\Frozen as FrozenRepo;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
7747
7748
/**
7749
 * RedBean Object Oriented DataBase.
7750
 * 
7751
 * The RedBean OODB Class is the main class of RedBeanPHP.
7752
 * It takes OODBBean objects and stores them to and loads them from the
7753
 * database as well as providing other CRUD functions. This class acts as a
7754
 * object database.
7755
 *
7756
 * @file    RedBeanPHP/OODB.php
7757
 * @author  Gabor de Mooij and the RedBeanPHP community
7758
 * @license BSD/GPLv2
7759
 *
7760
 * @copyright
7761
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
7762
 * This source file is subject to the BSD/GPLv2 License that is bundled
7763
 * with this source code in the file license.txt.
7764
 */
7765
class OODB extends Observable
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
7766
{
7767
	/**
7768
	 * @var array
7769
	 */
7770
	private static $sqlFilters = array();
7771
7772
	/**
7773
	 * @var array
7774
	 */
7775
	protected $chillList = array();
7776
7777
7778
	/**
7779
	 * @var array
7780
	 */
7781
	protected $stash = NULL;
7782
7783
	/*
7784
	 * @var integer
7785
	 */
7786
	protected $nesting = 0;
7787
7788
	/**
7789
	 * @var DBAdapter
7790
	 */
7791
	protected $writer;
7792
7793
	/**
7794
	 * @var boolean
7795
	 */
7796
	protected $isFrozen = FALSE;
7797
7798
	/**
7799
	 * @var FacadeBeanHelper
7800
	 */
7801
	protected $beanhelper = NULL;
7802
7803
	/**
7804
	 * @var AssociationManager
7805
	 */
7806
	protected $assocManager = NULL;
7807
7808
	/**
7809
	 * @var Repository
7810
	 */
7811
	protected $repository = NULL;
7812
7813
	/**
7814
	 * @var FrozenRepo
7815
	 */
7816
	protected $frozenRepository = NULL;
7817
7818
	/**
7819
	 * @var FluidRepo
7820
	 */
7821
	protected $fluidRepository = NULL;
7822
7823
	/**
7824
	 * Unboxes a bean from a FUSE model if needed and checks whether the bean is
7825
	 * an instance of OODBBean.
7826
	 *
7827
	 * @param OODBBean $bean bean you wish to unbox
7828
	 *
7829
	 * @return OODBBean
7830
	 *
7831
	 * @throws Security
7832
	 */
7833
	protected function unboxIfNeeded( $bean )
7834
	{
7835
		if ( $bean instanceof SimpleModel ) {
7836
			$bean = $bean->unbox();
7837
		}
7838
		if ( !( $bean instanceof OODBBean ) ) {
7839
			throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) );
7840
		}
7841
7842
		return $bean;
7843
	}
7844
7845
	/**
7846
	 * Constructor, requires a query writer.
7847
	 *
7848
	 * @param QueryWriter   $writer writer
7849
	 * @param array|boolean $frozen mode of operation: TRUE (frozen), FALSE (default, fluid) or ARRAY (chilled)
7850
	 */
7851
	public function __construct( QueryWriter $writer, $frozen = FALSE )
7852
	{
7853
		if ( $writer instanceof QueryWriter ) {
7854
			$this->writer = $writer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $writer of type object<RedBeanPHP\QueryWriter> is incompatible with the declared type object<RedBeanPHP\Adapter\DBAdapter> of property $writer.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
7855
		}
7856
7857
		$this->freeze( $frozen );
7858
	}
7859
7860
	/**
7861
	 * Toggles fluid or frozen mode. In fluid mode the database
7862
	 * structure is adjusted to accomodate your objects. In frozen mode
7863
	 * this is not the case.
7864
	 *
7865
	 * You can also pass an array containing a selection of frozen types.
7866
	 * Let's call this chilly mode, it's just like fluid mode except that
7867
	 * certain types (i.e. tables) aren't touched.
7868
	 *
7869
	 * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode
7870
	 *
7871
	 * @return void
7872
	 */
7873
	public function freeze( $toggle )
7874
	{
7875
		if ( is_array( $toggle ) ) {
7876
			$this->chillList = $toggle;
7877
			$this->isFrozen  = FALSE;
7878
		} else {
7879
			$this->isFrozen = (boolean) $toggle;
7880
		}
7881
7882
		if ( $this->isFrozen ) {
7883
			if ( !$this->frozenRepository ) {
7884
				$this->frozenRepository = new FrozenRepo( $this, $this->writer );
0 ignored issues
show
Documentation introduced by
$this->writer is of type object<RedBeanPHP\Adapter\DBAdapter>, but the function expects a object<RedBeanPHP\QueryWriter>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
7885
			}
7886
7887
			$this->repository = $this->frozenRepository;
7888
7889
		} else {
7890
			if ( !$this->fluidRepository ) {
7891
				$this->fluidRepository = new FluidRepo( $this, $this->writer );
0 ignored issues
show
Documentation introduced by
$this->writer is of type object<RedBeanPHP\Adapter\DBAdapter>, but the function expects a object<RedBeanPHP\QueryWriter>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
7892
			}
7893
7894
			$this->repository = $this->fluidRepository;
7895
		}
7896
7897
		if ( count( self::$sqlFilters ) ) {
7898
			AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
7899
		}
7900
7901
	}
7902
7903
	/**
7904
	 * Returns the current mode of operation of RedBean.
7905
	 * In fluid mode the database
7906
	 * structure is adjusted to accomodate your objects.
7907
	 * In frozen mode
7908
	 * this is not the case.
7909
	 *
7910
	 * @return boolean
7911
	 */
7912
	public function isFrozen()
7913
	{
7914
		return (bool) $this->isFrozen;
7915
	}
7916
7917
	/**
7918
	 * Determines whether a type is in the chill list.
7919
	 * If a type is 'chilled' it's frozen, so its schema cannot be
7920
	 * changed anymore. However other bean types may still be modified.
7921
	 * This method is a convenience method for other objects to check if
7922
	 * the schema of a certain type is locked for modification.
7923
	 *
7924
	 * @param string $type the type you wish to check
7925
	 *
7926
	 * @return boolean
7927
	 */
7928
	public function isChilled( $type )
7929
	{
7930
		return (boolean) ( in_array( $type, $this->chillList ) );
7931
	}
7932
7933
	/**
7934
	 * Dispenses a new bean (a OODBBean Bean Object)
7935
	 * of the specified type. Always
7936
	 * use this function to get an empty bean object. Never
7937
	 * instantiate a OODBBean yourself because it needs
7938
	 * to be configured before you can use it with RedBean. This
7939
	 * function applies the appropriate initialization /
7940
	 * configuration for you.
7941
	 *
7942
	 * @param string  $type              type of bean you want to dispense
7943
	 * @param string  $number            number of beans you would like to get
7944
	 * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
7945
	 *
7946
	 * @return OODBBean
7947
	 */
7948
	public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
7949
	{
7950
		if ( $number < 1 ) {
7951
			if ( $alwaysReturnArray ) return array();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type documented by RedBeanPHP\OODB::dispense of type RedBeanPHP\OODBBean.

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...
7952
			return NULL;
7953
		}
7954
7955
		return $this->repository->dispense( $type, $number, $alwaysReturnArray );
7956
	}
7957
7958
	/**
7959
	 * Sets bean helper to be given to beans.
7960
	 * Bean helpers assist beans in getting a reference to a toolbox.
7961
	 *
7962
	 * @param BeanHelper $beanhelper helper
7963
	 *
7964
	 * @return void
7965
	 */
7966
	public function setBeanHelper( BeanHelper $beanhelper )
7967
	{
7968
		$this->beanhelper = $beanhelper;
0 ignored issues
show
Documentation Bug introduced by
It seems like $beanhelper of type object<RedBeanPHP\BeanHelper> is incompatible with the declared type object<RedBeanPHP\BeanHelper\FacadeBeanHelper> of property $beanhelper.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
7969
	}
7970
7971
	/**
7972
	 * Returns the current bean helper.
7973
	 * Bean helpers assist beans in getting a reference to a toolbox.
7974
	 *
7975
	 * @return BeanHelper
7976
	 */
7977
	public function getBeanHelper()
7978
	{
7979
		return $this->beanhelper;
7980
	}
7981
7982
	/**
7983
	 * Checks whether a OODBBean bean is valid.
7984
	 * If the type is not valid or the ID is not valid it will
7985
	 * throw an exception: Security.
7986
	 *
7987
	 * @param OODBBean $bean the bean that needs to be checked
7988
	 *
7989
	 * @return void
7990
	 *
7991
	 * @throws Security $exception
7992
	 */
7993
	public function check( OODBBean $bean )
7994
	{
7995
		$this->repository->check( $bean );
7996
	}
7997
7998
	/**
7999
	 * Searches the database for a bean that matches conditions $conditions and sql $addSQL
8000
	 * and returns an array containing all the beans that have been found.
8001
	 *
8002
	 * Conditions need to take form:
8003
	 *
8004
	 * array(
8005
	 *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
8006
	 *    'PROPERTY' => array( POSSIBLE VALUES... )
8007
	 * );
8008
	 *
8009
	 * All conditions are glued together using the AND-operator, while all value lists
8010
	 * are glued using IN-operators thus acting as OR-conditions.
8011
	 *
8012
	 * Note that you can use property names; the columns will be extracted using the
8013
	 * appropriate bean formatter.
8014
	 *
8015
	 * @param string $type       type of beans you are looking for
8016
	 * @param array  $conditions list of conditions
8017
	 * @param string $addSQL     SQL to be used in query
0 ignored issues
show
Bug introduced by
There is no parameter named $addSQL. 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...
8018
	 * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
8019
	 *
8020
	 * @return array
8021
	 *
8022
	 * @throws SQL
8023
	 */
8024
	public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
8025
	{
8026
		return $this->repository->find( $type, $conditions, $sql, $bindings );
8027
	}
8028
8029
	/**
8030
	 * Same as find() but returns a BeanCollection.
8031
	 *
8032
	 * @param string $type       type of beans you are looking for
8033
	 * @param string $addSQL     SQL to be used in query
0 ignored issues
show
Bug introduced by
There is no parameter named $addSQL. 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...
8034
	 * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
8035
	 *
8036
	 * @return array
8037
	 *
8038
	 * @throws SQL
8039
	 */
8040
	public function findCollection(  $type, $sql = NULL, $bindings = array() )
8041
	{
8042
		return $this->repository->findCollection( $type, $sql, $bindings );
8043
	}
8044
8045
	/**
8046
	 * Checks whether the specified table already exists in the database.
8047
	 * Not part of the Object Database interface!
8048
	 *
8049
	 * @deprecated Use AQueryWriter::typeExists() instead.
8050
	 *
8051
	 * @param string $table table name
8052
	 *
8053
	 * @return boolean
8054
	 */
8055
	public function tableExists( $table )
8056
	{
8057
		return $this->repository->tableExists( $table );
0 ignored issues
show
Deprecated Code introduced by
The method RedBeanPHP\Repository::tableExists() has been deprecated with message: Use AQueryWriter::typeExists() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
8058
	}
8059
8060
	/**
8061
	 * Stores a bean in the database. This method takes a
8062
	 * OODBBean Bean Object $bean and stores it
8063
	 * in the database. If the database schema is not compatible
8064
	 * with this bean and RedBean runs in fluid mode the schema
8065
	 * will be altered to store the bean correctly.
8066
	 * If the database schema is not compatible with this bean and
8067
	 * RedBean runs in frozen mode it will throw an exception.
8068
	 * This function returns the primary key ID of the inserted
8069
	 * bean.
8070
	 *
8071
	 * The return value is an integer if possible. If it is not possible to
8072
	 * represent the value as an integer a string will be returned. We use
8073
	 * explicit casts instead of functions to preserve performance
8074
	 * (0.13 vs 0.28 for 10000 iterations on Core i3).
8075
	 *
8076
	 * @param OODBBean|SimpleModel $bean bean to store
8077
	 *
8078
	 * @return integer|string
8079
	 *
8080
	 * @throws Security
8081
	 */
8082
	public function store( $bean )
8083
	{
8084
		$bean = $this->unboxIfNeeded( $bean );
0 ignored issues
show
Bug introduced by
It seems like $bean can also be of type object<RedBeanPHP\SimpleModel>; however, RedBeanPHP\OODB::unboxIfNeeded() does only seem to accept object<RedBeanPHP\OODBBean>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
8085
		return $this->repository->store( $bean );
8086
	}
8087
8088
	/**
8089
	 * Loads a bean from the object database.
8090
	 * It searches for a OODBBean Bean Object in the
8091
	 * database. It does not matter how this bean has been stored.
8092
	 * RedBean uses the primary key ID $id and the string $type
8093
	 * to find the bean. The $type specifies what kind of bean you
8094
	 * are looking for; this is the same type as used with the
8095
	 * dispense() function. If RedBean finds the bean it will return
8096
	 * the OODB Bean object; if it cannot find the bean
8097
	 * RedBean will return a new bean of type $type and with
8098
	 * primary key ID 0. In the latter case it acts basically the
8099
	 * same as dispense().
8100
	 *
8101
	 * Important note:
8102
	 * If the bean cannot be found in the database a new bean of
8103
	 * the specified type will be generated and returned.
8104
	 *
8105
	 * @param string  $type type of bean you want to load
8106
	 * @param integer $id   ID of the bean you want to load
8107
	 *
8108
	 * @throws SQL
8109
	 *
8110
	 * @return OODBBean
8111
	 *
8112
	 */
8113
	public function load( $type, $id )
8114
	{
8115
		return $this->repository->load( $type, $id );
8116
	}
8117
8118
	/**
8119
	 * Removes a bean from the database.
8120
	 * This function will remove the specified OODBBean
8121
	 * Bean Object from the database.
8122
	 *
8123
	 * @param OODBBean|SimpleModel $bean bean you want to remove from database
8124
	 *
8125
	 * @return void
8126
	 *
8127
	 * @throws Security
8128
	 */
8129
	public function trash( $bean )
8130
	{
8131
		$bean = $this->unboxIfNeeded( $bean );
0 ignored issues
show
Bug introduced by
It seems like $bean can also be of type object<RedBeanPHP\SimpleModel>; however, RedBeanPHP\OODB::unboxIfNeeded() does only seem to accept object<RedBeanPHP\OODBBean>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
8132
		return $this->repository->trash( $bean );
8133
	}
8134
8135
	/**
8136
	 * Returns an array of beans. Pass a type and a series of ids and
8137
	 * this method will bring you the corresponding beans.
8138
	 *
8139
	 * important note: Because this method loads beans using the load()
8140
	 * function (but faster) it will return empty beans with ID 0 for
8141
	 * every bean that could not be located. The resulting beans will have the
8142
	 * passed IDs as their keys.
8143
	 *
8144
	 * @param string $type type of beans
8145
	 * @param array  $ids  ids to load
8146
	 *
8147
	 * @return array
8148
	 */
8149
	public function batch( $type, $ids )
8150
	{
8151
		return $this->repository->batch( $type, $ids );
8152
	}
8153
8154
	/**
8155
	 * This is a convenience method; it converts database rows
8156
	 * (arrays) into beans. Given a type and a set of rows this method
8157
	 * will return an array of beans of the specified type loaded with
8158
	 * the data fields provided by the result set from the database.
8159
	 *
8160
	 * @param string $type type of beans you would like to have
8161
	 * @param array  $rows rows from the database result
8162
	 *
8163
	 * @return array
8164
	 */
8165
	public function convertToBeans( $type, $rows )
8166
	{
8167
		return $this->repository->convertToBeans( $type, $rows );
8168
	}
8169
8170
	/**
8171
	 * Counts the number of beans of type $type.
8172
	 * This method accepts a second argument to modify the count-query.
8173
	 * A third argument can be used to provide bindings for the SQL snippet.
8174
	 *
8175
	 * @param string $type     type of bean we are looking for
8176
	 * @param string $addSQL   additional SQL snippet
8177
	 * @param array  $bindings parameters to bind to SQL
8178
	 *
8179
	 * @return integer
8180
	 *
8181
	 * @throws SQL
8182
	 */
8183
	public function count( $type, $addSQL = '', $bindings = array() )
8184
	{
8185
		return $this->repository->count( $type, $addSQL, $bindings );
8186
	}
8187
8188
	/**
8189
	 * Trash all beans of a given type. Wipes an entire type of bean.
8190
	 *
8191
	 * @param string $type type of bean you wish to delete all instances of
8192
	 *
8193
	 * @return boolean
8194
	 *
8195
	 * @throws SQL
8196
	 */
8197
	public function wipe( $type )
8198
	{
8199
		return $this->repository->wipe( $type );
8200
	}
8201
8202
	/**
8203
	 * Returns an Association Manager for use with OODB.
8204
	 * A simple getter function to obtain a reference to the association manager used for
8205
	 * storage and more.
8206
	 *
8207
	 * @return AssociationManager
8208
	 *
8209
	 * @throws Security
8210
	 */
8211
	public function getAssociationManager()
8212
	{
8213
		if ( !isset( $this->assocManager ) ) {
8214
			throw new RedException( 'No association manager available.' );
8215
		}
8216
8217
		return $this->assocManager;
8218
	}
8219
8220
	/**
8221
	 * Sets the association manager instance to be used by this OODB.
8222
	 * A simple setter function to set the association manager to be used for storage and
8223
	 * more.
8224
	 *
8225
	 * @param AssociationManager $assoc sets the association manager to be used
0 ignored issues
show
Bug introduced by
There is no parameter named $assoc. 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...
8226
	 *
8227
	 * @return void
8228
	 */
8229
	public function setAssociationManager( AssociationManager $assocManager )
8230
	{
8231
		$this->assocManager = $assocManager;
8232
	}
8233
8234
	/**
8235
	 * Returns the currently used repository instance.
8236
	 * For testing purposes only.
8237
	 *
8238
	 * @return Repository
8239
	 */
8240
	public function getCurrentRepository()
8241
	{
8242
		return $this->repository;
8243
	}
8244
8245
	/**
8246
	 * Binds an SQL function to a column.
8247
	 * This method can be used to setup a decode/encode scheme or
8248
	 * perform UUID insertion. This method is especially useful for handling
8249
	 * MySQL spatial columns, because they need to be processed first using
8250
	 * the asText/GeomFromText functions.
8251
	 *
8252
	 * @param string $mode (read or write)
8253
	 * @param string $field
8254
	 * @param string $function
8255
	 */
8256
	public function bindFunc( $mode, $field, $function )
8257
	{
8258
		list( $type, $property ) = explode( '.', $field );
8259
		$mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ;
8260
8261
		if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array();
8262
		if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array();
8263
8264
		if ( is_null( $function ) ) {
8265
			unset( self::$sqlFilters[$mode][$type][$property] );
8266
		} else {
8267
			if ($mode === QueryWriter::C_SQLFILTER_WRITE) {
8268
				self::$sqlFilters[$mode][$type][$property] = $function.'(?)';
8269
			} else {
8270
				self::$sqlFilters[$mode][$type][$property] = $function."($field)";
8271
			}
8272
		}
8273
8274
		AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
8275
	}
8276
}
8277
} 
8278
8279
namespace RedBeanPHP {
8280
8281
use RedBeanPHP\OODB as OODB;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8282
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8283
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8284
use RedBeanPHP\Adapter as Adapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8285
8286
/**
8287
 * ToolBox.
8288
 *
8289
 * The toolbox is an integral part of RedBeanPHP providing the basic
8290
 * architectural building blocks to manager objects, helpers and additional tools
8291
 * like plugins. A toolbox contains the three core components of RedBeanPHP:
8292
 * the adapter, the query writer and the core functionality of RedBeanPHP in
8293
 * OODB.
8294
 *
8295
 * @file      RedBeanPHP/ToolBox.php
8296
 * @author    Gabor de Mooij and the RedBeanPHP community
8297
 * @license   BSD/GPLv2
8298
 * 
8299
 * @copyright
8300
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
8301
 * This source file is subject to the BSD/GPLv2 License that is bundled
8302
 * with this source code in the file license.txt.
8303
 */
8304
class ToolBox
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
8305
{
8306
8307
	/**
8308
	 * @var OODB
8309
	 */
8310
	protected $oodb;
8311
8312
	/**
8313
	 * @var QueryWriter
8314
	 */
8315
	protected $writer;
8316
8317
	/**
8318
	 * @var DBAdapter
8319
	 */
8320
	protected $adapter;
8321
8322
	/**
8323
	 * Constructor.
8324
	 * The toolbox is an integral part of RedBeanPHP providing the basic
8325
	 * architectural building blocks to manager objects, helpers and additional tools
8326
	 * like plugins. A toolbox contains the three core components of RedBeanPHP:
8327
	 * the adapter, the query writer and the core functionality of RedBeanPHP in
8328
	 * OODB.
8329
	 *
8330
	 * @param OODB              $oodb    Object Database
8331
	 * @param DBAdapter $adapter Adapter
8332
	 * @param QueryWriter       $writer  Writer
8333
	 *
8334
	 * @return ToolBox
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
8335
	 */
8336
	public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer )
8337
	{
8338
		$this->oodb    = $oodb;
8339
		$this->adapter = $adapter;
0 ignored issues
show
Documentation Bug introduced by
$adapter is of type object<RedBeanPHP\Adapter>, but the property $adapter was declared to be of type object<RedBeanPHP\Adapter\DBAdapter>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
8340
		$this->writer  = $writer;
8341
8342
		return $this;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
8343
	}
8344
8345
	/**
8346
	 * Returns the query writer in this toolbox.
8347
	 * The Query Writer is responsible for building the queries for a
8348
	 * specific database and executing them through the adapter.
8349
	 *
8350
	 * @return QueryWriter
8351
	 */
8352
	public function getWriter()
8353
	{
8354
		return $this->writer;
8355
	}
8356
8357
	/**
8358
	 * Returns the OODB instance in this toolbox.
8359
	 * OODB is responsible for creating, storing, retrieving and deleting 
8360
	 * single beans. Other components rely
8361
	 * on OODB for their basic functionality.
8362
	 *
8363
	 * @return OODB
8364
	 */
8365
	public function getRedBean()
8366
	{
8367
		return $this->oodb;
8368
	}
8369
8370
	/**
8371
	 * Returns the database adapter in this toolbox.
8372
	 * The adapter is responsible for executing the query and binding the values.
8373
	 * The adapter also takes care of transaction handling.
8374
	 * 
8375
	 * @return DBAdapter
8376
	 */
8377
	public function getDatabaseAdapter()
8378
	{
8379
		return $this->adapter;
8380
	}
8381
}
8382
}
8383
8384
namespace RedBeanPHP {
8385
8386
use RedBeanPHP\ToolBox as ToolBox;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8387
use RedBeanPHP\OODB as OODB;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8388
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8389
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8390
8391
/**
8392
 * RedBeanPHP Finder.
8393
 * Service class to find beans. For the most part this class
8394
 * offers user friendly utility methods for interacting with the
8395
 * OODB::find() method, which is rather complex. This class can be
8396
 * used to find beans using plain old SQL queries.
8397
 *
8398
 * @file    RedBeanPHP/Finder.php
8399
 * @author  Gabor de Mooij and the RedBeanPHP Community
8400
 * @license BSD/GPLv2
8401
 *
8402
 * @copyright
8403
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
8404
 * This source file is subject to the BSD/GPLv2 License that is bundled
8405
 * with this source code in the file license.txt.
8406
 */
8407
class Finder
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
8408
{
8409
	/**
8410
	 * @var ToolBox
8411
	 */
8412
	protected $toolbox;
8413
8414
	/**
8415
	 * @var OODB
8416
	 */
8417
	protected $redbean;
8418
8419
	/**
8420
	 * Constructor.
8421
	 * The Finder requires a toolbox.
8422
	 *
8423
	 * @param ToolBox $toolbox
8424
	 */
8425
	public function __construct( ToolBox $toolbox )
8426
	{
8427
		$this->toolbox = $toolbox;
8428
		$this->redbean = $toolbox->getRedBean();
8429
	}
8430
8431
	/**
8432
	 * Finds a bean using a type and a where clause (SQL).
8433
	 * As with most Query tools in RedBean you can provide values to
8434
	 * be inserted in the SQL statement by populating the value
8435
	 * array parameter; you can either use the question mark notation
8436
	 * or the slot-notation (:keyname).
8437
	 *
8438
	 * @param string $type     type   the type of bean you are looking for
8439
	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
8440
	 * @param array  $bindings values array of values to be bound to parameters in query
8441
	 *
8442
	 * @return array
8443
	 *
8444
	 * @throws Security
8445
	 */
8446
	public function find( $type, $sql = NULL, $bindings = array() )
8447
	{
8448
		if ( !is_array( $bindings ) ) {
8449
			throw new RedException(
8450
				'Expected array, ' . gettype( $bindings ) . ' given.'
8451
			);
8452
		}
8453
8454
		return $this->redbean->find( $type, array(), $sql, $bindings );
8455
	}
8456
8457
	/**
8458
	 * Like find() but also exports the beans as an array.
8459
	 *
8460
	 * @see Finder::findAndExport
8461
	 *
8462
	 * @param string $type     type   the type of bean you are looking for
8463
	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
8464
	 * @param array  $bindings values array of values to be bound to parameters in query
8465
	 *
8466
	 * @return array
8467
	 */
8468
	public function findAndExport( $type, $sql = NULL, $bindings = array() )
8469
	{
8470
		$arr = array();
8471
		foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) {
8472
			$arr[] = $item->export();
8473
		}
8474
8475
		return $arr;
8476
	}
8477
8478
	/**
8479
	 * Like find() but returns just one bean instead of an array of beans.
8480
	 * This method will return only the first bean of the array.
8481
	 *
8482
	 * @see Finder::find
8483
	 *
8484
	 * @param string $type     type   the type of bean you are looking for
8485
	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
8486
	 * @param array  $bindings values array of values to be bound to parameters in query
8487
	 *
8488
	 * @return OODBBean
8489
	 */
8490
	public function findOne( $type, $sql = NULL, $bindings = array() )
8491
	{
8492
		$sql = $this->toolbox->getWriter()->glueLimitOne( $sql );
8493
		
8494
		$items = $this->find( $type, $sql, $bindings );
8495
8496
		if ( empty($items) ) {
8497
			return NULL;
8498
		}
8499
8500
		return reset( $items );
8501
	}
8502
8503
	/**
8504
	 * Like find() but returns the last bean of the result array.
8505
	 * Opposite of Finder::findLast().
8506
	 *
8507
	 * @see Finder::find
8508
	 *
8509
	 * @param string $type     the type of bean you are looking for
8510
	 * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
8511
	 * @param array  $bindings values array of values to be bound to parameters in query
8512
	 *
8513
	 * @return OODBBean
8514
	 */
8515
	public function findLast( $type, $sql = NULL, $bindings = array() )
8516
	{
8517
		$items = $this->find( $type, $sql, $bindings );
8518
8519
		if ( empty($items) ) {
8520
			return NULL;
8521
		}
8522
8523
		return end( $items );
8524
	}
8525
8526
	/**
8527
	 * Tries to find beans of a certain type,
8528
	 * if no beans are found, it dispenses a bean of that type.
8529
	 *
8530
	 * @see Finder::find
8531
	 *
8532
	 * @param  string $type     the type of bean you are looking for
8533
	 * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
8534
	 * @param  array  $bindings values array of values to be bound to parameters in query
8535
	 *
8536
	 * @return array
8537
	 */
8538
	public function findOrDispense( $type, $sql = NULL, $bindings = array() )
8539
	{
8540
		$foundBeans = $this->find( $type, $sql, $bindings );
8541
8542
		if ( empty( $foundBeans ) ) {
8543
			return array( $this->redbean->dispense( $type ) );
8544
		} else {
8545
			return $foundBeans;
8546
		}
8547
	}
8548
8549
	/**
8550
	 * Finds a BeanCollection using the repository.
8551
	 * A bean collection can be used to retrieve one bean at a time using
8552
	 * cursors - this is useful for processing large datasets. A bean collection
8553
	 * will not load all beans into memory all at once, just one at a time.
8554
	 *
8555
	 * @param  string $type     the type of bean you are looking for
8556
	 * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
8557
	 * @param  array  $bindings values array of values to be bound to parameters in query
8558
	 *
8559
	 * @return BeanCollection
8560
	 */
8561
	public function findCollection( $type, $sql, $bindings = array() )
8562
	{
8563
		return $this->redbean->findCollection( $type, $sql, $bindings );
8564
	}
8565
8566
	/**
8567
	 * Finds or creates a bean.
8568
	 * Tries to find a bean with certain properties specified in the second
8569
	 * parameter ($like). If the bean is found, it will be returned.
8570
	 * If multiple beans are found, only the first will be returned.
8571
	 * If no beans match the criteria, a new bean will be dispensed,
8572
	 * the criteria will be imported as properties and this new bean
8573
	 * will be stored and returned.
8574
	 *
8575
	 * Format of criteria set: property => value
8576
	 * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
8577
	 *
8578
	 * @param string $type type of bean to search for
8579
	 * @param array  $like criteria set describing bean to search for
8580
	 *
8581
	 * @return OODBBean
8582
	 */
8583
	public function findOrCreate( $type, $like = array() )
8584
	{
8585
			$beans = $this->findLike( $type, $like );
8586
			if ( count( $beans ) ) {
8587
				$bean = reset( $beans );
8588
				return $bean;
8589
			}
8590
8591
			$bean = $this->redbean->dispense( $type );
8592
			$bean->import( $like );
8593
			$this->redbean->store( $bean );
8594
			return $bean;
8595
	}
8596
8597
	/**
8598
	 * Finds beans by its type and a certain criteria set.
8599
	 *
8600
	 * Format of criteria set: property => value
8601
	 * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
8602
	 *
8603
	 * If the additional SQL is a condition, this condition will be glued to the rest
8604
	 * of the query using an AND operator. Note that this is as far as this method
8605
	 * can go, there is no way to glue additional SQL using an OR-condition.
8606
	 * This method provides access to an underlying mechanism in the RedBeanPHP architecture
8607
	 * to find beans using criteria sets. However, please do not use this method
8608
	 * for complex queries, use plain SQL instead ( the regular find method ) as it is
8609
	 * more suitable for the job. This method is
8610
	 * meant for basic search-by-example operations.
8611
	 *
8612
	 * @param string $type       type of bean to search for
8613
	 * @param array  $conditions criteria set describing the bean to search for
8614
	 * @param string $sql        additional SQL (for sorting)
8615
	 *
8616
	 * @return array
8617
	 */
8618
	public function findLike( $type, $conditions = array(), $sql = '' )
8619
	{
8620
		if ( count( $conditions ) > 0 ) {
8621
			foreach( $conditions as $key => $condition ) {
8622
				if ( !count( $condition ) ) unset( $conditions[$key] );
8623
			}
8624
		}
8625
8626
		return $this->redbean->find( $type, $conditions, $sql );
8627
	}
8628
8629
	/**
8630
	 * Returns a hashmap with bean arrays keyed by type using an SQL
8631
	 * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review'
8632
	 * this method will return movie and review beans.
8633
	 *
8634
	 * Example:
8635
	 *
8636
	 * $stuff = $finder->findMulti('movie,review', '
8637
	 *          SELECT movie.*, review.* FROM movie
8638
	 *          LEFT JOIN review ON review.movie_id = movie.id');
8639
	 *
8640
	 * After this operation, $stuff will contain an entry 'movie' containing all
8641
	 * movies and an entry named 'review' containing all reviews (all beans).
8642
	 * You can also pass bindings.
8643
	 *
8644
	 * If you want to re-map your beans, so you can use $movie->ownReviewList without
8645
	 * having RedBeanPHP executing an SQL query you can use the fourth parameter to
8646
	 * define a selection of remapping closures.
8647
	 *
8648
	 * The remapping argument (optional) should contain an array of arrays.
8649
	 * Each array in the remapping array should contain the following entries:
8650
	 *
8651
	 * array(
8652
	 * 	'a'       => TYPE A
8653
	 *    'b'       => TYPE B
8654
	 *    'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS
8655
	 *    'do'      => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS
8656
	 * )
8657
	 *
8658
	 * Using this mechanism you can build your own 'preloader' with tiny function
8659
	 * snippets (and those can be re-used and shared online of course).
8660
	 *
8661
	 * Example:
8662
	 *
8663
	 * array(
8664
	 * 	'a'       => 'movie'     //define A as movie
8665
	 *    'b'       => 'review'    //define B as review
8666
	 *    'matcher' => function( $a, $b ) {
8667
	 *       return ( $b->movie_id == $a->id );  //Perform action if review.movie_id equals movie.id
8668
	 *    }
8669
	 *    'do'      => function( $a, $b ) {
8670
	 *       $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie
8671
	 *       $a->clearHistory();                 //optional, act 'as if these beans have been loaded through ownReviewList'.
8672
	 *    }
8673
	 * )
8674
	 *
8675
	 * The Query Template parameter is optional as well but can be used to
8676
	 * set a different SQL template (sprintf-style) for processing the original query.
8677
	 *
8678
	 * @note the SQL query provided IS NOT THE ONE used internally by this function,
8679
	 * this function will pre-process the query to get all the data required to find the beans.
8680
	 *
8681
	 * @note if you use the 'book.*' notation make SURE you're
8682
	 * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because
8683
	 * it's actually an SQL-like template SLOT, not real SQL.
8684
	 *
8685
	 * @note instead of an SQL query you can pass a result array as well.
8686
	 *
8687
	 * @param string|array $types         a list of types (either array or comma separated string)
8688
	 * @param string|array $sqlOrArr      an SQL query or an array of prefetched records
0 ignored issues
show
Bug introduced by
There is no parameter named $sqlOrArr. 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...
8689
	 * @param array        $bindings      optional, bindings for SQL query
8690
	 * @param array        $remappings    optional, an array of remapping arrays
8691
	 * @param string       $queryTemplate optional, query template
8692
	 *
8693
	 * @return array
8694
	 */
8695
	public function findMulti( $types, $sql, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s' )
8696
	{
8697
		if ( !is_array( $types ) ) $types = explode( ',', $types );
8698
		if ( !is_array( $sql ) ) {
8699
			$writer = $this->toolbox->getWriter();
8700
			$adapter = $this->toolbox->getDatabaseAdapter();
8701
8702
			//Repair the query, replace book.* with book.id AS book_id etc..
8703
			foreach( $types as $type ) {
8704
				$pattern = " {$type}.*";
8705
				if ( strpos( $sql, $pattern ) !== FALSE ) {
8706
					$newSelectorArray = array();
8707
					$columns = $writer->getColumns( $type );
8708
					foreach( $columns as $column => $definition ) {
8709
						$newSelectorArray[] = sprintf( $queryTemplate, $type, $column, $type, $column );
8710
					}
8711
					$newSelector = implode( ',', $newSelectorArray );
8712
					$sql = str_replace( $pattern, $newSelector, $sql );
8713
				}
8714
			}
8715
8716
			$rows = $adapter->get( $sql, $bindings );
8717
		} else {
8718
			$rows = $sql;
8719
		}
8720
8721
		//Gather the bean data from the query results using the prefix
8722
		$wannaBeans = array();
8723
		foreach( $types as $type ) {
8724
			$wannaBeans[$type] = array();
8725
			$prefix            = "{$type}__";
8726
			foreach( $rows as $rowkey=>$row ) {
8727
				$wannaBean = array();
8728
				foreach( $row as $cell => $value ) {
8729
					if ( strpos( $cell, $prefix ) === 0 ) {
8730
						$property = substr( $cell, strlen( $prefix ) );
8731
						unset( $rows[$rowkey][$cell] );
8732
						$wannaBean[$property] = $value;
8733
					}
8734
				}
8735
				if ( !isset( $wannaBean['id'] ) ) continue;
8736
				if ( is_null( $wannaBean['id'] ) ) continue;
8737
				$wannaBeans[$type][$wannaBean['id']] = $wannaBean;
8738
			}
8739
		}
8740
8741
		//Turn the rows into beans
8742
		$beans = array();
8743
		foreach( $wannaBeans as $type => $wannabees ) {
8744
			$beans[$type] = $this->redbean->convertToBeans( $type, $wannabees );
8745
		}
8746
8747
		//Apply additional re-mappings
8748
		foreach($remappings as $remapping) {
8749
			$a       = $remapping['a'];
8750
			$b       = $remapping['b'];
8751
			$matcher = $remapping['matcher'];
8752
			$do      = $remapping['do'];
8753
			foreach( $beans[$a] as $bean ) {
8754
				foreach( $beans[$b] as $putBean ) {
8755
					if ( $matcher( $bean, $putBean, $beans ) ) $do( $bean, $putBean, $beans, $remapping );
8756
				}
8757
			}
8758
		}
8759
		return $beans;
8760
	}
8761
}
8762
}
8763
8764
namespace RedBeanPHP {
8765
8766
use RedBeanPHP\Observable as Observable;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8767
use RedBeanPHP\OODB as OODB;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8768
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8769
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8770
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8771
use RedBeanPHP\RedException as RedException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8772
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8773
use RedBeanPHP\RedException\SQL as SQLException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8774
use RedBeanPHP\ToolBox as ToolBox;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
8775
8776
/**
8777
 * Association Manager.
8778
 * Manages simple bean associations.
8779
 *
8780
 * @file    RedBeanPHP/AssociationManager.php
8781
 * @author  Gabor de Mooij and the RedBeanPHP Community
8782
 * @license BSD/GPLv2
8783
 *
8784
 * @copyright
8785
 * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
8786
 * This source file is subject to the BSD/GPLv2 License that is bundled
8787
 * with this source code in the file license.txt.
8788
 */
8789
class AssociationManager extends Observable
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
8790
{
8791
	/**
8792
	 * @var OODB
8793
	 */
8794
	protected $oodb;
8795
8796
	/**
8797
	 * @var DBAdapter
8798
	 */
8799
	protected $adapter;
8800
8801
	/**
8802
	 * @var QueryWriter
8803
	 */
8804
	protected $writer;
8805
8806
	/**
8807
	 * Handles\Exceptions. Suppresses exceptions caused by missing structures.
8808
	 *
8809
	 * @param\Exception $exception
8810
	 *
8811
	 * @return void
8812
	 *
8813
	 * @throws\Exception
8814
	 */
8815 View Code Duplication
	private function handleException(\Exception $exception )
8816
	{
8817
		if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(),
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getSQLState() does only exist in the following sub-classes of Exception: Doctrine\DBAL\Driver\AbstractDriverException, Doctrine\DBAL\Driver\Mysqli\MysqliException, Doctrine\DBAL\Driver\OCI8\OCI8Exception, Doctrine\DBAL\Driver\PDOException, Doctrine\DBAL\Driver\SQL...re\SQLAnywhereException, Doctrine\DBAL\Exception\ConnectionException, Doctrine\DBAL\Exception\...raintViolationException, Doctrine\DBAL\Exception\...seObjectExistsException, Doctrine\DBAL\Exception\...ObjectNotFoundException, Doctrine\DBAL\Exception\DriverException, Doctrine\DBAL\Exception\...raintViolationException, Doctrine\DBAL\Exception\InvalidFieldNameException, Doctrine\DBAL\Exception\...niqueFieldNameException, Doctrine\DBAL\Exception\...raintViolationException, Doctrine\DBAL\Exception\ReadOnlyException, Doctrine\DBAL\Exception\ServerException, Doctrine\DBAL\Exception\SyntaxErrorException, Doctrine\DBAL\Exception\TableExistsException, Doctrine\DBAL\Exception\TableNotFoundException, Doctrine\DBAL\Exception\...raintViolationException, RedBeanPHP\RedException\SQL. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
8818
			array(
8819
				QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
8820
				QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN )
8821
			)
8822
		) {
8823
			throw $exception;
8824
		}
8825
	}
8826
8827
	/**
8828
	 * Internal method.
8829
	 * Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and
8830
	 * $bindings bindings. If $getLinks is TRUE, link rows are returned instead.
8831
	 *
8832
	 * @param OODBBean $bean     reference bean
8833
	 * @param string           $type     target type
8834
	 * @param string           $sql      additional SQL snippet
8835
	 * @param array            $bindings bindings
8836
	 *
8837
	 * @return array
8838
	 *
8839
	 * @throws Security
8840
	 * @throws SQL
8841
	 */
8842
	private function relatedRows( $bean, $type, $sql = '', $bindings = array() )
8843
	{
8844
		$ids = array( $bean->id );
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
8845
		$sourceType = $bean->getMeta( 'type' );
8846
		try {
8847
			return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings );
8848
		} catch ( SQLException $exception ) {
8849
			$this->handleException( $exception );
8850
			return array();
8851
		}
8852
	}
8853
8854
	/**
8855
	 * Associates a pair of beans. This method associates two beans, no matter
8856
	 * what types. Accepts a base bean that contains data for the linking record.
8857
	 * This method is used by associate. This method also accepts a base bean to be used
8858
	 * as the template for the link record in the database.
8859
	 *
8860
	 * @param OODBBean $bean1 first bean
8861
	 * @param OODBBean $bean2 second bean
8862
	 * @param OODBBean $bean  base bean (association record)
8863
	 *
8864
	 * @throws\Exception|SQL
8865
	 *
8866
	 * @return mixed
8867
	 */
8868
	protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean )
8869
	{
8870
		$type      = $bean->getMeta( 'type' );
8871
		$property1 = $bean1->getMeta( 'type' ) . '_id';
8872
		$property2 = $bean2->getMeta( 'type' ) . '_id';
8873
8874
		if ( $property1 == $property2 ) {
8875
			$property2 = $bean2->getMeta( 'type' ) . '2_id';
8876
		}
8877
8878
		$this->oodb->store( $bean1 );
8879
		$this->oodb->store( $bean2 );
8880
8881
		$bean->setMeta( "cast.$property1", "id" );
8882
		$bean->setMeta( "cast.$property2", "id" );
8883
		$bean->setMeta( 'sys.buildcommand.unique', array( $property1, $property2 ) );
8884
8885
		$bean->$property1 = $bean1->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
8886
		$bean->$property2 = $bean2->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
8887
8888
		$results   = array();
8889
8890
		try {
8891
			$id = $this->oodb->store( $bean );
8892
			$results[] = $id;
8893
		} catch ( SQLException $exception ) {
8894
			if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
8895
				array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ) )
8896
			) {
8897
				throw $exception;
8898
			}
8899
		}
8900
8901
		return $results;
8902
	}
8903
8904
	/**
8905
	 * Constructor
8906
	 *
8907
	 * @param ToolBox $tools toolbox
8908
	 */
8909
	public function __construct( ToolBox $tools )
8910
	{
8911
		$this->oodb    = $tools->getRedBean();
8912
		$this->adapter = $tools->getDatabaseAdapter();
8913
		$this->writer  = $tools->getWriter();
8914
		$this->toolbox = $tools;
0 ignored issues
show
Bug introduced by
The property toolbox does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
8915
	}
8916
8917
	/**
8918
	 * Creates a table name based on a types array.
8919
	 * Manages the get the correct name for the linking table for the
8920
	 * types provided.
8921
	 *
8922
	 * @todo find a nice way to decouple this class from QueryWriter?
8923
	 *
8924
	 * @param array $types 2 types as strings
8925
	 *
8926
	 * @return string
8927
	 */
8928
	public function getTable( $types )
8929
	{
8930
		return $this->writer->getAssocTable( $types );
8931
	}
8932
8933
	/**
8934
	 * Associates two beans in a many-to-many relation.
8935
	 * This method will associate two beans and store the connection between the
8936
	 * two in a link table. Instead of two single beans this method also accepts
8937
	 * two sets of beans. Returns the ID or the IDs of the linking beans.
8938
	 * 
8939
	 * @param OODBBean|array $beans1 one or more beans to form the association
8940
	 * @param OODBBean|array $beans2 one or more beans to form the association
8941
	 *
8942
	 * @return array
8943
	 */
8944
	public function associate( $beans1, $beans2 )
8945
	{
8946
		if ( !is_array( $beans1 ) ) {
8947
			$beans1 = array( $beans1 );
8948
		}
8949
8950
		if ( !is_array( $beans2 ) ) {
8951
			$beans2 = array( $beans2 );
8952
		}
8953
8954
		$results = array();
8955
		foreach ( $beans1 as $bean1 ) {
8956
			foreach ( $beans2 as $bean2 ) {
8957
				$table     = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) );
8958
				$bean      = $this->oodb->dispense( $table );
8959
				$results[] = $this->associateBeans( $bean1, $bean2, $bean );
8960
			}
8961
		}
8962
8963
		return ( count( $results ) > 1 ) ? $results : reset( $results );
8964
	}
8965
8966
	/**
8967
	 * Counts the number of related beans in an N-M relation.
8968
	 * This method returns the number of beans of type $type associated
8969
	 * with reference bean(s) $bean. The query can be tuned using an
8970
	 * SQL snippet for additional filtering.
8971
	 *
8972
	 * @param OODBBean|array $bean     a bean object or an array of beans
8973
	 * @param string                 $type     type of bean you're interested in
8974
	 * @param string                 $sql      SQL snippet (optional)
8975
	 * @param array                  $bindings bindings for your SQL string
8976
	 *
8977
	 * @return integer
8978
	 *
8979
	 * @throws Security
8980
	 */
8981
	public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() )
8982
	{
8983
		if ( !( $bean instanceof OODBBean ) ) {
8984
			throw new RedException(
8985
				'Expected array or OODBBean but got:' . gettype( $bean )
8986
			);
8987
		}
8988
8989
		if ( !$bean->id ) {
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
8990
			return 0;
8991
		}
8992
8993
		$beanType = $bean->getMeta( 'type' );
8994
8995
		try {
8996
			return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings );
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
8997
		} catch ( SQLException $exception ) {
8998
			$this->handleException( $exception );
8999
9000
			return 0;
9001
		}
9002
	}
9003
9004
	/**
9005
	 * Breaks the association between two beans. This method unassociates two beans. If the
9006
	 * method succeeds the beans will no longer form an association. In the database
9007
	 * this means that the association record will be removed. This method uses the
9008
	 * OODB trash() method to remove the association links, thus giving FUSE models the
9009
	 * opportunity to hook-in additional business logic. If the $fast parameter is
9010
	 * set to boolean TRUE this method will remove the beans without their consent,
9011
	 * bypassing FUSE. This can be used to improve performance.
9012
	 *
9013
	 * @param OODBBean $bean1 first bean
0 ignored issues
show
Documentation introduced by
There is no parameter named $bean1. Did you maybe mean $beans1?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
9014
	 * @param OODBBean $bean2 second bean
0 ignored issues
show
Documentation introduced by
There is no parameter named $bean2. Did you maybe mean $beans2?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
9015
	 * @param boolean          $fast  If TRUE, removes the entries by query without FUSE
9016
	 *
9017
	 * @return void
9018
	 */
9019
	public function unassociate( $beans1, $beans2, $fast = NULL )
9020
	{
9021
		$beans1 = ( !is_array( $beans1 ) ) ? array( $beans1 ) : $beans1;
9022
		$beans2 = ( !is_array( $beans2 ) ) ? array( $beans2 ) : $beans2;
9023
9024
		foreach ( $beans1 as $bean1 ) {
9025
			foreach ( $beans2 as $bean2 ) {
9026
				try {
9027
					$this->oodb->store( $bean1 );
9028
					$this->oodb->store( $bean2 );
9029
9030
					$type1 = $bean1->getMeta( 'type' );
9031
					$type2 = $bean2->getMeta( 'type' );
9032
9033
					$row      = $this->writer->queryRecordLink( $type1, $type2, $bean1->id, $bean2->id );
9034
					$linkType = $this->getTable( array( $type1, $type2 ) );
9035
9036
					if ( $fast ) {
9037
						$this->writer->deleteRecord( $linkType, array( 'id' => $row['id'] ) );
9038
9039
						return;
9040
					}
9041
9042
					$beans = $this->oodb->convertToBeans( $linkType, array( $row ) );
9043
9044
					if ( count( $beans ) > 0 ) {
9045
						$bean = reset( $beans );
9046
						$this->oodb->trash( $bean );
9047
					}
9048
				} catch ( SQLException $exception ) {
9049
					$this->handleException( $exception );
9050
				}
9051
			}
9052
		}
9053
	}
9054
9055
	/**
9056
	 * Removes all relations for a bean. This method breaks every connection between
9057
	 * a certain bean $bean and every other bean of type $type. Warning: this method
9058
	 * is really fast because it uses a direct SQL query however it does not inform the
9059
	 * models about this. If you want to notify FUSE models about deletion use a foreach-loop
9060
	 * with unassociate() instead. (that might be slower though)
9061
	 *
9062
	 * @param OODBBean $bean reference bean
9063
	 * @param string           $type type of beans that need to be unassociated
9064
	 *
9065
	 * @return void
9066
	 */
9067
	public function clearRelations( OODBBean $bean, $type )
9068
	{
9069
		$this->oodb->store( $bean );
9070
		try {
9071
			$this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id );
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
9072
		} catch ( SQLException $exception ) {
9073
			$this->handleException( $exception );
9074
		}
9075
	}
9076
9077
	/**
9078
	 * Returns all the beans associated with $bean.
9079
	 * This method will return an array containing all the beans that have
9080
	 * been associated once with the associate() function and are still
9081
	 * associated with the bean specified. The type parameter indicates the
9082
	 * type of beans you are looking for. You can also pass some extra SQL and
9083
	 * values for that SQL to filter your results after fetching the
9084
	 * related beans.
9085
	 *
9086
	 * Don't try to make use of subqueries, a subquery using IN() seems to
9087
	 * be slower than two queries!
9088
	 *
9089
	 * Since 3.2, you can now also pass an array of beans instead just one
9090
	 * bean as the first parameter.
9091
	 *
9092
	 * @param OODBBean|array $bean      the bean you have
9093
	 * @param string                 $type      the type of beans you want
9094
	 * @param string                 $sql       SQL snippet for extra filtering
9095
	 * @param array                  $bindings  values to be inserted in SQL slots
9096
	 * @param boolean                $glue      whether the SQL should be prefixed with WHERE
0 ignored issues
show
Bug introduced by
There is no parameter named $glue. 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...
9097
	 *
9098
	 * @return array
9099
	 */
9100
	public function related( $bean, $type, $sql = '', $bindings = array() )
9101
	{
9102
		$sql   = $this->writer->glueSQLCondition( $sql );
9103
9104
		$rows  = $this->relatedRows( $bean, $type, $sql, $bindings );
0 ignored issues
show
Bug introduced by
It seems like $bean defined by parameter $bean on line 9100 can also be of type array; however, RedBeanPHP\AssociationManager::relatedRows() does only seem to accept object<RedBeanPHP\OODBBean>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
9105
9106
		$links = array();
9107
		foreach ( $rows as $key => $row ) {
9108
			if ( !isset( $links[$row['id']] ) ) {
9109
				$links[$row['id']] = array();
9110
			}
9111
9112
			$links[$row['id']][] = $row['linked_by'];
9113
9114
			unset( $rows[$key]['linked_by'] );
9115
		}
9116
9117
		$beans = $this->oodb->convertToBeans( $type, $rows );
9118
9119
		foreach ( $beans as $bean ) {
9120
			$bean->setMeta( 'sys.belongs-to', $links[$bean->id] );
9121
		}
9122
9123
		return $beans;
9124
	}
9125
}
9126
}
9127
9128
namespace RedBeanPHP {
9129
9130
use RedBeanPHP\ToolBox as ToolBox;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9131
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9132
9133
/**
9134
 * Bean Helper Interface.
9135
 * 
9136
 * Interface for Bean Helper.
9137
 * A little bolt that glues the whole machinery together.
9138
 *
9139
 * @file    RedBeanPHP/IBeanHelper.php
9140
 * @author  Gabor de Mooij and the RedBeanPHP Community
9141
 * @license BSD/GPLv2
9142
 *
9143
 * @copyright
9144
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
9145
 * This source file is subject to the BSD/GPLv2 License that is bundled
9146
 * with this source code in the file license.txt.
9147
 */
9148
interface BeanHelper
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
9149
{
9150
	/**
9151
	 * Returns a toolbox to empower the bean.
9152
	 * This allows beans to perform OODB operations by themselves,
9153
	 * as such the bean is a proxy for OODB. This allows beans to implement
9154
	 * their magic getters and setters and return lists.
9155
	 *
9156
	 * @return ToolBox $toolbox toolbox
9157
	 */
9158
	public function getToolbox();
9159
9160
	/**
9161
	 * Does approximately the same as getToolbox but also extracts the
9162
	 * toolbox for you.
9163
	 * This method returns a list with all toolbox items in Toolbox Constructor order:
9164
	 * OODB, adapter, writer and finally the toolbox itself!.
9165
	 *
9166
	 * @return array
9167
	 */
9168
	public function getExtractedToolbox();
9169
9170
	/**
9171
	 * Given a certain bean this method will
9172
	 * return the corresponding model.
9173
	 *
9174
	 * @param OODBBean $bean
9175
	 *
9176
	 * @return string
9177
	 */
9178
	public function getModelForBean( OODBBean $bean );
9179
}
9180
}
9181
9182
namespace RedBeanPHP\BeanHelper {
9183
9184
use RedBeanPHP\BeanHelper as BeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9185
use RedBeanPHP\Facade as Facade;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9186
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9187
use RedBeanPHP\SimpleModelHelper as SimpleModelHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9188
9189
/**
9190
 * Bean Helper.
9191
 *
9192
 * The Bean helper helps beans to access access the toolbox and
9193
 * FUSE models. This Bean Helper makes use of the facade to obtain a
9194
 * reference to the toolbox.
9195
 *
9196
 * @file    RedBeanPHP/BeanHelperFacade.php
9197
 * @author  Gabor de Mooij and the RedBeanPHP Community
9198
 * @license BSD/GPLv2
9199
 *
9200
 * @copyright
9201
 * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
9202
 * This source file is subject to the BSD/GPLv2 License that is bundled
9203
 * with this source code in the file license.txt.
9204
 */
9205
class SimpleFacadeBeanHelper implements BeanHelper
0 ignored issues
show
Coding Style Compatibility introduced by
Each interface must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
9206
{
9207
	/**
9208
	 * Factory function to create instance of Simple Model, if any.
9209
	 *
9210
	 * @var closure
9211
	 */
9212
	private static $factory = null;
9213
9214
	/**
9215
	 * Factory method using a customizable factory function to create
9216
	 * the instance of the Simple Model.
9217
	 *
9218
	 * @param string $modelClassName name of the class
9219
	 *
9220
	 * @return SimpleModel
9221
	 */
9222
	public static function factory( $modelClassName )
9223
	{
9224
		$factory = self::$factory;
9225
		return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName();
9226
	}
9227
9228
	/**
9229
	 * Sets the factory function to create the model when using FUSE
9230
	 * to connect a bean to a model.
9231
	 *
9232
	 * @param closure $factory
9233
	 *
9234
	 * @return void
9235
	 */
9236
	public static function setFactoryFunction( $factory ) 
9237
	{
9238
		self::$factory = $factory;
9239
	}
9240
9241
	/**
9242
	 * @see BeanHelper::getToolbox
9243
	 */
9244
	public function getToolbox()
9245
	{
9246
		return Facade::getToolBox();
9247
	}
9248
9249
	/**
9250
	 * @see BeanHelper::getModelForBean
9251
	 */
9252
	public function getModelForBean( OODBBean $bean )
9253
	{
9254
		$model     = $bean->getMeta( 'type' );
9255
		$prefix    = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_';
9256
9257
		if ( strpos( $model, '_' ) !== FALSE ) {
9258
			$modelParts = explode( '_', $model );
9259
			$modelName = '';
9260
			foreach( $modelParts as $part ) {
9261
				$modelName .= ucfirst( $part );
9262
			}
9263
			$modelName = $prefix . $modelName;
9264
9265 View Code Duplication
			if ( !class_exists( $modelName ) ) {
9266
				//second try
9267
				$modelName = $prefix . ucfirst( $model );
9268
				
9269
				if ( !class_exists( $modelName ) ) {
9270
					return NULL;
9271
				}
9272
			}
9273
9274 View Code Duplication
		} else {
9275
9276
			$modelName = $prefix . ucfirst( $model );
9277
			if ( !class_exists( $modelName ) ) {
9278
				return NULL;
9279
			}
9280
		}
9281
		$obj = self::factory( $modelName );
9282
		$obj->loadBean( $bean );
9283
9284
		return $obj;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $obj; (RedBeanPHP\BeanHelper\SimpleModel) is incompatible with the return type declared by the interface RedBeanPHP\BeanHelper::getModelForBean of type string.

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...
9285
	}
9286
9287
	/**
9288
	 * @see BeanHelper::getExtractedToolbox
9289
	 */
9290
	public function getExtractedToolbox()
9291
	{
9292
		return Facade::getExtractedToolbox();
9293
	}
9294
}
9295
} 
9296
9297
namespace RedBeanPHP {
9298
9299
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9300
9301
/**
9302
 * SimpleModel
9303
 * Base Model For All RedBeanPHP Models using FUSE.
9304
 *
9305
 * RedBeanPHP FUSE is a mechanism to connect beans to posthoc
9306
 * models. Models are connected to beans by naming conventions.
9307
 * Actions on beans will result in actions on models.
9308
 *
9309
 * @file       RedBeanPHP/SimpleModel.php
9310
 * @author     Gabor de Mooij and the RedBeanPHP Team
9311
 * @license    BSD/GPLv2
9312
 *
9313
 * @copyright
9314
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
9315
 * This source file is subject to the BSD/GPLv2 License that is bundled
9316
 * with this source code in the file license.txt.
9317
 */
9318
class SimpleModel
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
9319
{
9320
	/**
9321
	 * @var OODBBean
9322
	 */
9323
	protected $bean;
9324
9325
	/**
9326
	 * Used by FUSE: the ModelHelper class to connect a bean to a model.
9327
	 * This method loads a bean in the model.
9328
	 *
9329
	 * @param OODBBean $bean bean
9330
	 *
9331
	 * @return void
9332
	 */
9333
	public function loadBean( OODBBean $bean )
9334
	{
9335
		$this->bean = $bean;
9336
	}
9337
9338
	/**
9339
	 * Magic Getter to make the bean properties available from
9340
	 * the $this-scope.
9341
	 *
9342
	 * @note this method returns a value, not a reference!
9343
	 *       To obtain a reference unbox the bean first!
9344
	 *
9345
	 * @param string $prop property
9346
	 *
9347
	 * @return mixed
9348
	 */
9349
	public function __get( $prop )
9350
	{
9351
		return $this->bean->$prop;
9352
	}
9353
9354
	/**
9355
	 * Magic Setter.
9356
	 * Sets the value directly as a bean property.
9357
	 *
9358
	 * @param string $prop  property
9359
	 * @param mixed  $value value
9360
	 *
9361
	 * @return void
9362
	 */
9363
	public function __set( $prop, $value )
9364
	{
9365
		$this->bean->$prop = $value;
9366
	}
9367
9368
	/**
9369
	 * Isset implementation.
9370
	 * Implements the isset function for array-like access.
9371
	 *
9372
	 * @param  string $key key to check
9373
	 *
9374
	 * @return boolean
9375
	 */
9376
	public function __isset( $key )
9377
	{
9378
		return isset( $this->bean->$key );
9379
	}
9380
9381
	/**
9382
	 * Box the bean using the current model.
9383
	 * This method wraps the current bean in this model.
9384
	 * This method can be reached using FUSE through a simple
9385
	 * OODBBean. The method returns a RedBeanPHP Simple Model.
9386
	 * This is useful if you would like to rely on PHP type hinting.
9387
	 * You can box your beans before passing them to functions or methods
9388
	 * with typed parameters.
9389
	 *
9390
	 * @return SimpleModel
9391
	 */
9392
	public function box()
9393
	{
9394
		return $this;
9395
	}
9396
9397
	/**
9398
	 * Unbox the bean from the model.
9399
	 * This method returns the bean inside the model.
9400
	 *
9401
	 * @return OODBBean
9402
	 */
9403
	public function unbox()
9404
	{
9405
		return $this->bean;
9406
	}
9407
}
9408
} 
9409
9410
namespace RedBeanPHP {
9411
9412
use RedBeanPHP\Observer as Observer;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9413
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9414
use RedBeanPHP\Observable as Observable;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9415
9416
/**
9417
 * RedBean Model Helper.
9418
 *
9419
 * Connects beans to models.
9420
 * This is the core of so-called FUSE.
9421
 *
9422
 * @file    RedBeanPHP/ModelHelper.php
9423
 * @author  Gabor de Mooij and the RedBeanPHP Community
9424
 * @license BSD/GPLv2
9425
 *
9426
 * @copyright
9427
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
9428
 * This source file is subject to the BSD/GPLv2 License that is bundled
9429
 * with this source code in the file license.txt.
9430
 */
9431
class SimpleModelHelper implements Observer
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
9432
{
9433
9434
	/**
9435
	 * @see Observer::onEvent
9436
	 */
9437
	public function onEvent( $eventName, $bean )
9438
	{
9439
		$bean->$eventName();
9440
	}
9441
9442
	/**
9443
	 * Attaches the FUSE event listeners. Now the Model Helper will listen for
9444
	 * CRUD events. If a CRUD event occurs it will send a signal to the model
9445
	 * that belongs to the CRUD bean and this model will take over control from
9446
	 * there.
9447
	 *
9448
	 * @param Observable $observable
9449
	 *
9450
	 * @return void
9451
	 */
9452
	public function attachEventListeners( Observable $observable )
9453
	{
9454
		foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $e ) {
9455
			$observable->addEventListener( $e, $this );
9456
		}
9457
	}
9458
}
9459
} 
9460
9461
namespace RedBeanPHP {
9462
9463
use RedBeanPHP\ToolBox as ToolBox;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9464
use RedBeanPHP\AssociationManager as AssociationManager;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9465
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9466
9467
/**
9468
 * RedBeanPHP Tag Manager.
9469
 *
9470
 * The tag manager offers an easy way to quickly implement basic tagging
9471
 * functionality.
9472
 *
9473
 * Provides methods to tag beans and perform tag-based searches in the
9474
 * bean database.
9475
 *
9476
 * @file       RedBeanPHP/TagManager.php
9477
 * @author     Gabor de Mooij and the RedBeanPHP community
9478
 * @license    BSD/GPLv2
9479
 *
9480
 * @copyright
9481
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
9482
 * This source file is subject to the BSD/GPLv2 License that is bundled
9483
 * with this source code in the file license.txt.
9484
 */
9485
class TagManager
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
9486
{
9487
	/**
9488
	 * @var ToolBox
9489
	 */
9490
	protected $toolbox;
9491
9492
	/**
9493
	 * @var AssociationManager
9494
	 */
9495
	protected $associationManager;
9496
9497
	/**
9498
	 * @var OODBBean
9499
	 */
9500
	protected $redbean;
9501
9502
	/**
9503
	 * Checks if the argument is a comma separated string, in this case
9504
	 * it will split the string into words and return an array instead.
9505
	 * In case of an array the argument will be returned 'as is'.
9506
	 *
9507
	 * @param array|string $tagList list of tags
9508
	 *
9509
	 * @return array
9510
	 */
9511
	private function extractTagsIfNeeded( $tagList )
9512
	{
9513
		if ( $tagList !== FALSE && !is_array( $tagList ) ) {
9514
			$tags = explode( ',', (string) $tagList );
9515
		} else {
9516
			$tags = $tagList;
9517
		}
9518
9519
		return $tags;
9520
	}
9521
9522
	/**
9523
	 * Finds a tag bean by it's title.
9524
	 * Internal method.
9525
	 *
9526
	 * @param string $title title
9527
	 *
9528
	 * @return OODBBean
9529
	 */
9530
	protected function findTagByTitle( $title )
9531
	{
9532
		$beans = $this->redbean->find( 'tag', array( 'title' => array( $title ) ) );
0 ignored issues
show
Documentation Bug introduced by
The method find does not exist on object<RedBeanPHP\OODBBean>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
9533
9534
		if ( $beans ) {
9535
			$bean = reset( $beans );
9536
9537
			return $bean;
9538
		}
9539
9540
		return NULL;
9541
	}
9542
9543
	/**
9544
	 * Constructor.
9545
	 * The tag manager offers an easy way to quickly implement basic tagging
9546
	 * functionality.
9547
	 *
9548
	 * @param ToolBox $toolbox
9549
	 */
9550
	public function __construct( ToolBox $toolbox )
9551
	{
9552
		$this->toolbox = $toolbox;
9553
		$this->redbean = $toolbox->getRedBean();
0 ignored issues
show
Documentation Bug introduced by
It seems like $toolbox->getRedBean() of type object<RedBeanPHP\OODB> is incompatible with the declared type object<RedBeanPHP\OODBBean> of property $redbean.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
9554
9555
		$this->associationManager = $this->redbean->getAssociationManager();
9556
	}
9557
9558
	/**
9559
	 * Tests whether a bean has been associated with one ore more
9560
	 * of the listed tags. If the third parameter is TRUE this method
9561
	 * will return TRUE only if all tags that have been specified are indeed
9562
	 * associated with the given bean, otherwise FALSE.
9563
	 * If the third parameter is FALSE this
9564
	 * method will return TRUE if one of the tags matches, FALSE if none
9565
	 * match.
9566
	 * 
9567
	 * Tag list can be either an array with tag names or a comma separated list
9568
	 * of tag names.
9569
	 *
9570
	 * @param  OODBBean $bean bean to check for tags
9571
	 * @param  array|string     $tags list of tags
9572
	 * @param  boolean          $all  whether they must all match or just some
9573
	 *
9574
	 * @return boolean
9575
	 */
9576
	public function hasTag( $bean, $tags, $all = FALSE )
9577
	{
9578
		$foundtags = $this->tag( $bean );
9579
9580
		$tags = $this->extractTagsIfNeeded( $tags );
9581
		$same = array_intersect( $tags, $foundtags );
9582
9583
		if ( $all ) {
9584
			return ( implode( ',', $same ) === implode( ',', $tags ) );
9585
		}
9586
9587
		return (bool) ( count( $same ) > 0 );
9588
	}
9589
9590
	/**
9591
	 * Removes all sepcified tags from the bean. The tags specified in
9592
	 * the second parameter will no longer be associated with the bean.
9593
	 * 
9594
	 * Tag list can be either an array with tag names or a comma separated list
9595
	 * of tag names.
9596
	 *
9597
	 * @param  OODBBean $bean    tagged bean
9598
	 * @param  array|string     $tagList list of tags (names)
9599
	 *
9600
	 * @return void
9601
	 */
9602
	public function untag( $bean, $tagList )
9603
	{
9604
		$tags = $this->extractTagsIfNeeded( $tagList );
9605
9606
		foreach ( $tags as $tag ) {
9607
			if ( $t = $this->findTagByTitle( $tag ) ) {
9608
				$this->associationManager->unassociate( $bean, $t );
9609
			}
9610
		}
9611
	}
9612
9613
	/**
9614
	 * Tags a bean or returns tags associated with a bean.
9615
	 * If $tagList is NULL or omitted this method will return a
9616
	 * comma separated list of tags associated with the bean provided.
9617
	 * If $tagList is a comma separated list (string) of tags all tags will
9618
	 * be associated with the bean.
9619
	 * You may also pass an array instead of a string.
9620
	 * 
9621
	 * Tag list can be either an array with tag names or a comma separated list
9622
	 * of tag names.
9623
	 *
9624
	 * @param OODBBean $bean    bean to be tagged
9625
	 * @param array|string     $tagList a list of tags
9626
	 *
9627
	 * @return array
9628
	 */
9629
	public function tag( OODBBean $bean, $tagList = NULL )
9630
	{
9631
		if ( is_null( $tagList ) ) {
9632
			
9633
			$tags = $bean->sharedTag;
0 ignored issues
show
Documentation introduced by
The property sharedTag does not exist on object<RedBeanPHP\OODBBean>. 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...
9634
			$foundTags = array();
9635
9636
			foreach ( $tags as $tag ) {
9637
				$foundTags[] = $tag->title;
9638
			}
9639
9640
			return $foundTags;
9641
		}
9642
9643
		$this->associationManager->clearRelations( $bean, 'tag' );
9644
		$this->addTags( $bean, $tagList );
9645
9646
		return $tagList;
9647
	}
9648
9649
	/**
9650
	 * Adds tags to a bean.
9651
	 * If $tagList is a comma separated list of tags all tags will
9652
	 * be associated with the bean.
9653
	 * You may also pass an array instead of a string.
9654
	 * 
9655
	 * Tag list can be either an array with tag names or a comma separated list
9656
	 * of tag names.
9657
	 *
9658
	 * @param OODBBean $bean    bean to add tags to
9659
	 * @param array|string     $tagList list of tags to add to bean
9660
	 *
9661
	 * @return void
9662
	 */
9663
	public function addTags( OODBBean $bean, $tagList )
9664
	{
9665
		$tags = $this->extractTagsIfNeeded( $tagList );
9666
9667
		if ( $tagList === FALSE ) {
9668
			return;
9669
		}
9670
9671
		foreach ( $tags as $tag ) {
9672
			if ( !$t = $this->findTagByTitle( $tag ) ) {
9673
				$t        = $this->redbean->dispense( 'tag' );
0 ignored issues
show
Bug introduced by
The method dispense() does not exist on RedBeanPHP\OODBBean. Did you maybe mean initializeForDispense()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
9674
				$t->title = $tag;
9675
9676
				$this->redbean->store( $t );
0 ignored issues
show
Documentation Bug introduced by
The method store does not exist on object<RedBeanPHP\OODBBean>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
9677
			}
9678
9679
			$this->associationManager->associate( $bean, $t );
9680
		}
9681
	}
9682
9683
	/**
9684
	 * Returns all beans that have been tagged with one or more
9685
	 * of the specified tags.
9686
	 *
9687
	 * Tag list can be either an array with tag names or a comma separated list
9688
	 * of tag names.
9689
	 *
9690
	 * @param string       $beanType type of bean you are looking for
9691
	 * @param array|string $tagList  list of tags to match
9692
	 * @param string       $sql      additional SQL (use only for pagination)
9693
	 * @param array        $bindings bindings
9694
	 *
9695
	 * @return array
9696
	 */
9697 View Code Duplication
	public function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
9698
	{
9699
		$tags       = $this->extractTagsIfNeeded( $tagList );
9700
		$records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings );
9701
9702
		return $this->redbean->convertToBeans( $beanType, $records );
0 ignored issues
show
Documentation Bug introduced by
The method convertToBeans does not exist on object<RedBeanPHP\OODBBean>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
9703
	}
9704
9705
	/**
9706
	 * Returns all beans that have been tagged with ALL of the tags given.
9707
	 *
9708
	 * Tag list can be either an array with tag names or a comma separated list
9709
	 * of tag names.
9710
	 *
9711
	 * @param string        $beanType type of bean you are looking for
9712
	 * @param array|string  $tagList  list of tags to match
9713
	 *
9714
	 * @return array
9715
	 */
9716 View Code Duplication
	public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
9717
	{
9718
		$tags  = $this->extractTagsIfNeeded( $tagList );
9719
		$records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings );
9720
9721
		return $this->redbean->convertToBeans( $beanType, $records );
0 ignored issues
show
Documentation Bug introduced by
The method convertToBeans does not exist on object<RedBeanPHP\OODBBean>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
9722
	}
9723
}
9724
}
9725
9726
namespace RedBeanPHP {
9727
9728
use RedBeanPHP\ToolBox as ToolBox;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9729
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9730
9731
/**
9732
 * Label Maker.
9733
 * Makes so-called label beans.
9734
 * A label is a bean with only an id, type and name property.
9735
 * Labels can be used to create simple entities like categories, tags or enums.
9736
 * This service class provides convenience methods to deal with this kind of
9737
 * beans.
9738
 *
9739
 * @file       RedBeanPHP/LabelMaker.php
9740
 * @author     Gabor de Mooij and the RedBeanPHP Community
9741
 * @license    BSD/GPLv2
9742
 *
9743
 * @copyright
9744
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
9745
 * This source file is subject to the BSD/GPLv2 License that is bundled
9746
 * with this source code in the file license.txt.
9747
 */
9748
class LabelMaker
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
9749
{
9750
	/**
9751
	 * @var ToolBox
9752
	 */
9753
	protected $toolbox;
9754
9755
	/**
9756
	 * Constructor.
9757
	 *
9758
	 * @param ToolBox $toolbox
9759
	 */
9760
	public function __construct( ToolBox $toolbox )
9761
	{
9762
		$this->toolbox = $toolbox;
9763
	}
9764
9765
	/**
9766
	 * A label is a bean with only an id, type and name property.
9767
	 * This function will dispense beans for all entries in the array. The
9768
	 * values of the array will be assigned to the name property of each
9769
	 * individual bean.
9770
	 *
9771
	 * $people = R::dispenseLabels( 'person', [ 'Santa', 'Claus' ] );
9772
	 *
9773
	 * @param string $type   type of beans you would like to have
9774
	 * @param array  $labels list of labels, names for each bean
9775
	 *
9776
	 * @return array
9777
	 */
9778
	public function dispenseLabels( $type, $labels )
9779
	{
9780
		$labelBeans = array();
9781
		foreach ( $labels as $label ) {
9782
			$labelBean       = $this->toolbox->getRedBean()->dispense( $type );
9783
			$labelBean->name = $label;
0 ignored issues
show
Documentation introduced by
The property name does not exist on object<RedBeanPHP\OODBBean>. 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...
9784
			$labelBeans[]    = $labelBean;
9785
		}
9786
9787
		return $labelBeans;
9788
	}
9789
9790
	/**
9791
	 * Gathers labels from beans. This function loops through the beans,
9792
	 * collects the value of the name property for each individual bean
9793
	 * and stores the names in a new array. The array then gets sorted using the
9794
	 * default sort function of PHP (sort).
9795
	 *
9796
	 * Usage:
9797
	 *
9798
	 * $o1->name = 'hamburger';
9799
	 * $o2->name = 'pizza';
9800
	 * implode( ',', R::gatherLabels( [ $o1, $o2 ] ) ); //hamburger,pizza
9801
	 *
9802
	 * Note that the return value is an array of strings, not beans.
9803
	 *
9804
	 * @param array $beans list of beans to loop through
9805
	 *
9806
	 * @return array
9807
	 */
9808
	public function gatherLabels( $beans )
9809
	{
9810
		$labels = array();
9811
9812
		foreach ( $beans as $bean ) {
9813
			$labels[] = $bean->name;
9814
		}
9815
9816
		sort( $labels );
9817
9818
		return $labels;
9819
	}
9820
	
9821
	/**
9822
	 * Fetches an ENUM from the database and creates it if necessary.
9823
	 * An ENUM has the following format:
9824
	 *
9825
	 * ENUM:VALUE
9826
	 *
9827
	 * If you pass 'ENUM' only, this method will return an array of its
9828
	 * values:
9829
	 *
9830
	 * implode( ',', R::gatherLabels( R::enum( 'flavour' ) ) ) //'BANANA,MOCCA'
9831
	 *
9832
	 * If you pass 'ENUM:VALUE' this method will return the specified enum bean
9833
	 * and create it in the database if it does not exist yet:
9834
	 *
9835
	 * $bananaFlavour = R::enum( 'flavour:banana' );
9836
	 * $bananaFlavour->name;
9837
	 *
9838
	 * So you can use this method to set an ENUM value in a bean:
9839
	 *
9840
	 * $shake->flavour = R::enum( 'flavour:banana' );
9841
	 *
9842
	 * the property flavour now contains the enum bean, a parent bean.
9843
	 * In the database, flavour_id will point to the flavour record with name 'banana'.
9844
	 *
9845
	 * @param string $enum ENUM specification for label
9846
	 * 
9847
	 * @return array|OODBBean
9848
	 */
9849
	public function enum( $enum )
9850
	{
9851
		$oodb = $this->toolbox->getRedBean();
9852
		
9853
		if ( strpos( $enum, ':' ) === FALSE ) {
9854
			$type  = $enum;
9855
			$value = FALSE;
9856
		} else {
9857
			list( $type, $value ) = explode( ':', $enum );
9858
			$value                = preg_replace( '/\W+/', '_', strtoupper( trim( $value ) ) );
9859
		}
9860
		
9861
		$values = $oodb->find( $type );
9862
		
9863
		if ( $value === FALSE ) {
9864
			return $values;
9865
		}
9866
		
9867
		foreach( $values as $enumItem ) {
9868
				if ( $enumItem->name === $value ) return $enumItem;	
9869
		}
9870
		
9871
		$newEnumItems = $this->dispenseLabels( $type, array( $value ) );
9872
		$newEnumItem  = reset( $newEnumItems );
9873
		
9874
		$oodb->store( $newEnumItem );
9875
		
9876
		return $newEnumItem;
9877
	}
9878
}
9879
}
9880
9881
namespace RedBeanPHP {
9882
9883
use RedBeanPHP\ToolBox as ToolBox;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9884
use RedBeanPHP\OODB as OODB;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9885
use RedBeanPHP\QueryWriter as QueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9886
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9887
use RedBeanPHP\AssociationManager as AssociationManager;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9888
use RedBeanPHP\TagManager as TagManager;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9889
use RedBeanPHP\DuplicationManager as DuplicationManager;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9890
use RedBeanPHP\LabelMaker as LabelMaker;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9891
use RedBeanPHP\Finder as Finder;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9892
use RedBeanPHP\RedException\SQL as SQLException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9893
use RedBeanPHP\RedException\Security as Security;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9894
use RedBeanPHP\Logger as Logger;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9895
use RedBeanPHP\Logger\RDefault as RDefault;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9896
use RedBeanPHP\Logger\RDefault\Debug as Debug;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9897
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9898
use RedBeanPHP\SimpleModel as SimpleModel;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9899
use RedBeanPHP\SimpleModelHelper as SimpleModelHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9900
use RedBeanPHP\Adapter as Adapter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9901
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9902
use RedBeanPHP\RedException as RedException;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9903
use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9904
use RedBeanPHP\Driver\RPDO as RPDO;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
9905
9906
/**
9907
 * RedBean Facade
9908
 *
9909
 * Version Information
9910
 * RedBean Version @version 4.2
9911
 * 
9912
 * This class hides the object landscape of
9913
 * RedBeanPHP behind a single letter class providing
9914
 * almost all functionality with simple static calls.
9915
 *
9916
 * @file    RedBeanPHP/Facade.php
9917
 * @author  Gabor de Mooij and the RedBeanPHP Community
9918
 * @license BSD/GPLv2
9919
 *
9920
 * @copyright
9921
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
9922
 * This source file is subject to the BSD/GPLv2 License that is bundled
9923
 * with this source code in the file license.txt.
9924
 */
9925
class Facade
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
9926
{
9927
	/**
9928
	 * RedBeanPHP version constant.
9929
	 */
9930
	const C_REDBEANPHP_VERSION = '4.2';
9931
9932
	/**
9933
	 * @var ToolBox
9934
	 */
9935
	public static $toolbox;
9936
9937
	/**
9938
	 * @var OODB
9939
	 */
9940
	private static $redbean;
9941
9942
	/**
9943
	 * @var QueryWriter
9944
	 */
9945
	private static $writer;
9946
9947
	/**
9948
	 * @var DBAdapter
9949
	 */
9950
	private static $adapter;
9951
9952
	/**
9953
	 * @var AssociationManager
9954
	 */
9955
	private static $associationManager;
9956
9957
	/**
9958
	 * @var TagManager
9959
	 */
9960
	private static $tagManager;
9961
9962
	/**
9963
	 * @var DuplicationManager
9964
	 */
9965
	private static $duplicationManager;
9966
9967
	/**
9968
	 * @var LabelMaker
9969
	 */
9970
	private static $labelMaker;
9971
9972
	/**
9973
	 * @var Finder
9974
	 */
9975
	private static $finder;
9976
9977
	/**
9978
	 * @var Logger
9979
	 */
9980
	private static $logger;
0 ignored issues
show
Unused Code introduced by
The property $logger is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
9981
9982
	/**
9983
	 * @var array
9984
	 */
9985
	private static $plugins = array();
9986
9987
	/**
9988
	 * @var string
9989
	 */
9990
	private static $exportCaseStyle = 'default';
9991
	
9992
	/**
9993
	 * Not in use (backward compatibility SQLHelper)
9994
	 */
9995
	public static $f;
9996
9997
	/**
9998
	 * @var string
9999
	 */
10000
	public static $currentDB = '';
10001
10002
	/**
10003
	 * @var array
10004
	 */
10005
	public static $toolboxes = array();
10006
10007
	/**
10008
	 * Internal Query function, executes the desired query. Used by
10009
	 * all facade query functions. This keeps things DRY.
10010
	 *
10011
	 * @throws SQL
10012
	 *
10013
	 * @param string $method   desired query method (i.e. 'cell', 'col', 'exec' etc..)
10014
	 * @param string $sql      the sql you want to execute
10015
	 * @param array  $bindings array of values to be bound to query statement
10016
	 *
10017
	 * @return array
10018
	 */
10019
	private static function query( $method, $sql, $bindings )
10020
	{
10021
		if ( !self::$redbean->isFrozen() ) {
10022
			try {
10023
				$rs = Facade::$adapter->$method( $sql, $bindings );
10024
			} catch ( SQLException $exception ) {
10025
				if ( self::$writer->sqlStateIn( $exception->getSQLState(),
10026
					array(
10027
						QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
10028
						QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
10029
					)
10030
				) {
10031
					return ( $method === 'getCell' ) ? NULL : array();
10032
				} else {
10033
					throw $exception;
10034
				}
10035
			}
10036
10037
			return $rs;
10038
		} else {
10039
			return Facade::$adapter->$method( $sql, $bindings );
10040
		}
10041
	}
10042
10043
	/**
10044
	 * Returns the RedBeanPHP version string.
10045
	 * The RedBeanPHP version string always has the same format "X.Y"
10046
	 * where X is the major version number and Y is the minor version number.
10047
	 * Point releases are not mentioned in the version string.
10048
	 *
10049
	 * @return string
10050
	 */
10051
	public static function getVersion()
10052
	{
10053
		return self::C_REDBEANPHP_VERSION;
10054
	}
10055
10056
	/**
10057
	 * Tests the connection.
10058
	 * Returns TRUE if connection has been established and
10059
	 * FALSE otherwise.
10060
	 *
10061
	 * @return boolean
10062
	 */
10063
	public static function testConnection()
10064
	{
10065
		if ( !isset( self::$adapter ) ) return FALSE;
10066
10067
		$database = self::$adapter->getDatabase();
10068
		try {
10069
			@$database->connect();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
10070
		} catch ( \Exception $e ) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
10071
		return $database->isConnected();
10072
	}
10073
10074
	/**
10075
	 * Kickstarts redbean for you. This method should be called before you start using
10076
	 * RedBean. The Setup() method can be called without any arguments, in this case it will
10077
	 * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems).
10078
	 *
10079
	 * @param string  $dsn      Database connection string
10080
	 * @param string  $username Username for database
10081
	 * @param string  $password Password for database
10082
	 * @param boolean $frozen   TRUE if you want to setup in frozen mode
10083
	 *
10084
	 * @return ToolBox
10085
	 */
10086
	public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE )
10087
	{
10088
		if ( is_null( $dsn ) ) {
10089
			$dsn = 'sqlite:/' . sys_get_temp_dir() . '/red.db';
10090
		}
10091
10092
		self::addDatabase( 'default', $dsn, $username, $password, $frozen );
10093
		self::selectDatabase( 'default' );
10094
10095
		return self::$toolbox;
10096
	}
10097
10098
	/**
10099
	 * Toggles Narrow Field Mode.
10100
	 * See documentation in QueryWriter.
10101
	 *
10102
	 * @param boolean $mode TRUE = Narrow Field Mode
10103
	 *
10104
	 * @return void
10105
	 */
10106
	public static function setNarrowFieldMode( $mode )
10107
	{
10108
		AQueryWriter::setNarrowFieldMode( $mode );
10109
	}
10110
10111
	/**
10112
	 * Starts a transaction within a closure (or other valid callback).
10113
	 * If an\Exception is thrown inside, the operation is automatically rolled back.
10114
	 * If no\Exception happens, it commits automatically.
10115
	 * It also supports (simulated) nested transactions (that is useful when
10116
	 * you have many methods that needs transactions but are unaware of
10117
	 * each other).
10118
	 * ex:
10119
	 *        $from = 1;
10120
	 *        $to = 2;
10121
	 *        $amount = 300;
10122
	 *
10123
	 *        R::transaction(function() use($from, $to, $amount)
10124
	 *        {
10125
	 *            $accountFrom = R::load('account', $from);
10126
	 *            $accountTo = R::load('account', $to);
10127
	 *
10128
	 *            $accountFrom->money -= $amount;
10129
	 *            $accountTo->money += $amount;
10130
	 *
10131
	 *            R::store($accountFrom);
10132
	 *            R::store($accountTo);
10133
	 *      });
10134
	 *
10135
	 * @param callable $callback Closure (or other callable) with the transaction logic
10136
	 *
10137
	 * @throws Security
10138
	 *
10139
	 * @return mixed
10140
	 *
10141
	 */
10142
	public static function transaction( $callback )
10143
	{
10144
		if ( !is_callable( $callback ) ) {
10145
			throw new RedException( 'R::transaction needs a valid callback.' );
10146
		}
10147
10148
		static $depth = 0;
10149
		$result = null;
10150
		try {
10151
			if ( $depth == 0 ) {
10152
				self::begin();
10153
			}
10154
			$depth++;
10155
			$result = call_user_func( $callback ); //maintain 5.2 compatibility
10156
			$depth--;
10157
			if ( $depth == 0 ) {
10158
				self::commit();
10159
			}
10160
		} catch (\Exception $exception ) {
10161
			$depth--;
10162
			if ( $depth == 0 ) {
10163
				self::rollback();
10164
			}
10165
			throw $exception;
10166
		}
10167
		return $result;
10168
	}
10169
10170
	/**
10171
	 * Adds a database to the facade, afterwards you can select the database using
10172
	 * selectDatabase($key), where $key is the name you assigned to this database.
10173
	 *
10174
	 * Usage:
10175
	 *
10176
	 * R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' );
10177
	 * R::selectDatabase( 'database-1' ); //to select database again
10178
	 *
10179
	 * This method allows you to dynamically add (and select) new databases
10180
	 * to the facade. Adding a database with the same key will cause an exception.
10181
	 *
10182
	 * @param string      $key    ID for the database
10183
	 * @param string      $dsn    DSN for the database
10184
	 * @param string      $user   User for connection
10185
	 * @param NULL|string $pass   Password for connection
10186
	 * @param bool        $frozen Whether this database is frozen or not
10187
	 *
10188
	 * @return void
10189
	 */
10190
	public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE )
10191
	{
10192
		if ( isset( self::$toolboxes[$key] ) ) {
10193
			throw new RedException( 'A database has already be specified for this key.' );
10194
		}
10195
10196
		if ( is_object($dsn) ) {
10197
			$db  = new RPDO( $dsn );
10198
			$dbType = $db->getDatabaseType();
10199
		} else {
10200
			$db = new RPDO( $dsn, $user, $pass, TRUE );
0 ignored issues
show
Unused Code introduced by
The call to RPDO::__construct() 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...
10201
			$dbType = substr( $dsn, 0, strpos( $dsn, ':' ) );
10202
		}
10203
10204
		$adapter = new DBAdapter( $db );
10205
10206
		$writers     = array(
10207
                    'pgsql'  => 'PostgreSQL',
10208
                    'sqlite' => 'SQLiteT',
10209
                    'cubrid' => 'CUBRID',
10210
                    'mysql'  => 'MySQL',
10211
                    'sqlsrv' => 'SQLServer',
10212
                  );
10213
10214
		$wkey = trim( strtolower( $dbType ) );
10215
		if ( !isset( $writers[$wkey] ) ) trigger_error( 'Unsupported DSN: '.$wkey );
10216
		$writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey];
10217
		$writer      = new $writerClass( $adapter );
10218
		$redbean     = new OODB( $writer, $frozen );
10219
10220
		self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer );
10221
	}
10222
10223
	/**
10224
	 * Selects a different database for the Facade to work with.
10225
	 * If you use the R::setup() you don't need this method. This method is meant
10226
	 * for multiple database setups. This method selects the database identified by the
10227
	 * database ID ($key). Use addDatabase() to add a new database, which in turn
10228
	 * can be selected using selectDatabase(). If you use R::setup(), the resulting
10229
	 * database will be stored under key 'default', to switch (back) to this database
10230
	 * use R::selectDatabase( 'default' ). This method returns TRUE if the database has been
10231
	 * switched and FALSE otherwise (for instance if you already using the specified database).
10232
	 *
10233
	 * @param  string $key Key of the database to select
10234
	 *
10235
	 * @return boolean
10236
	 */
10237
	public static function selectDatabase( $key )
10238
	{
10239
		if ( self::$currentDB === $key ) {
10240
			return FALSE;
10241
		}
10242
10243
		self::configureFacadeWithToolbox( self::$toolboxes[$key] );
10244
		self::$currentDB = $key;
10245
10246
		return TRUE;
10247
	}
10248
10249
	/**
10250
	 * Toggles DEBUG mode.
10251
	 * In Debug mode all SQL that happens under the hood will
10252
	 * be printed to the screen or logged by provided logger.
10253
	 * If no database connection has been configured using R::setup() or
10254
	 * R::selectDatabase() this method will throw an exception.
10255
	 * Returns the attached logger instance.
10256
	 *
10257
	 * @param boolean $tf
10258
	 * @param integer $mode (0 = to STDOUT, 1 = to ARRAY)
10259
	 *
10260
	 * @throws Security
10261
	 *
10262
	 * @return Logger\RDefault
10263
	 */
10264
	public static function debug( $tf = TRUE, $mode = 0 )
10265
	{
10266
		if ($mode > 1) {
10267
			$mode -= 2;
10268
			$logger = new Debug;
10269
		} else {
10270
			$logger = new RDefault;
10271
		}
10272
10273
		if ( !isset( self::$adapter ) ) {
10274
			throw new RedException( 'Use R::setup() first.' );
10275
		}
10276
		$logger->setMode($mode);
10277
		self::$adapter->getDatabase()->setDebugMode( $tf, $logger );
0 ignored issues
show
Unused Code introduced by
The call to Driver::setDebugMode() has too many arguments starting with $logger.

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...
10278
10279
		return $logger;
10280
	}
10281
10282
	/**
10283
	 * Turns on the fancy debugger.
10284
	 */
10285
	public static function fancyDebug( $toggle )
10286
	{
10287
		self::debug( $toggle, 2 );
10288
	}
10289
10290
	/**
10291
	* Inspects the database schema. If you pass the type of a bean this
10292
	* method will return the fields of its table in the database.
10293
	* The keys of this array will be the field names and the values will be
10294
	* the column types used to store their values.
10295
	* If no type is passed, this method returns a list of all tables in the database.
10296
	*
10297
	* @param string $type Type of bean (i.e. table) you want to inspect
10298
	*
10299
	* @return array
10300
	*/
10301
	public static function inspect( $type = NULL )
10302
	{
10303
		return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type );
10304
	}
10305
10306
	/**
10307
	 * Stores a bean in the database. This method takes a
10308
	 * OODBBean Bean Object $bean and stores it
10309
	 * in the database. If the database schema is not compatible
10310
	 * with this bean and RedBean runs in fluid mode the schema
10311
	 * will be altered to store the bean correctly.
10312
	 * If the database schema is not compatible with this bean and
10313
	 * RedBean runs in frozen mode it will throw an exception.
10314
	 * This function returns the primary key ID of the inserted
10315
	 * bean.
10316
	 *
10317
	 * The return value is an integer if possible. If it is not possible to
10318
	 * represent the value as an integer a string will be returned.
10319
	 *
10320
	 * @param OODBBean|SimpleModel $bean bean to store
10321
	 *
10322
	 * @return integer|string
10323
	 *
10324
	 * @throws Security
10325
	 */
10326
	public static function store( $bean )
10327
	{
10328
		return self::$redbean->store( $bean );
10329
	}
10330
10331
	/**
10332
	 * Toggles fluid or frozen mode. In fluid mode the database
10333
	 * structure is adjusted to accomodate your objects. In frozen mode
10334
	 * this is not the case.
10335
	 *
10336
	 * You can also pass an array containing a selection of frozen types.
10337
	 * Let's call this chilly mode, it's just like fluid mode except that
10338
	 * certain types (i.e. tables) aren't touched.
10339
	 *
10340
	 * @param boolean|array $trueFalse
0 ignored issues
show
Bug introduced by
There is no parameter named $trueFalse. 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...
10341
	 */
10342
	public static function freeze( $tf = TRUE )
10343
	{
10344
		self::$redbean->freeze( $tf );
10345
	}
10346
10347
	/**
10348
	 * Loads multiple types of beans with the same ID.
10349
	 * This might look like a strange method, however it can be useful
10350
	 * for loading a one-to-one relation.
10351
	 *
10352
	 * Usage:
10353
	 * list($author, $bio) = R::load('author, bio', $id);
10354
	 *
10355
	 * @param string|array $types
10356
	 * @param mixed        $id
10357
	 *
10358
	 * @return OODBBean
10359
	 */
10360
	public static function loadMulti( $types, $id )
10361
	{
10362
		if ( is_string( $types ) ) {
10363
			$types = explode( ',', $types );
10364
		}
10365
10366
		if ( !is_array( $types ) ) {
10367
			return array();
10368
		}
10369
10370
		foreach ( $types as $k => $typeItem ) {
10371
			$types[$k] = self::$redbean->load( $typeItem, $id );
10372
		}
10373
10374
		return $types;
10375
	}
10376
10377
	/**
10378
	 * Loads a bean from the object database.
10379
	 * It searches for a OODBBean Bean Object in the
10380
	 * database. It does not matter how this bean has been stored.
10381
	 * RedBean uses the primary key ID $id and the string $type
10382
	 * to find the bean. The $type specifies what kind of bean you
10383
	 * are looking for; this is the same type as used with the
10384
	 * dispense() function. If RedBean finds the bean it will return
10385
	 * the OODB Bean object; if it cannot find the bean
10386
	 * RedBean will return a new bean of type $type and with
10387
	 * primary key ID 0. In the latter case it acts basically the
10388
	 * same as dispense().
10389
	 *
10390
	 * Important note:
10391
	 * If the bean cannot be found in the database a new bean of
10392
	 * the specified type will be generated and returned.
10393
	 *
10394
	 * @param string  $type type of bean you want to load
10395
	 * @param integer $id   ID of the bean you want to load
10396
	 *
10397
	 * @throws SQL
10398
	 *
10399
	 * @return OODBBean
10400
	 */
10401
	public static function load( $type, $id )
10402
	{
10403
		return self::$redbean->load( $type, $id );
10404
	}
10405
10406
	/**
10407
	 * Removes a bean from the database.
10408
	 * This function will remove the specified OODBBean
10409
	 * Bean Object from the database.
10410
	 *
10411
	 * This facade method also accepts a type-id combination,
10412
	 * in the latter case this method will attempt to load the specified bean
10413
	 * and THEN trash it.
10414
	 *
10415
	 * @param string|OODBBean|SimpleModel $bean bean you want to remove from database
0 ignored issues
show
Bug introduced by
There is no parameter named $bean. 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...
10416
	 * @param integer $id (optional)
10417
	 *
10418
	 * @return void
10419
	 */
10420
	public static function trash( $beanOrType, $id = NULL )
10421
	{
10422
		if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) );
10423
		return self::$redbean->trash( $beanOrType );
10424
	}
10425
10426
	/**
10427
	 * Dispenses a new RedBean OODB Bean for use with
10428
	 * the rest of the methods.
10429
	 *
10430
	 * @param string|array $typeOrBeanArray   type or bean array to import
10431
	 * @param integer      $number            number of beans to dispense
0 ignored issues
show
Bug introduced by
There is no parameter named $number. 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...
10432
	 * @param boolean	     $alwaysReturnArray if TRUE always returns the result as an array
10433
	 *
10434
	 * @return array|OODBBean
10435
	 *
10436
	 * @throws Security
10437
	 */
10438
	public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE )
10439
	{
10440
		if ( is_array($typeOrBeanArray) ) {
10441
10442
			if ( !isset( $typeOrBeanArray['_type'] ) ) {
10443
				$list = array();
10444
				foreach( $typeOrBeanArray as $beanArray ) if ( !( is_array( $beanArray ) && isset( $beanArray['_type'] ) ) ) throw new RedException( 'Invalid Array Bean' );
10445
				foreach( $typeOrBeanArray as $beanArray ) $list[] = self::dispense( $beanArray );
10446
				return $list;
10447
			}
10448
10449
			$import = $typeOrBeanArray;
10450
			$type = $import['_type'];
10451
			unset( $import['_type'] );
10452
		} else {
10453
			$type = $typeOrBeanArray;
10454
		}
10455
10456
		if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) {
10457
			throw new RedException( 'Invalid type: ' . $type );
10458
		}
10459
10460
		$beanOrBeans = self::$redbean->dispense( $type, $num, $alwaysReturnArray );
10461
10462
		if ( isset( $import ) ) {
10463
			$beanOrBeans->import( $import );
10464
		}
10465
10466
		return $beanOrBeans;
10467
	}
10468
10469
	/**
10470
	 * Takes a comma separated list of bean types
10471
	 * and dispenses these beans. For each type in the list
10472
	 * you can specify the number of beans to be dispensed.
10473
	 *
10474
	 * Usage:
10475
	 *
10476
	 * list($book, $page, $text) = R::dispenseAll('book,page,text');
10477
	 *
10478
	 * This will dispense a book, a page and a text. This way you can
10479
	 * quickly dispense beans of various types in just one line of code.
10480
	 *
10481
	 * Usage:
10482
	 *
10483
	 * list($book, $pages) = R::dispenseAll('book,page*100');
10484
	 *
10485
	 * This returns an array with a book bean and then another array
10486
	 * containing 100 page beans.
10487
	 *
10488
	 * @param string  $order      a description of the desired dispense order using the syntax above
10489
	 * @param boolean $onlyArrays return only arrays even if amount < 2
10490
	 *
10491
	 * @return array
10492
	 */
10493
	public static function dispenseAll( $order, $onlyArrays = FALSE )
10494
	{
10495
10496
		$list = array();
10497
10498
		foreach( explode( ',', $order ) as $order ) {
10499
			if ( strpos( $order, '*' ) !== false ) {
10500
				list( $type, $amount ) = explode( '*', $order );
10501
			} else {
10502
				$type   = $order;
10503
				$amount = 1;
10504
			}
10505
10506
			$list[] = self::dispense( $type, $amount, $onlyArrays );
10507
		}
10508
10509
		return $list;
10510
	}
10511
10512
	/**
10513
	 * Convience method. Tries to find beans of a certain type,
10514
	 * if no beans are found, it dispenses a bean of that type.
10515
	 *
10516
	 * @param  string $type     type of bean you are looking for
10517
	 * @param  string $sql      SQL code for finding the bean
10518
	 * @param  array  $bindings parameters to bind to SQL
10519
	 *
10520
	 * @return array
10521
	 */
10522
	public static function findOrDispense( $type, $sql = NULL, $bindings = array() )
10523
	{
10524
		return self::$finder->findOrDispense( $type, $sql, $bindings );
10525
	}
10526
10527
	/**
10528
	 * Finds a bean using a type and a where clause (SQL).
10529
	 * As with most Query tools in RedBean you can provide values to
10530
	 * be inserted in the SQL statement by populating the value
10531
	 * array parameter; you can either use the question mark notation
10532
	 * or the slot-notation (:keyname).
10533
	 *
10534
	 * @param string $type     type   the type of bean you are looking for
10535
	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10536
	 * @param array  $bindings values array of values to be bound to parameters in query
10537
	 *
10538
	 * @return array
10539
	 */
10540
	public static function find( $type, $sql = NULL, $bindings = array() )
10541
	{
10542
		return self::$finder->find( $type, $sql, $bindings );
10543
	}
10544
10545
	/**
10546
	 * @see Facade::find
10547
	 *      The findAll() method differs from the find() method in that it does
10548
	 *      not assume a WHERE-clause, so this is valid:
10549
	 *
10550
	 * R::findAll('person',' ORDER BY name DESC ');
10551
	 *
10552
	 * Your SQL does not have to start with a valid WHERE-clause condition.
10553
	 *
10554
	 * @param string $type     type   the type of bean you are looking for
10555
	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10556
	 * @param array  $bindings values array of values to be bound to parameters in query
10557
	 *
10558
	 * @return array
10559
	 */
10560
	public static function findAll( $type, $sql = NULL, $bindings = array() )
10561
	{
10562
		return self::$finder->find( $type, $sql, $bindings );
10563
	}
10564
10565
	/**
10566
	 * @see Facade::find
10567
	 * The variation also exports the beans (i.e. it returns arrays).
10568
	 *
10569
	 * @param string $type     type   the type of bean you are looking for
10570
	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10571
	 * @param array  $bindings values array of values to be bound to parameters in query
10572
	 *
10573
	 * @return array
10574
	 */
10575
	public static function findAndExport( $type, $sql = NULL, $bindings = array() )
10576
	{
10577
		return self::$finder->findAndExport( $type, $sql, $bindings );
10578
	}
10579
10580
	/**
10581
	 * @see Facade::find
10582
	 * This variation returns the first bean only.
10583
	 *
10584
	 * @param string $type     type   the type of bean you are looking for
10585
	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10586
	 * @param array  $bindings values array of values to be bound to parameters in query
10587
	 *
10588
	 * @return OODBBean
10589
	 */
10590
	public static function findOne( $type, $sql = NULL, $bindings = array() )
10591
	{
10592
		return self::$finder->findOne( $type, $sql, $bindings );
10593
	}
10594
10595
	/**
10596
	 * @see Facade::find
10597
	 * This variation returns the last bean only.
10598
	 *
10599
	 * @param string $type     type   the type of bean you are looking for
10600
	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10601
	 * @param array  $bindings values array of values to be bound to parameters in query
10602
	 *
10603
	 * @return OODBBean
10604
	 */
10605
	public static function findLast( $type, $sql = NULL, $bindings = array() )
10606
	{
10607
		return self::$finder->findLast( $type, $sql, $bindings );
10608
	}
10609
10610
	/**
10611
	 * Finds a bean collection.
10612
	 * Use this for large datasets.
10613
	 *
10614
	 * @param string $type     type   the type of bean you are looking for
10615
	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10616
	 * @param array  $bindings values array of values to be bound to parameters in query
10617
	 *
10618
	 * @return BeanCollection
10619
	 */
10620
	public static function findCollection( $type, $sql = NULL, $bindings = array() )
10621
	{
10622
		return self::$finder->findCollection( $type, $sql, $bindings );
10623
	}
10624
10625
	/**
10626
	 * Finds multiple types of beans at once and offers additional
10627
	 * remapping functionality. This is a very powerful yet complex function.
10628
	 * For details see Finder::findMulti().
10629
	 *
10630
	 * @see Finder::findMulti()
10631
	 *
10632
	 * @param array|string $types      a list of bean types to find
10633
	 * @param string|array $sqlOrArr   SQL query string or result set array
0 ignored issues
show
Bug introduced by
There is no parameter named $sqlOrArr. 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...
10634
	 * @param array        $bindings   SQL bindings
10635
	 * @param array        $remappings An array of remapping arrays containing closures
10636
	 *
10637
	 * @return array
10638
	 */
10639
	public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() )
10640
	{
10641
		return self::$finder->findMulti( $types, $sql, $bindings, $remappings );
10642
	}
10643
10644
	/**
10645
	 * Returns an array of beans. Pass a type and a series of ids and
10646
	 * this method will bring you the corresponding beans.
10647
	 *
10648
	 * important note: Because this method loads beans using the load()
10649
	 * function (but faster) it will return empty beans with ID 0 for
10650
	 * every bean that could not be located. The resulting beans will have the
10651
	 * passed IDs as their keys.
10652
	 *
10653
	 * @param string $type type of beans
10654
	 * @param array  $ids  ids to load
10655
	 *
10656
	 * @return array
10657
	 */
10658
	public static function batch( $type, $ids )
10659
	{
10660
		return self::$redbean->batch( $type, $ids );
10661
	}
10662
10663
	/**
10664
	 * @see Facade::batch
10665
	 *
10666
	 * Alias for batch(). Batch method is older but since we added so-called *All
10667
	 * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to
10668
	 * improve the consistency of the Facade API and also add an alias for batch() called
10669
	 * loadAll.
10670
	 *
10671
	 * @param string $type type of beans
10672
	 * @param array  $ids  ids to load
10673
	 *
10674
	 * @return array
10675
	 */
10676
	public static function loadAll( $type, $ids )
10677
	{
10678
		return self::$redbean->batch( $type, $ids );
10679
	}
10680
10681
	/**
10682
	 * Convenience function to execute Queries directly.
10683
	 * Executes SQL.
10684
	 *
10685
	 * @param string $sql       sql    SQL query to execute
10686
	 * @param array  $bindings  values a list of values to be bound to query parameters
10687
	 *
10688
	 * @return integer
10689
	 */
10690
	public static function exec( $sql, $bindings = array() )
10691
	{
10692
		return self::query( 'exec', $sql, $bindings );
10693
	}
10694
10695
	/**
10696
	 * Convenience function to execute Queries directly.
10697
	 * Executes SQL.
10698
	 *
10699
	 * @param string $sql       sql    SQL query to execute
10700
	 * @param array  $bindings  values a list of values to be bound to query parameters
10701
	 *
10702
	 * @return array
10703
	 */
10704
	public static function getAll( $sql, $bindings = array() )
10705
	{
10706
		return self::query( 'get', $sql, $bindings );
10707
	}
10708
10709
	/**
10710
	 * Convenience function to execute Queries directly.
10711
	 * Executes SQL.
10712
	 *
10713
	 * @param string $sql       sql    SQL query to execute
10714
	 * @param array  $bindings  values a list of values to be bound to query parameters
10715
	 *
10716
	 * @return string
10717
	 */
10718
	public static function getCell( $sql, $bindings = array() )
10719
	{
10720
		return self::query( 'getCell', $sql, $bindings );
10721
	}
10722
10723
	/**
10724
	 * Convenience function to execute Queries directly.
10725
	 * Executes SQL.
10726
	 *
10727
	 * @param string $sql       sql    SQL query to execute
10728
	 * @param array  $bindings  values a list of values to be bound to query parameters
10729
	 *
10730
	 * @return array
10731
	 */
10732
	public static function getRow( $sql, $bindings = array() )
10733
	{
10734
		return self::query( 'getRow', $sql, $bindings );
10735
	}
10736
10737
	/**
10738
	 * Convenience function to execute Queries directly.
10739
	 * Executes SQL.
10740
	 *
10741
	 * @param string $sql       sql    SQL query to execute
10742
	 * @param array  $bindings  values a list of values to be bound to query parameters
10743
	 *
10744
	 * @return array
10745
	 */
10746
	public static function getCol( $sql, $bindings = array() )
10747
	{
10748
		return self::query( 'getCol', $sql, $bindings );
10749
	}
10750
10751
	/**
10752
	 * Convenience function to execute Queries directly.
10753
	 * Executes SQL.
10754
	 * Results will be returned as an associative array. The first
10755
	 * column in the select clause will be used for the keys in this array and
10756
	 * the second column will be used for the values. If only one column is
10757
	 * selected in the query, both key and value of the array will have the
10758
	 * value of this field for each row.
10759
	 *
10760
	 * @param string $sql       sql    SQL query to execute
10761
	 * @param array  $bindings  values a list of values to be bound to query parameters
10762
	 *
10763
	 * @return array
10764
	 */
10765
	public static function getAssoc( $sql, $bindings = array() )
10766
	{
10767
		return self::query( 'getAssoc', $sql, $bindings );
10768
	}
10769
10770
	/**
10771
	 * Convenience function to execute Queries directly.
10772
	 * Executes SQL.
10773
	 * Results will be returned as an associative array indexed by the first
10774
	 * column in the select.
10775
	 *
10776
	 * @param string $sql       sql    SQL query to execute
10777
	 * @param array  $bindings  values a list of values to be bound to query parameters
10778
	 *
10779
	 * @return array
10780
	 */
10781
	public static function getAssocRow( $sql, $bindings = array() )
10782
	{
10783
		return self::query( 'getAssocRow', $sql, $bindings );
10784
	}
10785
10786
	/**
10787
	 * Returns the insert ID for databases that support/require this
10788
	 * functionality. Alias for R::getAdapter()->getInsertID().
10789
	 *
10790
	 * @return mixed
10791
	 */
10792
	public static function getInsertID()
10793
	{
10794
		return self::$adapter->getInsertID();
10795
	}
10796
10797
	/**
10798
	 * Makes a copy of a bean. This method makes a deep copy
10799
	 * of the bean.The copy will have the following features.
10800
	 * - All beans in own-lists will be duplicated as well
10801
	 * - All references to shared beans will be copied but not the shared beans themselves
10802
	 * - All references to parent objects (_id fields) will be copied but not the parents themselves
10803
	 * In most cases this is the desired scenario for copying beans.
10804
	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
10805
	 * (i.e. one that already has been processed) the ID of the bean will be returned.
10806
	 * This should not happen though.
10807
	 *
10808
	 * Note:
10809
	 * This function does a reflectional database query so it may be slow.
10810
	 *
10811
	 * @deprecated
10812
	 * This function is deprecated in favour of R::duplicate().
10813
	 * This function has a confusing method signature, the R::duplicate() function
10814
	 * only accepts two arguments: bean and filters.
10815
	 *
10816
	 * @param OODBBean $bean  bean to be copied
10817
	 * @param array    $trail for internal usage, pass array()
10818
	 * @param boolean  $pid   for internal usage
10819
	 * @param array	   $white white list filter with bean types to duplicate
0 ignored issues
show
Bug introduced by
There is no parameter named $white. 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...
10820
	 *
10821
	 * @return array
10822
	 */
10823
	public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() )
10824
	{
10825
		self::$duplicationManager->setFilters( $filters );
10826
		return self::$duplicationManager->dup( $bean, $trail, $pid );
10827
	}
10828
10829
	/**
10830
	 * Makes a deep copy of a bean. This method makes a deep copy
10831
	 * of the bean.The copy will have the following:
10832
	 *
10833
	 * - All beans in own-lists will be duplicated as well
10834
	 * - All references to shared beans will be copied but not the shared beans themselves
10835
	 * - All references to parent objects (_id fields) will be copied but not the parents themselves
10836
	 *
10837
	 * In most cases this is the desired scenario for copying beans.
10838
	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
10839
	 * (i.e. one that already has been processed) the ID of the bean will be returned.
10840
	 * This should not happen though.
10841
	 *
10842
	 * Note:
10843
	 * This function does a reflectional database query so it may be slow.
10844
	 *
10845
	 * Note:
10846
	 * This is a simplified version of the deprecated R::dup() function.
10847
	 *
10848
	 * @param OODBBean $bean  bean to be copied
10849
	 * @param array	   $white white list filter with bean types to duplicate
0 ignored issues
show
Bug introduced by
There is no parameter named $white. 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...
10850
	 *
10851
	 * @return array
10852
	 */
10853
	public static function duplicate( $bean, $filters = array() )
10854
	{
10855
		return self::dup( $bean, array(), FALSE, $filters );
0 ignored issues
show
Deprecated Code introduced by
The method RedBeanPHP\Facade::dup() has been deprecated with message: This function is deprecated in favour of R::duplicate().
This function has a confusing method signature, the R::duplicate() function
only accepts two arguments: bean and filters.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
10856
	}
10857
10858
	/**
10859
	 * Exports a collection of beans. Handy for XML/JSON exports with a
10860
	 * Javascript framework like Dojo or ExtJS.
10861
	 * What will be exported:
10862
	 * - contents of the bean
10863
	 * - all own bean lists (recursively)
10864
	 * - all shared beans (not THEIR own lists)
10865
	 *
10866
	 * @param    array|OODBBean $beans   beans to be exported
10867
	 * @param    boolean        $parents whether you want parent beans to be exported
10868
	 * @param    array          $filters whitelist of types
10869
	 *
10870
	 * @return    array
10871
	 */
10872
	public static function exportAll( $beans, $parents = FALSE, $filters = array())
10873
	{
10874
		return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle );
10875
	}
10876
10877
	/**
10878
	 * Selects case style for export.
10879
	 * This will determine the case style for the keys of exported beans (see exportAll).
10880
	 * The following options are accepted:
10881
	 *
10882
	 * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid )
10883
	 * 'camel'   Camel Case   (i.e. bookId isValid   )
10884
	 * 'dolphin' Dolphin Case (i.e. bookID isValid   ) Like CamelCase but ID is written all uppercase
10885
	 *
10886
	 * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different
10887
	 * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id.
10888
	 * Due to information loss this cannot be corrected. However if you might try
10889
	 * DolphinCase for IDs it takes into account the exception concerning IDs.
10890
	 *
10891
	 * @param string $caseStyle case style identifier
10892
	 *
10893
	 * @return void
10894
	 */
10895
	public static function useExportCase( $caseStyle = 'default' )
10896
	{
10897
		if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' );
10898
		self::$exportCaseStyle = $caseStyle;
10899
	}
10900
10901
	/**
10902
	 * Converts a series of rows to beans.
10903
	 * This method converts a series of rows to beans.
10904
	 * The type of the desired output beans can be specified in the
10905
	 * first parameter. The second parameter is meant for the database
10906
	 * result rows.
10907
	 *
10908
	 * @param string $type type of beans to produce
10909
	 * @param array  $rows must contain an array of array
10910
	 *
10911
	 * @return array
10912
	 */
10913
	public static function convertToBeans( $type, $rows )
10914
	{
10915
		return self::$redbean->convertToBeans( $type, $rows );
10916
	}
10917
10918
	/**
10919
	 * Part of RedBeanPHP Tagging API.
10920
	 * Tests whether a bean has been associated with one ore more
10921
	 * of the listed tags. If the third parameter is TRUE this method
10922
	 * will return TRUE only if all tags that have been specified are indeed
10923
	 * associated with the given bean, otherwise FALSE.
10924
	 * If the third parameter is FALSE this
10925
	 * method will return TRUE if one of the tags matches, FALSE if none
10926
	 * match.
10927
	 *
10928
	 * @param  OODBBean $bean bean to check for tags
10929
	 * @param  array            $tags list of tags
10930
	 * @param  boolean          $all  whether they must all match or just some
10931
	 *
10932
	 * @return boolean
10933
	 */
10934
	public static function hasTag( $bean, $tags, $all = FALSE )
10935
	{
10936
		return self::$tagManager->hasTag( $bean, $tags, $all );
10937
	}
10938
10939
	/**
10940
	 * Part of RedBeanPHP Tagging API.
10941
	 * Removes all specified tags from the bean. The tags specified in
10942
	 * the second parameter will no longer be associated with the bean.
10943
	 *
10944
	 * @param  OODBBean $bean    tagged bean
10945
	 * @param  array            $tagList list of tags (names)
10946
	 *
10947
	 * @return void
10948
	 */
10949
	public static function untag( $bean, $tagList )
10950
	{
10951
		self::$tagManager->untag( $bean, $tagList );
10952
	}
10953
10954
	/**
10955
	 * Part of RedBeanPHP Tagging API.
10956
	 * Tags a bean or returns tags associated with a bean.
10957
	 * If $tagList is NULL or omitted this method will return a
10958
	 * comma separated list of tags associated with the bean provided.
10959
	 * If $tagList is a comma separated list (string) of tags all tags will
10960
	 * be associated with the bean.
10961
	 * You may also pass an array instead of a string.
10962
	 *
10963
	 * @param OODBBean $bean    bean
10964
	 * @param mixed            $tagList tags
10965
	 *
10966
	 * @return string
10967
	 */
10968
	public static function tag( OODBBean $bean, $tagList = NULL )
10969
	{
10970
		return self::$tagManager->tag( $bean, $tagList );
10971
	}
10972
10973
	/**
10974
	 * Part of RedBeanPHP Tagging API.
10975
	 * Adds tags to a bean.
10976
	 * If $tagList is a comma separated list of tags all tags will
10977
	 * be associated with the bean.
10978
	 * You may also pass an array instead of a string.
10979
	 *
10980
	 * @param OODBBean $bean    bean
10981
	 * @param array            $tagList list of tags to add to bean
10982
	 *
10983
	 * @return void
10984
	 */
10985
	public static function addTags( OODBBean $bean, $tagList )
10986
	{
10987
		self::$tagManager->addTags( $bean, $tagList );
10988
	}
10989
10990
	/**
10991
	 * Part of RedBeanPHP Tagging API.
10992
	 * Returns all beans that have been tagged with one of the tags given.
10993
	 *
10994
	 * @param string $beanType type of bean you are looking for
10995
	 * @param array  $tagList  list of tags to match
10996
	 * @param string $sql      additional SQL
10997
	 * @param array  $bindings bindings
10998
	 *
10999
	 * @return array
11000
	 */
11001
	public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
11002
	{
11003
		return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings );
11004
	}
11005
11006
	/**
11007
	 * Part of RedBeanPHP Tagging API.
11008
	 * Returns all beans that have been tagged with ALL of the tags given.
11009
	 *
11010
	 * @param string $beanType type of bean you are looking for
11011
	 * @param array  $tagList  list of tags to match
11012
	 * @param string $sql      additional SQL
11013
	 * @param array  $bindings bindings
11014
	 *
11015
	 * @return array
11016
	 */
11017
	public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
11018
	{
11019
		return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings );
11020
	}
11021
11022
	/**
11023
	 * Wipes all beans of type $beanType.
11024
	 *
11025
	 * @param string $beanType type of bean you want to destroy entirely
11026
	 *
11027
	 * @return boolean
11028
	 */
11029
	public static function wipe( $beanType )
11030
	{
11031
		return Facade::$redbean->wipe( $beanType );
11032
	}
11033
11034
	/**
11035
	 * Counts the number of beans of type $type.
11036
	 * This method accepts a second argument to modify the count-query.
11037
	 * A third argument can be used to provide bindings for the SQL snippet.
11038
	 *
11039
	 * @param string $type     type of bean we are looking for
11040
	 * @param string $addSQL   additional SQL snippet
11041
	 * @param array  $bindings parameters to bind to SQL
11042
	 *
11043
	 * @return integer
11044
	 *
11045
	 * @throws SQL
11046
	 */
11047
	public static function count( $type, $addSQL = '', $bindings = array() )
11048
	{
11049
		return Facade::$redbean->count( $type, $addSQL, $bindings );
11050
	}
11051
11052
	/**
11053
	 * Configures the facade, want to have a new Writer? A new Object Database or a new
11054
	 * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new
11055
	 * toolbox.
11056
	 *
11057
	 * @param ToolBox $tb toolbox
11058
	 *
11059
	 * @return ToolBox
11060
	 */
11061
	public static function configureFacadeWithToolbox( ToolBox $tb )
11062
	{
11063
		$oldTools                 = self::$toolbox;
11064
11065
		self::$toolbox            = $tb;
11066
11067
		self::$writer             = self::$toolbox->getWriter();
11068
		self::$adapter            = self::$toolbox->getDatabaseAdapter();
11069
		self::$redbean            = self::$toolbox->getRedBean();
11070
		self::$finder             = new Finder( self::$toolbox );
11071
11072
		self::$associationManager = new AssociationManager( self::$toolbox );
11073
11074
		self::$redbean->setAssociationManager( self::$associationManager );
11075
11076
		self::$labelMaker         = new LabelMaker( self::$toolbox );
11077
11078
		$helper                   = new SimpleModelHelper();
11079
11080
		$helper->attachEventListeners( self::$redbean );
11081
11082
		self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper );
11083
11084
		self::$duplicationManager = new DuplicationManager( self::$toolbox );
11085
		self::$tagManager         = new TagManager( self::$toolbox );
11086
11087
		return $oldTools;
11088
	}
11089
11090
	/**
11091
	 * Facade Convience method for adapter transaction system.
11092
	 * Begins a transaction.
11093
	 *
11094
	 * @return bool
11095
	 */
11096
	public static function begin()
11097
	{
11098
		if ( !self::$redbean->isFrozen() ) return FALSE;
11099
11100
		self::$adapter->startTransaction();
11101
11102
		return TRUE;
11103
	}
11104
11105
	/**
11106
	 * Facade Convience method for adapter transaction system.
11107
	 * Commits a transaction.
11108
	 *
11109
	 * @return bool
11110
	 */
11111
	public static function commit()
11112
	{
11113
		if ( !self::$redbean->isFrozen() ) return FALSE;
11114
11115
		self::$adapter->commit();
11116
11117
		return TRUE;
11118
	}
11119
11120
	/**
11121
	 * Facade Convience method for adapter transaction system.
11122
	 * Rolls back a transaction.
11123
	 *
11124
	 * @return bool
11125
	 */
11126
	public static function rollback()
11127
	{
11128
		if ( !self::$redbean->isFrozen() ) return FALSE;
11129
11130
		self::$adapter->rollback();
11131
11132
		return TRUE;
11133
	}
11134
11135
	/**
11136
	 * Returns a list of columns. Format of this array:
11137
	 * array( fieldname => type )
11138
	 * Note that this method only works in fluid mode because it might be
11139
	 * quite heavy on production servers!
11140
	 *
11141
	 * @param  string $table   name of the table (not type) you want to get columns of
11142
	 *
11143
	 * @return array
11144
	 */
11145
	public static function getColumns( $table )
11146
	{
11147
		return self::$writer->getColumns( $table );
11148
	}
11149
11150
	/**
11151
	 * Generates question mark slots for an array of values.
11152
	 *
11153
	 * @param array  $array    array to generate question mark slots for
11154
	 *
11155
	 * @return string
11156
	 */
11157
	public static function genSlots( $array, $template = NULL )
11158
	{
11159
		$str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : '';
11160
		return ( is_null( $template ) ||  $str === '' ) ? $str : sprintf( $template, $str );
11161
	}
11162
11163
	/**
11164
	 * Flattens a multi dimensional bindings array for use with genSlots().
11165
	 *
11166
	 * @param array $array array to flatten
11167
	 *
11168
	 * @return array
11169
	 */
11170
	public static function flat( $array, $result = array() )
11171
	{
11172
		foreach( $array as $value ) {
11173
			if ( is_array( $value ) ) $result = self::flat( $value, $result );
11174
			else $result[] = $value;
11175
		}
11176
		return $result;
11177
	}
11178
11179
	/**
11180
	 * Nukes the entire database.
11181
	 * This will remove all schema structures from the database.
11182
	 * Only works in fluid mode. Be careful with this method.
11183
	 *
11184
	 * @warning dangerous method, will remove all tables, columns etc.
11185
	 *
11186
	 * @return void
11187
	 */
11188
	public static function nuke()
11189
	{
11190
		if ( !self::$redbean->isFrozen() ) {
11191
			self::$writer->wipeAll();
11192
		}
11193
	}
11194
11195
	/**
11196
	 * Short hand function to store a set of beans at once, IDs will be
11197
	 * returned as an array. For information please consult the R::store()
11198
	 * function.
11199
	 * A loop saver.
11200
	 *
11201
	 * @param array $beans list of beans to be stored
11202
	 *
11203
	 * @return array
11204
	 */
11205
	public static function storeAll( $beans )
11206
	{
11207
		$ids = array();
11208
		foreach ( $beans as $bean ) {
11209
			$ids[] = self::store( $bean );
11210
		}
11211
11212
		return $ids;
11213
	}
11214
11215
	/**
11216
	 * Short hand function to trash a set of beans at once.
11217
	 * For information please consult the R::trash() function.
11218
	 * A loop saver.
11219
	 *
11220
	 * @param array $beans list of beans to be trashed
11221
	 *
11222
	 * @return void
11223
	 */
11224
	public static function trashAll( $beans )
11225
	{
11226
		foreach ( $beans as $bean ) {
11227
			self::trash( $bean );
11228
		}
11229
	}
11230
11231
	/**
11232
	 * Toggles Writer Cache.
11233
	 * Turns the Writer Cache on or off. The Writer Cache is a simple
11234
	 * query based caching system that may improve performance without the need
11235
	 * for cache management. This caching system will cache non-modifying queries
11236
	 * that are marked with special SQL comments. As soon as a non-marked query
11237
	 * gets executed the cache will be flushed. Only non-modifying select queries
11238
	 * have been marked therefore this mechanism is a rather safe way of caching, requiring
11239
	 * no explicit flushes or reloads. Of course this does not apply if you intend to test
11240
	 * or simulate concurrent querying.
11241
	 *
11242
	 * @param boolean $yesNo TRUE to enable cache, FALSE to disable cache
11243
	 *
11244
	 * @return void
11245
	 */
11246
	public static function useWriterCache( $yesNo )
11247
	{
11248
		self::getWriter()->setUseCache( $yesNo );
11249
	}
11250
11251
11252
	/**
11253
	 * A label is a bean with only an id, type and name property.
11254
	 * This function will dispense beans for all entries in the array. The
11255
	 * values of the array will be assigned to the name property of each
11256
	 * individual bean.
11257
	 *
11258
	 * @param string $type   type of beans you would like to have
11259
	 * @param array  $labels list of labels, names for each bean
11260
	 *
11261
	 * @return array
11262
	 */
11263
	public static function dispenseLabels( $type, $labels )
11264
	{
11265
		return self::$labelMaker->dispenseLabels( $type, $labels );
11266
	}
11267
11268
	/**
11269
	 * Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs.
11270
	 * Either returns a (newly created) bean respresenting the desired ENUM
11271
	 * value or returns a list of all enums for the type.
11272
	 *
11273
	 * To obtain (and add if necessary) an ENUM value:
11274
	 *
11275
	 * $tea->flavour = R::enum( 'flavour:apple' );
11276
	 *
11277
	 * Returns a bean of type 'flavour' with  name = apple.
11278
	 * This will add a bean with property name (set to APPLE) to the database
11279
	 * if it does not exist yet.
11280
	 *
11281
	 * To obtain all flavours:
11282
	 *
11283
	 * R::enum('flavour');
11284
	 *
11285
	 * To get a list of all flavour names:
11286
	 *
11287
	 * R::gatherLabels( R::enum( 'flavour' ) );
11288
	 *
11289
	 * @param string $enum either type or type-value
11290
	 *
11291
	 * @return array|OODBBean
11292
	 */
11293
	public static function enum( $enum )
11294
	{
11295
		return self::$labelMaker->enum( $enum );
11296
	}
11297
11298
	/**
11299
	 * Gathers labels from beans. This function loops through the beans,
11300
	 * collects the values of the name properties of each individual bean
11301
	 * and stores the names in a new array. The array then gets sorted using the
11302
	 * default sort function of PHP (sort).
11303
	 *
11304
	 * @param array $beans list of beans to loop
11305
	 *
11306
	 * @return array
11307
	 */
11308
	public static function gatherLabels( $beans )
11309
	{
11310
		return self::$labelMaker->gatherLabels( $beans );
11311
	}
11312
11313
	/**
11314
	 * Closes the database connection.
11315
	 *
11316
	 * @return void
11317
	 */
11318
	public static function close()
11319
	{
11320
		if ( isset( self::$adapter ) ) {
11321
			self::$adapter->close();
11322
		}
11323
	}
11324
11325
	/**
11326
	 * Simple convenience function, returns ISO date formatted representation
11327
	 * of $time.
11328
	 *
11329
	 * @param mixed $time UNIX timestamp
11330
	 *
11331
	 * @return string
11332
	 */
11333
	public static function isoDate( $time = NULL )
11334
	{
11335
		if ( !$time ) {
11336
			$time = time();
11337
		}
11338
11339
		return @date( 'Y-m-d', $time );
11340
	}
11341
11342
	/**
11343
	 * Simple convenience function, returns ISO date time
11344
	 * formatted representation
11345
	 * of $time.
11346
	 *
11347
	 * @param mixed $time UNIX timestamp
11348
	 *
11349
	 * @return string
11350
	 */
11351
	public static function isoDateTime( $time = NULL )
11352
	{
11353
		if ( !$time ) $time = time();
11354
11355
		return @date( 'Y-m-d H:i:s', $time );
11356
	}
11357
11358
	/**
11359
	 * Optional accessor for neat code.
11360
	 * Sets the database adapter you want to use.
11361
	 *
11362
	 * @param Adapter $adapter
11363
	 *
11364
	 * @return void
11365
	 */
11366
	public static function setDatabaseAdapter( Adapter $adapter )
11367
	{
11368
		self::$adapter = $adapter;
0 ignored issues
show
Documentation Bug introduced by
$adapter is of type object<RedBeanPHP\Adapter>, but the property $adapter was declared to be of type object<RedBeanPHP\Adapter\DBAdapter>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
11369
	}
11370
11371
	/**
11372
	 * Optional accessor for neat code.
11373
	 * Sets the database adapter you want to use.
11374
	 *
11375
	 * @param QueryWriter $writer
11376
	 *
11377
	 * @return void
11378
	 */
11379
	public static function setWriter( QueryWriter $writer )
11380
	{
11381
		self::$writer = $writer;
11382
	}
11383
11384
	/**
11385
	 * Optional accessor for neat code.
11386
	 * Sets the database adapter you want to use.
11387
	 *
11388
	 * @param OODB $redbean
11389
	 */
11390
	public static function setRedBean( OODB $redbean )
11391
	{
11392
		self::$redbean = $redbean;
11393
	}
11394
11395
	/**
11396
	 * Optional accessor for neat code.
11397
	 * Sets the database adapter you want to use.
11398
	 *
11399
	 * @return DBAdapter
11400
	 */
11401
	public static function getDatabaseAdapter()
11402
	{
11403
		return self::$adapter;
11404
	}
11405
11406
	/**
11407
	 * Returns the current duplication manager instance.
11408
	 *
11409
	 * @return DuplicationManager
11410
	 */
11411
	public static function getDuplicationManager()
11412
	{
11413
		return self::$duplicationManager;
11414
	}
11415
11416
	/**
11417
	 * Optional accessor for neat code.
11418
	 * Sets the database adapter you want to use.
11419
	 *
11420
	 * @return QueryWriter
11421
	 */
11422
	public static function getWriter()
11423
	{
11424
		return self::$writer;
11425
	}
11426
11427
	/**
11428
	 * Optional accessor for neat code.
11429
	 * Sets the database adapter you want to use.
11430
	 *
11431
	 * @return OODB
11432
	 */
11433
	public static function getRedBean()
11434
	{
11435
		return self::$redbean;
11436
	}
11437
11438
	/**
11439
	 * Returns the toolbox currently used by the facade.
11440
	 * To set the toolbox use R::setup() or R::configureFacadeWithToolbox().
11441
	 * To create a toolbox use Setup::kickstart(). Or create a manual
11442
	 * toolbox using the ToolBox class.
11443
	 *
11444
	 * @return ToolBox
11445
	 */
11446
	public static function getToolBox()
11447
	{
11448
		return self::$toolbox;
11449
	}
11450
11451
	/**
11452
	 * Mostly for internal use, but might be handy
11453
	 * for some users.
11454
	 * This returns all the components of the currently
11455
	 * selected toolbox.
11456
	 *
11457
	 * Returns the components in the following order:
11458
	 *
11459
	 * 0 - OODB instance (getRedBean())
11460
	 * 1 - Database Adapter
11461
	 * 2 - Query Writer
11462
	 * 3 - Toolbox itself
11463
	 *
11464
	 * @return array
11465
	 */
11466
	public static function getExtractedToolbox()
11467
	{
11468
		return array(
11469
			self::$redbean,
11470
			self::$adapter,
11471
			self::$writer,
11472
			self::$toolbox
11473
		);
11474
	}
11475
11476
	/**
11477
	 * Facade method for AQueryWriter::renameAssociation()
11478
	 *
11479
	 * @param string|array $from
11480
	 * @param string       $to
11481
	 *
11482
	 * @return void
11483
	 */
11484
	public static function renameAssociation( $from, $to = NULL )
11485
	{
11486
		AQueryWriter::renameAssociation( $from, $to );
11487
	}
11488
11489
	/**
11490
	 * Little helper method for Resty Bean Can server and others.
11491
	 * Takes an array of beans and exports each bean.
11492
	 * Unlike exportAll this method does not recurse into own lists
11493
	 * and shared lists, the beans are exported as-is, only loaded lists
11494
	 * are exported.
11495
	 *
11496
	 * @param array $beans beans
11497
	 *
11498
	 * @return array
11499
	 */
11500
	public static function beansToArray( $beans )
11501
	{
11502
		$list = array();
11503
		foreach( $beans as $bean ) {
11504
			$list[] = $bean->export();
11505
		}
11506
		return $list;
11507
	}
11508
	
11509
	/**
11510
	 * Sets the error mode for FUSE.
11511
	 * What to do if a FUSE model method does not exist?
11512
	 * You can set the following options:
11513
	 *
11514
	 * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
11515
	 * OODBBean::C_ERR_LOG, logs the incident using error_log
11516
	 * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
11517
	 * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
11518
	 * OODBBean::C_ERR_EXCEPTION, throws an exception
11519
	 * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
11520
	 * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
11521
	 * 
11522
	 * Custom handler method signature: handler( array (
11523
	 * 	'message' => string
11524
	 * 	'bean' => OODBBean
11525
	 * 	'method' => string
11526
	 * ) )
11527
	 *
11528
	 * This method returns the old mode and handler as an array.
11529
	 *
11530
	 * @param integer       $mode mode
11531
	 * @param callable|NULL $func custom handler
11532
	 * 
11533
	 * @return array
11534
	 */
11535
	public static function setErrorHandlingFUSE( $mode, $func = NULL )
11536
	{
11537
		return OODBBean::setErrorHandlingFUSE( $mode, $func );
11538
	}
11539
11540
	/**
11541
	 * Simple but effective debug function.
11542
	 * Given a one or more beans this method will
11543
	 * return an array containing first part of the string
11544
	 * representation of each item in the array.
11545
	 *
11546
	 * @param OODBBean|array $data either a bean or an array of beans
11547
	 *
11548
	 * @return array
11549
	 *
11550
	 */
11551
	public static function dump( $data )
11552
	{
11553
		$array = array();
11554
11555
		if ( $data instanceof OODBBean ) {
11556
			$str = strval( $data );
11557
			if (strlen($str) > 35) {
11558
				$beanStr = substr( $str, 0, 35 ).'... ';
11559
			} else {
11560
				$beanStr = $str;
11561
			}
11562
			return $beanStr;
11563
		}
11564
11565
		if ( is_array( $data ) ) {
11566
			foreach( $data as $key => $item ) {
11567
				$array[$key] = self::dump( $item );
11568
			}
11569
		}
11570
11571
		return $array;
11572
	}
11573
11574
	/**
11575
	 * Binds an SQL function to a column.
11576
	 * This method can be used to setup a decode/encode scheme or
11577
	 * perform UUID insertion. This method is especially useful for handling
11578
	 * MySQL spatial columns, because they need to be processed first using
11579
	 * the asText/GeomFromText functions.
11580
	 *
11581
	 * Example:
11582
	 *
11583
	 * R::bindFunc( 'read', 'location.point', 'asText' );
11584
	 * R::bindFunc( 'write', 'location.point', 'GeomFromText' );
11585
	 *
11586
	 * Passing NULL as the function will reset (clear) the function
11587
	 * for this column/mode.
11588
	 *
11589
	 * @param string $mode (read or write)
11590
	 * @param string $field
11591
	 * @param string $function
11592
	 *
11593
	 */
11594
	public static function bindFunc( $mode, $field, $function )
11595
	{
11596
		self::$redbean->bindFunc( $mode, $field, $function );
11597
	}
11598
11599
	/**
11600
	 * Sets global aliases.
11601
	 *
11602
	 * @param array $list
11603
	 *
11604
	 * @return void
11605
	 */
11606
	public static function aliases( $list )
11607
	{
11608
		OODBBean::aliases( $list );
11609
	}
11610
11611
	/**
11612
	 * Tries to find a bean matching a certain type and
11613
	 * criteria set. If no beans are found a new bean
11614
	 * will be created, the criteria will be imported into this
11615
	 * bean and the bean will be stored and returned.
11616
	 * If multiple beans match the criteria only the first one
11617
	 * will be returned.
11618
	 *
11619
	 * @param string $type type of bean to search for
11620
	 * @param array  $like criteria set describing the bean to search for
11621
	 *
11622
	 * @return OODBBean
11623
	 */
11624
	public static function findOrCreate( $type, $like = array() )
11625
	{
11626
		return self::$finder->findOrCreate( $type, $like );
11627
	}
11628
11629
	/**
11630
	 * Tries to find beans matching the specified type and
11631
	 * criteria set.
11632
	 *
11633
	 * If the optional additional SQL snippet is a condition, it will
11634
	 * be glued to the rest of the query using the AND operator.
11635
	 *
11636
	 * @param string $type type of bean to search for
11637
	 * @param array  $like optional criteria set describing the bean to search for
11638
	 * @param string $sql  optional additional SQL for sorting
11639
	 *
11640
	 * @return array
11641
	 */
11642
	public static function findLike( $type, $like = array(), $sql = '' )
11643
	{
11644
		return self::$finder->findLike( $type, $like, $sql );
11645
	}
11646
11647
	/**
11648
	 * Starts logging queries.
11649
	 * Use this method to start logging SQL queries being
11650
	 * executed by the adapter.
11651
	 *
11652
	 * @note you cannot use R::debug and R::startLogging
11653
	 * at the same time because R::debug is essentially a
11654
	 * special kind of logging.
11655
	 *
11656
	 * @return void
11657
	 */
11658
	public static function startLogging()
11659
	{
11660
		self::debug( TRUE, RDefault::C_LOGGER_ARRAY );
11661
	}
11662
11663
	/**
11664
	 * Stops logging, comfortable method to stop logging of queries.
11665
	 *
11666
	 * @return void
11667
	 */
11668
	public static function stopLogging()
11669
	{
11670
		self::debug( FALSE );
11671
	}
11672
11673
	/**
11674
	 * Returns the log entries written after the startLogging.
11675
	 *
11676
	 * @return array
11677
	 */
11678
	public static function getLogs()
11679
	{
11680
		return self::getLogger()->getLogs();
11681
	}
11682
11683
	/**
11684
	 * Resets the Query counter.
11685
	 *
11686
	 * @return integer
11687
	 */
11688
	public static function resetQueryCount()
11689
	{
11690
		self::$adapter->getDatabase()->resetCounter();
11691
	}
11692
11693
	/**
11694
	 * Returns the number of SQL queries processed.
11695
	 *
11696
	 * @return integer
11697
	 */
11698
	public static function getQueryCount()
11699
	{
11700
		return self::$adapter->getDatabase()->getQueryCount();
11701
	}
11702
11703
	/**
11704
	 * Returns the current logger instance being used by the
11705
	 * database object.
11706
	 *
11707
	 * @return Logger
11708
	 */
11709
	public static function getLogger()
11710
	{
11711
		return self::$adapter->getDatabase()->getLogger();
11712
	}
11713
11714
	/**
11715
	 * Alias for setAutoResolve() method on OODBBean.
11716
	 * Enables or disables auto-resolving fetch types.
11717
	 * Auto-resolving aliased parent beans is convenient but can
11718
	 * be slower and can create infinite recursion if you
11719
	 * used aliases to break cyclic relations in your domain.
11720
	 *
11721
	 * @param boolean $automatic TRUE to enable automatic resolving aliased parents
11722
	 *
11723
	 * @return void
11724
	 */
11725
	public static function setAutoResolve( $automatic = TRUE )
11726
	{
11727
		OODBBean::setAutoResolve( (boolean) $automatic );
11728
	}
11729
11730
	/**
11731
	 * Dynamically extends the facade with a plugin.
11732
	 * Using this method you can register your plugin with the facade and then
11733
	 * use the plugin by invoking the name specified plugin name as a method on
11734
	 * the facade.
11735
	 *
11736
	 * Usage:
11737
	 *
11738
	 * R::ext( 'makeTea', function() { ... }  );
11739
	 *
11740
	 * Now you can use your makeTea plugin like this:
11741
	 *
11742
	 * R::makeTea();
11743
	 *
11744
	 * @param string   $pluginName name of the method to call the plugin
11745
	 * @param callable $callable   a PHP callable
11746
	 */
11747
	public static function ext( $pluginName, $callable )
11748
	{
11749
		if ( !ctype_alnum( $pluginName ) ) {
11750
			throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
11751
		}
11752
		self::$plugins[$pluginName] = $callable;
11753
	}
11754
11755
	/**
11756
	 * Call static for use with dynamic plugins. This magic method will
11757
	 * intercept static calls and route them to the specified plugin.
11758
	 *
11759
	 * @param string $pluginName name of the plugin
11760
	 * @param array  $params     list of arguments to pass to plugin method
11761
	 *
11762
	 * @return mixed
11763
	 */
11764
	public static function __callStatic( $pluginName, $params )
11765
	{
11766
		if ( !ctype_alnum( $pluginName) ) {
11767
			throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
11768
		}
11769
		if ( !isset( self::$plugins[$pluginName] ) ) {
11770
			throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' );
11771
		}
11772
		return call_user_func_array( self::$plugins[$pluginName], $params );
11773
	}
11774
}
11775
11776
}
11777
11778
namespace RedBeanPHP {
11779
11780
use RedBeanPHP\ToolBox as ToolBox;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
11781
use RedBeanPHP\AssociationManager as AssociationManager;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
11782
use RedBeanPHP\OODB as OODB;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
11783
use RedBeanPHP\OODBBean as OODBBean;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
11784
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
0 ignored issues
show
Coding Style introduced by
USE declarations must go after the first namespace declaration
Loading history...
11785
11786
/**
11787
 * Duplication Manager.
11788
 * Creates deep copies of beans.
11789
 *
11790
 * @file    RedBeanPHP/DuplicationManager.php
11791
 * @author  Gabor de Mooij and the RedBeanPHP Community
11792
 * @license BSD/GPLv2
11793
 *
11794
 * @copyright
11795
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
11796
 * This source file is subject to the BSD/GPLv2 License that is bundled
11797
 * with this source code in the file license.txt.
11798
 */
11799
class DuplicationManager
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
11800
{
11801
	/**
11802
	 * @var ToolBox
11803
	 */
11804
	protected $toolbox;
11805
11806
	/**
11807
	 * @var AssociationManager
11808
	 */
11809
	protected $associationManager;
11810
11811
	/**
11812
	 * @var OODB
11813
	 */
11814
	protected $redbean;
11815
11816
	/**
11817
	 * @var array
11818
	 */
11819
	protected $tables = array();
11820
11821
	/**
11822
	 * @var array
11823
	 */
11824
	protected $columns = array();
11825
11826
	/**
11827
	 * @var array
11828
	 */
11829
	protected $filters = array();
11830
11831
	/**
11832
	 * @var array
11833
	 */
11834
	protected $cacheTables = FALSE;
11835
11836
	/**
11837
	 * Copies the shared beans in a bean, i.e. all the sharedBean-lists.
11838
	 *
11839
	 * @param OODBBean $copy   target bean to copy lists to
11840
	 * @param string   $shared name of the shared list
11841
	 * @param array    $beans  array with shared beans to copy
11842
	 *
11843
	 * @return void
11844
	 */
11845
	private function copySharedBeans( OODBBean $copy, $shared, $beans )
11846
	{
11847
		$copy->$shared = array();
11848
11849
		foreach ( $beans as $subBean ) {
11850
			array_push( $copy->$shared, $subBean );
11851
		}
11852
	}
11853
11854
	/**
11855
	 * Copies the own beans in a bean, i.e. all the ownBean-lists.
11856
	 * Each bean in the own-list belongs exclusively to its owner so
11857
	 * we need to invoke the duplicate method again to duplicate each bean here.
11858
	 *
11859
	 * @param OODBBean $copy        target bean to copy lists to
11860
	 * @param string   $owned       name of the own list
11861
	 * @param array    $beans       array with shared beans to copy
11862
	 * @param array    $trail       array with former beans to detect recursion
11863
	 * @param boolean  $preserveIDs TRUE means preserve IDs, for export only
11864
	 *
11865
	 * @return void
11866
	 */
11867
	private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs )
11868
	{
11869
		$copy->$owned = array();
11870
		foreach ( $beans as $subBean ) {
11871
			array_push( $copy->$owned, $this->duplicate( $subBean, $trail, $preserveIDs ) );
11872
		}
11873
	}
11874
11875
	/**
11876
	 * Creates a copy of bean $bean and copies all primitive properties (not lists)
11877
	 * and the parents beans to the newly created bean. Also sets the ID of the bean
11878
	 * to 0.
11879
	 *
11880
	 * @param OODBBean $bean bean to copy
11881
	 *
11882
	 * @return OODBBean
11883
	 */
11884
	private function createCopy( OODBBean $bean )
11885
	{
11886
		$type = $bean->getMeta( 'type' );
11887
11888
		$copy = $this->redbean->dispense( $type );
11889
		$copy->setMeta( 'sys.dup-from-id', $bean->id );
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
11890
		$copy->setMeta( 'sys.old-id', $bean->id );
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
11891
		$copy->importFrom( $bean );
11892
		$copy->id = 0;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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...
11893
11894
		return $copy;
11895
	}
11896
11897
	/**
11898
	 * Generates a key from the bean type and its ID and determines if the bean
11899
	 * occurs in the trail, if not the bean will be added to the trail.
11900
	 * Returns TRUE if the bean occurs in the trail and FALSE otherwise.
11901
	 *
11902
	 * @param array    $trail list of former beans
11903
	 * @param OODBBean $bean  currently selected bean
11904
	 *
11905
	 * @return boolean
11906
	 */
11907
	private function inTrailOrAdd( &$trail, OODBBean $bean )
11908
	{
11909
		$type = $bean->getMeta( 'type' );
11910
		$key  = $type . $bean->getID();
11911
11912
		if ( isset( $trail[$key] ) ) {
11913
			return TRUE;
11914
		}
11915
11916
		$trail[$key] = $bean;
11917
11918
		return FALSE;
11919
	}
11920
11921
	/**
11922
	 * Given the type name of a bean this method returns the canonical names
11923
	 * of the own-list and the shared-list properties respectively.
11924
	 * Returns a list with two elements: name of the own-list, and name
11925
	 * of the shared list.
11926
	 *
11927
	 * @param string $typeName bean type name
11928
	 *
11929
	 * @return array
11930
	 */
11931
	private function getListNames( $typeName )
11932
	{
11933
		$owned  = 'own' . ucfirst( $typeName );
11934
		$shared = 'shared' . ucfirst( $typeName );
11935
11936
		return array( $owned, $shared );
11937
	}
11938
11939
	/**
11940
	 * Determines whether the bean has an own list based on
11941
	 * schema inspection from realtime schema or cache.
11942
	 *
11943
	 * @param string $type   bean type to get list for
11944
	 * @param string $target type of list you want to detect
11945
	 *
11946
	 * @return boolean
11947
	 */
11948
	protected function hasOwnList( $type, $target )
11949
	{
11950
		return isset( $this->columns[$target][$type . '_id'] );
11951
	}
11952
11953
	/**
11954
	 * Determines whether the bea has a shared list based on
11955
	 * schema inspection from realtime schema or cache.
11956
	 *
11957
	 * @param string $type   bean type to get list for
11958
	 * @param string $target type of list you are looking for
11959
	 *
11960
	 * @return boolean
11961
	 */
11962
	protected function hasSharedList( $type, $target )
11963
	{
11964
		return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables );
11965
	}
11966
11967
	/**
11968
	 * @see DuplicationManager::dup
11969
	 *
11970
	 * @param OODBBean $bean        bean to be copied
11971
	 * @param array    $trail       trail to prevent infinite loops
11972
	 * @param boolean  $preserveIDs preserve IDs
11973
	 *
11974
	 * @return OODBBean
11975
	 */
11976
	protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
11977
	{
11978
		if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean;
11979
11980
		$type = $bean->getMeta( 'type' );
11981
11982
		$copy = $this->createCopy( $bean );
11983
		foreach ( $this->tables as $table ) {
11984
11985
			if ( !empty( $this->filters ) ) {
11986
				if ( !in_array( $table, $this->filters ) ) continue;
11987
			}
11988
11989
			list( $owned, $shared ) = $this->getListNames( $table );
11990
11991
			if ( $this->hasSharedList( $type, $table ) ) {
11992
				if ( $beans = $bean->$shared ) {
11993
					$this->copySharedBeans( $copy, $shared, $beans );
11994
				}
11995
			} elseif ( $this->hasOwnList( $type, $table ) ) {
11996
				if ( $beans = $bean->$owned ) {
11997
					$this->copyOwnBeans( $copy, $owned, $beans, $trail, $preserveIDs );
11998
				}
11999
12000
				$copy->setMeta( 'sys.shadow.' . $owned, NULL );
12001
			}
12002
12003
			$copy->setMeta( 'sys.shadow.' . $shared, NULL );
12004
		}
12005
12006
		$copy->id = ( $preserveIDs ) ? $bean->id : $copy->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RedBeanPHP\OODBBean>. 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 id does not exist on object<RedBeanPHP\OODBBean>. 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...
12007
12008
		return $copy;
12009
	}
12010
12011
	/**
12012
	 * Constructor,
12013
	 * creates a new instance of DupManager.
12014
	 *
12015
	 * @param ToolBox $toolbox
12016
	 */
12017
	public function __construct( ToolBox $toolbox )
12018
	{
12019
		$this->toolbox            = $toolbox;
12020
		$this->redbean            = $toolbox->getRedBean();
12021
		$this->associationManager = $this->redbean->getAssociationManager();
12022
	}
12023
12024
	/**
12025
	 * Recursively turns the keys of an array into
12026
	 * camelCase.
12027
	 *
12028
	 * @param array   $array       array to camelize
12029
	 * @param boolean $dolphinMode whether you want the exception for IDs.
12030
	 *
12031
	 * @return array
12032
	 */
12033
	public function camelfy( $array, $dolphinMode = false ) {
12034
		$newArray = array();
12035
		foreach( $array as $key => $element ) {
12036
			$newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){
12037
				return strtoupper( $matches[1] );
12038
			}, $key);
12039
12040
			if ( $dolphinMode ) {
12041
				$newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey );
12042
			}
12043
12044
			$newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element;
12045
		}
12046
		return $newArray;
12047
	}
12048
12049
	/**
12050
	 * For better performance you can pass the tables in an array to this method.
12051
	 * If the tables are available the duplication manager will not query them so
12052
	 * this might be beneficial for performance.
12053
	 *
12054
	 * This method allows two array formats:
12055
	 *
12056
	 * array( TABLE1, TABLE2 ... )
12057
	 *
12058
	 * or
12059
	 *
12060
	 * array( TABLE1 => array( COLUMN1, COLUMN2 ... ) ... )
12061
	 *
12062
	 * @param array $tables a table cache array
12063
	 *
12064
	 * @return void
12065
	 */
12066
	public function setTables( $tables )
12067
	{
12068
		foreach ( $tables as $key => $value ) {
12069
			if ( is_numeric( $key ) ) {
12070
				$this->tables[] = $value;
12071
			} else {
12072
				$this->tables[]      = $key;
12073
				$this->columns[$key] = $value;
12074
			}
12075
		}
12076
12077
		$this->cacheTables = TRUE;
0 ignored issues
show
Documentation Bug introduced by
It seems like TRUE of type boolean is incompatible with the declared type array of property $cacheTables.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
12078
	}
12079
12080
	/**
12081
	 * Returns a schema array for cache.
12082
	 * You can use the return value of this method as a cache,
12083
	 * store it in RAM or on disk and pass it to setTables later.
12084
	 *
12085
	 * @return array
12086
	 */
12087
	public function getSchema()
12088
	{
12089
		return $this->columns;
12090
	}
12091
12092
	/**
12093
	 * Indicates whether you want the duplication manager to cache the database schema.
12094
	 * If this flag is set to TRUE the duplication manager will query the database schema
12095
	 * only once. Otherwise the duplicationmanager will, by default, query the schema
12096
	 * every time a duplication action is performed (dup()).
12097
	 *
12098
	 * @param boolean $yesNo TRUE to use caching, FALSE otherwise
12099
	 */
12100
	public function setCacheTables( $yesNo )
12101
	{
12102
		$this->cacheTables = $yesNo;
0 ignored issues
show
Documentation Bug introduced by
It seems like $yesNo of type boolean is incompatible with the declared type array of property $cacheTables.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
12103
	}
12104
12105
	/**
12106
	 * A filter array is an array with table names.
12107
	 * By setting a table filter you can make the duplication manager only take into account
12108
	 * certain bean types. Other bean types will be ignored when exporting or making a
12109
	 * deep copy. If no filters are set all types will be taking into account, this is
12110
	 * the default behavior.
12111
	 *
12112
	 * @param array $filters list of tables to be filtered
12113
	 */
12114
	public function setFilters( $filters )
12115
	{
12116
		if ( !is_array( $filters ) ) {
12117
			$filters = array( $filters );
12118
		}
12119
12120
		$this->filters = $filters;
12121
	}
12122
12123
	/**
12124
	 * Makes a copy of a bean. This method makes a deep copy
12125
	 * of the bean.The copy will have the following features.
12126
	 * - All beans in own-lists will be duplicated as well
12127
	 * - All references to shared beans will be copied but not the shared beans themselves
12128
	 * - All references to parent objects (_id fields) will be copied but not the parents themselves
12129
	 * In most cases this is the desired scenario for copying beans.
12130
	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
12131
	 * (i.e. one that already has been processed) the ID of the bean will be returned.
12132
	 * This should not happen though.
12133
	 *
12134
	 * Note:
12135
	 * This function does a reflectional database query so it may be slow.
12136
	 *
12137
	 * Note:
12138
	 * this function actually passes the arguments to a protected function called
12139
	 * duplicate() that does all the work. This method takes care of creating a clone
12140
	 * of the bean to avoid the bean getting tainted (triggering saving when storing it).
12141
	 *
12142
	 * @param OODBBean $bean        bean to be copied
12143
	 * @param array    $trail       for internal usage, pass array()
12144
	 * @param boolean  $preserveIDs for internal usage
12145
	 *
12146
	 * @return OODBBean
12147
	 */
12148
	public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
12149
	{
12150
		if ( !count( $this->tables ) ) {
12151
			$this->tables = $this->toolbox->getWriter()->getTables();
12152
		}
12153
12154
		if ( !count( $this->columns ) ) {
12155
			foreach ( $this->tables as $table ) {
12156
				$this->columns[$table] = $this->toolbox->getWriter()->getColumns( $table );
12157
			}
12158
		}
12159
12160
		$rs = $this->duplicate( ( clone $bean ), $trail, $preserveIDs );
12161
12162
		if ( !$this->cacheTables ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cacheTables of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
12163
			$this->tables  = array();
12164
			$this->columns = array();
12165
		}
12166
12167
		return $rs;
12168
	}
12169
12170
	/**
12171
	 * Exports a collection of beans recursively.
12172
	 * This method will export an array of beans in the first argument to a
12173
	 * set of arrays. This can be used to send JSON or XML representations
12174
	 * of bean hierarchies to the client.
12175
	 *
12176
	 * For every bean in the array this method will export:
12177
	 *
12178
	 * - contents of the bean
12179
	 * - all own bean lists (recursively)
12180
	 * - all shared beans (but not THEIR own lists)
12181
	 *
12182
	 * If the second parameter is set to TRUE the parents of the beans in the
12183
	 * array will be exported as well (but not THEIR parents).
12184
	 *
12185
	 * The third parameter can be used to provide a white-list array
12186
	 * for filtering. This is an array of strings representing type names,
12187
	 * only the type names in the filter list will be exported.
12188
	 *
12189
	 * The fourth parameter can be used to change the keys of the resulting
12190
	 * export arrays. The default mode is 'snake case' but this leaves the
12191
	 * keys as-is, because 'snake' is the default case style used by
12192
	 * RedBeanPHP in the database. You can set this to 'camel' for
12193
	 * camel cased keys or 'dolphin' (same as camelcase but id will be
12194
	 * converted to ID instead of Id).
12195
	 *
12196
	 * @param array|OODBBean $beans     beans to be exported
12197
	 * @param boolean        $parents   also export parents
12198
	 * @param array          $filters   only these types (whitelist)
12199
	 * @param string         $caseStyle case style identifier
12200
	 *
12201
	 * @return array
12202
	 */
12203
	public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake')
12204
	{
12205
		$array = array();
12206
12207
		if ( !is_array( $beans ) ) {
12208
			$beans = array( $beans );
12209
		}
12210
12211
		foreach ( $beans as $bean ) {
12212
			$this->setFilters( $filters );
12213
12214
			$duplicate = $this->dup( $bean, array(), TRUE );
12215
12216
			$array[]   = $duplicate->export( FALSE, $parents, FALSE, $filters );
12217
		}
12218
12219
		if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array );
12220
		if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, true );
12221
12222
		return $array;
12223
	}
12224
}
12225
}
12226
12227
namespace RedBeanPHP {
12228
12229
/**
12230
 * RedBean Plugin.
12231
 * Marker interface for plugins.
12232
 *
12233
 * @file    RedBean/Plugin.php
12234
 * @author  Gabor de Mooij and the RedBeanPHP Community
12235
 * @license BSD/GPLv2
12236
 *
12237
 * @copyright
12238
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
12239
 * This source file is subject to the BSD/GPLv2 License that is bundled
12240
 * with this source code in the file license.txt.
12241
 */
12242
interface Plugin
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
12243
{
12244
}
12245
12246
;
12247
}
12248
	namespace {
12249
12250
	//make some classes available for backward compatibility
12251
	class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {};
0 ignored issues
show
Coding Style Compatibility introduced by
Each interface must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
12252
12253
	if (!class_exists('R')) {
12254
		class R extends \RedBeanPHP\Facade{};
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
12255
	}
12256
12257
	
12258
12259
/**
12260
 * Support functions for RedBeanPHP.
12261
 * Additional convenience shortcut functions for RedBeanPHP.
12262
 *
12263
 * @file    RedBeanPHP/Functions.php
12264
 * @author  Gabor de Mooij and the RedBeanPHP community
12265
 * @license BSD/GPLv2
12266
 * 
12267
 * @copyright
12268
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
12269
 * This source file is subject to the BSD/GPLv2 License that is bundled
12270
 * with this source code in the file license.txt.
12271
 */
12272
12273
/**
12274
 * Convenience function for ENUM short syntax in queries.
12275
 *
12276
 * Usage:
12277
 *
12278
 * R::find( 'paint', ' color_id = ? ', [ EID('color:yellow') ] );
12279
 *
12280
 * If a function called EID() already exists you'll have to write this
12281
 * wrapper yourself ;)
12282
 *
12283
 * @param string $enumName enum code as you would pass to R::enum()
12284
 *
12285
 * @return mixed
12286
 */
12287
if (!function_exists('EID')) {
12288
12289
	function EID($enumName)
12290
	{
12291
		return \RedBeanPHP\Facade::enum( $enumName )->id;
12292
	}
12293
12294
}
12295
12296
/**
12297
 * Prints the result of R::dump() to the screen using
12298
 * print_r.
12299
 *
12300
 * @param mixed $data data to dump
12301
 *
12302
 * @return void
12303
 */
12304
if ( !function_exists( 'dump' ) ) {
12305
12306
	function dmp( $list )
12307
	{
12308
		print_r( \RedBeanPHP\Facade::dump( $list ) );
12309
	}
12310
}
12311
12312
/**
12313
 * Function alias for R::genSlots().
12314
 */
12315
if ( !function_exists( 'genslots' ) ) {
12316
12317
	function genslots( $slots, $tpl = NULL )
12318
	{
12319
		return \RedBeanPHP\Facade::genSlots( $slots, $tpl );
12320
	}
12321
}
12322
12323
/**
12324
 * Function alias for R::flat().
12325
 */
12326
if ( !function_exists( 'array_flatten' ) ) {
12327
12328
	function array_flatten( $array )
12329
	{
12330
		return \RedBeanPHP\Facade::flat( $array );
12331
	}
12332
}
12333
12334
12335
	}
12336