Completed
Branch master (14318b)
by Melquecedec
04:52
created
RedBean.php 1 patch
Indentation   +15228 added lines, -15228 removed lines patch added patch discarded remove patch
@@ -18,20 +18,20 @@  discard block
 block discarded – undo
18 18
  */
19 19
 interface Logger
20 20
 {
21
-	/**
22
-	 * A logger (for PDO or OCI driver) needs to implement the log method.
23
-	 * The log method will receive logging data. Note that the number of parameters is 0, this means
24
-	 * all parameters are optional and the number may vary. This way the logger can be used in a very
25
-	 * flexible way. Sometimes the logger is used to log a simple error message and in other
26
-	 * situations sql and bindings are passed.
27
-	 * The log method should be able to accept all kinds of parameters and data by using
28
-	 * functions like func_num_args/func_get_args.
29
-	 *
30
-	 * @param string $message, ...
31
-	 *
32
-	 * @return void
33
-	 */
34
-	public function log();
21
+    /**
22
+     * A logger (for PDO or OCI driver) needs to implement the log method.
23
+     * The log method will receive logging data. Note that the number of parameters is 0, this means
24
+     * all parameters are optional and the number may vary. This way the logger can be used in a very
25
+     * flexible way. Sometimes the logger is used to log a simple error message and in other
26
+     * situations sql and bindings are passed.
27
+     * The log method should be able to accept all kinds of parameters and data by using
28
+     * functions like func_num_args/func_get_args.
29
+     *
30
+     * @param string $message, ...
31
+     *
32
+     * @return void
33
+     */
34
+    public function log();
35 35
 }
36 36
 }
37 37
 
@@ -54,117 +54,117 @@  discard block
 block discarded – undo
54 54
  */
55 55
 class RDefault implements Logger
56 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 = var_export( $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>" . PHP_EOL;
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
-	}
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 = var_export( $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>" . PHP_EOL;
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 168
 }
169 169
 }
170 170
 
@@ -190,215 +190,215 @@  discard block
 block discarded – undo
190 190
  */
191 191
 class Debug extends RDefault implements Logger
192 192
 {
193
-	/**
194
-	 * @var integer
195
-	 */
196
-	protected $strLen = 40;
197
-
198
-	/**
199
-	 * @var boolean
200
-	 */
201
-	protected static $noCLI = FALSE;
202
-
203
-	/**
204
-	 * Toggles CLI override. By default debugging functions will
205
-	 * output differently based on PHP_SAPI values. This function
206
-	 * allows you to override the PHP_SAPI setting. If you set
207
-	 * this to TRUE, CLI output will be supressed in favour of
208
-	 * HTML output. So, to get HTML on the command line use
209
-	 * setOverrideCLIOutput( TRUE ).
210
-	 *
211
-	 * @param boolean $yesNo CLI-override setting flag
212
-	 *
213
-	 * @return void
214
-	 */
215
-	public static function setOverrideCLIOutput( $yesNo )
216
-	{
217
-		self::$noCLI = $yesNo;
218
-	}
219
-
220
-	/**
221
-	 * Writes a query for logging with all bindings / params filled
222
-	 * in.
223
-	 *
224
-	 * @param string $newSql      the query
225
-	 * @param array  $newBindings the bindings to process (key-value pairs)
226
-	 *
227
-	 * @return string
228
-	 */
229
-	protected function writeQuery( $newSql, $newBindings )
230
-	{
231
-		//avoid str_replace collisions: slot1 and slot10 (issue 407).
232
-		uksort( $newBindings, function( $a, $b ) {
233
-			return ( strlen( $b ) - strlen( $a ) );
234
-		} );
235
-
236
-		$newStr = $newSql;
237
-		foreach( $newBindings as $slot => $value ) {
238
-			if ( strpos( $slot, ':' ) === 0 ) {
239
-				$newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr );
240
-			}
241
-		}
242
-		return $newStr;
243
-	}
244
-
245
-	/**
246
-	 * Fills in a value of a binding and truncates the
247
-	 * resulting string if necessary.
248
-	 *
249
-	 * @param mixed $value bound value
250
-	 *
251
-	 * @return string
252
-	 */
253
-	protected function fillInValue( $value )
254
-	{
255
-		if ( is_null( $value ) ) $value = 'NULL';
256
-
257
-		$value = strval( $value );
258
-		if ( strlen( $value ) > ( $this->strLen ) ) {
259
-			$value = substr( $value, 0, ( $this->strLen ) ).'... ';
260
-		}
261
-
262
-		if ( !\RedBeanPHP\QueryWriter\AQueryWriter::canBeTreatedAsInt( $value ) && $value !== 'NULL') {
263
-			$value = '\''.$value.'\'';
264
-		}
265
-
266
-		return $value;
267
-	}
268
-
269
-	/**
270
-	 * Dependending on the current mode of operation,
271
-	 * this method will either log and output to STDIN or
272
-	 * just log.
273
-	 *
274
-	 * Depending on the value of constant PHP_SAPI this function
275
-	 * will format output for console or HTML.
276
-	 *
277
-	 * @param string $str string to log or output and log
278
-	 *
279
-	 * @return void
280
-	 */
281
-	protected function output( $str )
282
-	{
283
-		$this->logs[] = $str;
284
-		if ( !$this->mode ) {
285
-			$highlight = FALSE;
286
-			/* just a quick heuritsic to highlight schema changes */
287
-			if ( strpos( $str, 'CREATE' ) === 0
288
-			|| strpos( $str, 'ALTER' ) === 0
289
-			|| strpos( $str, 'DROP' ) === 0) {
290
-				$highlight = TRUE;
291
-			}
292
-			if (PHP_SAPI === 'cli' && !self::$noCLI) {
293
-				if ($highlight) echo "\e[91m";
294
-				echo $str, PHP_EOL;
295
-				echo "\e[39m";
296
-			} else {
297
-				if ($highlight) {
298
-					echo "<b style=\"color:red\">{$str}</b>";
299
-				} else {
300
-					echo $str;
301
-				}
302
-				echo '<br />';
303
-			}
304
-		}
305
-	}
306
-
307
-	/**
308
-	 * Normalizes the slots in an SQL string.
309
-	 * Replaces question mark slots with :slot1 :slot2 etc.
310
-	 *
311
-	 * @param string $sql sql to normalize
312
-	 *
313
-	 * @return string
314
-	 */
315
-	protected function normalizeSlots( $sql )
316
-	{
317
-		$newSql = $sql;
318
-		$i = 0;
319
-		while(strpos($newSql, '?') !== FALSE ){
320
-			$pos   = strpos( $newSql, '?' );
321
-			$slot  = ':slot'.$i;
322
-			$begin = substr( $newSql, 0, $pos );
323
-			$end   = substr( $newSql, $pos+1 );
324
-			if (PHP_SAPI === 'cli' && !self::$noCLI) {
325
-				$newSql = "{$begin}\e[32m{$slot}\e[39m{$end}";
326
-			} else {
327
-				$newSql = "{$begin}<b style=\"color:green\">$slot</b>{$end}";
328
-			}
329
-			$i ++;
330
-		}
331
-		return $newSql;
332
-	}
333
-
334
-	/**
335
-	 * Normalizes the bindings.
336
-	 * Replaces numeric binding keys with :slot1 :slot2 etc.
337
-	 *
338
-	 * @param array $bindings bindings to normalize
339
-	 *
340
-	 * @return array
341
-	 */
342
-	protected function normalizeBindings( $bindings )
343
-	{
344
-		$i = 0;
345
-		$newBindings = array();
346
-		foreach( $bindings as $key => $value ) {
347
-			if ( is_numeric($key) ) {
348
-				$newKey = ':slot'.$i;
349
-				$newBindings[$newKey] = $value;
350
-				$i++;
351
-			} else {
352
-				$newBindings[$key] = $value;
353
-			}
354
-		}
355
-		return $newBindings;
356
-	}
357
-
358
-	/**
359
-	 * Logger method.
360
-	 *
361
-	 * Takes a number of arguments tries to create
362
-	 * a proper debug log based on the available data.
363
-	 *
364
-	 * @return void
365
-	 */
366
-	public function log()
367
-	{
368
-		if ( func_num_args() < 1 ) return;
369
-
370
-		$sql = func_get_arg( 0 );
371
-
372
-		if ( func_num_args() < 2) {
373
-			$bindings = array();
374
-		} else {
375
-			$bindings = func_get_arg( 1 );
376
-		}
377
-
378
-		if ( !is_array( $bindings ) ) {
379
-			return $this->output( $sql );
380
-		}
381
-
382
-		$newSql = $this->normalizeSlots( $sql );
383
-		$newBindings = $this->normalizeBindings( $bindings );
384
-		$newStr = $this->writeQuery( $newSql, $newBindings );
385
-		$this->output( $newStr );
386
-	}
387
-
388
-	/**
389
-	 * Sets the max string length for the parameter output in
390
-	 * SQL queries. Set this value to a reasonable number to
391
-	 * keep you SQL queries readable.
392
-	 *
393
-	 * @param integer $len string length
394
-	 *
395
-	 * @return self
396
-	 */
397
-	public function setParamStringLength( $len = 20 )
398
-	{
399
-		$this->strLen = max(0, $len);
400
-		return $this;
401
-	}
193
+    /**
194
+     * @var integer
195
+     */
196
+    protected $strLen = 40;
197
+
198
+    /**
199
+     * @var boolean
200
+     */
201
+    protected static $noCLI = FALSE;
202
+
203
+    /**
204
+     * Toggles CLI override. By default debugging functions will
205
+     * output differently based on PHP_SAPI values. This function
206
+     * allows you to override the PHP_SAPI setting. If you set
207
+     * this to TRUE, CLI output will be supressed in favour of
208
+     * HTML output. So, to get HTML on the command line use
209
+     * setOverrideCLIOutput( TRUE ).
210
+     *
211
+     * @param boolean $yesNo CLI-override setting flag
212
+     *
213
+     * @return void
214
+     */
215
+    public static function setOverrideCLIOutput( $yesNo )
216
+    {
217
+        self::$noCLI = $yesNo;
218
+    }
219
+
220
+    /**
221
+     * Writes a query for logging with all bindings / params filled
222
+     * in.
223
+     *
224
+     * @param string $newSql      the query
225
+     * @param array  $newBindings the bindings to process (key-value pairs)
226
+     *
227
+     * @return string
228
+     */
229
+    protected function writeQuery( $newSql, $newBindings )
230
+    {
231
+        //avoid str_replace collisions: slot1 and slot10 (issue 407).
232
+        uksort( $newBindings, function( $a, $b ) {
233
+            return ( strlen( $b ) - strlen( $a ) );
234
+        } );
235
+
236
+        $newStr = $newSql;
237
+        foreach( $newBindings as $slot => $value ) {
238
+            if ( strpos( $slot, ':' ) === 0 ) {
239
+                $newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr );
240
+            }
241
+        }
242
+        return $newStr;
243
+    }
244
+
245
+    /**
246
+     * Fills in a value of a binding and truncates the
247
+     * resulting string if necessary.
248
+     *
249
+     * @param mixed $value bound value
250
+     *
251
+     * @return string
252
+     */
253
+    protected function fillInValue( $value )
254
+    {
255
+        if ( is_null( $value ) ) $value = 'NULL';
256
+
257
+        $value = strval( $value );
258
+        if ( strlen( $value ) > ( $this->strLen ) ) {
259
+            $value = substr( $value, 0, ( $this->strLen ) ).'... ';
260
+        }
261
+
262
+        if ( !\RedBeanPHP\QueryWriter\AQueryWriter::canBeTreatedAsInt( $value ) && $value !== 'NULL') {
263
+            $value = '\''.$value.'\'';
264
+        }
265
+
266
+        return $value;
267
+    }
268
+
269
+    /**
270
+     * Dependending on the current mode of operation,
271
+     * this method will either log and output to STDIN or
272
+     * just log.
273
+     *
274
+     * Depending on the value of constant PHP_SAPI this function
275
+     * will format output for console or HTML.
276
+     *
277
+     * @param string $str string to log or output and log
278
+     *
279
+     * @return void
280
+     */
281
+    protected function output( $str )
282
+    {
283
+        $this->logs[] = $str;
284
+        if ( !$this->mode ) {
285
+            $highlight = FALSE;
286
+            /* just a quick heuritsic to highlight schema changes */
287
+            if ( strpos( $str, 'CREATE' ) === 0
288
+            || strpos( $str, 'ALTER' ) === 0
289
+            || strpos( $str, 'DROP' ) === 0) {
290
+                $highlight = TRUE;
291
+            }
292
+            if (PHP_SAPI === 'cli' && !self::$noCLI) {
293
+                if ($highlight) echo "\e[91m";
294
+                echo $str, PHP_EOL;
295
+                echo "\e[39m";
296
+            } else {
297
+                if ($highlight) {
298
+                    echo "<b style=\"color:red\">{$str}</b>";
299
+                } else {
300
+                    echo $str;
301
+                }
302
+                echo '<br />';
303
+            }
304
+        }
305
+    }
306
+
307
+    /**
308
+     * Normalizes the slots in an SQL string.
309
+     * Replaces question mark slots with :slot1 :slot2 etc.
310
+     *
311
+     * @param string $sql sql to normalize
312
+     *
313
+     * @return string
314
+     */
315
+    protected function normalizeSlots( $sql )
316
+    {
317
+        $newSql = $sql;
318
+        $i = 0;
319
+        while(strpos($newSql, '?') !== FALSE ){
320
+            $pos   = strpos( $newSql, '?' );
321
+            $slot  = ':slot'.$i;
322
+            $begin = substr( $newSql, 0, $pos );
323
+            $end   = substr( $newSql, $pos+1 );
324
+            if (PHP_SAPI === 'cli' && !self::$noCLI) {
325
+                $newSql = "{$begin}\e[32m{$slot}\e[39m{$end}";
326
+            } else {
327
+                $newSql = "{$begin}<b style=\"color:green\">$slot</b>{$end}";
328
+            }
329
+            $i ++;
330
+        }
331
+        return $newSql;
332
+    }
333
+
334
+    /**
335
+     * Normalizes the bindings.
336
+     * Replaces numeric binding keys with :slot1 :slot2 etc.
337
+     *
338
+     * @param array $bindings bindings to normalize
339
+     *
340
+     * @return array
341
+     */
342
+    protected function normalizeBindings( $bindings )
343
+    {
344
+        $i = 0;
345
+        $newBindings = array();
346
+        foreach( $bindings as $key => $value ) {
347
+            if ( is_numeric($key) ) {
348
+                $newKey = ':slot'.$i;
349
+                $newBindings[$newKey] = $value;
350
+                $i++;
351
+            } else {
352
+                $newBindings[$key] = $value;
353
+            }
354
+        }
355
+        return $newBindings;
356
+    }
357
+
358
+    /**
359
+     * Logger method.
360
+     *
361
+     * Takes a number of arguments tries to create
362
+     * a proper debug log based on the available data.
363
+     *
364
+     * @return void
365
+     */
366
+    public function log()
367
+    {
368
+        if ( func_num_args() < 1 ) return;
369
+
370
+        $sql = func_get_arg( 0 );
371
+
372
+        if ( func_num_args() < 2) {
373
+            $bindings = array();
374
+        } else {
375
+            $bindings = func_get_arg( 1 );
376
+        }
377
+
378
+        if ( !is_array( $bindings ) ) {
379
+            return $this->output( $sql );
380
+        }
381
+
382
+        $newSql = $this->normalizeSlots( $sql );
383
+        $newBindings = $this->normalizeBindings( $bindings );
384
+        $newStr = $this->writeQuery( $newSql, $newBindings );
385
+        $this->output( $newStr );
386
+    }
387
+
388
+    /**
389
+     * Sets the max string length for the parameter output in
390
+     * SQL queries. Set this value to a reasonable number to
391
+     * keep you SQL queries readable.
392
+     *
393
+     * @param integer $len string length
394
+     *
395
+     * @return self
396
+     */
397
+    public function setParamStringLength( $len = 20 )
398
+    {
399
+        $this->strLen = max(0, $len);
400
+        return $this;
401
+    }
402 402
 }
403 403
 }
404 404
 
@@ -420,163 +420,163 @@  discard block
 block discarded – undo
420 420
  */
421 421
 interface Driver
422 422
 {
423
-	/**
424
-	 * Runs a query and fetches results as a multi dimensional array.
425
-	 *
426
-	 * @param string $sql      SQL query to execute
427
-	 * @param array  $bindings list of values to bind to SQL snippet
428
-	 *
429
-	 * @return array
430
-	 */
431
-	public function GetAll( $sql, $bindings = array() );
432
-
433
-	/**
434
-	 * Runs a query and fetches results as a column.
435
-	 *
436
-	 * @param string $sql      SQL query to execute
437
-	 * @param array  $bindings list of values to bind to SQL snippet
438
-	 *
439
-	 * @return array
440
-	 */
441
-	public function GetCol( $sql, $bindings = array() );
442
-
443
-	/**
444
-	 * Runs a query and returns results as a single cell.
445
-	 *
446
-	 * @param string $sql      SQL query to execute
447
-	 * @param array  $bindings list of values to bind to SQL snippet
448
-	 *
449
-	 * @return mixed
450
-	 */
451
-	public function GetOne( $sql, $bindings = array() );
452
-
453
-	/**
454
-	 * Runs a query and returns results as an associative array
455
-	 * indexed by the first column.
456
-	 *
457
-	 * @param string $sql      SQL query to execute
458
-	 * @param array  $bindings list of values to bind to SQL snippet
459
-	 *
460
-	 * @return mixed
461
-	 */
462
-	public function GetAssocRow( $sql, $bindings = array() );
463
-
464
-	/**
465
-	 * Runs a query and returns a flat array containing the values of
466
-	 * one row.
467
-	 *
468
-	 * @param string $sql      SQL query to execute
469
-	 * @param array  $bindings list of values to bind to SQL snippet
470
-	 *
471
-	 * @return array
472
-	 */
473
-	public function GetRow( $sql, $bindings = array() );
474
-
475
-	/**
476
-	 * Executes SQL code and allows key-value binding.
477
-	 * This function allows you to provide an array with values to bind
478
-	 * to query parameters. For instance you can bind values to question
479
-	 * marks in the query. Each value in the array corresponds to the
480
-	 * question mark in the query that matches the position of the value in the
481
-	 * array. You can also bind values using explicit keys, for instance
482
-	 * array(":key"=>123) will bind the integer 123 to the key :key in the
483
-	 * SQL. This method has no return value.
484
-	 *
485
-	 * @param string $sql      SQL query to execute
486
-	 * @param array  $bindings list of values to bind to SQL snippet
487
-	 *
488
-	 * @return array Affected Rows
489
-	 */
490
-	public function Execute( $sql, $bindings = array() );
491
-
492
-	/**
493
-	 * Returns the latest insert ID if driver does support this
494
-	 * feature.
495
-	 *
496
-	 * @return integer
497
-	 */
498
-	public function GetInsertID();
499
-
500
-	/**
501
-	 * Returns the number of rows affected by the most recent query
502
-	 * if the currently selected driver driver supports this feature.
503
-	 *
504
-	 * @return integer
505
-	 */
506
-	public function Affected_Rows();
507
-
508
-	/**
509
-	 * Returns a cursor-like object from the database.
510
-	 *
511
-	 * @param string $sql      SQL query to execute
512
-	 * @param array  $bindings list of values to bind to SQL snippet
513
-	 *
514
-	 * @return mixed
515
-	 */
516
-	public function GetCursor( $sql, $bindings = array() );
517
-
518
-	/**
519
-	 * Toggles debug mode. In debug mode the driver will print all
520
-	 * SQL to the screen together with some information about the
521
-	 * results.
522
-	 *
523
-	 * This method is for more fine-grained control. Normally
524
-	 * you should use the facade to start the query debugger for
525
-	 * you. The facade will manage the object wirings necessary
526
-	 * to use the debugging functionality.
527
-	 *
528
-	 * Usage (through facade):
529
-	 *
530
-	 * <code>
531
-	 * R::debug( TRUE );
532
-	 * ...rest of program...
533
-	 * R::debug( FALSE );
534
-	 * </code>
535
-	 *
536
-	 * The example above illustrates how to use the RedBeanPHP
537
-	 * query debugger through the facade.
538
-	 *
539
-	 * @param boolean $trueFalse turn on/off
540
-	 * @param Logger  $logger    logger instance
541
-	 *
542
-	 * @return void
543
-	 */
544
-	public function setDebugMode( $tf, $customLogger );
545
-
546
-	/**
547
-	 * Starts a transaction.
548
-	 *
549
-	 * @return void
550
-	 */
551
-	public function CommitTrans();
552
-
553
-	/**
554
-	 * Commits a transaction.
555
-	 *
556
-	 * @return void
557
-	 */
558
-	public function StartTrans();
559
-
560
-	/**
561
-	 * Rolls back a transaction.
562
-	 *
563
-	 * @return void
564
-	 */
565
-	public function FailTrans();
566
-
567
-	/**
568
-	 * Resets the internal Query Counter.
569
-	 *
570
-	 * @return self
571
-	 */
572
-	public function resetCounter();
573
-
574
-	/**
575
-	 * Returns the number of SQL queries processed.
576
-	 *
577
-	 * @return integer
578
-	 */
579
-	public function getQueryCount();
423
+    /**
424
+     * Runs a query and fetches results as a multi dimensional array.
425
+     *
426
+     * @param string $sql      SQL query to execute
427
+     * @param array  $bindings list of values to bind to SQL snippet
428
+     *
429
+     * @return array
430
+     */
431
+    public function GetAll( $sql, $bindings = array() );
432
+
433
+    /**
434
+     * Runs a query and fetches results as a column.
435
+     *
436
+     * @param string $sql      SQL query to execute
437
+     * @param array  $bindings list of values to bind to SQL snippet
438
+     *
439
+     * @return array
440
+     */
441
+    public function GetCol( $sql, $bindings = array() );
442
+
443
+    /**
444
+     * Runs a query and returns results as a single cell.
445
+     *
446
+     * @param string $sql      SQL query to execute
447
+     * @param array  $bindings list of values to bind to SQL snippet
448
+     *
449
+     * @return mixed
450
+     */
451
+    public function GetOne( $sql, $bindings = array() );
452
+
453
+    /**
454
+     * Runs a query and returns results as an associative array
455
+     * indexed by the first column.
456
+     *
457
+     * @param string $sql      SQL query to execute
458
+     * @param array  $bindings list of values to bind to SQL snippet
459
+     *
460
+     * @return mixed
461
+     */
462
+    public function GetAssocRow( $sql, $bindings = array() );
463
+
464
+    /**
465
+     * Runs a query and returns a flat array containing the values of
466
+     * one row.
467
+     *
468
+     * @param string $sql      SQL query to execute
469
+     * @param array  $bindings list of values to bind to SQL snippet
470
+     *
471
+     * @return array
472
+     */
473
+    public function GetRow( $sql, $bindings = array() );
474
+
475
+    /**
476
+     * Executes SQL code and allows key-value binding.
477
+     * This function allows you to provide an array with values to bind
478
+     * to query parameters. For instance you can bind values to question
479
+     * marks in the query. Each value in the array corresponds to the
480
+     * question mark in the query that matches the position of the value in the
481
+     * array. You can also bind values using explicit keys, for instance
482
+     * array(":key"=>123) will bind the integer 123 to the key :key in the
483
+     * SQL. This method has no return value.
484
+     *
485
+     * @param string $sql      SQL query to execute
486
+     * @param array  $bindings list of values to bind to SQL snippet
487
+     *
488
+     * @return array Affected Rows
489
+     */
490
+    public function Execute( $sql, $bindings = array() );
491
+
492
+    /**
493
+     * Returns the latest insert ID if driver does support this
494
+     * feature.
495
+     *
496
+     * @return integer
497
+     */
498
+    public function GetInsertID();
499
+
500
+    /**
501
+     * Returns the number of rows affected by the most recent query
502
+     * if the currently selected driver driver supports this feature.
503
+     *
504
+     * @return integer
505
+     */
506
+    public function Affected_Rows();
507
+
508
+    /**
509
+     * Returns a cursor-like object from the database.
510
+     *
511
+     * @param string $sql      SQL query to execute
512
+     * @param array  $bindings list of values to bind to SQL snippet
513
+     *
514
+     * @return mixed
515
+     */
516
+    public function GetCursor( $sql, $bindings = array() );
517
+
518
+    /**
519
+     * Toggles debug mode. In debug mode the driver will print all
520
+     * SQL to the screen together with some information about the
521
+     * results.
522
+     *
523
+     * This method is for more fine-grained control. Normally
524
+     * you should use the facade to start the query debugger for
525
+     * you. The facade will manage the object wirings necessary
526
+     * to use the debugging functionality.
527
+     *
528
+     * Usage (through facade):
529
+     *
530
+     * <code>
531
+     * R::debug( TRUE );
532
+     * ...rest of program...
533
+     * R::debug( FALSE );
534
+     * </code>
535
+     *
536
+     * The example above illustrates how to use the RedBeanPHP
537
+     * query debugger through the facade.
538
+     *
539
+     * @param boolean $trueFalse turn on/off
540
+     * @param Logger  $logger    logger instance
541
+     *
542
+     * @return void
543
+     */
544
+    public function setDebugMode( $tf, $customLogger );
545
+
546
+    /**
547
+     * Starts a transaction.
548
+     *
549
+     * @return void
550
+     */
551
+    public function CommitTrans();
552
+
553
+    /**
554
+     * Commits a transaction.
555
+     *
556
+     * @return void
557
+     */
558
+    public function StartTrans();
559
+
560
+    /**
561
+     * Rolls back a transaction.
562
+     *
563
+     * @return void
564
+     */
565
+    public function FailTrans();
566
+
567
+    /**
568
+     * Resets the internal Query Counter.
569
+     *
570
+     * @return self
571
+     */
572
+    public function resetCounter();
573
+
574
+    /**
575
+     * Returns the number of SQL queries processed.
576
+     *
577
+     * @return integer
578
+     */
579
+    public function getQueryCount();
580 580
 }
581 581
 }
582 582
 
@@ -608,830 +608,830 @@  discard block
 block discarded – undo
608 608
  */
609 609
 class RPDO implements Driver
610 610
 {
611
-	/**
612
-	 * @var integer
613
-	 */
614
-	protected $max;
615
-
616
-	/**
617
-	 * @var string
618
-	 */
619
-	protected $dsn;
620
-
621
-	/**
622
-	 * @var boolean
623
-	 */
624
-	protected $loggingEnabled = FALSE;
625
-
626
-	/**
627
-	 * @var Logger
628
-	 */
629
-	protected $logger = NULL;
630
-
631
-	/**
632
-	 * @var PDO
633
-	 */
634
-	protected $pdo;
635
-
636
-	/**
637
-	 * @var integer
638
-	 */
639
-	protected $affectedRows;
640
-
641
-	/**
642
-	 * @var integer
643
-	 */
644
-	protected $resultArray;
645
-
646
-	/**
647
-	 * @var array
648
-	 */
649
-	protected $connectInfo = array();
650
-
651
-	/**
652
-	 * @var boolean
653
-	 */
654
-	protected $isConnected = FALSE;
655
-
656
-	/**
657
-	 * @var bool
658
-	 */
659
-	protected $flagUseStringOnlyBinding = FALSE;
660
-
661
-	/**
662
-	 * @var integer
663
-	 */
664
-	protected $queryCounter = 0;
665
-
666
-	/**
667
-	 * @var string
668
-	 */
669
-	protected $mysqlCharset = '';
611
+    /**
612
+     * @var integer
613
+     */
614
+    protected $max;
615
+
616
+    /**
617
+     * @var string
618
+     */
619
+    protected $dsn;
620
+
621
+    /**
622
+     * @var boolean
623
+     */
624
+    protected $loggingEnabled = FALSE;
625
+
626
+    /**
627
+     * @var Logger
628
+     */
629
+    protected $logger = NULL;
630
+
631
+    /**
632
+     * @var PDO
633
+     */
634
+    protected $pdo;
635
+
636
+    /**
637
+     * @var integer
638
+     */
639
+    protected $affectedRows;
640
+
641
+    /**
642
+     * @var integer
643
+     */
644
+    protected $resultArray;
645
+
646
+    /**
647
+     * @var array
648
+     */
649
+    protected $connectInfo = array();
650
+
651
+    /**
652
+     * @var boolean
653
+     */
654
+    protected $isConnected = FALSE;
655
+
656
+    /**
657
+     * @var bool
658
+     */
659
+    protected $flagUseStringOnlyBinding = FALSE;
660
+
661
+    /**
662
+     * @var integer
663
+     */
664
+    protected $queryCounter = 0;
665
+
666
+    /**
667
+     * @var string
668
+     */
669
+    protected $mysqlCharset = '';
670 670
 	
671
-	/**
672
-	 * @var string
673
-	 */
674
-	protected $mysqlCollate = '';
675
-
676
-	/**
677
-	 * @var boolean
678
-	 */
679
-	protected $stringifyFetches = TRUE;
680
-
681
-	/**
682
-	 * @var string
683
-	 */
684
-	protected $initSQL = NULL;
685
-
686
-	/**
687
-	 * Binds parameters. This method binds parameters to a PDOStatement for
688
-	 * Query Execution. This method binds parameters as NULL, INTEGER or STRING
689
-	 * and supports both named keys and question mark keys.
690
-	 *
691
-	 * @param PDOStatement $statement PDO Statement instance
692
-	 * @param array        $bindings  values that need to get bound to the statement
693
-	 *
694
-	 * @return void
695
-	 */
696
-	protected function bindParams( $statement, $bindings )
697
-	{
698
-		foreach ( $bindings as $key => &$value ) {
699
-			$k = is_integer( $key ) ? $key + 1 : $key;
700
-
701
-			if ( is_array( $value ) && count( $value ) == 2 ) {
702
-				$paramType = end( $value );
703
-				$value = reset( $value );
704
-			} else {
705
-				$paramType = NULL;
706
-			}
707
-
708
-			if ( is_null( $value ) ) {
709
-				$statement->bindValue( $k, NULL, \PDO::PARAM_NULL );
710
-				continue;
711
-			}
712
-
713
-			if ( $paramType != \PDO::PARAM_INT && $paramType != \PDO::PARAM_STR ) {
714
-				if ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
715
-					$paramType = \PDO::PARAM_INT;
716
-				} else {
717
-					$paramType = \PDO::PARAM_STR;
718
-				}
719
-			}
720
-
721
-			$statement->bindParam( $k, $value, $paramType );
722
-		}
723
-	}
724
-
725
-	/**
726
-	 * This method runs the actual SQL query and binds a list of parameters to the query.
727
-	 * slots. The result of the query will be stored in the protected property
728
-	 * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
729
-	 * is stored in protected property $affectedRows. If the debug flag is set
730
-	 * this function will send debugging output to screen buffer.
731
-	 *
732
-	 * @param string $sql      the SQL string to be send to database server
733
-	 * @param array  $bindings the values that need to get bound to the query slots
734
-	 * @param array  $options
735
-	 *
736
-	 * @return mixed
737
-	 * @throws SQL
738
-	 */
739
-	protected function runQuery( $sql, $bindings, $options = array() )
740
-	{
741
-		$this->connect();
742
-		if ( $this->loggingEnabled && $this->logger ) {
743
-			$this->logger->log( $sql, $bindings );
744
-		}
745
-		try {
746
-			if ( strpos( 'pgsql', $this->dsn ) === 0 ) {
747
-				//one line because unable to test this otherwise (coverage trick).
748
-				if ( defined( '\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT' ) ) { $statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) ); } else { $statement = $this->pdo->prepare( $sql ); }
749
-			} else {
750
-				$statement = $this->pdo->prepare( $sql );
751
-			}
752
-			$this->bindParams( $statement, $bindings );
753
-			$statement->execute();
754
-			$this->queryCounter ++;
755
-			$this->affectedRows = $statement->rowCount();
756
-			if ( $statement->columnCount() ) {
757
-				$fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL;
758
-				if ( isset( $options['noFetch'] ) && $options['noFetch'] ) {
759
-					$this->resultArray = array();
760
-					return $statement;
761
-				}
762
-				$this->resultArray = $statement->fetchAll( $fetchStyle );
763
-				if ( $this->loggingEnabled && $this->logger ) {
764
-					$this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' );
765
-				}
766
-			} else {
767
-				$this->resultArray = array();
768
-			}
769
-		} catch ( \PDOException $e ) {
770
-			//Unfortunately the code field is supposed to be int by default (php)
771
-			//So we need a property to convey the SQL State code.
772
-			$err = $e->getMessage();
773
-			if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err );
774
-			$exception = new SQL( $err, 0, $e );
775
-			$exception->setSQLState( $e->getCode() );
776
-			$exception->setDriverDetails( $e->errorInfo );
777
-			throw $exception;
778
-		}
779
-	}
780
-
781
-	/**
782
-	 * Try to fix MySQL character encoding problems.
783
-	 * MySQL < 5.5.3 does not support proper 4 byte unicode but they
784
-	 * seem to have added it with version 5.5.3 under a different label: utf8mb4.
785
-	 * We try to select the best possible charset based on your version data.
786
-	 *
787
-	 * @return void
788
-	 */
789
-	protected function setEncoding()
790
-	{
791
-		$driver = $this->pdo->getAttribute( \PDO::ATTR_DRIVER_NAME );
792
-		if ($driver === 'mysql') {
793
-			$charset = $this->hasCap( 'utf8mb4' ) ? 'utf8mb4' : 'utf8';
794
-			$collate = $this->hasCap( 'utf8mb4_520' ) ? '_unicode_520_ci' : '_unicode_ci';
795
-			$this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '. $charset ); //on every re-connect
796
-			/* #624 removed space before SET NAMES because it causes trouble with ProxySQL */
797
-			$this->pdo->exec('SET NAMES '. $charset); //also for current connection
798
-			$this->mysqlCharset = $charset;
799
-			$this->mysqlCollate = $charset . $collate;
800
-		}
801
-	}
802
-
803
-	/**
804
-	 * Determine if a database supports a particular feature.
805
-	 * Currently this function can be used to detect the following features:
806
-	 *
807
-	 * - utf8mb4
808
-	 * - utf8mb4 520
809
-	 *
810
-	 * Usage:
811
-	 *
812
-	 * <code>
813
-	 * $this->hasCap( 'utf8mb4_520' );
814
-	 * </code>
815
-	 *
816
-	 * By default, RedBeanPHP uses this method under the hood to make sure
817
-	 * you use the latest UTF8 encoding possible for your database.
818
-	 *
819
-	 * @param $db_cap identifier of database capability
820
-	 *
821
-	 * @return int|false Whether the database feature is supported, FALSE otherwise.
822
-	 **/
823
-	protected function hasCap( $db_cap )
824
-	{
825
-		$compare = FALSE;
826
-		$version = $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION );
827
-		switch ( strtolower( $db_cap ) ) {
828
-			case 'utf8mb4':
829
-				//oneliner, to boost code coverage (coverage does not span versions)
830
-				if ( version_compare( $version, '5.5.3', '<' ) ) { return FALSE; }
831
-				$client_version = $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
832
-				/*
671
+    /**
672
+     * @var string
673
+     */
674
+    protected $mysqlCollate = '';
675
+
676
+    /**
677
+     * @var boolean
678
+     */
679
+    protected $stringifyFetches = TRUE;
680
+
681
+    /**
682
+     * @var string
683
+     */
684
+    protected $initSQL = NULL;
685
+
686
+    /**
687
+     * Binds parameters. This method binds parameters to a PDOStatement for
688
+     * Query Execution. This method binds parameters as NULL, INTEGER or STRING
689
+     * and supports both named keys and question mark keys.
690
+     *
691
+     * @param PDOStatement $statement PDO Statement instance
692
+     * @param array        $bindings  values that need to get bound to the statement
693
+     *
694
+     * @return void
695
+     */
696
+    protected function bindParams( $statement, $bindings )
697
+    {
698
+        foreach ( $bindings as $key => &$value ) {
699
+            $k = is_integer( $key ) ? $key + 1 : $key;
700
+
701
+            if ( is_array( $value ) && count( $value ) == 2 ) {
702
+                $paramType = end( $value );
703
+                $value = reset( $value );
704
+            } else {
705
+                $paramType = NULL;
706
+            }
707
+
708
+            if ( is_null( $value ) ) {
709
+                $statement->bindValue( $k, NULL, \PDO::PARAM_NULL );
710
+                continue;
711
+            }
712
+
713
+            if ( $paramType != \PDO::PARAM_INT && $paramType != \PDO::PARAM_STR ) {
714
+                if ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
715
+                    $paramType = \PDO::PARAM_INT;
716
+                } else {
717
+                    $paramType = \PDO::PARAM_STR;
718
+                }
719
+            }
720
+
721
+            $statement->bindParam( $k, $value, $paramType );
722
+        }
723
+    }
724
+
725
+    /**
726
+     * This method runs the actual SQL query and binds a list of parameters to the query.
727
+     * slots. The result of the query will be stored in the protected property
728
+     * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
729
+     * is stored in protected property $affectedRows. If the debug flag is set
730
+     * this function will send debugging output to screen buffer.
731
+     *
732
+     * @param string $sql      the SQL string to be send to database server
733
+     * @param array  $bindings the values that need to get bound to the query slots
734
+     * @param array  $options
735
+     *
736
+     * @return mixed
737
+     * @throws SQL
738
+     */
739
+    protected function runQuery( $sql, $bindings, $options = array() )
740
+    {
741
+        $this->connect();
742
+        if ( $this->loggingEnabled && $this->logger ) {
743
+            $this->logger->log( $sql, $bindings );
744
+        }
745
+        try {
746
+            if ( strpos( 'pgsql', $this->dsn ) === 0 ) {
747
+                //one line because unable to test this otherwise (coverage trick).
748
+                if ( defined( '\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT' ) ) { $statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) ); } else { $statement = $this->pdo->prepare( $sql ); }
749
+            } else {
750
+                $statement = $this->pdo->prepare( $sql );
751
+            }
752
+            $this->bindParams( $statement, $bindings );
753
+            $statement->execute();
754
+            $this->queryCounter ++;
755
+            $this->affectedRows = $statement->rowCount();
756
+            if ( $statement->columnCount() ) {
757
+                $fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL;
758
+                if ( isset( $options['noFetch'] ) && $options['noFetch'] ) {
759
+                    $this->resultArray = array();
760
+                    return $statement;
761
+                }
762
+                $this->resultArray = $statement->fetchAll( $fetchStyle );
763
+                if ( $this->loggingEnabled && $this->logger ) {
764
+                    $this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' );
765
+                }
766
+            } else {
767
+                $this->resultArray = array();
768
+            }
769
+        } catch ( \PDOException $e ) {
770
+            //Unfortunately the code field is supposed to be int by default (php)
771
+            //So we need a property to convey the SQL State code.
772
+            $err = $e->getMessage();
773
+            if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err );
774
+            $exception = new SQL( $err, 0, $e );
775
+            $exception->setSQLState( $e->getCode() );
776
+            $exception->setDriverDetails( $e->errorInfo );
777
+            throw $exception;
778
+        }
779
+    }
780
+
781
+    /**
782
+     * Try to fix MySQL character encoding problems.
783
+     * MySQL < 5.5.3 does not support proper 4 byte unicode but they
784
+     * seem to have added it with version 5.5.3 under a different label: utf8mb4.
785
+     * We try to select the best possible charset based on your version data.
786
+     *
787
+     * @return void
788
+     */
789
+    protected function setEncoding()
790
+    {
791
+        $driver = $this->pdo->getAttribute( \PDO::ATTR_DRIVER_NAME );
792
+        if ($driver === 'mysql') {
793
+            $charset = $this->hasCap( 'utf8mb4' ) ? 'utf8mb4' : 'utf8';
794
+            $collate = $this->hasCap( 'utf8mb4_520' ) ? '_unicode_520_ci' : '_unicode_ci';
795
+            $this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '. $charset ); //on every re-connect
796
+            /* #624 removed space before SET NAMES because it causes trouble with ProxySQL */
797
+            $this->pdo->exec('SET NAMES '. $charset); //also for current connection
798
+            $this->mysqlCharset = $charset;
799
+            $this->mysqlCollate = $charset . $collate;
800
+        }
801
+    }
802
+
803
+    /**
804
+     * Determine if a database supports a particular feature.
805
+     * Currently this function can be used to detect the following features:
806
+     *
807
+     * - utf8mb4
808
+     * - utf8mb4 520
809
+     *
810
+     * Usage:
811
+     *
812
+     * <code>
813
+     * $this->hasCap( 'utf8mb4_520' );
814
+     * </code>
815
+     *
816
+     * By default, RedBeanPHP uses this method under the hood to make sure
817
+     * you use the latest UTF8 encoding possible for your database.
818
+     *
819
+     * @param $db_cap identifier of database capability
820
+     *
821
+     * @return int|false Whether the database feature is supported, FALSE otherwise.
822
+     **/
823
+    protected function hasCap( $db_cap )
824
+    {
825
+        $compare = FALSE;
826
+        $version = $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION );
827
+        switch ( strtolower( $db_cap ) ) {
828
+            case 'utf8mb4':
829
+                //oneliner, to boost code coverage (coverage does not span versions)
830
+                if ( version_compare( $version, '5.5.3', '<' ) ) { return FALSE; }
831
+                $client_version = $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
832
+                /*
833 833
 				 * libmysql has supported utf8mb4 since 5.5.3, same as the MySQL server.
834 834
 				 * mysqlnd has supported utf8mb4 since 5.0.9.
835 835
 				 */
836
-				if ( strpos( $client_version, 'mysqlnd' ) !== FALSE ) {
837
-					$client_version = preg_replace( '/^\D+([\d.]+).*/', '$1', $client_version );
838
-					$compare = version_compare( $client_version, '5.0.9', '>=' );
839
-				} else {
840
-					$compare = version_compare( $client_version, '5.5.3', '>=' );
841
-				}
842
-			break;
843
-			case 'utf8mb4_520':
844
-				$compare = version_compare( $version, '5.6', '>=' );
845
-			break;
846
-		}
847
-
848
-		return $compare;
849
-	}
850
-
851
-	/**
852
-	 * Constructor. You may either specify dsn, user and password or
853
-	 * just give an existing PDO connection.
854
-	 *
855
-	 * Usage:
856
-	 *
857
-	 * <code>
858
-	 * $driver = new RPDO( $dsn, $user, $password );
859
-	 * </code>
860
-	 *
861
-	 * The example above illustrates how to create a driver
862
-	 * instance from a database connection string (dsn), a username
863
-	 * and a password. It's also possible to pass a PDO object.
864
-	 *
865
-	 * Usage:
866
-	 *
867
-	 * <code>
868
-	 * $driver = new RPDO( $existingConnection );
869
-	 * </code>
870
-	 *
871
-	 * The second example shows how to create an RPDO instance
872
-	 * from an existing PDO object.
873
-	 *
874
-	 * @param string|object $dsn  database connection string
875
-	 * @param string        $user optional, usename to sign in
876
-	 * @param string        $pass optional, password for connection login
877
-	 *
878
-	 * @return void
879
-	 */
880
-	public function __construct( $dsn, $user = NULL, $pass = NULL )
881
-	{
882
-		if ( is_object( $dsn ) ) {
883
-			$this->pdo = $dsn;
884
-			$this->isConnected = TRUE;
885
-			$this->setEncoding();
886
-			$this->pdo->setAttribute( \PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
887
-			$this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
888
-			// make sure that the dsn at least contains the type
889
-			$this->dsn = $this->getDatabaseType();
890
-		} else {
891
-			$this->dsn = $dsn;
892
-			$this->connectInfo = array( 'pass' => $pass, 'user' => $user );
893
-		}
894
-
895
-		//PHP 5.3 PDO SQLite has a bug with large numbers:
896
-		if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) ||  defined('HHVM_VERSION') || $this->dsn === 'test-sqlite-53' ) {
897
-			$this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis.
898
-		} elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) {
899
-			$this->max = 2147483647; //bindParam in pdo_cubrid also fails...
900
-		} else {
901
-			$this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause)
902
-		}
903
-	}
904
-
905
-	/**
906
-	 * Sets PDO in stringify fetch mode.
907
-	 * If set to TRUE, this method will make sure all data retrieved from
908
-	 * the database will be fetched as a string. Default: TRUE.
909
-	 *
910
-	 * To set it to FALSE...
911
-	 *
912
-	 * Usage:
913
-	 *
914
-	 * <code>
915
-	 * R::getDatabaseAdapter()->getDatabase()->stringifyFetches( FALSE );
916
-	 * </code>
917
-	 *
918
-	 * Important!
919
-	 * Note, this method only works if you set the value BEFORE the connection
920
-	 * has been establish. Also, this setting ONLY works with SOME drivers.
921
-	 * It's up to the driver to honour this setting.
922
-	 *
923
-	 * @param boolean $bool
924
-	 */
925
-	public function stringifyFetches( $bool ) {
926
-		$this->stringifyFetches = $bool;
927
-	}
928
-
929
-	/**
930
-	 * Returns the best possible encoding for MySQL based on version data.
931
-	 * This method can be used to obtain the best character set parameters
932
-	 * possible for your database when constructing a table creation query
933
-	 * containing clauses like:  CHARSET=... COLLATE=...
934
-	 * This is a MySQL-specific method and not part of the driver interface.
935
-	 *
936
-	 * Usage:
937
-	 *
938
-	 * <code>
939
-	 * $charset_collate = $this->adapter->getDatabase()->getMysqlEncoding( TRUE );
940
-	 * </code>
941
-	 *
942
-	 * @param boolean $retCol pass TRUE to return both charset/collate
943
-	 *
944
-	 * @return string|array
945
-	 */
946
-	public function getMysqlEncoding( $retCol = FALSE )
947
-	{
948
-		if( $retCol )
949
-			return array( 'charset' => $this->mysqlCharset, 'collate' => $this->mysqlCollate );
950
-		return $this->mysqlCharset;
951
-	}
952
-
953
-	/**
954
-	 * Whether to bind all parameters as strings.
955
-	 * If set to TRUE this will cause all integers to be bound as STRINGS.
956
-	 * This will NOT affect NULL values.
957
-	 *
958
-	 * @param boolean $yesNo pass TRUE to bind all parameters as strings.
959
-	 *
960
-	 * @return void
961
-	 */
962
-	public function setUseStringOnlyBinding( $yesNo )
963
-	{
964
-		$this->flagUseStringOnlyBinding = (boolean) $yesNo;
965
-	}
966
-
967
-	/**
968
-	 * Sets the maximum value to be bound as integer, normally
969
-	 * this value equals PHP's MAX INT constant, however sometimes
970
-	 * PDO driver bindings cannot bind large integers as integers.
971
-	 * This method allows you to manually set the max integer binding
972
-	 * value to manage portability/compatibility issues among different
973
-	 * PHP builds. This method will return the old value.
974
-	 *
975
-	 * @param integer $max maximum value for integer bindings
976
-	 *
977
-	 * @return integer
978
-	 */
979
-	public function setMaxIntBind( $max )
980
-	{
981
-		if ( !is_integer( $max ) ) throw new RedException( 'Parameter has to be integer.' );
982
-		$oldMax = $this->max;
983
-		$this->max = $max;
984
-		return $oldMax;
985
-	}
986
-
987
-	/**
988
-	 * Establishes a connection to the database using PHP\PDO
989
-	 * functionality. If a connection has already been established this
990
-	 * method will simply return directly. This method also turns on
991
-	 * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as
992
-	 * PDO-FETCH-ASSOC.
993
-	 *
994
-	 * @return void
995
-	 */
996
-	public function connect()
997
-	{
998
-		if ( $this->isConnected ) return;
999
-		try {
1000
-			$user = $this->connectInfo['user'];
1001
-			$pass = $this->connectInfo['pass'];
1002
-			$this->pdo = new \PDO( $this->dsn, $user, $pass );
1003
-			$this->setEncoding();
1004
-			$this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, $this->stringifyFetches );
1005
-			//cant pass these as argument to constructor, CUBRID driver does not understand...
1006
-			$this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
1007
-			$this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC );
1008
-			$this->isConnected = TRUE;
1009
-			/* run initialisation query if any */
1010
-			if ( $this->initSQL !== NULL ) {
1011
-				$this->Execute( $this->initSQL );
1012
-				$this->initSQL = NULL;
1013
-			}
1014
-		} catch ( \PDOException $exception ) {
1015
-			$matches = array();
1016
-			$dbname  = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?';
1017
-			throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() );
1018
-		}
1019
-	}
1020
-
1021
-	/**
1022
-	 * Directly sets PDO instance into driver.
1023
-	 * This method might improve performance, however since the driver does
1024
-	 * not configure this instance terrible things may happen... only use
1025
-	 * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and
1026
-	 * you know your database server VERY WELL.
1027
-	 *
1028
-	 * @param PDO $pdo PDO instance
1029
-	 *
1030
-	 * @return void
1031
-	 */
1032
-	public function setPDO( \PDO $pdo ) {
1033
-		$this->pdo = $pdo;
1034
-	}
1035
-
1036
-	/**
1037
-	 * @see Driver::GetAll
1038
-	 */
1039
-	public function GetAll( $sql, $bindings = array() )
1040
-	{
1041
-		$this->runQuery( $sql, $bindings );
1042
-		return $this->resultArray;
1043
-	}
1044
-
1045
-	/**
1046
-	 * @see Driver::GetAssocRow
1047
-	 */
1048
-	public function GetAssocRow( $sql, $bindings = array() )
1049
-	{
1050
-		$this->runQuery( $sql, $bindings, array(
1051
-				'fetchStyle' => \PDO::FETCH_ASSOC
1052
-			)
1053
-		);
1054
-		return $this->resultArray;
1055
-	}
1056
-
1057
-	/**
1058
-	 * @see Driver::GetCol
1059
-	 */
1060
-	public function GetCol( $sql, $bindings = array() )
1061
-	{
1062
-		$rows = $this->GetAll( $sql, $bindings );
1063
-
1064
-		if ( empty( $rows ) || !is_array( $rows ) ) {
1065
-			return array();
1066
-		}
1067
-
1068
-		$cols = array();
1069
-		foreach ( $rows as $row ) {
1070
-			$cols[] = reset( $row );
1071
-		}
1072
-
1073
-		return $cols;
1074
-	}
1075
-
1076
-	/**
1077
-	 * @see Driver::GetOne
1078
-	 */
1079
-	public function GetOne( $sql, $bindings = array() )
1080
-	{
1081
-		$arr = $this->GetAll( $sql, $bindings );
1082
-
1083
-		if ( empty( $arr[0] ) || !is_array( $arr[0] ) ) {
1084
-			return NULL;
1085
-		}
1086
-
1087
-		return reset( $arr[0] );
1088
-	}
1089
-
1090
-	/**
1091
-	 * Alias for getOne().
1092
-	 * Backward compatibility.
1093
-	 *
1094
-	 * @param string $sql      SQL
1095
-	 * @param array  $bindings bindings
1096
-	 *
1097
-	 * @return mixed
1098
-	 */
1099
-	public function GetCell( $sql, $bindings = array() )
1100
-	{
1101
-		return $this->GetOne( $sql, $bindings );
1102
-	}
1103
-
1104
-	/**
1105
-	 * @see Driver::GetRow
1106
-	 */
1107
-	public function GetRow( $sql, $bindings = array() )
1108
-	{
1109
-		$arr = $this->GetAll( $sql, $bindings );
1110
-		return reset( $arr );
1111
-	}
1112
-
1113
-	/**
1114
-	 * @see Driver::Excecute
1115
-	 */
1116
-	public function Execute( $sql, $bindings = array() )
1117
-	{
1118
-		$this->runQuery( $sql, $bindings );
1119
-		return $this->affectedRows;
1120
-	}
1121
-
1122
-	/**
1123
-	 * @see Driver::GetInsertID
1124
-	 */
1125
-	public function GetInsertID()
1126
-	{
1127
-		$this->connect();
1128
-
1129
-		return (int) $this->pdo->lastInsertId();
1130
-	}
1131
-
1132
-	/**
1133
-	 * @see Driver::GetCursor
1134
-	 */
1135
-	public function GetCursor( $sql, $bindings = array() )
1136
-	{
1137
-		$statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) );
1138
-		$cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC );
1139
-		return $cursor;
1140
-	}
1141
-
1142
-	/**
1143
-	 * @see Driver::Affected_Rows
1144
-	 */
1145
-	public function Affected_Rows()
1146
-	{
1147
-		$this->connect();
1148
-		return (int) $this->affectedRows;
1149
-	}
1150
-
1151
-	/**
1152
-	 * @see Driver::setDebugMode
1153
-	 */
1154
-	public function setDebugMode( $tf, $logger = NULL )
1155
-	{
1156
-		$this->connect();
1157
-		$this->loggingEnabled = (bool) $tf;
1158
-		if ( $this->loggingEnabled and !$logger ) {
1159
-			$logger = new RDefault();
1160
-		}
1161
-		$this->setLogger( $logger );
1162
-	}
1163
-
1164
-	/**
1165
-	 * Injects Logger object.
1166
-	 * Sets the logger instance you wish to use.
1167
-	 *
1168
-	 * This method is for more fine-grained control. Normally
1169
-	 * you should use the facade to start the query debugger for
1170
-	 * you. The facade will manage the object wirings necessary
1171
-	 * to use the debugging functionality.
1172
-	 *
1173
-	 * Usage (through facade):
1174
-	 *
1175
-	 * <code>
1176
-	 * R::debug( TRUE );
1177
-	 * ...rest of program...
1178
-	 * R::debug( FALSE );
1179
-	 * </code>
1180
-	 *
1181
-	 * The example above illustrates how to use the RedBeanPHP
1182
-	 * query debugger through the facade.
1183
-	 *
1184
-	 * @param Logger $logger the logger instance to be used for logging
1185
-	 *
1186
-	 * @return self
1187
-	 */
1188
-	public function setLogger( Logger $logger )
1189
-	{
1190
-		$this->logger = $logger;
1191
-		return $this;
1192
-	}
1193
-
1194
-	/**
1195
-	 * Gets Logger object.
1196
-	 * Returns the currently active Logger instance.
1197
-	 *
1198
-	 * @return Logger
1199
-	 */
1200
-	public function getLogger()
1201
-	{
1202
-		return $this->logger;
1203
-	}
1204
-
1205
-	/**
1206
-	 * @see Driver::StartTrans
1207
-	 */
1208
-	public function StartTrans()
1209
-	{
1210
-		$this->connect();
1211
-		$this->pdo->beginTransaction();
1212
-	}
1213
-
1214
-	/**
1215
-	 * @see Driver::CommitTrans
1216
-	 */
1217
-	public function CommitTrans()
1218
-	{
1219
-		$this->connect();
1220
-		$this->pdo->commit();
1221
-	}
1222
-
1223
-	/**
1224
-	 * @see Driver::FailTrans
1225
-	 */
1226
-	public function FailTrans()
1227
-	{
1228
-		$this->connect();
1229
-		$this->pdo->rollback();
1230
-	}
1231
-
1232
-	/**
1233
-	 * Returns the name of database driver for PDO.
1234
-	 * Uses the PDO attribute DRIVER NAME to obtain the name of the
1235
-	 * PDO driver. Use this method to identify the current PDO driver
1236
-	 * used to provide access to the database. Example of a database
1237
-	 * driver string:
1238
-	 *
1239
-	 * <code>
1240
-	 * mysql
1241
-	 * </code>
1242
-	 *
1243
-	 * Usage:
1244
-	 *
1245
-	 * <code>
1246
-	 * echo R::getDatabaseAdapter()->getDatabase()->getDatabaseType();
1247
-	 * </code>
1248
-	 *
1249
-	 * The example above prints the current database driver string to
1250
-	 * stdout.
1251
-	 *
1252
-	 * Note that this is a driver-specific method, not part of the
1253
-	 * driver interface. This method might not be available in other
1254
-	 * drivers since it relies on PDO.
1255
-	 *
1256
-	 * @return string
1257
-	 */
1258
-	public function getDatabaseType()
1259
-	{
1260
-		$this->connect();
1261
-		return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
1262
-	}
1263
-
1264
-	/**
1265
-	 * Returns the version identifier string of the database.
1266
-	 * This method can be used to identify the currently installed
1267
-	 * database. Note that this method will also establish a connection
1268
-	 * (because this is required to obtain the version information).
1269
-	 *
1270
-	 * Example of a version string:
1271
-	 *
1272
-	 * <code>
1273
-	 * mysqlnd 5.0.12-dev - 20150407 - $Id: b5c5906d452ec590732a93b051f3827e02749b83 $
1274
-	 * </code>
1275
-	 *
1276
-	 * Usage:
1277
-	 *
1278
-	 * <code>
1279
-	 * echo R::getDatabaseAdapter()->getDatabase()->getDatabaseVersion();
1280
-	 * </code>
1281
-	 *
1282
-	 * The example above will print the version string to stdout.
1283
-	 *
1284
-	 * Note that this is a driver-specific method, not part of the
1285
-	 * driver interface. This method might not be available in other
1286
-	 * drivers since it relies on PDO.
1287
-	 *
1288
-	 * @return mixed
1289
-	 */
1290
-	public function getDatabaseVersion()
1291
-	{
1292
-		$this->connect();
1293
-		return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
1294
-	}
1295
-
1296
-	/**
1297
-	 * Returns the underlying PHP PDO instance.
1298
-	 * For some low-level database operations you'll need access to the PDO
1299
-	 * object. Not that this method is only available in RPDO and other
1300
-	 * PDO based database drivers for RedBeanPHP. Other drivers may not have
1301
-	 * a method like this. The following example demonstrates how to obtain
1302
-	 * a reference to the PDO instance from the facade:
1303
-	 *
1304
-	 * Usage:
1305
-	 *
1306
-	 * <code>
1307
-	 * $pdo = R::getDatabaseAdapter()->getDatabase()->getPDO();
1308
-	 * </code>
1309
-	 *
1310
-	 * @return PDO
1311
-	 */
1312
-	public function getPDO()
1313
-	{
1314
-		$this->connect();
1315
-		return $this->pdo;
1316
-	}
1317
-
1318
-	/**
1319
-	 * Closes the database connection.
1320
-	 * While database connections are closed automatically at the end of the PHP script,
1321
-	 * closing database connections is generally recommended to improve performance.
1322
-	 * Closing a database connection will immediately return the resources to PHP.
1323
-	 *
1324
-	 * Usage:
1325
-	 *
1326
-	 * <code>
1327
-	 * R::setup( ... );
1328
-	 * ... do stuff ...
1329
-	 * R::close();
1330
-	 * </code>
1331
-	 *
1332
-	 * @return void
1333
-	 */
1334
-	public function close()
1335
-	{
1336
-		$this->pdo         = NULL;
1337
-		$this->isConnected = FALSE;
1338
-	}
1339
-
1340
-	/**
1341
-	 * Returns TRUE if the current PDO instance is connected.
1342
-	 *
1343
-	 * @return boolean
1344
-	 */
1345
-	public function isConnected()
1346
-	{
1347
-		return $this->isConnected && $this->pdo;
1348
-	}
1349
-
1350
-	/**
1351
-	 * Toggles logging, enables or disables logging.
1352
-	 *
1353
-	 * @param boolean $enable TRUE to enable logging
1354
-	 *
1355
-	 * @return self
1356
-	 */
1357
-	public function setEnableLogging( $enable )
1358
-	{
1359
-		$this->loggingEnabled = (boolean) $enable;
1360
-		return $this;
1361
-	}
1362
-
1363
-	/**
1364
-	 * Resets the query counter.
1365
-	 * The query counter can be used to monitor the number
1366
-	 * of database queries that have
1367
-	 * been processed according to the database driver. You can use this
1368
-	 * to monitor the number of queries required to render a page.
1369
-	 *
1370
-	 * Usage:
1371
-	 *
1372
-	 * <code>
1373
-	 * R::resetQueryCount();
1374
-	 * echo R::getQueryCount() . ' queries processed.';
1375
-	 * </code>
1376
-	 *
1377
-	 * @return self
1378
-	 */
1379
-	public function resetCounter()
1380
-	{
1381
-		$this->queryCounter = 0;
1382
-		return $this;
1383
-	}
1384
-
1385
-	/**
1386
-	 * Returns the number of SQL queries processed.
1387
-	 * This method returns the number of database queries that have
1388
-	 * been processed according to the database driver. You can use this
1389
-	 * to monitor the number of queries required to render a page.
1390
-	 *
1391
-	 * Usage:
1392
-	 *
1393
-	 * <code>
1394
-	 * echo R::getQueryCount() . ' queries processed.';
1395
-	 * </code>
1396
-	 *
1397
-	 * @return integer
1398
-	 */
1399
-	public function getQueryCount()
1400
-	{
1401
-		return $this->queryCounter;
1402
-	}
1403
-
1404
-	/**
1405
-	 * Returns the maximum value treated as integer parameter
1406
-	 * binding.
1407
-	 *
1408
-	 * This method is mainly for testing purposes but it can help
1409
-	 * you solve some issues relating to integer bindings.
1410
-	 *
1411
-	 * @return integer
1412
-	 */
1413
-	public function getIntegerBindingMax()
1414
-	{
1415
-		return $this->max;
1416
-	}
1417
-
1418
-	/**
1419
-	 * Sets a query to be executed upon connecting to the database.
1420
-	 * This method provides an opportunity to configure the connection
1421
-	 * to a database through an SQL-based interface. Objects can provide
1422
-	 * an SQL string to be executed upon establishing a connection to
1423
-	 * the database. This has been used to solve issues with default
1424
-	 * foreign key settings in SQLite3 for instance, see Github issues:
1425
-	 * #545 and #548.
1426
-	 *
1427
-	 * @param string $sql SQL query to run upon connecting to database
1428
-	 *
1429
-	 * @return self
1430
-	 */
1431
-	public function setInitQuery( $sql ) {
1432
-		$this->initSQL = $sql;
1433
-		return $this;
1434
-	}
836
+                if ( strpos( $client_version, 'mysqlnd' ) !== FALSE ) {
837
+                    $client_version = preg_replace( '/^\D+([\d.]+).*/', '$1', $client_version );
838
+                    $compare = version_compare( $client_version, '5.0.9', '>=' );
839
+                } else {
840
+                    $compare = version_compare( $client_version, '5.5.3', '>=' );
841
+                }
842
+            break;
843
+            case 'utf8mb4_520':
844
+                $compare = version_compare( $version, '5.6', '>=' );
845
+            break;
846
+        }
847
+
848
+        return $compare;
849
+    }
850
+
851
+    /**
852
+     * Constructor. You may either specify dsn, user and password or
853
+     * just give an existing PDO connection.
854
+     *
855
+     * Usage:
856
+     *
857
+     * <code>
858
+     * $driver = new RPDO( $dsn, $user, $password );
859
+     * </code>
860
+     *
861
+     * The example above illustrates how to create a driver
862
+     * instance from a database connection string (dsn), a username
863
+     * and a password. It's also possible to pass a PDO object.
864
+     *
865
+     * Usage:
866
+     *
867
+     * <code>
868
+     * $driver = new RPDO( $existingConnection );
869
+     * </code>
870
+     *
871
+     * The second example shows how to create an RPDO instance
872
+     * from an existing PDO object.
873
+     *
874
+     * @param string|object $dsn  database connection string
875
+     * @param string        $user optional, usename to sign in
876
+     * @param string        $pass optional, password for connection login
877
+     *
878
+     * @return void
879
+     */
880
+    public function __construct( $dsn, $user = NULL, $pass = NULL )
881
+    {
882
+        if ( is_object( $dsn ) ) {
883
+            $this->pdo = $dsn;
884
+            $this->isConnected = TRUE;
885
+            $this->setEncoding();
886
+            $this->pdo->setAttribute( \PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
887
+            $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
888
+            // make sure that the dsn at least contains the type
889
+            $this->dsn = $this->getDatabaseType();
890
+        } else {
891
+            $this->dsn = $dsn;
892
+            $this->connectInfo = array( 'pass' => $pass, 'user' => $user );
893
+        }
894
+
895
+        //PHP 5.3 PDO SQLite has a bug with large numbers:
896
+        if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) ||  defined('HHVM_VERSION') || $this->dsn === 'test-sqlite-53' ) {
897
+            $this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis.
898
+        } elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) {
899
+            $this->max = 2147483647; //bindParam in pdo_cubrid also fails...
900
+        } else {
901
+            $this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause)
902
+        }
903
+    }
904
+
905
+    /**
906
+     * Sets PDO in stringify fetch mode.
907
+     * If set to TRUE, this method will make sure all data retrieved from
908
+     * the database will be fetched as a string. Default: TRUE.
909
+     *
910
+     * To set it to FALSE...
911
+     *
912
+     * Usage:
913
+     *
914
+     * <code>
915
+     * R::getDatabaseAdapter()->getDatabase()->stringifyFetches( FALSE );
916
+     * </code>
917
+     *
918
+     * Important!
919
+     * Note, this method only works if you set the value BEFORE the connection
920
+     * has been establish. Also, this setting ONLY works with SOME drivers.
921
+     * It's up to the driver to honour this setting.
922
+     *
923
+     * @param boolean $bool
924
+     */
925
+    public function stringifyFetches( $bool ) {
926
+        $this->stringifyFetches = $bool;
927
+    }
928
+
929
+    /**
930
+     * Returns the best possible encoding for MySQL based on version data.
931
+     * This method can be used to obtain the best character set parameters
932
+     * possible for your database when constructing a table creation query
933
+     * containing clauses like:  CHARSET=... COLLATE=...
934
+     * This is a MySQL-specific method and not part of the driver interface.
935
+     *
936
+     * Usage:
937
+     *
938
+     * <code>
939
+     * $charset_collate = $this->adapter->getDatabase()->getMysqlEncoding( TRUE );
940
+     * </code>
941
+     *
942
+     * @param boolean $retCol pass TRUE to return both charset/collate
943
+     *
944
+     * @return string|array
945
+     */
946
+    public function getMysqlEncoding( $retCol = FALSE )
947
+    {
948
+        if( $retCol )
949
+            return array( 'charset' => $this->mysqlCharset, 'collate' => $this->mysqlCollate );
950
+        return $this->mysqlCharset;
951
+    }
952
+
953
+    /**
954
+     * Whether to bind all parameters as strings.
955
+     * If set to TRUE this will cause all integers to be bound as STRINGS.
956
+     * This will NOT affect NULL values.
957
+     *
958
+     * @param boolean $yesNo pass TRUE to bind all parameters as strings.
959
+     *
960
+     * @return void
961
+     */
962
+    public function setUseStringOnlyBinding( $yesNo )
963
+    {
964
+        $this->flagUseStringOnlyBinding = (boolean) $yesNo;
965
+    }
966
+
967
+    /**
968
+     * Sets the maximum value to be bound as integer, normally
969
+     * this value equals PHP's MAX INT constant, however sometimes
970
+     * PDO driver bindings cannot bind large integers as integers.
971
+     * This method allows you to manually set the max integer binding
972
+     * value to manage portability/compatibility issues among different
973
+     * PHP builds. This method will return the old value.
974
+     *
975
+     * @param integer $max maximum value for integer bindings
976
+     *
977
+     * @return integer
978
+     */
979
+    public function setMaxIntBind( $max )
980
+    {
981
+        if ( !is_integer( $max ) ) throw new RedException( 'Parameter has to be integer.' );
982
+        $oldMax = $this->max;
983
+        $this->max = $max;
984
+        return $oldMax;
985
+    }
986
+
987
+    /**
988
+     * Establishes a connection to the database using PHP\PDO
989
+     * functionality. If a connection has already been established this
990
+     * method will simply return directly. This method also turns on
991
+     * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as
992
+     * PDO-FETCH-ASSOC.
993
+     *
994
+     * @return void
995
+     */
996
+    public function connect()
997
+    {
998
+        if ( $this->isConnected ) return;
999
+        try {
1000
+            $user = $this->connectInfo['user'];
1001
+            $pass = $this->connectInfo['pass'];
1002
+            $this->pdo = new \PDO( $this->dsn, $user, $pass );
1003
+            $this->setEncoding();
1004
+            $this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, $this->stringifyFetches );
1005
+            //cant pass these as argument to constructor, CUBRID driver does not understand...
1006
+            $this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
1007
+            $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC );
1008
+            $this->isConnected = TRUE;
1009
+            /* run initialisation query if any */
1010
+            if ( $this->initSQL !== NULL ) {
1011
+                $this->Execute( $this->initSQL );
1012
+                $this->initSQL = NULL;
1013
+            }
1014
+        } catch ( \PDOException $exception ) {
1015
+            $matches = array();
1016
+            $dbname  = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?';
1017
+            throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() );
1018
+        }
1019
+    }
1020
+
1021
+    /**
1022
+     * Directly sets PDO instance into driver.
1023
+     * This method might improve performance, however since the driver does
1024
+     * not configure this instance terrible things may happen... only use
1025
+     * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and
1026
+     * you know your database server VERY WELL.
1027
+     *
1028
+     * @param PDO $pdo PDO instance
1029
+     *
1030
+     * @return void
1031
+     */
1032
+    public function setPDO( \PDO $pdo ) {
1033
+        $this->pdo = $pdo;
1034
+    }
1035
+
1036
+    /**
1037
+     * @see Driver::GetAll
1038
+     */
1039
+    public function GetAll( $sql, $bindings = array() )
1040
+    {
1041
+        $this->runQuery( $sql, $bindings );
1042
+        return $this->resultArray;
1043
+    }
1044
+
1045
+    /**
1046
+     * @see Driver::GetAssocRow
1047
+     */
1048
+    public function GetAssocRow( $sql, $bindings = array() )
1049
+    {
1050
+        $this->runQuery( $sql, $bindings, array(
1051
+                'fetchStyle' => \PDO::FETCH_ASSOC
1052
+            )
1053
+        );
1054
+        return $this->resultArray;
1055
+    }
1056
+
1057
+    /**
1058
+     * @see Driver::GetCol
1059
+     */
1060
+    public function GetCol( $sql, $bindings = array() )
1061
+    {
1062
+        $rows = $this->GetAll( $sql, $bindings );
1063
+
1064
+        if ( empty( $rows ) || !is_array( $rows ) ) {
1065
+            return array();
1066
+        }
1067
+
1068
+        $cols = array();
1069
+        foreach ( $rows as $row ) {
1070
+            $cols[] = reset( $row );
1071
+        }
1072
+
1073
+        return $cols;
1074
+    }
1075
+
1076
+    /**
1077
+     * @see Driver::GetOne
1078
+     */
1079
+    public function GetOne( $sql, $bindings = array() )
1080
+    {
1081
+        $arr = $this->GetAll( $sql, $bindings );
1082
+
1083
+        if ( empty( $arr[0] ) || !is_array( $arr[0] ) ) {
1084
+            return NULL;
1085
+        }
1086
+
1087
+        return reset( $arr[0] );
1088
+    }
1089
+
1090
+    /**
1091
+     * Alias for getOne().
1092
+     * Backward compatibility.
1093
+     *
1094
+     * @param string $sql      SQL
1095
+     * @param array  $bindings bindings
1096
+     *
1097
+     * @return mixed
1098
+     */
1099
+    public function GetCell( $sql, $bindings = array() )
1100
+    {
1101
+        return $this->GetOne( $sql, $bindings );
1102
+    }
1103
+
1104
+    /**
1105
+     * @see Driver::GetRow
1106
+     */
1107
+    public function GetRow( $sql, $bindings = array() )
1108
+    {
1109
+        $arr = $this->GetAll( $sql, $bindings );
1110
+        return reset( $arr );
1111
+    }
1112
+
1113
+    /**
1114
+     * @see Driver::Excecute
1115
+     */
1116
+    public function Execute( $sql, $bindings = array() )
1117
+    {
1118
+        $this->runQuery( $sql, $bindings );
1119
+        return $this->affectedRows;
1120
+    }
1121
+
1122
+    /**
1123
+     * @see Driver::GetInsertID
1124
+     */
1125
+    public function GetInsertID()
1126
+    {
1127
+        $this->connect();
1128
+
1129
+        return (int) $this->pdo->lastInsertId();
1130
+    }
1131
+
1132
+    /**
1133
+     * @see Driver::GetCursor
1134
+     */
1135
+    public function GetCursor( $sql, $bindings = array() )
1136
+    {
1137
+        $statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) );
1138
+        $cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC );
1139
+        return $cursor;
1140
+    }
1141
+
1142
+    /**
1143
+     * @see Driver::Affected_Rows
1144
+     */
1145
+    public function Affected_Rows()
1146
+    {
1147
+        $this->connect();
1148
+        return (int) $this->affectedRows;
1149
+    }
1150
+
1151
+    /**
1152
+     * @see Driver::setDebugMode
1153
+     */
1154
+    public function setDebugMode( $tf, $logger = NULL )
1155
+    {
1156
+        $this->connect();
1157
+        $this->loggingEnabled = (bool) $tf;
1158
+        if ( $this->loggingEnabled and !$logger ) {
1159
+            $logger = new RDefault();
1160
+        }
1161
+        $this->setLogger( $logger );
1162
+    }
1163
+
1164
+    /**
1165
+     * Injects Logger object.
1166
+     * Sets the logger instance you wish to use.
1167
+     *
1168
+     * This method is for more fine-grained control. Normally
1169
+     * you should use the facade to start the query debugger for
1170
+     * you. The facade will manage the object wirings necessary
1171
+     * to use the debugging functionality.
1172
+     *
1173
+     * Usage (through facade):
1174
+     *
1175
+     * <code>
1176
+     * R::debug( TRUE );
1177
+     * ...rest of program...
1178
+     * R::debug( FALSE );
1179
+     * </code>
1180
+     *
1181
+     * The example above illustrates how to use the RedBeanPHP
1182
+     * query debugger through the facade.
1183
+     *
1184
+     * @param Logger $logger the logger instance to be used for logging
1185
+     *
1186
+     * @return self
1187
+     */
1188
+    public function setLogger( Logger $logger )
1189
+    {
1190
+        $this->logger = $logger;
1191
+        return $this;
1192
+    }
1193
+
1194
+    /**
1195
+     * Gets Logger object.
1196
+     * Returns the currently active Logger instance.
1197
+     *
1198
+     * @return Logger
1199
+     */
1200
+    public function getLogger()
1201
+    {
1202
+        return $this->logger;
1203
+    }
1204
+
1205
+    /**
1206
+     * @see Driver::StartTrans
1207
+     */
1208
+    public function StartTrans()
1209
+    {
1210
+        $this->connect();
1211
+        $this->pdo->beginTransaction();
1212
+    }
1213
+
1214
+    /**
1215
+     * @see Driver::CommitTrans
1216
+     */
1217
+    public function CommitTrans()
1218
+    {
1219
+        $this->connect();
1220
+        $this->pdo->commit();
1221
+    }
1222
+
1223
+    /**
1224
+     * @see Driver::FailTrans
1225
+     */
1226
+    public function FailTrans()
1227
+    {
1228
+        $this->connect();
1229
+        $this->pdo->rollback();
1230
+    }
1231
+
1232
+    /**
1233
+     * Returns the name of database driver for PDO.
1234
+     * Uses the PDO attribute DRIVER NAME to obtain the name of the
1235
+     * PDO driver. Use this method to identify the current PDO driver
1236
+     * used to provide access to the database. Example of a database
1237
+     * driver string:
1238
+     *
1239
+     * <code>
1240
+     * mysql
1241
+     * </code>
1242
+     *
1243
+     * Usage:
1244
+     *
1245
+     * <code>
1246
+     * echo R::getDatabaseAdapter()->getDatabase()->getDatabaseType();
1247
+     * </code>
1248
+     *
1249
+     * The example above prints the current database driver string to
1250
+     * stdout.
1251
+     *
1252
+     * Note that this is a driver-specific method, not part of the
1253
+     * driver interface. This method might not be available in other
1254
+     * drivers since it relies on PDO.
1255
+     *
1256
+     * @return string
1257
+     */
1258
+    public function getDatabaseType()
1259
+    {
1260
+        $this->connect();
1261
+        return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
1262
+    }
1263
+
1264
+    /**
1265
+     * Returns the version identifier string of the database.
1266
+     * This method can be used to identify the currently installed
1267
+     * database. Note that this method will also establish a connection
1268
+     * (because this is required to obtain the version information).
1269
+     *
1270
+     * Example of a version string:
1271
+     *
1272
+     * <code>
1273
+     * mysqlnd 5.0.12-dev - 20150407 - $Id: b5c5906d452ec590732a93b051f3827e02749b83 $
1274
+     * </code>
1275
+     *
1276
+     * Usage:
1277
+     *
1278
+     * <code>
1279
+     * echo R::getDatabaseAdapter()->getDatabase()->getDatabaseVersion();
1280
+     * </code>
1281
+     *
1282
+     * The example above will print the version string to stdout.
1283
+     *
1284
+     * Note that this is a driver-specific method, not part of the
1285
+     * driver interface. This method might not be available in other
1286
+     * drivers since it relies on PDO.
1287
+     *
1288
+     * @return mixed
1289
+     */
1290
+    public function getDatabaseVersion()
1291
+    {
1292
+        $this->connect();
1293
+        return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
1294
+    }
1295
+
1296
+    /**
1297
+     * Returns the underlying PHP PDO instance.
1298
+     * For some low-level database operations you'll need access to the PDO
1299
+     * object. Not that this method is only available in RPDO and other
1300
+     * PDO based database drivers for RedBeanPHP. Other drivers may not have
1301
+     * a method like this. The following example demonstrates how to obtain
1302
+     * a reference to the PDO instance from the facade:
1303
+     *
1304
+     * Usage:
1305
+     *
1306
+     * <code>
1307
+     * $pdo = R::getDatabaseAdapter()->getDatabase()->getPDO();
1308
+     * </code>
1309
+     *
1310
+     * @return PDO
1311
+     */
1312
+    public function getPDO()
1313
+    {
1314
+        $this->connect();
1315
+        return $this->pdo;
1316
+    }
1317
+
1318
+    /**
1319
+     * Closes the database connection.
1320
+     * While database connections are closed automatically at the end of the PHP script,
1321
+     * closing database connections is generally recommended to improve performance.
1322
+     * Closing a database connection will immediately return the resources to PHP.
1323
+     *
1324
+     * Usage:
1325
+     *
1326
+     * <code>
1327
+     * R::setup( ... );
1328
+     * ... do stuff ...
1329
+     * R::close();
1330
+     * </code>
1331
+     *
1332
+     * @return void
1333
+     */
1334
+    public function close()
1335
+    {
1336
+        $this->pdo         = NULL;
1337
+        $this->isConnected = FALSE;
1338
+    }
1339
+
1340
+    /**
1341
+     * Returns TRUE if the current PDO instance is connected.
1342
+     *
1343
+     * @return boolean
1344
+     */
1345
+    public function isConnected()
1346
+    {
1347
+        return $this->isConnected && $this->pdo;
1348
+    }
1349
+
1350
+    /**
1351
+     * Toggles logging, enables or disables logging.
1352
+     *
1353
+     * @param boolean $enable TRUE to enable logging
1354
+     *
1355
+     * @return self
1356
+     */
1357
+    public function setEnableLogging( $enable )
1358
+    {
1359
+        $this->loggingEnabled = (boolean) $enable;
1360
+        return $this;
1361
+    }
1362
+
1363
+    /**
1364
+     * Resets the query counter.
1365
+     * The query counter can be used to monitor the number
1366
+     * of database queries that have
1367
+     * been processed according to the database driver. You can use this
1368
+     * to monitor the number of queries required to render a page.
1369
+     *
1370
+     * Usage:
1371
+     *
1372
+     * <code>
1373
+     * R::resetQueryCount();
1374
+     * echo R::getQueryCount() . ' queries processed.';
1375
+     * </code>
1376
+     *
1377
+     * @return self
1378
+     */
1379
+    public function resetCounter()
1380
+    {
1381
+        $this->queryCounter = 0;
1382
+        return $this;
1383
+    }
1384
+
1385
+    /**
1386
+     * Returns the number of SQL queries processed.
1387
+     * This method returns the number of database queries that have
1388
+     * been processed according to the database driver. You can use this
1389
+     * to monitor the number of queries required to render a page.
1390
+     *
1391
+     * Usage:
1392
+     *
1393
+     * <code>
1394
+     * echo R::getQueryCount() . ' queries processed.';
1395
+     * </code>
1396
+     *
1397
+     * @return integer
1398
+     */
1399
+    public function getQueryCount()
1400
+    {
1401
+        return $this->queryCounter;
1402
+    }
1403
+
1404
+    /**
1405
+     * Returns the maximum value treated as integer parameter
1406
+     * binding.
1407
+     *
1408
+     * This method is mainly for testing purposes but it can help
1409
+     * you solve some issues relating to integer bindings.
1410
+     *
1411
+     * @return integer
1412
+     */
1413
+    public function getIntegerBindingMax()
1414
+    {
1415
+        return $this->max;
1416
+    }
1417
+
1418
+    /**
1419
+     * Sets a query to be executed upon connecting to the database.
1420
+     * This method provides an opportunity to configure the connection
1421
+     * to a database through an SQL-based interface. Objects can provide
1422
+     * an SQL string to be executed upon establishing a connection to
1423
+     * the database. This has been used to solve issues with default
1424
+     * foreign key settings in SQLite3 for instance, see Github issues:
1425
+     * #545 and #548.
1426
+     *
1427
+     * @param string $sql SQL query to run upon connecting to database
1428
+     *
1429
+     * @return self
1430
+     */
1431
+    public function setInitQuery( $sql ) {
1432
+        $this->initSQL = $sql;
1433
+        return $this;
1434
+    }
1435 1435
 }
1436 1436
 }
1437 1437
 
@@ -1467,2273 +1467,2273 @@  discard block
 block discarded – undo
1467 1467
  */
1468 1468
 class OODBBean implements\IteratorAggregate,\ArrayAccess,\Countable,Jsonable
1469 1469
 {
1470
-	/**
1471
-	 * FUSE error modes.
1472
-	 */
1473
-	const C_ERR_IGNORE    = FALSE;
1474
-	const C_ERR_LOG       = 1;
1475
-	const C_ERR_NOTICE    = 2;
1476
-	const C_ERR_WARN      = 3;
1477
-	const C_ERR_EXCEPTION = 4;
1478
-	const C_ERR_FUNC      = 5;
1479
-	const C_ERR_FATAL     = 6;
1480
-
1481
-	/**
1482
-	 * @var boolean
1483
-	 */
1484
-	protected static $convertArraysToJSON = FALSE;
1485
-
1486
-	/**
1487
-	 * @var boolean
1488
-	 */
1489
-	protected static $errorHandlingFUSE = FALSE;
1490
-
1491
-	/**
1492
-	 * @var callable|NULL
1493
-	 */
1494
-	protected static $errorHandler = NULL;
1495
-
1496
-	/**
1497
-	 * @var array
1498
-	 */
1499
-	protected static $aliases = array();
1500
-
1501
-	/**
1502
-	 * @var boolean
1503
-	 */
1504
-	protected static $autoResolve = FALSE;
1505
-
1506
-	/**
1507
-	 * If this is set to TRUE, the __toString function will
1508
-	 * encode all properties as UTF-8 to repair invalid UTF-8
1509
-	 * encodings and prevent exceptions (which are uncatchable from within
1510
-	 * a __toString-function).
1511
-	 *
1512
-	 * @var boolean
1513
-	 */
1514
-	protected static $enforceUTF8encoding = FALSE;
1515
-
1516
-	/**
1517
-	 * This is where the real properties of the bean live. They are stored and retrieved
1518
-	 * by the magic getter and setter (__get and __set).
1519
-	 *
1520
-	 * @var array $properties
1521
-	 */
1522
-	protected $properties = array();
1523
-
1524
-	/**
1525
-	 * Here we keep the meta data of a bean.
1526
-	 *
1527
-	 * @var array
1528
-	 */
1529
-	protected $__info = array();
1530
-
1531
-	/**
1532
-	 * The BeanHelper allows the bean to access the toolbox objects to implement
1533
-	 * rich functionality, otherwise you would have to do everything with R or
1534
-	 * external objects.
1535
-	 *
1536
-	 * @var BeanHelper
1537
-	 */
1538
-	protected $beanHelper = NULL;
1539
-
1540
-	/**
1541
-	 * @var null
1542
-	 */
1543
-	protected $fetchType = NULL;
1544
-
1545
-	/**
1546
-	 * @var string
1547
-	 */
1548
-	protected $withSql = '';
1549
-
1550
-	/**
1551
-	 * @var array
1552
-	 */
1553
-	protected $withParams = array();
1554
-
1555
-	/**
1556
-	 * @var string
1557
-	 */
1558
-	protected $aliasName = NULL;
1559
-
1560
-	/**
1561
-	 * @var string
1562
-	 */
1563
-	protected $via = NULL;
1564
-
1565
-	/**
1566
-	 * @var boolean
1567
-	 */
1568
-	protected $noLoad = FALSE;
1569
-
1570
-	/**
1571
-	 * @var boolean
1572
-	 */
1573
-	protected $all = FALSE;
1574
-
1575
-	/**
1576
-	 * If this is set to TRUE, the __toString function will
1577
-	 * encode all properties as UTF-8 to repair invalid UTF-8
1578
-	 * encodings and prevent exceptions (which are uncatchable from within
1579
-	 * a __toString-function).
1580
-	 *
1581
-	 * @param boolean $toggle TRUE to enforce UTF-8 encoding (slower)
1582
-	 *
1583
-	 * @return void
1584
-	 */
1585
-	 public static function setEnforceUTF8encoding( $toggle )
1586
-	 {
1587
-		 self::$enforceUTF8encoding = (boolean) $toggle;
1588
-	 }
1589
-
1590
-	/**
1591
-	 * Sets the error mode for FUSE.
1592
-	 * What to do if a FUSE model method does not exist?
1593
-	 * You can set the following options:
1594
-	 *
1595
-	 * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
1596
-	 * * OODBBean::C_ERR_LOG, logs the incident using error_log
1597
-	 * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
1598
-	 * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
1599
-	 * * OODBBean::C_ERR_EXCEPTION, throws an exception
1600
-	 * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
1601
-	 * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
1602
-	 *
1603
-	 * <code>
1604
-	 * Custom handler method signature: handler( array (
1605
-	 * 	'message' => string
1606
-	 * 	'bean' => OODBBean
1607
-	 * 	'method' => string
1608
-	 * ) )
1609
-	 * </code>
1610
-	 *
1611
-	 * This method returns the old mode and handler as an array.
1612
-	 *
1613
-	 * @param integer       $mode error handling mode
1614
-	 * @param callable|NULL $func custom handler
1615
-	 *
1616
-	 * @return array
1617
-	 */
1618
-	public static function setErrorHandlingFUSE($mode, $func = NULL) {
1619
-		if (
1620
-			   $mode !== self::C_ERR_IGNORE
1621
-			&& $mode !== self::C_ERR_LOG
1622
-			&& $mode !== self::C_ERR_NOTICE
1623
-			&& $mode !== self::C_ERR_WARN
1624
-			&& $mode !== self::C_ERR_EXCEPTION
1625
-			&& $mode !== self::C_ERR_FUNC
1626
-			&& $mode !== self::C_ERR_FATAL
1627
-		) throw new \Exception( 'Invalid error mode selected' );
1628
-
1629
-		if ( $mode === self::C_ERR_FUNC && !is_callable( $func ) ) {
1630
-			throw new \Exception( 'Invalid error handler' );
1631
-		}
1632
-
1633
-		$old = array( self::$errorHandlingFUSE, self::$errorHandler );
1634
-		self::$errorHandlingFUSE = $mode;
1635
-		if ( is_callable( $func ) ) {
1636
-			self::$errorHandler = $func;
1637
-		} else {
1638
-			self::$errorHandler = NULL;
1639
-		}
1640
-		return $old;
1641
-	}
1642
-
1643
-	/**
1644
-	 * Toggles array to JSON conversion. If set to TRUE any array
1645
-	 * set to a bean property that's not a list will be turned into
1646
-	 * a JSON string. Used together with AQueryWriter::useJSONColumns this
1647
-	 * extends the data type support for JSON columns. Returns the previous
1648
-	 * value of the flag.
1649
-	 *
1650
-	 * @param boolean $flag flag
1651
-	 *
1652
-	 * @return boolean
1653
-	 */
1654
-	public static function convertArraysToJSON( $flag )
1655
-	{
1656
-		$old = self::$convertArraysToJSON;
1657
-		self::$convertArraysToJSON = $flag;
1658
-		return $old;
1659
-	}
1660
-
1661
-	/**
1662
-	 * Sets global aliases.
1663
-	 * Registers a batch of aliases in one go. This works the same as
1664
-	 * fetchAs and setAutoResolve but explicitly. For instance if you register
1665
-	 * the alias 'cover' for 'page' a property containing a reference to a
1666
-	 * page bean called 'cover' will correctly return the page bean and not
1667
-	 * a (non-existant) cover bean.
1668
-	 *
1669
-	 * <code>
1670
-	 * R::aliases( array( 'cover' => 'page' ) );
1671
-	 * $book = R::dispense( 'book' );
1672
-	 * $page = R::dispense( 'page' );
1673
-	 * $book->cover = $page;
1674
-	 * R::store( $book );
1675
-	 * $book = $book->fresh();
1676
-	 * $cover = $book->cover;
1677
-	 * echo $cover->getMeta( 'type' ); //page
1678
-	 * </code>
1679
-	 *
1680
-	 * The format of the aliases registration array is:
1681
-	 *
1682
-	 * {alias} => {actual type}
1683
-	 *
1684
-	 * In the example above we use:
1685
-	 *
1686
-	 * cover => page
1687
-	 *
1688
-	 * From that point on, every bean reference to a cover
1689
-	 * will return a 'page' bean. Note that with autoResolve this
1690
-	 * feature along with fetchAs() is no longer very important, although
1691
-	 * relying on explicit aliases can be a bit faster.
1692
-	 *
1693
-	 * @param array $list list of global aliases to use
1694
-	 *
1695
-	 * @return void
1696
-	 */
1697
-	public static function aliases( $list )
1698
-	{
1699
-		self::$aliases = $list;
1700
-	}
1701
-
1702
-	/**
1703
-	 * Enables or disables auto-resolving fetch types.
1704
-	 * Auto-resolving aliased parent beans is convenient but can
1705
-	 * be slower and can create infinite recursion if you
1706
-	 * used aliases to break cyclic relations in your domain.
1707
-	 *
1708
-	 * @param boolean $automatic TRUE to enable automatic resolving aliased parents
1709
-	 *
1710
-	 * @return void
1711
-	 */
1712
-	public static function setAutoResolve( $automatic = TRUE )
1713
-	{
1714
-		self::$autoResolve = (boolean) $automatic;
1715
-	}
1716
-
1717
-	/**
1718
-	 * Sets a meta property for all beans. This is a quicker way to set
1719
-	 * the meta properties for a collection of beans because this method
1720
-	 * can directly access the property arrays of the beans.
1721
-	 * This method returns the beans.
1722
-	 *
1723
-	 * @param array  $beans    beans to set the meta property of
1724
-	 * @param string $property property to set
1725
-	 * @param mixed  $value    value
1726
-	 *
1727
-	 * @return array
1728
-	 */
1729
-	public static function setMetaAll( $beans, $property, $value )
1730
-	{
1731
-		foreach( $beans as $bean ) {
1732
-			if ( $bean instanceof OODBBean ) $bean->__info[ $property ] = $value;
1733
-			if ( $property == 'type' && !empty($bean->beanHelper)) {
1734
-				$bean->__info['model'] = $bean->beanHelper->getModelForBean( $bean );
1735
-			}
1736
-		}
1737
-		return $beans;
1738
-	}
1739
-
1740
-	/**
1741
-	 * Parses the join in the with-snippet.
1742
-	 * For instance:
1743
-	 *
1744
-	 * <code>
1745
-	 * $author
1746
-	 * 	->withCondition(' @joined.detail.title LIKE ? ')
1747
-	 *  ->ownBookList;
1748
-	 * </code>
1749
-	 *
1750
-	 * will automatically join 'detail' on book to
1751
-	 * access the title field.
1752
-	 *
1753
-	 * @note this feature requires Narrow Field Mode and Join Feature
1754
-	 * to be both activated (default).
1755
-	 *
1756
-	 * @param string $type the source type for the join
1757
-	 *
1758
-	 * @return string
1759
-	 */
1760
-	private function parseJoin( $type )
1761
-	{
1762
-		if ( strpos($this->withSql, '@joined.' ) === FALSE ) {
1763
-			return '';
1764
-		}
1470
+    /**
1471
+     * FUSE error modes.
1472
+     */
1473
+    const C_ERR_IGNORE    = FALSE;
1474
+    const C_ERR_LOG       = 1;
1475
+    const C_ERR_NOTICE    = 2;
1476
+    const C_ERR_WARN      = 3;
1477
+    const C_ERR_EXCEPTION = 4;
1478
+    const C_ERR_FUNC      = 5;
1479
+    const C_ERR_FATAL     = 6;
1480
+
1481
+    /**
1482
+     * @var boolean
1483
+     */
1484
+    protected static $convertArraysToJSON = FALSE;
1485
+
1486
+    /**
1487
+     * @var boolean
1488
+     */
1489
+    protected static $errorHandlingFUSE = FALSE;
1490
+
1491
+    /**
1492
+     * @var callable|NULL
1493
+     */
1494
+    protected static $errorHandler = NULL;
1495
+
1496
+    /**
1497
+     * @var array
1498
+     */
1499
+    protected static $aliases = array();
1500
+
1501
+    /**
1502
+     * @var boolean
1503
+     */
1504
+    protected static $autoResolve = FALSE;
1505
+
1506
+    /**
1507
+     * If this is set to TRUE, the __toString function will
1508
+     * encode all properties as UTF-8 to repair invalid UTF-8
1509
+     * encodings and prevent exceptions (which are uncatchable from within
1510
+     * a __toString-function).
1511
+     *
1512
+     * @var boolean
1513
+     */
1514
+    protected static $enforceUTF8encoding = FALSE;
1515
+
1516
+    /**
1517
+     * This is where the real properties of the bean live. They are stored and retrieved
1518
+     * by the magic getter and setter (__get and __set).
1519
+     *
1520
+     * @var array $properties
1521
+     */
1522
+    protected $properties = array();
1523
+
1524
+    /**
1525
+     * Here we keep the meta data of a bean.
1526
+     *
1527
+     * @var array
1528
+     */
1529
+    protected $__info = array();
1530
+
1531
+    /**
1532
+     * The BeanHelper allows the bean to access the toolbox objects to implement
1533
+     * rich functionality, otherwise you would have to do everything with R or
1534
+     * external objects.
1535
+     *
1536
+     * @var BeanHelper
1537
+     */
1538
+    protected $beanHelper = NULL;
1539
+
1540
+    /**
1541
+     * @var null
1542
+     */
1543
+    protected $fetchType = NULL;
1544
+
1545
+    /**
1546
+     * @var string
1547
+     */
1548
+    protected $withSql = '';
1549
+
1550
+    /**
1551
+     * @var array
1552
+     */
1553
+    protected $withParams = array();
1554
+
1555
+    /**
1556
+     * @var string
1557
+     */
1558
+    protected $aliasName = NULL;
1559
+
1560
+    /**
1561
+     * @var string
1562
+     */
1563
+    protected $via = NULL;
1564
+
1565
+    /**
1566
+     * @var boolean
1567
+     */
1568
+    protected $noLoad = FALSE;
1569
+
1570
+    /**
1571
+     * @var boolean
1572
+     */
1573
+    protected $all = FALSE;
1574
+
1575
+    /**
1576
+     * If this is set to TRUE, the __toString function will
1577
+     * encode all properties as UTF-8 to repair invalid UTF-8
1578
+     * encodings and prevent exceptions (which are uncatchable from within
1579
+     * a __toString-function).
1580
+     *
1581
+     * @param boolean $toggle TRUE to enforce UTF-8 encoding (slower)
1582
+     *
1583
+     * @return void
1584
+     */
1585
+        public static function setEnforceUTF8encoding( $toggle )
1586
+        {
1587
+            self::$enforceUTF8encoding = (boolean) $toggle;
1588
+        }
1589
+
1590
+    /**
1591
+     * Sets the error mode for FUSE.
1592
+     * What to do if a FUSE model method does not exist?
1593
+     * You can set the following options:
1594
+     *
1595
+     * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
1596
+     * * OODBBean::C_ERR_LOG, logs the incident using error_log
1597
+     * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
1598
+     * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
1599
+     * * OODBBean::C_ERR_EXCEPTION, throws an exception
1600
+     * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
1601
+     * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
1602
+     *
1603
+     * <code>
1604
+     * Custom handler method signature: handler( array (
1605
+     * 	'message' => string
1606
+     * 	'bean' => OODBBean
1607
+     * 	'method' => string
1608
+     * ) )
1609
+     * </code>
1610
+     *
1611
+     * This method returns the old mode and handler as an array.
1612
+     *
1613
+     * @param integer       $mode error handling mode
1614
+     * @param callable|NULL $func custom handler
1615
+     *
1616
+     * @return array
1617
+     */
1618
+    public static function setErrorHandlingFUSE($mode, $func = NULL) {
1619
+        if (
1620
+                $mode !== self::C_ERR_IGNORE
1621
+            && $mode !== self::C_ERR_LOG
1622
+            && $mode !== self::C_ERR_NOTICE
1623
+            && $mode !== self::C_ERR_WARN
1624
+            && $mode !== self::C_ERR_EXCEPTION
1625
+            && $mode !== self::C_ERR_FUNC
1626
+            && $mode !== self::C_ERR_FATAL
1627
+        ) throw new \Exception( 'Invalid error mode selected' );
1628
+
1629
+        if ( $mode === self::C_ERR_FUNC && !is_callable( $func ) ) {
1630
+            throw new \Exception( 'Invalid error handler' );
1631
+        }
1632
+
1633
+        $old = array( self::$errorHandlingFUSE, self::$errorHandler );
1634
+        self::$errorHandlingFUSE = $mode;
1635
+        if ( is_callable( $func ) ) {
1636
+            self::$errorHandler = $func;
1637
+        } else {
1638
+            self::$errorHandler = NULL;
1639
+        }
1640
+        return $old;
1641
+    }
1642
+
1643
+    /**
1644
+     * Toggles array to JSON conversion. If set to TRUE any array
1645
+     * set to a bean property that's not a list will be turned into
1646
+     * a JSON string. Used together with AQueryWriter::useJSONColumns this
1647
+     * extends the data type support for JSON columns. Returns the previous
1648
+     * value of the flag.
1649
+     *
1650
+     * @param boolean $flag flag
1651
+     *
1652
+     * @return boolean
1653
+     */
1654
+    public static function convertArraysToJSON( $flag )
1655
+    {
1656
+        $old = self::$convertArraysToJSON;
1657
+        self::$convertArraysToJSON = $flag;
1658
+        return $old;
1659
+    }
1660
+
1661
+    /**
1662
+     * Sets global aliases.
1663
+     * Registers a batch of aliases in one go. This works the same as
1664
+     * fetchAs and setAutoResolve but explicitly. For instance if you register
1665
+     * the alias 'cover' for 'page' a property containing a reference to a
1666
+     * page bean called 'cover' will correctly return the page bean and not
1667
+     * a (non-existant) cover bean.
1668
+     *
1669
+     * <code>
1670
+     * R::aliases( array( 'cover' => 'page' ) );
1671
+     * $book = R::dispense( 'book' );
1672
+     * $page = R::dispense( 'page' );
1673
+     * $book->cover = $page;
1674
+     * R::store( $book );
1675
+     * $book = $book->fresh();
1676
+     * $cover = $book->cover;
1677
+     * echo $cover->getMeta( 'type' ); //page
1678
+     * </code>
1679
+     *
1680
+     * The format of the aliases registration array is:
1681
+     *
1682
+     * {alias} => {actual type}
1683
+     *
1684
+     * In the example above we use:
1685
+     *
1686
+     * cover => page
1687
+     *
1688
+     * From that point on, every bean reference to a cover
1689
+     * will return a 'page' bean. Note that with autoResolve this
1690
+     * feature along with fetchAs() is no longer very important, although
1691
+     * relying on explicit aliases can be a bit faster.
1692
+     *
1693
+     * @param array $list list of global aliases to use
1694
+     *
1695
+     * @return void
1696
+     */
1697
+    public static function aliases( $list )
1698
+    {
1699
+        self::$aliases = $list;
1700
+    }
1701
+
1702
+    /**
1703
+     * Enables or disables auto-resolving fetch types.
1704
+     * Auto-resolving aliased parent beans is convenient but can
1705
+     * be slower and can create infinite recursion if you
1706
+     * used aliases to break cyclic relations in your domain.
1707
+     *
1708
+     * @param boolean $automatic TRUE to enable automatic resolving aliased parents
1709
+     *
1710
+     * @return void
1711
+     */
1712
+    public static function setAutoResolve( $automatic = TRUE )
1713
+    {
1714
+        self::$autoResolve = (boolean) $automatic;
1715
+    }
1716
+
1717
+    /**
1718
+     * Sets a meta property for all beans. This is a quicker way to set
1719
+     * the meta properties for a collection of beans because this method
1720
+     * can directly access the property arrays of the beans.
1721
+     * This method returns the beans.
1722
+     *
1723
+     * @param array  $beans    beans to set the meta property of
1724
+     * @param string $property property to set
1725
+     * @param mixed  $value    value
1726
+     *
1727
+     * @return array
1728
+     */
1729
+    public static function setMetaAll( $beans, $property, $value )
1730
+    {
1731
+        foreach( $beans as $bean ) {
1732
+            if ( $bean instanceof OODBBean ) $bean->__info[ $property ] = $value;
1733
+            if ( $property == 'type' && !empty($bean->beanHelper)) {
1734
+                $bean->__info['model'] = $bean->beanHelper->getModelForBean( $bean );
1735
+            }
1736
+        }
1737
+        return $beans;
1738
+    }
1739
+
1740
+    /**
1741
+     * Parses the join in the with-snippet.
1742
+     * For instance:
1743
+     *
1744
+     * <code>
1745
+     * $author
1746
+     * 	->withCondition(' @joined.detail.title LIKE ? ')
1747
+     *  ->ownBookList;
1748
+     * </code>
1749
+     *
1750
+     * will automatically join 'detail' on book to
1751
+     * access the title field.
1752
+     *
1753
+     * @note this feature requires Narrow Field Mode and Join Feature
1754
+     * to be both activated (default).
1755
+     *
1756
+     * @param string $type the source type for the join
1757
+     *
1758
+     * @return string
1759
+     */
1760
+    private function parseJoin( $type )
1761
+    {
1762
+        if ( strpos($this->withSql, '@joined.' ) === FALSE ) {
1763
+            return '';
1764
+        }
1765 1765
 		
1766
-		$joinSql = ' ';
1767
-		$joins = array();
1768
-		$writer   = $this->beanHelper->getToolBox()->getWriter();
1769
-		$oldParts = $parts = explode( '@joined.', $this->withSql );
1770
-		array_shift( $parts );
1771
-		foreach($parts as $part) {
1772
-			$explosion = explode( '.', $part );
1773
-			$joinInfo  = reset( $explosion );
1774
-			//Dont join more than once..
1775
-			if ( !isset( $joins[$joinInfo] ) ) {
1776
-				$joins[ $joinInfo ] = TRUE;
1777
-				$joinSql  .= $writer->writeJoin( $type, $joinInfo, 'LEFT' );
1778
-			}
1779
-		}
1780
-		$this->withSql = implode( '', $oldParts );
1781
-		$joinSql      .= ' WHERE ';
1766
+        $joinSql = ' ';
1767
+        $joins = array();
1768
+        $writer   = $this->beanHelper->getToolBox()->getWriter();
1769
+        $oldParts = $parts = explode( '@joined.', $this->withSql );
1770
+        array_shift( $parts );
1771
+        foreach($parts as $part) {
1772
+            $explosion = explode( '.', $part );
1773
+            $joinInfo  = reset( $explosion );
1774
+            //Dont join more than once..
1775
+            if ( !isset( $joins[$joinInfo] ) ) {
1776
+                $joins[ $joinInfo ] = TRUE;
1777
+                $joinSql  .= $writer->writeJoin( $type, $joinInfo, 'LEFT' );
1778
+            }
1779
+        }
1780
+        $this->withSql = implode( '', $oldParts );
1781
+        $joinSql      .= ' WHERE ';
1782 1782
 		
1783
-		return $joinSql;
1784
-	}
1785
-
1786
-	/**
1787
-	 * Accesses the shared list of a bean.
1788
-	 * To access beans that have been associated with the current bean
1789
-	 * using a many-to-many relationship use sharedXList where
1790
-	 * X is the type of beans in the list.
1791
-	 *
1792
-	 * Usage:
1793
-	 *
1794
-	 * <code>
1795
-	 * $person = R::load( 'person', $id );
1796
-	 * $friends = $person->sharedFriendList;
1797
-	 * </code>
1798
-	 *
1799
-	 * The code snippet above demonstrates how to obtain all beans of
1800
-	 * type 'friend' that have associated using an N-M relation.
1801
-	 * This is a private method used by the magic getter / accessor.
1802
-	 * The example illustrates usage through these accessors.
1803
-	 *
1804
-	 * @param string  $type    the name of the list you want to retrieve
1805
-	 * @param OODB    $redbean instance of the RedBeanPHP OODB class
1806
-	 * @param ToolBox $toolbox instance of ToolBox (to get access to core objects)
1807
-	 *
1808
-	 * @return array
1809
-	 */
1810
-	private function getSharedList( $type, $redbean, $toolbox )
1811
-	{
1812
-		$writer = $toolbox->getWriter();
1813
-		if ( $this->via ) {
1814
-			$oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
1815
-			if ( $oldName !== $this->via ) {
1816
-				//set the new renaming rule
1817
-				$writer->renameAssocTable( $oldName, $this->via );
1818
-			}
1819
-			$this->via = NULL;
1820
-		}
1821
-		$beans = array();
1822
-		if ($this->getID()) {
1823
-			$type             = $this->beau( $type );
1824
-			$assocManager     = $redbean->getAssociationManager();
1825
-			$beans            = $assocManager->related( $this, $type, $this->withSql, $this->withParams );
1826
-		}
1827
-		return $beans;
1828
-	}
1829
-
1830
-	/**
1831
-	 * Accesses the ownList. The 'own' list contains beans
1832
-	 * associated using a one-to-many relation. The own-lists can
1833
-	 * be accessed through the magic getter/setter property
1834
-	 * ownXList where X is the type of beans in that list.
1835
-	 *
1836
-	 * Usage:
1837
-	 *
1838
-	 * <code>
1839
-	 * $book = R::load( 'book', $id );
1840
-	 * $pages = $book->ownPageList;
1841
-	 * </code>
1842
-	 *
1843
-	 * The example above demonstrates how to access the
1844
-	 * pages associated with the book. Since this is a private method
1845
-	 * meant to be used by the magic accessors, the example uses the
1846
-	 * magic getter instead.
1847
-	 *
1848
-	 * @param string      $type   name of the list you want to retrieve
1849
-	 * @param OODB        $oodb   The RB OODB object database instance
1850
-	 *
1851
-	 * @return array
1852
-	 */
1853
-	private function getOwnList( $type, $redbean )
1854
-	{
1855
-		$type = $this->beau( $type );
1856
-		if ( $this->aliasName ) {
1857
-			$parentField = $this->aliasName;
1858
-			$myFieldLink = $parentField . '_id';
1859
-
1860
-			$this->__info['sys.alias.' . $type] = $this->aliasName;
1861
-
1862
-			$this->aliasName = NULL;
1863
-		} else {
1864
-			$parentField = $this->__info['type'];
1865
-			$myFieldLink = $parentField . '_id';
1866
-		}
1867
-		$beans = array();
1868
-		if ( $this->getID() ) {
1869
-			reset( $this->withParams );
1870
-			$joinSql = $this->parseJoin( $type );
1871
-			$firstKey = count( $this->withParams ) > 0
1872
-				? key( $this->withParams )
1873
-				: 0;
1874
-			if ( is_int( $firstKey ) ) {
1875
-				$bindings = array_merge( array( $this->getID() ), $this->withParams );
1876
-				$beans = $redbean->find( $type, array(), "{$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
1877
-			} else {
1878
-				$bindings           = $this->withParams;
1879
-				$bindings[':slot0'] = $this->getID();
1880
-				$beans = $redbean->find( $type, array(), "{$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
1881
-			}
1882
-		}
1883
-		foreach ( $beans as $beanFromList ) {
1884
-			$beanFromList->__info['sys.parentcache.' . $parentField] = $this;
1885
-		}
1886
-		return $beans;
1887
-	}
1888
-
1889
-	/**
1890
-	 * Initializes a bean. Used by OODB for dispensing beans.
1891
-	 * It is not recommended to use this method to initialize beans. Instead
1892
-	 * use the OODB object to dispense new beans. You can use this method
1893
-	 * if you build your own bean dispensing mechanism.
1894
-	 * This is not recommended.
1895
-	 *
1896
-	 * Unless you know what you are doing, do NOT use this method.
1897
-	 * This is for advanced users only!
1898
-	 *
1899
-	 * @param string     $type       type of the new bean
1900
-	 * @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model
1901
-	 *
1902
-	 * @return void
1903
-	 */
1904
-	public function initializeForDispense( $type, $beanhelper = NULL )
1905
-	{
1906
-		$this->beanHelper         = $beanhelper;
1907
-		$this->__info['type']     = $type;
1908
-		$this->__info['sys.id']   = 'id';
1909
-		$this->__info['sys.orig'] = array( 'id' => 0 );
1910
-		$this->__info['tainted']  = TRUE;
1911
-		$this->__info['changed']  = TRUE;
1912
-		$this->__info['changelist'] = array();
1913
-		if ( $beanhelper ) {
1914
-			$this->__info['model'] = $this->beanHelper->getModelForBean( $this );
1915
-		}
1916
-		$this->properties['id']   = 0;
1917
-	}
1918
-
1919
-	/**
1920
-	 * Sets the Bean Helper. Normally the Bean Helper is set by OODB.
1921
-	 * Here you can change the Bean Helper. The Bean Helper is an object
1922
-	 * providing access to a toolbox for the bean necessary to retrieve
1923
-	 * nested beans (bean lists: ownBean, sharedBean) without the need to
1924
-	 * rely on static calls to the facade (or make this class dep. on OODB).
1925
-	 *
1926
-	 * @param BeanHelper $helper helper to use for this bean
1927
-	 *
1928
-	 * @return void
1929
-	 */
1930
-	public function setBeanHelper( BeanHelper $helper )
1931
-	{
1932
-		$this->beanHelper = $helper;
1933
-	}
1934
-
1935
-	/**
1936
-	 * Returns an ArrayIterator so you can treat the bean like
1937
-	 * an array with the properties container as its contents.
1938
-	 * This method is meant for PHP and allows you to access beans as if
1939
-	 * they were arrays, i.e. using array notation:
1940
-	 *
1941
-	 * <code>
1942
-	 * $bean[$key] = $value;
1943
-	 * </code>
1944
-	 *
1945
-	 * Note that not all PHP functions work with the array interface.
1946
-	 *
1947
-	 * @return ArrayIterator
1948
-	 */
1949
-	public function getIterator()
1950
-	{
1951
-		return new \ArrayIterator( $this->properties );
1952
-	}
1953
-
1954
-	/**
1955
-	 * Imports all values from an associative array $array. Chainable.
1956
-	 * This method imports the values in the first argument as bean
1957
-	 * propery and value pairs. Use the second parameter to provide a
1958
-	 * selection. If a selection array is passed, only the entries
1959
-	 * having keys mentioned in the selection array will be imported.
1960
-	 * Set the third parameter to TRUE to preserve spaces in selection keys.
1961
-	 *
1962
-	 * @param array        $array     what you want to import
1963
-	 * @param string|array $selection selection of values
1964
-	 * @param boolean      $notrim    if TRUE selection keys will NOT be trimmed
1965
-	 *
1966
-	 * @return OODBBean
1967
-	 */
1968
-	public function import( $array, $selection = FALSE, $notrim = FALSE )
1969
-	{
1970
-		if ( is_string( $selection ) ) {
1971
-			$selection = explode( ',', $selection );
1972
-		}
1973
-		if ( is_array( $selection ) ) {
1974
-			if ( $notrim ) {
1975
-				$selected = array_flip($selection);
1976
-			} else {
1977
-				$selected = array();
1978
-				foreach ( $selection as $key => $select ) {
1979
-					$selected[trim( $select )] = TRUE;
1980
-				}
1981
-			}
1982
-		} else {
1983
-			$selected = FALSE;
1984
-		}
1985
-		foreach ( $array as $key => $value ) {
1986
-			if ( $key != '__info' ) {
1987
-				if ( !$selected || isset( $selected[$key] ) ) {
1988
-					if ( is_array($value ) ) {
1989
-						if ( isset( $value['_type'] ) ) {
1990
-							$bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] );
1991
-							unset( $value['_type'] );
1992
-							$bean->import($value);
1993
-							$this->$key = $bean;
1994
-						} else {
1995
-							$listBeans = array();
1996
-							foreach( $value as $listKey => $listItem ) {
1997
-								$bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] );
1998
-								unset( $listItem['_type'] );
1999
-								$bean->import($listItem);
2000
-								$list = &$this->$key;
2001
-								$list[ $listKey ] = $bean;
2002
-							}
2003
-						}
2004
-					} else {
2005
-						$this->$key = $value;
2006
-					}
2007
-				}
2008
-			}
2009
-		}
2010
-		return $this;
2011
-	}
2012
-
2013
-	/**
2014
-	* Imports an associative array directly into the
2015
-	* internal property array of the bean as well as the
2016
-	* meta property sys.orig and sets the changed flag to FALSE.
2017
-	* This is used by the repository objects to inject database rows
2018
-	* into the beans. It is not recommended to use this method outside
2019
-	* of a bean repository.
2020
-	*
2021
-	* @param array $row a database row
2022
-	*
2023
-	* @return self
2024
-	*/
2025
-	public function importRow( $row )
2026
-	{
2027
-		$this->properties = $row;
2028
-		$this->__info['sys.orig'] = $row;
2029
-		$this->__info['changed'] = FALSE;
2030
-		return $this;
2031
-	}
2032
-
2033
-	/**
2034
-	 * Imports data from another bean. Chainable.
2035
-	 * Copies the properties from the source bean to the internal
2036
-	 * property list.
2037
-	 *
2038
-	 * Usage:
2039
-	 *
2040
-	 * <code>
2041
-	 * $copy->importFrom( $bean );
2042
-	 * </code>
2043
-	 *
2044
-	 * The example above demonstrates how to make a shallow copy
2045
-	 * of a bean using the importFrom() method.
2046
-	 *
2047
-	 * @param OODBBean $sourceBean the source bean to take properties from
2048
-	 *
2049
-	 * @return OODBBean
2050
-	 */
2051
-	public function importFrom( OODBBean $sourceBean )
2052
-	{
2053
-		$this->__info['tainted'] = TRUE;
2054
-		$this->__info['changed'] = TRUE;
2055
-		$this->properties = $sourceBean->properties;
2056
-
2057
-		return $this;
2058
-	}
2059
-
2060
-	/**
2061
-	 * Injects the properties of another bean but keeps the original ID.
2062
-	 * Just like import() but keeps the original ID.
2063
-	 * Chainable.
2064
-	 *
2065
-	 * @param OODBBean $otherBean the bean whose properties you would like to copy
2066
-	 *
2067
-	 * @return OODBBean
2068
-	 */
2069
-	public function inject( OODBBean $otherBean )
2070
-	{
2071
-		$myID = $this->properties['id'];
2072
-		$this->import( $otherBean->export( FALSE, FALSE, TRUE ) );
2073
-		$this->id = $myID;
2074
-
2075
-		return $this;
2076
-	}
2077
-
2078
-	/**
2079
-	 * Exports the bean as an array.
2080
-	 * This function exports the contents of a bean to an array and returns
2081
-	 * the resulting array. Depending on the parameters you can also
2082
-	 * export an entire graph of beans, apply filters or exclude meta data.
2083
-	 *
2084
-	 * Usage:
2085
-	 *
2086
-	 * <code>
2087
-	 * $bookData = $book->export( TRUE, TRUE, FALSE, [ 'author' ] );
2088
-	 * </code>
2089
-	 *
2090
-	 * The example above exports all bean properties to an array
2091
-	 * called $bookData including its meta data, parent objects but without
2092
-	 * any beans of type 'author'.
2093
-	 *
2094
-	 * @param boolean $meta    set to TRUE if you want to export meta data as well
2095
-	 * @param boolean $parents set to TRUE if you want to export parents as well
2096
-	 * @param boolean $onlyMe  set to TRUE if you want to export only this bean
2097
-	 * @param array   $filters optional whitelist for export
2098
-	 *
2099
-	 * @return array
2100
-	 */
2101
-	public function export( $meta = FALSE, $parents = FALSE, $onlyMe = FALSE, $filters = array() )
2102
-	{
2103
-		$arr = array();
2104
-		if ( $parents ) {
2105
-			foreach ( $this as $key => $value ) {
2106
-				if ( substr( $key, -3 ) != '_id' ) continue;
2107
-
2108
-				$prop = substr( $key, 0, strlen( $key ) - 3 );
2109
-				$this->$prop;
2110
-			}
2111
-		}
2112
-		$hasFilters = is_array( $filters ) && count( $filters );
2113
-		foreach ( $this as $key => $value ) {
2114
-			if ( !$onlyMe && is_array( $value ) ) {
2115
-				$vn = array();
2116
-
2117
-				foreach ( $value as $i => $b ) {
2118
-					if ( !( $b instanceof OODBBean ) ) continue;
2119
-					$vn[] = $b->export( $meta, FALSE, FALSE, $filters );
2120
-					$value = $vn;
2121
-				}
2122
-			} elseif ( $value instanceof OODBBean ) { if ( $hasFilters ) { //has to be on one line, otherwise code coverage miscounts as miss
2123
-					if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue;
2124
-				}
2125
-				$value = $value->export( $meta, $parents, FALSE, $filters );
2126
-			}
2127
-			$arr[$key] = $value;
2128
-		}
2129
-		if ( $meta ) {
2130
-			$arr['__info'] = $this->__info;
2131
-		}
2132
-		return $arr;
2133
-	}
2134
-
2135
-	/**
2136
-	 * Implements isset() function for use as an array.
2137
-	 * This allows you to use isset() on bean properties.
2138
-	 *
2139
-	 * Usage:
2140
-	 *
2141
-	 * <code>
2142
-	 * $book->title = 'my book';
2143
-	 * echo isset($book['title']); //TRUE
2144
-	 * </code>
2145
-	 *
2146
-	 * The example illustrates how one can apply the
2147
-	 * isset() function to a bean.
2148
-	 *
2149
-	 * @param string $property name of the property you want to check
2150
-	 *
2151
-	 * @return boolean
2152
-	 */
2153
-	public function __isset( $property )
2154
-	{
2155
-		$property = $this->beau( $property );
2156
-		if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2157
-			$property = substr($property, 1);
2158
-		}
2159
-		return isset( $this->properties[$property] );
2160
-	}
2161
-
2162
-	/**
2163
-	 * Checks whether a related bean exists.
2164
-	 * For instance if a post bean has a related author, this method
2165
-	 * can be used to check if the author is set without loading the author.
2166
-	 * This method works by checking the related ID-field.
2167
-	 *
2168
-	 * @param string $property name of the property you wish to check
2169
-	 *
2170
-	 * @return boolean
2171
-	 */
2172
-	public function exists( $property )
2173
-	{
2174
-		$property = $this->beau( $property );
2175
-		/* fixes issue #549, see Base/Bean test */
2176
-		$hiddenRelationField = "{$property}_id";
2177
-		if ( array_key_exists( $hiddenRelationField, $this->properties ) ) {
2178
-			if ( !is_null( $this->properties[$hiddenRelationField] ) ) {
2179
-				return TRUE;
2180
-			}
2181
-		}
2182
-		return FALSE;
2183
-	}
2184
-
2185
-	/**
2186
-	 * Returns the ID of the bean.
2187
-	 * If for some reason the ID has not been set, this method will
2188
-	 * return NULL. This is actually the same as accessing the
2189
-	 * id property using $bean->id. The ID of a bean is it's primary
2190
-	 * key and should always correspond with a table column named
2191
-	 * 'id'.
2192
-	 *
2193
-	 * @return string|null
2194
-	 */
2195
-	public function getID()
2196
-	{
2197
-		return ( isset( $this->properties['id'] ) ) ? (string) $this->properties['id'] : NULL;
2198
-	}
2199
-
2200
-	/**
2201
-	 * Unsets a property of a bean.
2202
-	 * Magic method, gets called implicitly when
2203
-	 * performing the unset() operation
2204
-	 * on a bean property.
2205
-	 *
2206
-	 * @param  string $property property to unset
2207
-	 *
2208
-	 * @return void
2209
-	 */
2210
-	public function __unset( $property )
2211
-	{
2212
-		$property = $this->beau( $property );
2213
-
2214
-		if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2215
-			$property = substr($property, 1);
2216
-		}
2217
-		unset( $this->properties[$property] );
2218
-		$shadowKey = 'sys.shadow.'.$property;
2219
-		if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] );
2220
-		//also clear modifiers
2221
-		$this->clearModifiers();
2222
-		return;
2223
-	}
2224
-
2225
-	/**
2226
-	 * Adds WHERE clause conditions to ownList retrieval.
2227
-	 * For instance to get the pages that belong to a book you would
2228
-	 * issue the following command: $book->ownPage
2229
-	 * However, to order these pages by number use:
2230
-	 *
2231
-	 * <code>
2232
-	 * $book->with(' ORDER BY `number` ASC ')->ownPage
2233
-	 * </code>
2234
-	 *
2235
-	 * the additional SQL snippet will be merged into the final
2236
-	 * query.
2237
-	 *
2238
-	 * @param string $sql      SQL to be added to retrieval query.
2239
-	 * @param array  $bindings array with parameters to bind to SQL snippet
2240
-	 *
2241
-	 * @return OODBBean
2242
-	 */
2243
-	public function with( $sql, $bindings = array() )
2244
-	{
2245
-		$this->withSql    = $sql;
2246
-		$this->withParams = $bindings;
2247
-		return $this;
2248
-	}
2249
-
2250
-	/**
2251
-	 * Just like with(). Except that this method prepends the SQL query snippet
2252
-	 * with AND which makes it slightly more comfortable to use a conditional
2253
-	 * SQL snippet. For instance to filter an own-list with pages (belonging to
2254
-	 * a book) on specific chapters you can use:
2255
-	 *
2256
-	 * $book->withCondition(' chapter = 3 ')->ownPage
2257
-	 *
2258
-	 * This will return in the own list only the pages having 'chapter == 3'.
2259
-	 *
2260
-	 * @param string $sql      SQL to be added to retrieval query (prefixed by AND)
2261
-	 * @param array  $bindings array with parameters to bind to SQL snippet
2262
-	 *
2263
-	 * @return OODBBean
2264
-	 */
2265
-	public function withCondition( $sql, $bindings = array() )
2266
-	{
2267
-		$this->withSql    = ' AND ' . $sql;
2268
-		$this->withParams = $bindings;
2269
-		return $this;
2270
-	}
2271
-
2272
-	/**
2273
-	 * Tells the bean to (re)load the following list without any
2274
-	 * conditions. If you have an ownList or sharedList with a
2275
-	 * condition you can use this method to reload the entire list.
2276
-	 *
2277
-	 * Usage:
2278
-	 *
2279
-	 * <code>
2280
-	 * $bean->with( ' LIMIT 3 ' )->ownPage; //Just 3
2281
-	 * $bean->all()->ownPage; //Reload all pages
2282
-	 * </code>
2283
-	 *
2284
-	 * @return self
2285
-	 */
2286
-	public function all()
2287
-	{
2288
-		$this->all = TRUE;
2289
-		return $this;
2290
-	}
2291
-
2292
-	/**
2293
-	 * Tells the bean to only access the list but not load
2294
-	 * its contents. Use this if you only want to add something to a list
2295
-	 * and you have no interest in retrieving its contents from the database.
2296
-	 *
2297
-	 * Usage:
2298
-	 *
2299
-	 * <code>
2300
-	 * $book->noLoad()->ownPage[] = $newPage;
2301
-	 * </code>
2302
-	 *
2303
-	 * In the example above we add the $newPage bean to the
2304
-	 * page list of book without loading all the pages first.
2305
-	 * If you know in advance that you are not going to use
2306
-	 * the contents of the list, you may use the noLoad() modifier
2307
-	 * to make sure the queries required to load the list will not
2308
-	 * be executed.
2309
-	 *
2310
-	 * @return self
2311
-	 */
2312
-	public function noLoad()
2313
-	{
2314
-		$this->noLoad = TRUE;
2315
-		return $this;
2316
-	}
2317
-
2318
-	/**
2319
-	 * Prepares an own-list to use an alias. This is best explained using
2320
-	 * an example. Imagine a project and a person. The project always involves
2321
-	 * two persons: a teacher and a student. The person beans have been aliased in this
2322
-	 * case, so to the project has a teacher_id pointing to a person, and a student_id
2323
-	 * also pointing to a person. Given a project, we obtain the teacher like this:
2324
-	 *
2325
-	 * <code>
2326
-	 * $project->fetchAs('person')->teacher;
2327
-	 * </code>
2328
-	 *
2329
-	 * Now, if we want all projects of a teacher we cant say:
2330
-	 *
2331
-	 * <code>
2332
-	 * $teacher->ownProject
2333
-	 * </code>
2334
-	 *
2335
-	 * because the $teacher is a bean of type 'person' and no project has been
2336
-	 * assigned to a person. Instead we use the alias() method like this:
2337
-	 *
2338
-	 * <code>
2339
-	 * $teacher->alias('teacher')->ownProject
2340
-	 * </code>
2341
-	 *
2342
-	 * now we get the projects associated with the person bean aliased as
2343
-	 * a teacher.
2344
-	 *
2345
-	 * @param string $aliasName the alias name to use
2346
-	 *
2347
-	 * @return OODBBean
2348
-	 */
2349
-	public function alias( $aliasName )
2350
-	{
2351
-		$this->aliasName = $this->beau( $aliasName );
2352
-		return $this;
2353
-	}
2354
-
2355
-	/**
2356
-	 * Returns properties of bean as an array.
2357
-	 * This method returns the raw internal property list of the
2358
-	 * bean. Only use this method for optimization purposes. Otherwise
2359
-	 * use the export() method to export bean data to arrays.
2360
-	 *
2361
-	 * @return array
2362
-	 */
2363
-	public function getProperties()
2364
-	{
2365
-		return $this->properties;
2366
-	}
2367
-
2368
-	/**
2369
-	 * Returns properties of bean as an array.
2370
-	 * This method returns the raw internal property list of the
2371
-	 * bean. Only use this method for optimization purposes. Otherwise
2372
-	 * use the export() method to export bean data to arrays.
2373
-	 * This method returns an array with the properties array and
2374
-	 * the type (string).
2375
-	 *
2376
-	 * @return array
2377
-	 */
2378
-	public function getPropertiesAndType()
2379
-	{
2380
-		return array( $this->properties, $this->__info['type'] );
2381
-	}
2382
-
2383
-	/**
2384
-	 * Turns a camelcase property name into an underscored property name.
2385
-	 *
2386
-	 * Examples:
2387
-	 *
2388
-	 * - oneACLRoute -> one_acl_route
2389
-	 * - camelCase -> camel_case
2390
-	 *
2391
-	 * Also caches the result to improve performance.
2392
-	 *
2393
-	 * @param string $property property to un-beautify
2394
-	 *
2395
-	 * @return string
2396
-	 */
2397
-	public function beau( $property )
2398
-	{
2399
-		static $beautifulColumns = array();
2400
-
2401
-		if ( ctype_lower( $property ) ) return $property;
2402
-		if (
2403
-			( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )
2404
-			|| ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) )
2405
-			|| ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) )
2406
-		) {
2407
-
2408
-			$property = preg_replace( '/List$/', '', $property );
2409
-			return $property;
2410
-		}
2411
-		if ( !isset( $beautifulColumns[$property] ) ) {
2412
-			$beautifulColumns[$property] = AQueryWriter::camelsSnake( $property );
2413
-		}
2414
-		return $beautifulColumns[$property];
2415
-	}
2416
-
2417
-	/**
2418
-	 * Modifiers are a powerful concept in RedBeanPHP, they make it possible
2419
-	 * to change the way a property has to be loaded.
2420
-	 * RedBeanPHP uses property modifiers using a prefix notation like this:
2421
-	 *
2422
-	 * <code>
2423
-	 * $book->fetchAs('page')->cover;
2424
-	 * </code>
2425
-	 *
2426
-	 * Here, we load a bean of type page, identified by the cover property
2427
-	 * (or cover_id in the database). Because the modifier is called before
2428
-	 * the property is accessed, the modifier must be remembered somehow,
2429
-	 * this changes the state of the bean. Accessing a property causes the
2430
-	 * bean to clear its modifiers. To clear the modifiers manually you can
2431
-	 * use this method.
2432
-	 *
2433
-	 * Usage:
2434
-	 *
2435
-	 * <code>
2436
-	 * $book->with( 'LIMIT 1' );
2437
-	 * $book->clearModifiers()->ownPageList;
2438
-	 * </code>
2439
-	 *
2440
-	 * In the example above, the 'LIMIT 1' clause is
2441
-	 * cleared before accessing the pages of the book, causing all pages
2442
-	 * to be loaded in the list instead of just one.
2443
-	 *
2444
-	 * @return self
2445
-	 */
2446
-	public function clearModifiers()
2447
-	{
2448
-		$this->withSql    = '';
2449
-		$this->withParams = array();
2450
-		$this->aliasName  = NULL;
2451
-		$this->fetchType  = NULL;
2452
-		$this->noLoad     = FALSE;
2453
-		$this->all        = FALSE;
2454
-		$this->via        = NULL;
2455
-		return $this;
2456
-	}
2457
-
2458
-	/**
2459
-	 * Determines whether a list is opened in exclusive mode or not.
2460
-	 * If a list has been opened in exclusive mode this method will return TRUE,
2461
-	 * othwerwise it will return FALSE.
2462
-	 *
2463
-	 * @param string $listName name of the list to check
2464
-	 *
2465
-	 * @return boolean
2466
-	 */
2467
-	public function isListInExclusiveMode( $listName )
2468
-	{
2469
-		$listName = $this->beau( $listName );
2470
-
2471
-		if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) {
2472
-			$listName = substr($listName, 1);
2473
-		}
2474
-		$listName = lcfirst( substr( $listName, 3 ) );
2475
-		return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] );
2476
-	}
2477
-
2478
-	/**
2479
-	 * Magic Getter. Gets the value for a specific property in the bean.
2480
-	 * If the property does not exist this getter will make sure no error
2481
-	 * occurs. This is because RedBean allows you to query (probe) for
2482
-	 * properties. If the property can not be found this method will
2483
-	 * return NULL instead.
2484
-	 *
2485
-	 * Usage:
2486
-	 *
2487
-	 * <code>
2488
-	 * $title = $book->title;
2489
-	 * $pages = $book->ownPageList;
2490
-	 * $tags  = $book->sharedTagList;
2491
-	 * </code>
2492
-	 *
2493
-	 * The example aboves lists several ways to invoke the magic getter.
2494
-	 * You can use the magic setter to access properties, own-lists,
2495
-	 * exclusive own-lists (xownLists) and shared-lists.
2496
-	 *
2497
-	 * @param string $property name of the property you wish to obtain the value of
2498
-	 *
2499
-	 * @return mixed
2500
-	 */
2501
-	public function &__get( $property )
2502
-	{
2503
-		$isEx          = FALSE;
2504
-		$isOwn         = FALSE;
2505
-		$isShared      = FALSE;
2506
-		if ( !ctype_lower( $property ) ) {
2507
-			$property = $this->beau( $property );
2508
-			if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2509
-				$property = substr($property, 1);
2510
-				$listName = lcfirst( substr( $property, 3 ) );
2511
-				$isEx     = TRUE;
2512
-				$isOwn    = TRUE;
2513
-				$this->__info['sys.exclusive-'.$listName] = TRUE;
2514
-			} elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
2515
-				$isOwn    = TRUE;
2516
-				$listName = lcfirst( substr( $property, 3 ) );
2517
-			} elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
2518
-				$isShared = TRUE;
2519
-			}
2520
-		}
2521
-		$fieldLink      = $property . '_id';
2522
-		$exists         = isset( $this->properties[$property] );
2523
-
2524
-		//If not exists and no field link and no list, bail out.
2525
-		if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) {
2526
-			$this->clearModifiers();
2527
-			/**
2528
-			 * Github issue:
2529
-			 * Remove $NULL to directly return NULL #625
2530
-			 * @@ -1097,8 +1097,7 @@ public function &__get( $property )
2531
-			 *		$this->all        = FALSE;
2532
-			 *		$this->via        = NULL;
2533
-			 *
2534
-			 * - $NULL = NULL;
2535
-			 * - return $NULL;
2536
-			 * + return NULL;
2537
-			 *
2538
-			 * leads to regression:
2539
-			 * PHP Stack trace:
2540
-			 * PHP 1. {main}() testje.php:0
2541
-			 * PHP 2. RedBeanPHP\OODBBean->__get() testje.php:22
2542
-			 * Notice: Only variable references should be returned by reference in rb.php on line 2529
2543
-			 */
2544
-			$NULL = NULL;
2545
-			return $NULL;
2546
-		}
2547
-
2548
-		$hasAlias       = (!is_null($this->aliasName));
2549
-		$differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
2550
-									($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2551
-		$hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2552
-		$hasAll         = (boolean) ($this->all);
2553
-
2554
-		//If exists and no list or exits and list not changed, bail out.
2555
-		if ( $exists && ((!$isOwn && !$isShared ) || (!$hasSQL && !$differentAlias && !$hasAll)) ) {
2556
-			$this->clearModifiers();
2557
-			return $this->properties[$property];
2558
-		}
2559
-
2560
-		list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2561
-
2562
-		//If it's another bean, then we load it and return
2563
-		if ( isset( $this->$fieldLink ) ) {
2564
-			$this->__info['tainted'] = TRUE;
2565
-			if ( isset( $this->__info["sys.parentcache.$property"] ) ) {
2566
-				$bean = $this->__info["sys.parentcache.$property"];
2567
-			} else {
2568
-				if ( isset( self::$aliases[$property] ) ) {
2569
-					$type = self::$aliases[$property];
2570
-				} elseif ( $this->fetchType ) {
2571
-					$type = $this->fetchType;
2572
-					$this->fetchType = NULL;
2573
-				} else {
2574
-					$type = $property;
2575
-				}
2576
-				$bean = NULL;
2577
-				if ( !is_null( $this->properties[$fieldLink] ) ) {
2578
-					$bean = $redbean->load( $type, $this->properties[$fieldLink] );
2579
-					//If the IDs dont match, we failed to load, so try autoresolv in that case...
2580
-					if ( $bean->id !== $this->properties[$fieldLink] && self::$autoResolve ) {
2581
-						$type = $this->beanHelper->getToolbox()->getWriter()->inferFetchType( $this->__info['type'], $property );
2582
-						if ( !is_null( $type) ) {
2583
-							$bean = $redbean->load( $type, $this->properties[$fieldLink] );
2584
-							$this->__info["sys.autoresolved.{$property}"] = $type;
2585
-						}
2586
-					}
2587
-				}
2588
-			}
2589
-			$this->properties[$property] = $bean;
2590
-			$this->clearModifiers();
2591
-			return $this->properties[$property];
2592
-		}
2593
-
2594
-		//Implicit: elseif ( $isOwn || $isShared ) {
2595
-		if ( $this->noLoad ) {
2596
-			$beans = array();
2597
-		} elseif ( $isOwn ) {
2598
-			$beans = $this->getOwnList( $listName, $redbean );
2599
-		} else {
2600
-			$beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2601
-		}
2602
-		$this->properties[$property]          = $beans;
2603
-		$this->__info["sys.shadow.$property"] = $beans;
2604
-		$this->__info['tainted']              = TRUE;
1783
+        return $joinSql;
1784
+    }
1785
+
1786
+    /**
1787
+     * Accesses the shared list of a bean.
1788
+     * To access beans that have been associated with the current bean
1789
+     * using a many-to-many relationship use sharedXList where
1790
+     * X is the type of beans in the list.
1791
+     *
1792
+     * Usage:
1793
+     *
1794
+     * <code>
1795
+     * $person = R::load( 'person', $id );
1796
+     * $friends = $person->sharedFriendList;
1797
+     * </code>
1798
+     *
1799
+     * The code snippet above demonstrates how to obtain all beans of
1800
+     * type 'friend' that have associated using an N-M relation.
1801
+     * This is a private method used by the magic getter / accessor.
1802
+     * The example illustrates usage through these accessors.
1803
+     *
1804
+     * @param string  $type    the name of the list you want to retrieve
1805
+     * @param OODB    $redbean instance of the RedBeanPHP OODB class
1806
+     * @param ToolBox $toolbox instance of ToolBox (to get access to core objects)
1807
+     *
1808
+     * @return array
1809
+     */
1810
+    private function getSharedList( $type, $redbean, $toolbox )
1811
+    {
1812
+        $writer = $toolbox->getWriter();
1813
+        if ( $this->via ) {
1814
+            $oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
1815
+            if ( $oldName !== $this->via ) {
1816
+                //set the new renaming rule
1817
+                $writer->renameAssocTable( $oldName, $this->via );
1818
+            }
1819
+            $this->via = NULL;
1820
+        }
1821
+        $beans = array();
1822
+        if ($this->getID()) {
1823
+            $type             = $this->beau( $type );
1824
+            $assocManager     = $redbean->getAssociationManager();
1825
+            $beans            = $assocManager->related( $this, $type, $this->withSql, $this->withParams );
1826
+        }
1827
+        return $beans;
1828
+    }
1829
+
1830
+    /**
1831
+     * Accesses the ownList. The 'own' list contains beans
1832
+     * associated using a one-to-many relation. The own-lists can
1833
+     * be accessed through the magic getter/setter property
1834
+     * ownXList where X is the type of beans in that list.
1835
+     *
1836
+     * Usage:
1837
+     *
1838
+     * <code>
1839
+     * $book = R::load( 'book', $id );
1840
+     * $pages = $book->ownPageList;
1841
+     * </code>
1842
+     *
1843
+     * The example above demonstrates how to access the
1844
+     * pages associated with the book. Since this is a private method
1845
+     * meant to be used by the magic accessors, the example uses the
1846
+     * magic getter instead.
1847
+     *
1848
+     * @param string      $type   name of the list you want to retrieve
1849
+     * @param OODB        $oodb   The RB OODB object database instance
1850
+     *
1851
+     * @return array
1852
+     */
1853
+    private function getOwnList( $type, $redbean )
1854
+    {
1855
+        $type = $this->beau( $type );
1856
+        if ( $this->aliasName ) {
1857
+            $parentField = $this->aliasName;
1858
+            $myFieldLink = $parentField . '_id';
1859
+
1860
+            $this->__info['sys.alias.' . $type] = $this->aliasName;
1861
+
1862
+            $this->aliasName = NULL;
1863
+        } else {
1864
+            $parentField = $this->__info['type'];
1865
+            $myFieldLink = $parentField . '_id';
1866
+        }
1867
+        $beans = array();
1868
+        if ( $this->getID() ) {
1869
+            reset( $this->withParams );
1870
+            $joinSql = $this->parseJoin( $type );
1871
+            $firstKey = count( $this->withParams ) > 0
1872
+                ? key( $this->withParams )
1873
+                : 0;
1874
+            if ( is_int( $firstKey ) ) {
1875
+                $bindings = array_merge( array( $this->getID() ), $this->withParams );
1876
+                $beans = $redbean->find( $type, array(), "{$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
1877
+            } else {
1878
+                $bindings           = $this->withParams;
1879
+                $bindings[':slot0'] = $this->getID();
1880
+                $beans = $redbean->find( $type, array(), "{$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
1881
+            }
1882
+        }
1883
+        foreach ( $beans as $beanFromList ) {
1884
+            $beanFromList->__info['sys.parentcache.' . $parentField] = $this;
1885
+        }
1886
+        return $beans;
1887
+    }
1888
+
1889
+    /**
1890
+     * Initializes a bean. Used by OODB for dispensing beans.
1891
+     * It is not recommended to use this method to initialize beans. Instead
1892
+     * use the OODB object to dispense new beans. You can use this method
1893
+     * if you build your own bean dispensing mechanism.
1894
+     * This is not recommended.
1895
+     *
1896
+     * Unless you know what you are doing, do NOT use this method.
1897
+     * This is for advanced users only!
1898
+     *
1899
+     * @param string     $type       type of the new bean
1900
+     * @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model
1901
+     *
1902
+     * @return void
1903
+     */
1904
+    public function initializeForDispense( $type, $beanhelper = NULL )
1905
+    {
1906
+        $this->beanHelper         = $beanhelper;
1907
+        $this->__info['type']     = $type;
1908
+        $this->__info['sys.id']   = 'id';
1909
+        $this->__info['sys.orig'] = array( 'id' => 0 );
1910
+        $this->__info['tainted']  = TRUE;
1911
+        $this->__info['changed']  = TRUE;
1912
+        $this->__info['changelist'] = array();
1913
+        if ( $beanhelper ) {
1914
+            $this->__info['model'] = $this->beanHelper->getModelForBean( $this );
1915
+        }
1916
+        $this->properties['id']   = 0;
1917
+    }
1918
+
1919
+    /**
1920
+     * Sets the Bean Helper. Normally the Bean Helper is set by OODB.
1921
+     * Here you can change the Bean Helper. The Bean Helper is an object
1922
+     * providing access to a toolbox for the bean necessary to retrieve
1923
+     * nested beans (bean lists: ownBean, sharedBean) without the need to
1924
+     * rely on static calls to the facade (or make this class dep. on OODB).
1925
+     *
1926
+     * @param BeanHelper $helper helper to use for this bean
1927
+     *
1928
+     * @return void
1929
+     */
1930
+    public function setBeanHelper( BeanHelper $helper )
1931
+    {
1932
+        $this->beanHelper = $helper;
1933
+    }
1934
+
1935
+    /**
1936
+     * Returns an ArrayIterator so you can treat the bean like
1937
+     * an array with the properties container as its contents.
1938
+     * This method is meant for PHP and allows you to access beans as if
1939
+     * they were arrays, i.e. using array notation:
1940
+     *
1941
+     * <code>
1942
+     * $bean[$key] = $value;
1943
+     * </code>
1944
+     *
1945
+     * Note that not all PHP functions work with the array interface.
1946
+     *
1947
+     * @return ArrayIterator
1948
+     */
1949
+    public function getIterator()
1950
+    {
1951
+        return new \ArrayIterator( $this->properties );
1952
+    }
1953
+
1954
+    /**
1955
+     * Imports all values from an associative array $array. Chainable.
1956
+     * This method imports the values in the first argument as bean
1957
+     * propery and value pairs. Use the second parameter to provide a
1958
+     * selection. If a selection array is passed, only the entries
1959
+     * having keys mentioned in the selection array will be imported.
1960
+     * Set the third parameter to TRUE to preserve spaces in selection keys.
1961
+     *
1962
+     * @param array        $array     what you want to import
1963
+     * @param string|array $selection selection of values
1964
+     * @param boolean      $notrim    if TRUE selection keys will NOT be trimmed
1965
+     *
1966
+     * @return OODBBean
1967
+     */
1968
+    public function import( $array, $selection = FALSE, $notrim = FALSE )
1969
+    {
1970
+        if ( is_string( $selection ) ) {
1971
+            $selection = explode( ',', $selection );
1972
+        }
1973
+        if ( is_array( $selection ) ) {
1974
+            if ( $notrim ) {
1975
+                $selected = array_flip($selection);
1976
+            } else {
1977
+                $selected = array();
1978
+                foreach ( $selection as $key => $select ) {
1979
+                    $selected[trim( $select )] = TRUE;
1980
+                }
1981
+            }
1982
+        } else {
1983
+            $selected = FALSE;
1984
+        }
1985
+        foreach ( $array as $key => $value ) {
1986
+            if ( $key != '__info' ) {
1987
+                if ( !$selected || isset( $selected[$key] ) ) {
1988
+                    if ( is_array($value ) ) {
1989
+                        if ( isset( $value['_type'] ) ) {
1990
+                            $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] );
1991
+                            unset( $value['_type'] );
1992
+                            $bean->import($value);
1993
+                            $this->$key = $bean;
1994
+                        } else {
1995
+                            $listBeans = array();
1996
+                            foreach( $value as $listKey => $listItem ) {
1997
+                                $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] );
1998
+                                unset( $listItem['_type'] );
1999
+                                $bean->import($listItem);
2000
+                                $list = &$this->$key;
2001
+                                $list[ $listKey ] = $bean;
2002
+                            }
2003
+                        }
2004
+                    } else {
2005
+                        $this->$key = $value;
2006
+                    }
2007
+                }
2008
+            }
2009
+        }
2010
+        return $this;
2011
+    }
2012
+
2013
+    /**
2014
+     * Imports an associative array directly into the
2015
+     * internal property array of the bean as well as the
2016
+     * meta property sys.orig and sets the changed flag to FALSE.
2017
+     * This is used by the repository objects to inject database rows
2018
+     * into the beans. It is not recommended to use this method outside
2019
+     * of a bean repository.
2020
+     *
2021
+     * @param array $row a database row
2022
+     *
2023
+     * @return self
2024
+     */
2025
+    public function importRow( $row )
2026
+    {
2027
+        $this->properties = $row;
2028
+        $this->__info['sys.orig'] = $row;
2029
+        $this->__info['changed'] = FALSE;
2030
+        return $this;
2031
+    }
2032
+
2033
+    /**
2034
+     * Imports data from another bean. Chainable.
2035
+     * Copies the properties from the source bean to the internal
2036
+     * property list.
2037
+     *
2038
+     * Usage:
2039
+     *
2040
+     * <code>
2041
+     * $copy->importFrom( $bean );
2042
+     * </code>
2043
+     *
2044
+     * The example above demonstrates how to make a shallow copy
2045
+     * of a bean using the importFrom() method.
2046
+     *
2047
+     * @param OODBBean $sourceBean the source bean to take properties from
2048
+     *
2049
+     * @return OODBBean
2050
+     */
2051
+    public function importFrom( OODBBean $sourceBean )
2052
+    {
2053
+        $this->__info['tainted'] = TRUE;
2054
+        $this->__info['changed'] = TRUE;
2055
+        $this->properties = $sourceBean->properties;
2056
+
2057
+        return $this;
2058
+    }
2059
+
2060
+    /**
2061
+     * Injects the properties of another bean but keeps the original ID.
2062
+     * Just like import() but keeps the original ID.
2063
+     * Chainable.
2064
+     *
2065
+     * @param OODBBean $otherBean the bean whose properties you would like to copy
2066
+     *
2067
+     * @return OODBBean
2068
+     */
2069
+    public function inject( OODBBean $otherBean )
2070
+    {
2071
+        $myID = $this->properties['id'];
2072
+        $this->import( $otherBean->export( FALSE, FALSE, TRUE ) );
2073
+        $this->id = $myID;
2074
+
2075
+        return $this;
2076
+    }
2077
+
2078
+    /**
2079
+     * Exports the bean as an array.
2080
+     * This function exports the contents of a bean to an array and returns
2081
+     * the resulting array. Depending on the parameters you can also
2082
+     * export an entire graph of beans, apply filters or exclude meta data.
2083
+     *
2084
+     * Usage:
2085
+     *
2086
+     * <code>
2087
+     * $bookData = $book->export( TRUE, TRUE, FALSE, [ 'author' ] );
2088
+     * </code>
2089
+     *
2090
+     * The example above exports all bean properties to an array
2091
+     * called $bookData including its meta data, parent objects but without
2092
+     * any beans of type 'author'.
2093
+     *
2094
+     * @param boolean $meta    set to TRUE if you want to export meta data as well
2095
+     * @param boolean $parents set to TRUE if you want to export parents as well
2096
+     * @param boolean $onlyMe  set to TRUE if you want to export only this bean
2097
+     * @param array   $filters optional whitelist for export
2098
+     *
2099
+     * @return array
2100
+     */
2101
+    public function export( $meta = FALSE, $parents = FALSE, $onlyMe = FALSE, $filters = array() )
2102
+    {
2103
+        $arr = array();
2104
+        if ( $parents ) {
2105
+            foreach ( $this as $key => $value ) {
2106
+                if ( substr( $key, -3 ) != '_id' ) continue;
2107
+
2108
+                $prop = substr( $key, 0, strlen( $key ) - 3 );
2109
+                $this->$prop;
2110
+            }
2111
+        }
2112
+        $hasFilters = is_array( $filters ) && count( $filters );
2113
+        foreach ( $this as $key => $value ) {
2114
+            if ( !$onlyMe && is_array( $value ) ) {
2115
+                $vn = array();
2116
+
2117
+                foreach ( $value as $i => $b ) {
2118
+                    if ( !( $b instanceof OODBBean ) ) continue;
2119
+                    $vn[] = $b->export( $meta, FALSE, FALSE, $filters );
2120
+                    $value = $vn;
2121
+                }
2122
+            } elseif ( $value instanceof OODBBean ) { if ( $hasFilters ) { //has to be on one line, otherwise code coverage miscounts as miss
2123
+                    if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue;
2124
+                }
2125
+                $value = $value->export( $meta, $parents, FALSE, $filters );
2126
+            }
2127
+            $arr[$key] = $value;
2128
+        }
2129
+        if ( $meta ) {
2130
+            $arr['__info'] = $this->__info;
2131
+        }
2132
+        return $arr;
2133
+    }
2134
+
2135
+    /**
2136
+     * Implements isset() function for use as an array.
2137
+     * This allows you to use isset() on bean properties.
2138
+     *
2139
+     * Usage:
2140
+     *
2141
+     * <code>
2142
+     * $book->title = 'my book';
2143
+     * echo isset($book['title']); //TRUE
2144
+     * </code>
2145
+     *
2146
+     * The example illustrates how one can apply the
2147
+     * isset() function to a bean.
2148
+     *
2149
+     * @param string $property name of the property you want to check
2150
+     *
2151
+     * @return boolean
2152
+     */
2153
+    public function __isset( $property )
2154
+    {
2155
+        $property = $this->beau( $property );
2156
+        if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2157
+            $property = substr($property, 1);
2158
+        }
2159
+        return isset( $this->properties[$property] );
2160
+    }
2161
+
2162
+    /**
2163
+     * Checks whether a related bean exists.
2164
+     * For instance if a post bean has a related author, this method
2165
+     * can be used to check if the author is set without loading the author.
2166
+     * This method works by checking the related ID-field.
2167
+     *
2168
+     * @param string $property name of the property you wish to check
2169
+     *
2170
+     * @return boolean
2171
+     */
2172
+    public function exists( $property )
2173
+    {
2174
+        $property = $this->beau( $property );
2175
+        /* fixes issue #549, see Base/Bean test */
2176
+        $hiddenRelationField = "{$property}_id";
2177
+        if ( array_key_exists( $hiddenRelationField, $this->properties ) ) {
2178
+            if ( !is_null( $this->properties[$hiddenRelationField] ) ) {
2179
+                return TRUE;
2180
+            }
2181
+        }
2182
+        return FALSE;
2183
+    }
2184
+
2185
+    /**
2186
+     * Returns the ID of the bean.
2187
+     * If for some reason the ID has not been set, this method will
2188
+     * return NULL. This is actually the same as accessing the
2189
+     * id property using $bean->id. The ID of a bean is it's primary
2190
+     * key and should always correspond with a table column named
2191
+     * 'id'.
2192
+     *
2193
+     * @return string|null
2194
+     */
2195
+    public function getID()
2196
+    {
2197
+        return ( isset( $this->properties['id'] ) ) ? (string) $this->properties['id'] : NULL;
2198
+    }
2199
+
2200
+    /**
2201
+     * Unsets a property of a bean.
2202
+     * Magic method, gets called implicitly when
2203
+     * performing the unset() operation
2204
+     * on a bean property.
2205
+     *
2206
+     * @param  string $property property to unset
2207
+     *
2208
+     * @return void
2209
+     */
2210
+    public function __unset( $property )
2211
+    {
2212
+        $property = $this->beau( $property );
2213
+
2214
+        if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2215
+            $property = substr($property, 1);
2216
+        }
2217
+        unset( $this->properties[$property] );
2218
+        $shadowKey = 'sys.shadow.'.$property;
2219
+        if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] );
2220
+        //also clear modifiers
2221
+        $this->clearModifiers();
2222
+        return;
2223
+    }
2224
+
2225
+    /**
2226
+     * Adds WHERE clause conditions to ownList retrieval.
2227
+     * For instance to get the pages that belong to a book you would
2228
+     * issue the following command: $book->ownPage
2229
+     * However, to order these pages by number use:
2230
+     *
2231
+     * <code>
2232
+     * $book->with(' ORDER BY `number` ASC ')->ownPage
2233
+     * </code>
2234
+     *
2235
+     * the additional SQL snippet will be merged into the final
2236
+     * query.
2237
+     *
2238
+     * @param string $sql      SQL to be added to retrieval query.
2239
+     * @param array  $bindings array with parameters to bind to SQL snippet
2240
+     *
2241
+     * @return OODBBean
2242
+     */
2243
+    public function with( $sql, $bindings = array() )
2244
+    {
2245
+        $this->withSql    = $sql;
2246
+        $this->withParams = $bindings;
2247
+        return $this;
2248
+    }
2249
+
2250
+    /**
2251
+     * Just like with(). Except that this method prepends the SQL query snippet
2252
+     * with AND which makes it slightly more comfortable to use a conditional
2253
+     * SQL snippet. For instance to filter an own-list with pages (belonging to
2254
+     * a book) on specific chapters you can use:
2255
+     *
2256
+     * $book->withCondition(' chapter = 3 ')->ownPage
2257
+     *
2258
+     * This will return in the own list only the pages having 'chapter == 3'.
2259
+     *
2260
+     * @param string $sql      SQL to be added to retrieval query (prefixed by AND)
2261
+     * @param array  $bindings array with parameters to bind to SQL snippet
2262
+     *
2263
+     * @return OODBBean
2264
+     */
2265
+    public function withCondition( $sql, $bindings = array() )
2266
+    {
2267
+        $this->withSql    = ' AND ' . $sql;
2268
+        $this->withParams = $bindings;
2269
+        return $this;
2270
+    }
2271
+
2272
+    /**
2273
+     * Tells the bean to (re)load the following list without any
2274
+     * conditions. If you have an ownList or sharedList with a
2275
+     * condition you can use this method to reload the entire list.
2276
+     *
2277
+     * Usage:
2278
+     *
2279
+     * <code>
2280
+     * $bean->with( ' LIMIT 3 ' )->ownPage; //Just 3
2281
+     * $bean->all()->ownPage; //Reload all pages
2282
+     * </code>
2283
+     *
2284
+     * @return self
2285
+     */
2286
+    public function all()
2287
+    {
2288
+        $this->all = TRUE;
2289
+        return $this;
2290
+    }
2291
+
2292
+    /**
2293
+     * Tells the bean to only access the list but not load
2294
+     * its contents. Use this if you only want to add something to a list
2295
+     * and you have no interest in retrieving its contents from the database.
2296
+     *
2297
+     * Usage:
2298
+     *
2299
+     * <code>
2300
+     * $book->noLoad()->ownPage[] = $newPage;
2301
+     * </code>
2302
+     *
2303
+     * In the example above we add the $newPage bean to the
2304
+     * page list of book without loading all the pages first.
2305
+     * If you know in advance that you are not going to use
2306
+     * the contents of the list, you may use the noLoad() modifier
2307
+     * to make sure the queries required to load the list will not
2308
+     * be executed.
2309
+     *
2310
+     * @return self
2311
+     */
2312
+    public function noLoad()
2313
+    {
2314
+        $this->noLoad = TRUE;
2315
+        return $this;
2316
+    }
2317
+
2318
+    /**
2319
+     * Prepares an own-list to use an alias. This is best explained using
2320
+     * an example. Imagine a project and a person. The project always involves
2321
+     * two persons: a teacher and a student. The person beans have been aliased in this
2322
+     * case, so to the project has a teacher_id pointing to a person, and a student_id
2323
+     * also pointing to a person. Given a project, we obtain the teacher like this:
2324
+     *
2325
+     * <code>
2326
+     * $project->fetchAs('person')->teacher;
2327
+     * </code>
2328
+     *
2329
+     * Now, if we want all projects of a teacher we cant say:
2330
+     *
2331
+     * <code>
2332
+     * $teacher->ownProject
2333
+     * </code>
2334
+     *
2335
+     * because the $teacher is a bean of type 'person' and no project has been
2336
+     * assigned to a person. Instead we use the alias() method like this:
2337
+     *
2338
+     * <code>
2339
+     * $teacher->alias('teacher')->ownProject
2340
+     * </code>
2341
+     *
2342
+     * now we get the projects associated with the person bean aliased as
2343
+     * a teacher.
2344
+     *
2345
+     * @param string $aliasName the alias name to use
2346
+     *
2347
+     * @return OODBBean
2348
+     */
2349
+    public function alias( $aliasName )
2350
+    {
2351
+        $this->aliasName = $this->beau( $aliasName );
2352
+        return $this;
2353
+    }
2354
+
2355
+    /**
2356
+     * Returns properties of bean as an array.
2357
+     * This method returns the raw internal property list of the
2358
+     * bean. Only use this method for optimization purposes. Otherwise
2359
+     * use the export() method to export bean data to arrays.
2360
+     *
2361
+     * @return array
2362
+     */
2363
+    public function getProperties()
2364
+    {
2365
+        return $this->properties;
2366
+    }
2367
+
2368
+    /**
2369
+     * Returns properties of bean as an array.
2370
+     * This method returns the raw internal property list of the
2371
+     * bean. Only use this method for optimization purposes. Otherwise
2372
+     * use the export() method to export bean data to arrays.
2373
+     * This method returns an array with the properties array and
2374
+     * the type (string).
2375
+     *
2376
+     * @return array
2377
+     */
2378
+    public function getPropertiesAndType()
2379
+    {
2380
+        return array( $this->properties, $this->__info['type'] );
2381
+    }
2382
+
2383
+    /**
2384
+     * Turns a camelcase property name into an underscored property name.
2385
+     *
2386
+     * Examples:
2387
+     *
2388
+     * - oneACLRoute -> one_acl_route
2389
+     * - camelCase -> camel_case
2390
+     *
2391
+     * Also caches the result to improve performance.
2392
+     *
2393
+     * @param string $property property to un-beautify
2394
+     *
2395
+     * @return string
2396
+     */
2397
+    public function beau( $property )
2398
+    {
2399
+        static $beautifulColumns = array();
2400
+
2401
+        if ( ctype_lower( $property ) ) return $property;
2402
+        if (
2403
+            ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )
2404
+            || ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) )
2405
+            || ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) )
2406
+        ) {
2407
+
2408
+            $property = preg_replace( '/List$/', '', $property );
2409
+            return $property;
2410
+        }
2411
+        if ( !isset( $beautifulColumns[$property] ) ) {
2412
+            $beautifulColumns[$property] = AQueryWriter::camelsSnake( $property );
2413
+        }
2414
+        return $beautifulColumns[$property];
2415
+    }
2416
+
2417
+    /**
2418
+     * Modifiers are a powerful concept in RedBeanPHP, they make it possible
2419
+     * to change the way a property has to be loaded.
2420
+     * RedBeanPHP uses property modifiers using a prefix notation like this:
2421
+     *
2422
+     * <code>
2423
+     * $book->fetchAs('page')->cover;
2424
+     * </code>
2425
+     *
2426
+     * Here, we load a bean of type page, identified by the cover property
2427
+     * (or cover_id in the database). Because the modifier is called before
2428
+     * the property is accessed, the modifier must be remembered somehow,
2429
+     * this changes the state of the bean. Accessing a property causes the
2430
+     * bean to clear its modifiers. To clear the modifiers manually you can
2431
+     * use this method.
2432
+     *
2433
+     * Usage:
2434
+     *
2435
+     * <code>
2436
+     * $book->with( 'LIMIT 1' );
2437
+     * $book->clearModifiers()->ownPageList;
2438
+     * </code>
2439
+     *
2440
+     * In the example above, the 'LIMIT 1' clause is
2441
+     * cleared before accessing the pages of the book, causing all pages
2442
+     * to be loaded in the list instead of just one.
2443
+     *
2444
+     * @return self
2445
+     */
2446
+    public function clearModifiers()
2447
+    {
2448
+        $this->withSql    = '';
2449
+        $this->withParams = array();
2450
+        $this->aliasName  = NULL;
2451
+        $this->fetchType  = NULL;
2452
+        $this->noLoad     = FALSE;
2453
+        $this->all        = FALSE;
2454
+        $this->via        = NULL;
2455
+        return $this;
2456
+    }
2457
+
2458
+    /**
2459
+     * Determines whether a list is opened in exclusive mode or not.
2460
+     * If a list has been opened in exclusive mode this method will return TRUE,
2461
+     * othwerwise it will return FALSE.
2462
+     *
2463
+     * @param string $listName name of the list to check
2464
+     *
2465
+     * @return boolean
2466
+     */
2467
+    public function isListInExclusiveMode( $listName )
2468
+    {
2469
+        $listName = $this->beau( $listName );
2470
+
2471
+        if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) {
2472
+            $listName = substr($listName, 1);
2473
+        }
2474
+        $listName = lcfirst( substr( $listName, 3 ) );
2475
+        return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] );
2476
+    }
2477
+
2478
+    /**
2479
+     * Magic Getter. Gets the value for a specific property in the bean.
2480
+     * If the property does not exist this getter will make sure no error
2481
+     * occurs. This is because RedBean allows you to query (probe) for
2482
+     * properties. If the property can not be found this method will
2483
+     * return NULL instead.
2484
+     *
2485
+     * Usage:
2486
+     *
2487
+     * <code>
2488
+     * $title = $book->title;
2489
+     * $pages = $book->ownPageList;
2490
+     * $tags  = $book->sharedTagList;
2491
+     * </code>
2492
+     *
2493
+     * The example aboves lists several ways to invoke the magic getter.
2494
+     * You can use the magic setter to access properties, own-lists,
2495
+     * exclusive own-lists (xownLists) and shared-lists.
2496
+     *
2497
+     * @param string $property name of the property you wish to obtain the value of
2498
+     *
2499
+     * @return mixed
2500
+     */
2501
+    public function &__get( $property )
2502
+    {
2503
+        $isEx          = FALSE;
2504
+        $isOwn         = FALSE;
2505
+        $isShared      = FALSE;
2506
+        if ( !ctype_lower( $property ) ) {
2507
+            $property = $this->beau( $property );
2508
+            if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2509
+                $property = substr($property, 1);
2510
+                $listName = lcfirst( substr( $property, 3 ) );
2511
+                $isEx     = TRUE;
2512
+                $isOwn    = TRUE;
2513
+                $this->__info['sys.exclusive-'.$listName] = TRUE;
2514
+            } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
2515
+                $isOwn    = TRUE;
2516
+                $listName = lcfirst( substr( $property, 3 ) );
2517
+            } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
2518
+                $isShared = TRUE;
2519
+            }
2520
+        }
2521
+        $fieldLink      = $property . '_id';
2522
+        $exists         = isset( $this->properties[$property] );
2523
+
2524
+        //If not exists and no field link and no list, bail out.
2525
+        if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) {
2526
+            $this->clearModifiers();
2527
+            /**
2528
+             * Github issue:
2529
+             * Remove $NULL to directly return NULL #625
2530
+             * @@ -1097,8 +1097,7 @@ public function &__get( $property )
2531
+             *		$this->all        = FALSE;
2532
+             *		$this->via        = NULL;
2533
+             *
2534
+             * - $NULL = NULL;
2535
+             * - return $NULL;
2536
+             * + return NULL;
2537
+             *
2538
+             * leads to regression:
2539
+             * PHP Stack trace:
2540
+             * PHP 1. {main}() testje.php:0
2541
+             * PHP 2. RedBeanPHP\OODBBean->__get() testje.php:22
2542
+             * Notice: Only variable references should be returned by reference in rb.php on line 2529
2543
+             */
2544
+            $NULL = NULL;
2545
+            return $NULL;
2546
+        }
2547
+
2548
+        $hasAlias       = (!is_null($this->aliasName));
2549
+        $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
2550
+                                    ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2551
+        $hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2552
+        $hasAll         = (boolean) ($this->all);
2553
+
2554
+        //If exists and no list or exits and list not changed, bail out.
2555
+        if ( $exists && ((!$isOwn && !$isShared ) || (!$hasSQL && !$differentAlias && !$hasAll)) ) {
2556
+            $this->clearModifiers();
2557
+            return $this->properties[$property];
2558
+        }
2559
+
2560
+        list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2561
+
2562
+        //If it's another bean, then we load it and return
2563
+        if ( isset( $this->$fieldLink ) ) {
2564
+            $this->__info['tainted'] = TRUE;
2565
+            if ( isset( $this->__info["sys.parentcache.$property"] ) ) {
2566
+                $bean = $this->__info["sys.parentcache.$property"];
2567
+            } else {
2568
+                if ( isset( self::$aliases[$property] ) ) {
2569
+                    $type = self::$aliases[$property];
2570
+                } elseif ( $this->fetchType ) {
2571
+                    $type = $this->fetchType;
2572
+                    $this->fetchType = NULL;
2573
+                } else {
2574
+                    $type = $property;
2575
+                }
2576
+                $bean = NULL;
2577
+                if ( !is_null( $this->properties[$fieldLink] ) ) {
2578
+                    $bean = $redbean->load( $type, $this->properties[$fieldLink] );
2579
+                    //If the IDs dont match, we failed to load, so try autoresolv in that case...
2580
+                    if ( $bean->id !== $this->properties[$fieldLink] && self::$autoResolve ) {
2581
+                        $type = $this->beanHelper->getToolbox()->getWriter()->inferFetchType( $this->__info['type'], $property );
2582
+                        if ( !is_null( $type) ) {
2583
+                            $bean = $redbean->load( $type, $this->properties[$fieldLink] );
2584
+                            $this->__info["sys.autoresolved.{$property}"] = $type;
2585
+                        }
2586
+                    }
2587
+                }
2588
+            }
2589
+            $this->properties[$property] = $bean;
2590
+            $this->clearModifiers();
2591
+            return $this->properties[$property];
2592
+        }
2593
+
2594
+        //Implicit: elseif ( $isOwn || $isShared ) {
2595
+        if ( $this->noLoad ) {
2596
+            $beans = array();
2597
+        } elseif ( $isOwn ) {
2598
+            $beans = $this->getOwnList( $listName, $redbean );
2599
+        } else {
2600
+            $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2601
+        }
2602
+        $this->properties[$property]          = $beans;
2603
+        $this->__info["sys.shadow.$property"] = $beans;
2604
+        $this->__info['tainted']              = TRUE;
2605 2605
 		
2606
-		$this->clearModifiers();
2607
-		return $this->properties[$property];
2608
-
2609
-	}
2610
-
2611
-	/**
2612
-	 * Magic Setter. Sets the value for a specific property.
2613
-	 * This setter acts as a hook for OODB to mark beans as tainted.
2614
-	 * The tainted meta property can be retrieved using getMeta("tainted").
2615
-	 * The tainted meta property indicates whether a bean has been modified and
2616
-	 * can be used in various caching mechanisms.
2617
-	 *
2618
-	 * @param string $property name of the property you wish to assign a value to
2619
-	 * @param  mixed $value    the value you want to assign
2620
-	 *
2621
-	 * @return void
2622
-	 */
2623
-	public function __set( $property, $value )
2624
-	{
2625
-		$isEx          = FALSE;
2626
-		$isOwn         = FALSE;
2627
-		$isShared      = FALSE;
2628
-
2629
-		if ( !ctype_lower( $property ) ) {
2630
-			$property = $this->beau( $property );
2631
-			if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2632
-				$property = substr($property, 1);
2633
-				$listName = lcfirst( substr( $property, 3 ) );
2634
-				$isEx     = TRUE;
2635
-				$isOwn    = TRUE;
2636
-				$this->__info['sys.exclusive-'.$listName] = TRUE;
2637
-			} elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
2638
-				$isOwn    = TRUE;
2639
-				$listName = lcfirst( substr( $property, 3 ) );
2640
-			} elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
2641
-				$isShared = TRUE;
2642
-			}
2643
-		} elseif ( self::$convertArraysToJSON && is_array( $value ) ) {
2644
-			$value = json_encode( $value );
2645
-		}
2646
-
2647
-		$hasAlias       = (!is_null($this->aliasName));
2648
-		$differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
2649
-								($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2650
-		$hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2651
-		$exists         = isset( $this->properties[$property] );
2652
-		$fieldLink      = $property . '_id';
2653
-		$isFieldLink	= (($pos = strrpos($property, '_id')) !== FALSE) && array_key_exists( ($fieldName = substr($property, 0, $pos)), $this->properties );
2654
-
2655
-
2656
-		if ( ($isOwn || $isShared) &&  (!$exists || $hasSQL || $differentAlias) ) {
2657
-
2658
-			if ( !$this->noLoad ) {
2659
-				list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2660
-				if ( $isOwn ) {
2661
-					$beans = $this->getOwnList( $listName, $redbean );
2662
-				} else {
2663
-					$beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2664
-				}
2665
-				$this->__info["sys.shadow.$property"] = $beans;
2666
-			}
2667
-		}
2668
-
2669
-		$this->clearModifiers();
2670
-
2671
-		$this->__info['tainted'] = TRUE;
2672
-		$this->__info['changed'] = TRUE;
2673
-		array_push( $this->__info['changelist'], $property );
2674
-
2675
-		if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) {
2676
-			if ( is_null( $value ) || $value === FALSE ) {
2677
-
2678
-				unset( $this->properties[ $property ]);
2679
-				$this->properties[ $fieldLink ] = NULL;
2680
-
2681
-				return;
2682
-			} else {
2683
-				throw new RedException( 'Cannot cast to bean.' );
2684
-			}
2685
-		}
2606
+        $this->clearModifiers();
2607
+        return $this->properties[$property];
2608
+
2609
+    }
2610
+
2611
+    /**
2612
+     * Magic Setter. Sets the value for a specific property.
2613
+     * This setter acts as a hook for OODB to mark beans as tainted.
2614
+     * The tainted meta property can be retrieved using getMeta("tainted").
2615
+     * The tainted meta property indicates whether a bean has been modified and
2616
+     * can be used in various caching mechanisms.
2617
+     *
2618
+     * @param string $property name of the property you wish to assign a value to
2619
+     * @param  mixed $value    the value you want to assign
2620
+     *
2621
+     * @return void
2622
+     */
2623
+    public function __set( $property, $value )
2624
+    {
2625
+        $isEx          = FALSE;
2626
+        $isOwn         = FALSE;
2627
+        $isShared      = FALSE;
2628
+
2629
+        if ( !ctype_lower( $property ) ) {
2630
+            $property = $this->beau( $property );
2631
+            if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2632
+                $property = substr($property, 1);
2633
+                $listName = lcfirst( substr( $property, 3 ) );
2634
+                $isEx     = TRUE;
2635
+                $isOwn    = TRUE;
2636
+                $this->__info['sys.exclusive-'.$listName] = TRUE;
2637
+            } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
2638
+                $isOwn    = TRUE;
2639
+                $listName = lcfirst( substr( $property, 3 ) );
2640
+            } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
2641
+                $isShared = TRUE;
2642
+            }
2643
+        } elseif ( self::$convertArraysToJSON && is_array( $value ) ) {
2644
+            $value = json_encode( $value );
2645
+        }
2646
+
2647
+        $hasAlias       = (!is_null($this->aliasName));
2648
+        $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
2649
+                                ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2650
+        $hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2651
+        $exists         = isset( $this->properties[$property] );
2652
+        $fieldLink      = $property . '_id';
2653
+        $isFieldLink	= (($pos = strrpos($property, '_id')) !== FALSE) && array_key_exists( ($fieldName = substr($property, 0, $pos)), $this->properties );
2654
+
2655
+
2656
+        if ( ($isOwn || $isShared) &&  (!$exists || $hasSQL || $differentAlias) ) {
2657
+
2658
+            if ( !$this->noLoad ) {
2659
+                list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2660
+                if ( $isOwn ) {
2661
+                    $beans = $this->getOwnList( $listName, $redbean );
2662
+                } else {
2663
+                    $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2664
+                }
2665
+                $this->__info["sys.shadow.$property"] = $beans;
2666
+            }
2667
+        }
2668
+
2669
+        $this->clearModifiers();
2670
+
2671
+        $this->__info['tainted'] = TRUE;
2672
+        $this->__info['changed'] = TRUE;
2673
+        array_push( $this->__info['changelist'], $property );
2674
+
2675
+        if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) {
2676
+            if ( is_null( $value ) || $value === FALSE ) {
2677
+
2678
+                unset( $this->properties[ $property ]);
2679
+                $this->properties[ $fieldLink ] = NULL;
2680
+
2681
+                return;
2682
+            } else {
2683
+                throw new RedException( 'Cannot cast to bean.' );
2684
+            }
2685
+        }
2686 2686
 		
2687
-		if ( $isFieldLink ){
2688
-			unset( $this->properties[ $fieldName ]);
2689
-			$this->properties[ $property ] = NULL;
2690
-		}
2691
-
2692
-
2693
-		if ( $value === FALSE ) {
2694
-			$value = '0';
2695
-		} elseif ( $value === TRUE ) {
2696
-			$value = '1';
2697
-			/* for some reason there is some kind of bug in xdebug so that it doesnt count this line otherwise... */
2698
-		} elseif ( $value instanceof \DateTime ) { $value = $value->format( 'Y-m-d H:i:s' ); }
2699
-		$this->properties[$property] = $value;
2700
-	}
2701
-
2702
-	/**
2703
-	 * @deprecated
2704
-	 *
2705
-	 * Sets a property of the bean allowing you to keep track of
2706
-	 * the state yourself. This method sets a property of the bean and
2707
-	 * allows you to control how the state of the bean will be affected.
2708
-	 *
2709
-	 * While there may be some circumstances where this method is needed,
2710
-	 * this method is considered to be extremely dangerous.
2711
-	 * This method is only for advanced users.
2712
-	 *
2713
-	 * @param string  $property     property
2714
-	 * @param mixed   $value        value
2715
-	 * @param boolean $updateShadow whether you want to update the shadow
2716
-	 * @param boolean $taint        whether you want to mark the bean as tainted
2717
-	 *
2718
-	 * @return void
2719
-	 */
2720
-	public function setProperty( $property, $value, $updateShadow = FALSE, $taint = FALSE )
2721
-	{
2722
-		$this->properties[$property] = $value;
2723
-
2724
-		if ( $updateShadow ) {
2725
-			$this->__info['sys.shadow.' . $property] = $value;
2726
-		}
2727
-
2728
-		if ( $taint ) {
2729
-			$this->__info['tainted'] = TRUE;
2730
-			$this->__info['changed'] = TRUE;
2731
-		}
2732
-	}
2733
-
2734
-	/**
2735
-	 * Returns the value of a meta property. A meta property
2736
-	 * contains additional information about the bean object that will not
2737
-	 * be stored in the database. Meta information is used to instruct
2738
-	 * RedBeanPHP as well as other systems how to deal with the bean.
2739
-	 * If the property cannot be found this getter will return NULL instead.
2740
-	 *
2741
-	 * Example:
2742
-	 *
2743
-	 * <code>
2744
-	 * $bean->setMeta( 'flush-cache', TRUE );
2745
-	 * </code>
2746
-	 *
2747
-	 * RedBeanPHP also stores meta data in beans, this meta data uses
2748
-	 * keys prefixed with 'sys.' (system).
2749
-	 *
2750
-	 * @param string $path    path to property in meta data
2751
-	 * @param mixed  $default default value
2752
-	 *
2753
-	 * @return mixed
2754
-	 */
2755
-	public function getMeta( $path, $default = NULL )
2756
-	{
2757
-		return ( isset( $this->__info[$path] ) ) ? $this->__info[$path] : $default;
2758
-	}
2759
-
2760
-	/**
2761
-	 * Gets and unsets a meta property.
2762
-	 * Moves a meta property out of the bean.
2763
-	 * This is a short-cut method that can be used instead
2764
-	 * of combining a get/unset.
2765
-	 *
2766
-	 * @param string $path    path to property in meta data
2767
-	 * @param mixed  $default default value
2768
-	 *
2769
-	 * @return mixed
2770
-	 */
2771
-	public function moveMeta( $path, $value = NULL )
2772
-	{
2773
-		if ( isset( $this->__info[$path] ) ) {
2774
-			$value = $this->__info[ $path ];
2775
-			unset( $this->__info[ $path ] );
2776
-		}
2777
-		return $value;
2778
-	}
2779
-
2780
-	/**
2781
-	 * Stores a value in the specified Meta information property.
2782
-	 * The first argument should be the key to store the value under,
2783
-	 * the second argument should be the value. It is common to use
2784
-	 * a path-like notation for meta data in RedBeanPHP like:
2785
-	 * 'my.meta.data', however the dots are purely for readability, the
2786
-	 * meta data methods do not store nested structures or hierarchies.
2787
-	 *
2788
-	 * @param string $path  path / key to store value under
2789
-	 * @param mixed  $value value to store in bean (not in database) as meta data
2790
-	 *
2791
-	 * @return OODBBean
2792
-	 */
2793
-	public function setMeta( $path, $value )
2794
-	{
2795
-		$this->__info[$path] = $value;
2796
-		if ( $path == 'type' && !empty($this->beanHelper)) {
2797
-			$this->__info['model'] = $this->beanHelper->getModelForBean( $this );
2798
-		}
2799
-
2800
-		return $this;
2801
-	}
2802
-
2803
-	/**
2804
-	 * Copies the meta information of the specified bean
2805
-	 * This is a convenience method to enable you to
2806
-	 * exchange meta information easily.
2807
-	 *
2808
-	 * @param OODBBean $bean bean to copy meta data of
2809
-	 *
2810
-	 * @return OODBBean
2811
-	 */
2812
-	public function copyMetaFrom( OODBBean $bean )
2813
-	{
2814
-		$this->__info = $bean->__info;
2815
-
2816
-		return $this;
2817
-	}
2818
-
2819
-	/**
2820
-	 * Sends the call to the registered model.
2821
-	 * This method can also be used to override bean behaviour.
2822
-	 * In that case you don't want an error or exception to be triggered
2823
-	 * if the method does not exist in the model (because it's optional).
2824
-	 * Unfortunately we cannot add an extra argument to __call() for this
2825
-	 * because the signature is fixed. Another option would be to set
2826
-	 * a special flag ( i.e. $this->isOptionalCall ) but that would
2827
-	 * cause additional complexity because we have to deal with extra temporary state.
2828
-	 * So, instead I allowed the method name to be prefixed with '@', in practice
2829
-	 * nobody creates methods like that - however the '@' symbol in PHP is widely known
2830
-	 * to suppress error handling, so we can reuse the semantics of this symbol.
2831
-	 * If a method name gets passed starting with '@' the overrideDontFail variable
2832
-	 * will be set to TRUE and the '@' will be stripped from the function name before
2833
-	 * attempting to invoke the method on the model. This way, we have all the
2834
-	 * logic in one place.
2835
-	 *
2836
-	 * @param string $method name of the method
2837
-	 * @param array  $args   argument list
2838
-	 *
2839
-	 * @return mixed
2840
-	 */
2841
-	public function __call( $method, $args )
2842
-	{
2843
-		if ( empty( $this->__info['model'] ) ) {
2844
-			return NULL;
2845
-		}
2687
+        if ( $isFieldLink ){
2688
+            unset( $this->properties[ $fieldName ]);
2689
+            $this->properties[ $property ] = NULL;
2690
+        }
2691
+
2692
+
2693
+        if ( $value === FALSE ) {
2694
+            $value = '0';
2695
+        } elseif ( $value === TRUE ) {
2696
+            $value = '1';
2697
+            /* for some reason there is some kind of bug in xdebug so that it doesnt count this line otherwise... */
2698
+        } elseif ( $value instanceof \DateTime ) { $value = $value->format( 'Y-m-d H:i:s' ); }
2699
+        $this->properties[$property] = $value;
2700
+    }
2701
+
2702
+    /**
2703
+     * @deprecated
2704
+     *
2705
+     * Sets a property of the bean allowing you to keep track of
2706
+     * the state yourself. This method sets a property of the bean and
2707
+     * allows you to control how the state of the bean will be affected.
2708
+     *
2709
+     * While there may be some circumstances where this method is needed,
2710
+     * this method is considered to be extremely dangerous.
2711
+     * This method is only for advanced users.
2712
+     *
2713
+     * @param string  $property     property
2714
+     * @param mixed   $value        value
2715
+     * @param boolean $updateShadow whether you want to update the shadow
2716
+     * @param boolean $taint        whether you want to mark the bean as tainted
2717
+     *
2718
+     * @return void
2719
+     */
2720
+    public function setProperty( $property, $value, $updateShadow = FALSE, $taint = FALSE )
2721
+    {
2722
+        $this->properties[$property] = $value;
2723
+
2724
+        if ( $updateShadow ) {
2725
+            $this->__info['sys.shadow.' . $property] = $value;
2726
+        }
2727
+
2728
+        if ( $taint ) {
2729
+            $this->__info['tainted'] = TRUE;
2730
+            $this->__info['changed'] = TRUE;
2731
+        }
2732
+    }
2733
+
2734
+    /**
2735
+     * Returns the value of a meta property. A meta property
2736
+     * contains additional information about the bean object that will not
2737
+     * be stored in the database. Meta information is used to instruct
2738
+     * RedBeanPHP as well as other systems how to deal with the bean.
2739
+     * If the property cannot be found this getter will return NULL instead.
2740
+     *
2741
+     * Example:
2742
+     *
2743
+     * <code>
2744
+     * $bean->setMeta( 'flush-cache', TRUE );
2745
+     * </code>
2746
+     *
2747
+     * RedBeanPHP also stores meta data in beans, this meta data uses
2748
+     * keys prefixed with 'sys.' (system).
2749
+     *
2750
+     * @param string $path    path to property in meta data
2751
+     * @param mixed  $default default value
2752
+     *
2753
+     * @return mixed
2754
+     */
2755
+    public function getMeta( $path, $default = NULL )
2756
+    {
2757
+        return ( isset( $this->__info[$path] ) ) ? $this->__info[$path] : $default;
2758
+    }
2759
+
2760
+    /**
2761
+     * Gets and unsets a meta property.
2762
+     * Moves a meta property out of the bean.
2763
+     * This is a short-cut method that can be used instead
2764
+     * of combining a get/unset.
2765
+     *
2766
+     * @param string $path    path to property in meta data
2767
+     * @param mixed  $default default value
2768
+     *
2769
+     * @return mixed
2770
+     */
2771
+    public function moveMeta( $path, $value = NULL )
2772
+    {
2773
+        if ( isset( $this->__info[$path] ) ) {
2774
+            $value = $this->__info[ $path ];
2775
+            unset( $this->__info[ $path ] );
2776
+        }
2777
+        return $value;
2778
+    }
2779
+
2780
+    /**
2781
+     * Stores a value in the specified Meta information property.
2782
+     * The first argument should be the key to store the value under,
2783
+     * the second argument should be the value. It is common to use
2784
+     * a path-like notation for meta data in RedBeanPHP like:
2785
+     * 'my.meta.data', however the dots are purely for readability, the
2786
+     * meta data methods do not store nested structures or hierarchies.
2787
+     *
2788
+     * @param string $path  path / key to store value under
2789
+     * @param mixed  $value value to store in bean (not in database) as meta data
2790
+     *
2791
+     * @return OODBBean
2792
+     */
2793
+    public function setMeta( $path, $value )
2794
+    {
2795
+        $this->__info[$path] = $value;
2796
+        if ( $path == 'type' && !empty($this->beanHelper)) {
2797
+            $this->__info['model'] = $this->beanHelper->getModelForBean( $this );
2798
+        }
2799
+
2800
+        return $this;
2801
+    }
2802
+
2803
+    /**
2804
+     * Copies the meta information of the specified bean
2805
+     * This is a convenience method to enable you to
2806
+     * exchange meta information easily.
2807
+     *
2808
+     * @param OODBBean $bean bean to copy meta data of
2809
+     *
2810
+     * @return OODBBean
2811
+     */
2812
+    public function copyMetaFrom( OODBBean $bean )
2813
+    {
2814
+        $this->__info = $bean->__info;
2815
+
2816
+        return $this;
2817
+    }
2818
+
2819
+    /**
2820
+     * Sends the call to the registered model.
2821
+     * This method can also be used to override bean behaviour.
2822
+     * In that case you don't want an error or exception to be triggered
2823
+     * if the method does not exist in the model (because it's optional).
2824
+     * Unfortunately we cannot add an extra argument to __call() for this
2825
+     * because the signature is fixed. Another option would be to set
2826
+     * a special flag ( i.e. $this->isOptionalCall ) but that would
2827
+     * cause additional complexity because we have to deal with extra temporary state.
2828
+     * So, instead I allowed the method name to be prefixed with '@', in practice
2829
+     * nobody creates methods like that - however the '@' symbol in PHP is widely known
2830
+     * to suppress error handling, so we can reuse the semantics of this symbol.
2831
+     * If a method name gets passed starting with '@' the overrideDontFail variable
2832
+     * will be set to TRUE and the '@' will be stripped from the function name before
2833
+     * attempting to invoke the method on the model. This way, we have all the
2834
+     * logic in one place.
2835
+     *
2836
+     * @param string $method name of the method
2837
+     * @param array  $args   argument list
2838
+     *
2839
+     * @return mixed
2840
+     */
2841
+    public function __call( $method, $args )
2842
+    {
2843
+        if ( empty( $this->__info['model'] ) ) {
2844
+            return NULL;
2845
+        }
2846 2846
 		
2847
-		$overrideDontFail = FALSE;
2848
-		if ( strpos( $method, '@' ) === 0 ) {
2849
-			$method = substr( $method, 1 );
2850
-			$overrideDontFail = TRUE;
2851
-		}
2847
+        $overrideDontFail = FALSE;
2848
+        if ( strpos( $method, '@' ) === 0 ) {
2849
+            $method = substr( $method, 1 );
2850
+            $overrideDontFail = TRUE;
2851
+        }
2852 2852
 		
2853
-		if ( !method_exists( $this->__info['model'], $method ) ) {
2854
-
2855
-			if ( self::$errorHandlingFUSE === FALSE || $overrideDontFail ) {
2856
-				return NULL;
2857
-			}
2858
-
2859
-			if ( in_array( $method, array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ), TRUE ) ) {
2860
-				return NULL;
2861
-			}
2862
-
2863
-			$message = "FUSE: method does not exist in model: $method";
2864
-			if ( self::$errorHandlingFUSE === self::C_ERR_LOG ) {
2865
-				error_log( $message );
2866
-				return NULL;
2867
-			} elseif ( self::$errorHandlingFUSE === self::C_ERR_NOTICE ) {
2868
-				trigger_error( $message, E_USER_NOTICE );
2869
-				return NULL;
2870
-			} elseif ( self::$errorHandlingFUSE === self::C_ERR_WARN ) {
2871
-				trigger_error( $message, E_USER_WARNING );
2872
-				return NULL;
2873
-			} elseif ( self::$errorHandlingFUSE === self::C_ERR_EXCEPTION ) {
2874
-				throw new \Exception( $message );
2875
-			} elseif ( self::$errorHandlingFUSE === self::C_ERR_FUNC ) {
2876
-				$func = self::$errorHandler;
2877
-				return $func(array(
2878
-					'message' => $message,
2879
-					'method' => $method,
2880
-					'args' => $args,
2881
-					'bean' => $this
2882
-				));
2883
-			}
2884
-			trigger_error( $message, E_USER_ERROR );
2885
-			return NULL;
2886
-		}
2887
-
2888
-		return call_user_func_array( array( $this->__info['model'], $method ), $args );
2889
-	}
2890
-
2891
-	/**
2892
-	 * Implementation of __toString Method
2893
-	 * Routes call to Model. If the model implements a __toString() method this
2894
-	 * method will be called and the result will be returned. In case of an
2895
-	 * echo-statement this result will be printed. If the model does not
2896
-	 * implement a __toString method, this method will return a JSON
2897
-	 * representation of the current bean.
2898
-	 *
2899
-	 * @return string
2900
-	 */
2901
-	public function __toString()
2902
-	{
2903
-		$string = $this->__call( '@__toString', array() );
2904
-
2905
-		if ( $string === NULL ) {
2906
-			$list = array();
2907
-			foreach($this->properties as $property => $value) {
2908
-				if (is_scalar($value)) {
2909
-					if ( self::$enforceUTF8encoding ) {
2910
-						$list[$property] = mb_convert_encoding($value, 'UTF-8', 'UTF-8');
2911
-					} else {
2912
-						$list[$property] = $value;
2913
-					}
2914
-				}
2915
-			}
2916
-			$data = json_encode( $list );
2917
-			return $data;
2918
-		} else {
2919
-			return $string;
2920
-		}
2921
-	}
2922
-
2923
-	/**
2924
-	 * Implementation of Array Access Interface, you can access bean objects
2925
-	 * like an array.
2926
-	 * Call gets routed to __set.
2927
-	 *
2928
-	 * @param  mixed $offset offset string
2929
-	 * @param  mixed $value  value
2930
-	 *
2931
-	 * @return void
2932
-	 */
2933
-	public function offsetSet( $offset, $value )
2934
-	{
2935
-		$this->__set( $offset, $value );
2936
-	}
2937
-
2938
-	/**
2939
-	 * Implementation of Array Access Interface, you can access bean objects
2940
-	 * like an array.
2941
-	 *
2942
-	 * Array functions do not reveal x-own-lists and list-alias because
2943
-	 * you dont want duplicate entries in foreach-loops.
2944
-	 * Also offers a slight performance improvement for array access.
2945
-	 *
2946
-	 * @param  mixed $offset property
2947
-	 *
2948
-	 * @return boolean
2949
-	 */
2950
-	public function offsetExists( $offset )
2951
-	{
2952
-		return $this->__isset( $offset );
2953
-	}
2954
-
2955
-	/**
2956
-	 * Implementation of Array Access Interface, you can access bean objects
2957
-	 * like an array.
2958
-	 * Unsets a value from the array/bean.
2959
-	 *
2960
-	 * Array functions do not reveal x-own-lists and list-alias because
2961
-	 * you dont want duplicate entries in foreach-loops.
2962
-	 * Also offers a slight performance improvement for array access.
2963
-	 *
2964
-	 * @param  mixed $offset property
2965
-	 *
2966
-	 * @return void
2967
-	 */
2968
-	public function offsetUnset( $offset )
2969
-	{
2970
-		$this->__unset( $offset );
2971
-	}
2972
-
2973
-	/**
2974
-	 * Implementation of Array Access Interface, you can access bean objects
2975
-	 * like an array.
2976
-	 * Returns value of a property.
2977
-	 *
2978
-	 * Array functions do not reveal x-own-lists and list-alias because
2979
-	 * you dont want duplicate entries in foreach-loops.
2980
-	 * Also offers a slight performance improvement for array access.
2981
-	 *
2982
-	 * @param  mixed $offset property
2983
-	 *
2984
-	 * @return mixed
2985
-	 */
2986
-	public function &offsetGet( $offset )
2987
-	{
2988
-		return $this->__get( $offset );
2989
-	}
2990
-
2991
-	/**
2992
-	 * Chainable method to cast a certain ID to a bean; for instance:
2993
-	 * $person = $club->fetchAs('person')->member;
2994
-	 * This will load a bean of type person using member_id as ID.
2995
-	 *
2996
-	 * @param  string $type preferred fetch type
2997
-	 *
2998
-	 * @return OODBBean
2999
-	 */
3000
-	public function fetchAs( $type )
3001
-	{
3002
-		$this->fetchType = $type;
3003
-
3004
-		return $this;
3005
-	}
3006
-
3007
-	/**
3008
-	 * Prepares to load a bean using the bean type specified by
3009
-	 * another property.
3010
-	 * Similar to fetchAs but uses a column instead of a direct value.
3011
-	 *
3012
-	 * Usage:
3013
-	 *
3014
-	 * <code>
3015
-	 * $car = R::load( 'car', $id );
3016
-	 * $engine = $car->poly('partType')->part;
3017
-	 * </code>
3018
-	 *
3019
-	 * In the example above, we have a bean of type car that
3020
-	 * may consists of several parts (i.e. chassis, wheels).
3021
-	 * To obtain the 'engine' we access the property 'part'
3022
-	 * using the type (i.e. engine) specified by the property
3023
-	 * indicated by the argument of poly().
3024
-	 * This essentially is a polymorph relation, hence the name.
3025
-	 * In database this relation might look like this:
3026
-	 *
3027
-	 * partType | part_id
3028
-	 * --------------------
3029
-	 * engine   | 1020300
3030
-	 * wheel    | 4820088
3031
-	 * chassis  | 7823122
3032
-	 *
3033
-	 * @param string $field field name to use for mapping
3034
-	 *
3035
-	 * @return OODBBean
3036
-	 */
3037
-	public function poly( $field )
3038
-	{
3039
-		return $this->fetchAs( $this->$field );
3040
-	}
3041
-
3042
-	/**
3043
-	 * Traverses a bean property with the specified function.
3044
-	 * Recursively iterates through the property invoking the
3045
-	 * function for each bean along the way passing the bean to it.
3046
-	 *
3047
-	 * Can be used together with with, withCondition, alias and fetchAs.
3048
-	 *
3049
-	 * <code>
3050
-	 * $task
3051
-	 *    ->withCondition(' priority >= ? ', [ $priority ])
3052
-	 *    ->traverse('ownTaskList', function( $t ) use ( &$todo ) {
3053
-	 *       $todo[] = $t->descr;
3054
-	 *    } );
3055
-	 * </code>
3056
-	 *
3057
-	 * In the example, we create a to-do list by traversing a
3058
-	 * hierarchical list of tasks while filtering out all tasks
3059
-	 * having a low priority.
3060
-	 *
3061
-	 * @param string $property property
3062
-	 * @param callable $function function
3063
-	 * @param integer $maxDepth maximum depth for traversal
3064
-	 *
3065
-	 * @return OODBBean
3066
-	 * @throws RedException
3067
-	 */
3068
-	public function traverse( $property, $function, $maxDepth = NULL )
3069
-	{
3070
-		$this->via = NULL;
3071
-		if ( strpos( $property, 'shared' ) !== FALSE ) {
3072
-			throw new RedException( 'Traverse only works with (x)own-lists.' );
3073
-		}
3074
-
3075
-		if ( !is_null( $maxDepth ) ) {
3076
-			if ( !$maxDepth-- ) return $this;
3077
-		}
3078
-
3079
-		$oldFetchType = $this->fetchType;
3080
-		$oldAliasName = $this->aliasName;
3081
-		$oldWith      = $this->withSql;
3082
-		$oldBindings  = $this->withParams;
3083
-
3084
-		$beans = $this->$property;
3085
-
3086
-		if ( $beans === NULL ) return $this;
3087
-
3088
-		if ( !is_array( $beans ) ) $beans = array( $beans );
3089
-
3090
-		foreach( $beans as $bean ) {
3091
-			$function( $bean );
3092
-			$bean->fetchType  = $oldFetchType;
3093
-			$bean->aliasName  = $oldAliasName;
3094
-			$bean->withSql    = $oldWith;
3095
-			$bean->withParams = $oldBindings;
3096
-
3097
-			$bean->traverse( $property, $function, $maxDepth );
3098
-		}
3099
-
3100
-		return $this;
3101
-	}
3102
-
3103
-	/**
3104
-	 * Implementation of Countable interface. Makes it possible to use
3105
-	 * count() function on a bean. This method gets invoked if you use
3106
-	 * the count() function on a bean. The count() method will return
3107
-	 * the number of properties of the bean, this includes the id property.
3108
-	 *
3109
-	 * Usage:
3110
-	 *
3111
-	 * <code>
3112
-	 * $bean = R::dispense('bean');
3113
-	 * $bean->property1 = 1;
3114
-	 * $bean->property2 = 2;
3115
-	 * echo count($bean); //prints 3 (cause id is also a property)
3116
-	 * </code>
3117
-	 *
3118
-	 * The example above will print the number 3 to stdout.
3119
-	 * Although we have assigned values to just two properties, the
3120
-	 * primary key id is also a property of the bean and together
3121
-	 * that makes 3. Besides using the count() function, you can also
3122
-	 * call this method using a method notation: $bean->count().
3123
-	 *
3124
-	 * @return integer
3125
-	 */
3126
-	public function count()
3127
-	{
3128
-		return count( $this->properties );
3129
-	}
3130
-
3131
-	/**
3132
-	 * Checks whether a bean is empty or not.
3133
-	 * A bean is empty if it has no other properties than the id field OR
3134
-	 * if all the other properties are 'empty()' (this might
3135
-	 * include NULL and FALSE values).
3136
-	 *
3137
-	 * Usage:
3138
-	 *
3139
-	 * <code>
3140
-	 * $newBean = R::dispense( 'bean' );
3141
-	 * $newBean->isEmpty(); // TRUE
3142
-	 * </code>
3143
-	 *
3144
-	 * The example above demonstrates that newly dispensed beans are
3145
-	 * considered 'empty'.
3146
-	 *
3147
-	 * @return boolean
3148
-	 */
3149
-	public function isEmpty()
3150
-	{
3151
-		$empty = TRUE;
3152
-		foreach ( $this->properties as $key => $value ) {
3153
-			if ( $key == 'id' ) {
3154
-				continue;
3155
-			}
3156
-			if ( !empty( $value ) ) {
3157
-				$empty = FALSE;
3158
-			}
3159
-		}
3160
-
3161
-		return $empty;
3162
-	}
3163
-
3164
-	/**
3165
-	 * Chainable setter.
3166
-	 * This method is actually the same as just setting a value
3167
-	 * using a magic setter (->property = ...). The difference
3168
-	 * is that you can chain these setters like this:
3169
-	 *
3170
-	 * Usage:
3171
-	 *
3172
-	 * <code>
3173
-	 * $book->setAttr('title', 'mybook')->setAttr('author', 'me');
3174
-	 * </code>
3175
-	 *
3176
-	 * This is the same as setting both properties $book->title and
3177
-	 * $book->author. Sometimes a chained notation can improve the
3178
-	 * readability of the code.
3179
-	 *
3180
-	 * @param string $property the property of the bean
3181
-	 * @param mixed  $value    the value you want to set
3182
-	 *
3183
-	 * @return OODBBean
3184
-	 */
3185
-	public function setAttr( $property, $value )
3186
-	{
3187
-		$this->$property = $value;
3188
-
3189
-		return $this;
3190
-	}
3191
-
3192
-	/**
3193
-	 * Convience method.
3194
-	 * Unsets all properties in the internal properties array.
3195
-	 *
3196
-	 * Usage:
3197
-	 *
3198
-	 * <code>
3199
-	 * $bean->property = 1;
3200
-	 * $bean->unsetAll( array( 'property' ) );
3201
-	 * $bean->property; //NULL
3202
-	 * </code>
3203
-	 *
3204
-	 * In the example above the 'property' of the bean will be
3205
-	 * unset, resulting in the getter returning NULL instead of 1.
3206
-	 *
3207
-	 * @param array $properties properties you want to unset.
3208
-	 *
3209
-	 * @return OODBBean
3210
-	 */
3211
-	public function unsetAll( $properties )
3212
-	{
3213
-		foreach ( $properties as $prop ) {
3214
-			if ( isset( $this->properties[$prop] ) ) {
3215
-				unset( $this->properties[$prop] );
3216
-			}
3217
-		}
3218
-		return $this;
3219
-	}
3220
-
3221
-	/**
3222
-	 * Returns original (old) value of a property.
3223
-	 * You can use this method to see what has changed in a
3224
-	 * bean. The original value of a property is the value that
3225
-	 * this property has had since the bean has been retrieved
3226
-	 * from the databases.
3227
-	 *
3228
-	 * <code>
3229
-	 * $book->title = 'new title';
3230
-	 * $oldTitle = $book->old('title');
3231
-	 * </code>
3232
-	 *
3233
-	 * The example shows how to use the old() method.
3234
-	 * Here we set the title property of the bean to 'new title', then
3235
-	 * we obtain the original value using old('title') and store it in
3236
-	 * a variable $oldTitle.
3237
-	 *
3238
-	 * @param string $property name of the property you want the old value of
3239
-	 *
3240
-	 * @return mixed
3241
-	 */
3242
-	public function old( $property )
3243
-	{
3244
-		$old = $this->getMeta( 'sys.orig', array() );
3245
-
3246
-		if ( array_key_exists( $property, $old ) ) {
3247
-			return $old[$property];
3248
-		}
3249
-
3250
-		return NULL;
3251
-	}
3252
-
3253
-	/**
3254
-	 * Convenience method.
3255
-	 *
3256
-	 * Returns TRUE if the bean has been changed, or FALSE otherwise.
3257
-	 * Same as $bean->getMeta('tainted');
3258
-	 * Note that a bean becomes tainted as soon as you retrieve a list from
3259
-	 * the bean. This is because the bean lists are arrays and the bean cannot
3260
-	 * determine whether you have made modifications to a list so RedBeanPHP
3261
-	 * will mark the whole bean as tainted.
3262
-	 *
3263
-	 * @return boolean
3264
-	 */
3265
-	public function isTainted()
3266
-	{
3267
-		return $this->getMeta( 'tainted' );
3268
-	}
3269
-
3270
-	/**
3271
-	 * Returns TRUE if the value of a certain property of the bean has been changed and
3272
-	 * FALSE otherwise.
3273
-	 *
3274
-	 * Note that this method will return TRUE if applied to a loaded list.
3275
-	 * Also note that this method keeps track of the bean's history regardless whether
3276
-	 * it has been stored or not. Storing a bean does not undo it's history,
3277
-	 * to clean the history of a bean use: clearHistory().
3278
-	 *
3279
-	 * @param string  $property name of the property you want the change-status of
3280
-	 *
3281
-	 * @return boolean
3282
-	 */
3283
-	public function hasChanged( $property )
3284
-	{
3285
-		return ( array_key_exists( $property, $this->properties ) ) ?
3286
-			$this->old( $property ) != $this->properties[$property] : FALSE;
3287
-	}
3288
-
3289
-	/**
3290
-	 * Returns TRUE if the specified list exists, has been loaded
3291
-	 * and has been changed:
3292
-	 * beans have been added or deleted.
3293
-	 * This method will not tell you anything about
3294
-	 * the state of the beans in the list.
3295
-	 *
3296
-	 * Usage:
3297
-	 *
3298
-	 * <code>
3299
-	 * $book->hasListChanged( 'ownPage' ); // FALSE
3300
-	 * array_pop( $book->ownPageList );
3301
-	 * $book->hasListChanged( 'ownPage' ); // TRUE
3302
-	 * </code>
3303
-	 *
3304
-	 * In the example, the first time we ask whether the
3305
-	 * own-page list has been changed we get FALSE. Then we pop
3306
-	 * a page from the list and the hasListChanged() method returns TRUE.
3307
-	 *
3308
-	 * @param string $property name of the list to check
3309
-	 *
3310
-	 * @return boolean
3311
-	 */
3312
-	public function hasListChanged( $property )
3313
-	{
3314
-		if ( !array_key_exists( $property, $this->properties ) ) return FALSE;
3315
-		$diffAdded = array_diff_assoc( $this->properties[$property], $this->__info['sys.shadow.'.$property] );
3316
-		if ( count( $diffAdded ) ) return TRUE;
3317
-		$diffMissing = array_diff_assoc( $this->__info['sys.shadow.'.$property], $this->properties[$property] );
3318
-		if ( count( $diffMissing ) ) return TRUE;
3319
-		return FALSE;
3320
-	}
3321
-
3322
-	/**
3323
-	 * Clears (syncs) the history of the bean.
3324
-	 * Resets all shadow values of the bean to their current value.
3325
-	 *
3326
-	 * Usage:
3327
-	 *
3328
-	 * <code>
3329
-	 * $book->title = 'book';
3330
-	 * echo $book->hasChanged( 'title' ); //TRUE
3331
-	 * R::store( $book );
3332
-	 * echo $book->hasChanged( 'title' ); //TRUE
3333
-	 * $book->clearHistory();
3334
-	 * echo $book->hasChanged( 'title' ); //FALSE
3335
-	 * </code>
3336
-	 *
3337
-	 * Note that even after store(), the history of the bean still
3338
-	 * contains the act of changing the title of the book.
3339
-	 * Only after invoking clearHistory() will the history of the bean
3340
-	 * be cleared and will hasChanged() return FALSE.
3341
-	 *
3342
-	 * @return self
3343
-	 */
3344
-	public function clearHistory()
3345
-	{
3346
-		$this->__info['sys.orig'] = array();
3347
-		foreach( $this->properties as $key => $value ) {
3348
-			if ( is_scalar($value) ) {
3349
-				$this->__info['sys.orig'][$key] = $value;
3350
-			} else {
3351
-				$this->__info['sys.shadow.'.$key] = $value;
3352
-			}
3353
-		}
3354
-		$this->__info[ 'changelist' ] = array();
3355
-		return $this;
3356
-	}
3357
-
3358
-	/**
3359
-	 * Creates a N-M relation by linking an intermediate bean.
3360
-	 * This method can be used to quickly connect beans using indirect
3361
-	 * relations. For instance, given an album and a song you can connect the two
3362
-	 * using a track with a number like this:
3363
-	 *
3364
-	 * Usage:
3365
-	 *
3366
-	 * <code>
3367
-	 * $album->link('track', array('number'=>1))->song = $song;
3368
-	 * </code>
3369
-	 *
3370
-	 * or:
3371
-	 *
3372
-	 * <code>
3373
-	 * $album->link($trackBean)->song = $song;
3374
-	 * </code>
3375
-	 *
3376
-	 * What this method does is adding the link bean to the own-list, in this case
3377
-	 * ownTrack. If the first argument is a string and the second is an array or
3378
-	 * a JSON string then the linking bean gets dispensed on-the-fly as seen in
3379
-	 * example #1. After preparing the linking bean, the bean is returned thus
3380
-	 * allowing the chained setter: ->song = $song.
3381
-	 *
3382
-	 * @param string|OODBBean $typeOrBean    type of bean to dispense or the full bean
3383
-	 * @param string|array    $qualification JSON string or array (optional)
3384
-	 *
3385
-	 * @return OODBBean
3386
-	 */
3387
-	public function link( $typeOrBean, $qualification = array() )
3388
-	{
3389
-		if ( is_string( $typeOrBean ) ) {
3390
-			$typeOrBean = AQueryWriter::camelsSnake( $typeOrBean );
3391
-			$bean = $this->beanHelper->getToolBox()->getRedBean()->dispense( $typeOrBean );
3392
-			if ( is_string( $qualification ) ) {
3393
-				$data = json_decode( $qualification, TRUE );
3394
-			} else {
3395
-				$data = $qualification;
3396
-			}
3397
-			foreach ( $data as $key => $value ) {
3398
-				$bean->$key = $value;
3399
-			}
3400
-		} else {
3401
-			$bean = $typeOrBean;
3402
-		}
3403
-		$list = 'own' . ucfirst( $bean->getMeta( 'type' ) );
3404
-		array_push( $this->$list, $bean );
3405
-		return $bean;
3406
-	}
3407
-
3408
-	/**
3409
-	 * Returns a bean of the given type with the same ID of as
3410
-	 * the current one. This only happens in a one-to-one relation.
3411
-	 * This is as far as support for 1-1 goes in RedBeanPHP. This
3412
-	 * method will only return a reference to the bean, changing it
3413
-	 * and storing the bean will not update the related one-bean.
3414
-	 *
3415
-	 * Usage:
3416
-	 *
3417
-	 * <code>
3418
-	 * $author = R::load( 'author', $id );
3419
-	 * $biography = $author->one( 'bio' );
3420
-	 * </code>
3421
-	 *
3422
-	 * The example loads the biography associated with the author
3423
-	 * using a one-to-one relation. These relations are generally not
3424
-	 * created (nor supported) by RedBeanPHP.
3425
-	 *
3426
-	 * @param  $type type of bean to load
3427
-	 *
3428
-	 * @return OODBBean
3429
-	 */
3430
-	public function one( $type ) {
3431
-		return $this->beanHelper
3432
-			->getToolBox()
3433
-			->getRedBean()
3434
-			->load( $type, $this->id );
3435
-	}
3436
-
3437
-	/**
3438
-	 * Reloads the bean.
3439
-	 * Returns the same bean freshly loaded from the database.
3440
-	 * This method is equal to the following code:
3441
-	 *
3442
-	 * <code>
3443
-	 * $id = $bean->id;
3444
-	 * $type = $bean->getMeta( 'type' );
3445
-	 * $bean = R::load( $type, $id );
3446
-	 * </code>
3447
-	 *
3448
-	 * This is just a convenience method to reload beans
3449
-	 * quickly.
3450
-	 *
3451
-	 * Usage:
3452
-	 *
3453
-	 * <code>
3454
-	 * R::exec( ...update query... );
3455
-	 * $book = $book->fresh();
3456
-	 * </code>
3457
-	 *
3458
-	 * The code snippet above illustrates how to obtain changes
3459
-	 * caused by an UPDATE query, simply by reloading the bean using
3460
-	 * the fresh() method.
3461
-	 *
3462
-	 * @return OODBBean
3463
-	 */
3464
-	public function fresh()
3465
-	{
3466
-		return $this->beanHelper
3467
-			->getToolbox()
3468
-			->getRedBean()
3469
-			->load( $this->getMeta( 'type' ), $this->properties['id'] );
3470
-	}
3471
-
3472
-	/**
3473
-	 * Registers a association renaming globally.
3474
-	 * Use via() and link() to associate shared beans using a
3475
-	 * 3rd bean that will act as an intermediate type. For instance
3476
-	 * consider an employee and a project. We could associate employees
3477
-	 * with projects using a sharedEmployeeList. But, maybe there is more
3478
-	 * to the relationship than just the association. Maybe we want
3479
-	 * to qualify the relation between a project and an employee with
3480
-	 * a role: 'developer', 'designer', 'tester' and so on. In that case,
3481
-	 * it might be better to introduce a new concept to reflect this:
3482
-	 * the participant. However, we still want the flexibility to
3483
-	 * query our employees in one go. This is where link() and via()
3484
-	 * can help. You can still introduce the more applicable
3485
-	 * concept (participant) and have your easy access to the shared beans.
3486
-	 *
3487
-	 * <code>
3488
-	 * $Anna = R::dispense( 'employee' );
3489
-	 * $Anna->badge   = 'Anna';
3490
-	 * $project = R::dispense( 'project' );
3491
-	 * $project->name = 'x';
3492
-	 * $Anna->link( 'participant', array(
3493
-	 *	 'arole' => 'developer'
3494
-	 *	) )->project = $project;
3495
-	 * R::storeAll( array( $project,  $Anna )  );
3496
-	 * $employees = $project
3497
-	 *	->with(' ORDER BY badge ASC ')
3498
-	 *  ->via( 'participant' )
3499
-	 *  ->sharedEmployee;
3500
-	 * </code>
3501
-	 *
3502
-	 * This piece of code creates a project and an employee.
3503
-	 * It then associates the two using a via-relation called
3504
-	 * 'participant' ( employee <-> participant <-> project ).
3505
-	 * So, there will be a table named 'participant' instead of
3506
-	 * a table named 'employee_project'. Using the via() method, the
3507
-	 * employees associated with the project are retrieved 'via'
3508
-	 * the participant table (and an SQL snippet to order them by badge).
3509
-	 *
3510
-	 * @param string $via type you wish to use for shared lists
3511
-	 *
3512
-	 * @return OODBBean
3513
-	 */
3514
-	public function via( $via )
3515
-	{
3516
-		$this->via = AQueryWriter::camelsSnake( $via );
3517
-
3518
-		return $this;
3519
-	}
3520
-
3521
-	/**
3522
-	 * Counts all own beans of type $type.
3523
-	 * Also works with alias(), with() and withCondition().
3524
-	 * Own-beans or xOwn-beans (exclusively owned beans) are beans
3525
-	 * that have been associated using a one-to-many relation. They can
3526
-	 * be accessed through the ownXList where X is the type of the
3527
-	 * associated beans.
3528
-	 *
3529
-	 * Usage:
3530
-	 *
3531
-	 * <code>
3532
-	 * $Bill->alias( 'author' )
3533
-	 *      ->countOwn( 'book' );
3534
-	 * </code>
3535
-	 *
3536
-	 * The example above counts all the books associated with 'author'
3537
-	 * $Bill.
3538
-	 *
3539
-	 * @param string $type the type of bean you want to count
3540
-	 *
3541
-	 * @return integer
3542
-	 */
3543
-	public function countOwn( $type )
3544
-	{
3545
-		$type = $this->beau( $type );
3546
-		if ( $this->aliasName ) {
3547
-			$myFieldLink     = $this->aliasName . '_id';
3548
-			$this->aliasName = NULL;
3549
-		} else {
3550
-			$myFieldLink = $this->__info['type'] . '_id';
3551
-		}
3552
-		$count = 0;
3553
-		if ( $this->getID() ) {
3554
-			reset( $this->withParams );
3555
-			$joinSql = $this->parseJoin( $type );
3556
-			$firstKey = count( $this->withParams ) > 0
3557
-				? key( $this->withParams )
3558
-				: 0;
3559
-			if ( is_int( $firstKey ) ) {
3560
-				$bindings = array_merge( array( $this->getID() ), $this->withParams );
3561
-				$count    = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), "{$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
3562
-			} else {
3563
-				$bindings           = $this->withParams;
3564
-				$bindings[':slot0'] = $this->getID();
3565
-				$count              = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), "{$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
3566
-			}
3567
-		}
3568
-		$this->clearModifiers();
3569
-		return (int) $count;
3570
-	}
3571
-
3572
-	/**
3573
-	 * Counts all shared beans of type $type.
3574
-	 * Also works with via(), with() and withCondition().
3575
-	 * Shared beans are beans that have an many-to-many relation.
3576
-	 * They can be accessed using the sharedXList, where X the
3577
-	 * type of the shared bean.
3578
-	 *
3579
-	 * Usage:
3580
-	 *
3581
-	 * <code>
3582
-	 * $book = R::dispense( 'book' );
3583
-	 * $book->sharedPageList = R::dispense( 'page', 5 );
3584
-	 * R::store( $book );
3585
-	 * echo $book->countShared( 'page' );
3586
-	 * </code>
3587
-	 *
3588
-	 * The code snippet above will output '5', because there
3589
-	 * are 5 beans of type 'page' in the shared list.
3590
-	 *
3591
-	 * @param string $type type of bean you wish to count
3592
-	 *
3593
-	 * @return integer
3594
-	 */
3595
-	public function countShared( $type )
3596
-	{
3597
-		$toolbox = $this->beanHelper->getToolbox();
3598
-		$redbean = $toolbox->getRedBean();
3599
-		$writer  = $toolbox->getWriter();
3600
-		if ( $this->via ) {
3601
-			$oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
3602
-			if ( $oldName !== $this->via ) {
3603
-				//set the new renaming rule
3604
-				$writer->renameAssocTable( $oldName, $this->via );
3605
-				$this->via = NULL;
3606
-			}
3607
-		}
3608
-		$type  = $this->beau( $type );
3609
-		$count = 0;
3610
-		if ( $this->getID() ) {
3611
-			$count = $redbean->getAssociationManager()->relatedCount( $this, $type, $this->withSql, $this->withParams );
3612
-		}
3613
-		$this->clearModifiers();
3614
-		return (integer) $count;
3615
-	}
3616
-
3617
-	/**
3618
-	 * Iterates through the specified own-list and
3619
-	 * fetches all properties (with their type) and
3620
-	 * returns the references.
3621
-	 * Use this method to quickly load indirectly related
3622
-	 * beans in an own-list. Whenever you cannot use a
3623
-	 * shared-list this method offers the same convenience
3624
-	 * by aggregating the parent beans of all children in
3625
-	 * the specified own-list.
3626
-	 *
3627
-	 * Example:
3628
-	 *
3629
-	 * <code>
3630
-	 * $quest->aggr( 'xownQuestTarget', 'target', 'quest' );
3631
-	 * </code>
3632
-	 *
3633
-	 * Loads (in batch) and returns references to all
3634
-	 * quest beans residing in the $questTarget->target properties
3635
-	 * of each element in the xownQuestTargetList.
3636
-	 *
3637
-	 * @param string $list     the list you wish to process
3638
-	 * @param string $property the property to load
3639
-	 * @param string $type     the type of bean residing in this property (optional)
3640
-	 *
3641
-	 * @return array
3642
-	 */
3643
-	public function &aggr( $list, $property, $type = NULL )
3644
-	{
3645
-		$this->via = NULL;
3646
-		$ids = $beanIndex = $references = array();
3647
-
3648
-		if ( strlen( $list ) < 4 ) throw new RedException('Invalid own-list.');
3649
-		if ( strpos( $list, 'own') !== 0 ) throw new RedException('Only own-lists can be aggregated.');
3650
-		if ( !ctype_upper( substr( $list, 3, 1 ) ) ) throw new RedException('Invalid own-list.');
3651
-
3652
-		if ( is_null( $type ) ) $type = $property;
3653
-
3654
-		foreach( $this->$list as $bean ) {
3655
-			$field = $property . '_id';
3656
-			if ( isset( $bean->$field)  ) {
3657
-				$ids[] = $bean->$field;
3658
-				$beanIndex[$bean->$field] = $bean;
3659
-			}
3660
-		}
3661
-
3662
-		$beans = $this->beanHelper->getToolBox()->getRedBean()->batch( $type, $ids );
3663
-
3664
-		//now preload the beans as well
3665
-		foreach( $beans as $bean ) {
3666
-			$beanIndex[$bean->id]->setProperty( $property, $bean );
3667
-		}
3668
-
3669
-		foreach( $beanIndex as $indexedBean ) {
3670
-			$references[] = $indexedBean->$property;
3671
-		}
3672
-
3673
-		return $references;
3674
-	}
3675
-
3676
-	/**
3677
-	 * Tests whether the database identities of two beans are equal.
3678
-	 * Two beans are considered 'equal' if:
3679
-	 *
3680
-	 * a. the types of the beans match
3681
-	 * b. the ids of the beans match
3682
-	 *
3683
-	 * Returns TRUE if the beans are considered equal according to this
3684
-	 * specification and FALSE otherwise.
3685
-	 *
3686
-	 * Usage:
3687
-	 *
3688
-	 * <code>
3689
-	 * $coffee->fetchAs( 'flavour' )->taste->equals(
3690
-	 *    R::enum('flavour:mocca')
3691
-	 * );
3692
-	 * </code>
3693
-	 *
3694
-	 * The example above compares the flavour label 'mocca' with
3695
-	 * the flavour label attachec to the $coffee bean. This illustrates
3696
-	 * how to use equals() with RedBeanPHP-style enums.
3697
-	 *
3698
-	 * @param OODBBean $bean other bean
3699
-	 *
3700
-	 * @return boolean
3701
-	 */
3702
-	public function equals(OODBBean $bean)
3703
-	{
3704
-		return (bool) (
3705
-			   ( (string) $this->properties['id'] === (string) $bean->properties['id'] )
3706
-			&& ( (string) $this->__info['type']   === (string) $bean->__info['type']   )
3707
-		);
3708
-	}
3709
-
3710
-	/**
3711
-	 * Magic method jsonSerialize,
3712
-	 * implementation for the \JsonSerializable interface,
3713
-	 * this method gets called by json_encode and
3714
-	 * facilitates a better JSON representation
3715
-	 * of the bean. Exports the bean on JSON serialization,
3716
-	 * for the JSON fans.
3717
-	 *
3718
-	 * Models can override jsonSerialize (issue #651) by
3719
-	 * implementing a __jsonSerialize method which should return
3720
-	 * an array. The __jsonSerialize override gets called with
3721
-	 * the @ modifier to prevent errors or warnings.
3722
-	 *
3723
-	 * @see  http://php.net/manual/en/class.jsonserializable.php
3724
-	 *
3725
-	 * @return array
3726
-	 */
3727
-	public function jsonSerialize()
3728
-	{
3729
-		$json = $this->__call( '@__jsonSerialize', array( ) );
3730
-
3731
-		if ( $json !== NULL ) {
3732
-			return $json;
3733
-		}
3734
-
3735
-		return $this->export();
3736
-	}
2853
+        if ( !method_exists( $this->__info['model'], $method ) ) {
2854
+
2855
+            if ( self::$errorHandlingFUSE === FALSE || $overrideDontFail ) {
2856
+                return NULL;
2857
+            }
2858
+
2859
+            if ( in_array( $method, array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ), TRUE ) ) {
2860
+                return NULL;
2861
+            }
2862
+
2863
+            $message = "FUSE: method does not exist in model: $method";
2864
+            if ( self::$errorHandlingFUSE === self::C_ERR_LOG ) {
2865
+                error_log( $message );
2866
+                return NULL;
2867
+            } elseif ( self::$errorHandlingFUSE === self::C_ERR_NOTICE ) {
2868
+                trigger_error( $message, E_USER_NOTICE );
2869
+                return NULL;
2870
+            } elseif ( self::$errorHandlingFUSE === self::C_ERR_WARN ) {
2871
+                trigger_error( $message, E_USER_WARNING );
2872
+                return NULL;
2873
+            } elseif ( self::$errorHandlingFUSE === self::C_ERR_EXCEPTION ) {
2874
+                throw new \Exception( $message );
2875
+            } elseif ( self::$errorHandlingFUSE === self::C_ERR_FUNC ) {
2876
+                $func = self::$errorHandler;
2877
+                return $func(array(
2878
+                    'message' => $message,
2879
+                    'method' => $method,
2880
+                    'args' => $args,
2881
+                    'bean' => $this
2882
+                ));
2883
+            }
2884
+            trigger_error( $message, E_USER_ERROR );
2885
+            return NULL;
2886
+        }
2887
+
2888
+        return call_user_func_array( array( $this->__info['model'], $method ), $args );
2889
+    }
2890
+
2891
+    /**
2892
+     * Implementation of __toString Method
2893
+     * Routes call to Model. If the model implements a __toString() method this
2894
+     * method will be called and the result will be returned. In case of an
2895
+     * echo-statement this result will be printed. If the model does not
2896
+     * implement a __toString method, this method will return a JSON
2897
+     * representation of the current bean.
2898
+     *
2899
+     * @return string
2900
+     */
2901
+    public function __toString()
2902
+    {
2903
+        $string = $this->__call( '@__toString', array() );
2904
+
2905
+        if ( $string === NULL ) {
2906
+            $list = array();
2907
+            foreach($this->properties as $property => $value) {
2908
+                if (is_scalar($value)) {
2909
+                    if ( self::$enforceUTF8encoding ) {
2910
+                        $list[$property] = mb_convert_encoding($value, 'UTF-8', 'UTF-8');
2911
+                    } else {
2912
+                        $list[$property] = $value;
2913
+                    }
2914
+                }
2915
+            }
2916
+            $data = json_encode( $list );
2917
+            return $data;
2918
+        } else {
2919
+            return $string;
2920
+        }
2921
+    }
2922
+
2923
+    /**
2924
+     * Implementation of Array Access Interface, you can access bean objects
2925
+     * like an array.
2926
+     * Call gets routed to __set.
2927
+     *
2928
+     * @param  mixed $offset offset string
2929
+     * @param  mixed $value  value
2930
+     *
2931
+     * @return void
2932
+     */
2933
+    public function offsetSet( $offset, $value )
2934
+    {
2935
+        $this->__set( $offset, $value );
2936
+    }
2937
+
2938
+    /**
2939
+     * Implementation of Array Access Interface, you can access bean objects
2940
+     * like an array.
2941
+     *
2942
+     * Array functions do not reveal x-own-lists and list-alias because
2943
+     * you dont want duplicate entries in foreach-loops.
2944
+     * Also offers a slight performance improvement for array access.
2945
+     *
2946
+     * @param  mixed $offset property
2947
+     *
2948
+     * @return boolean
2949
+     */
2950
+    public function offsetExists( $offset )
2951
+    {
2952
+        return $this->__isset( $offset );
2953
+    }
2954
+
2955
+    /**
2956
+     * Implementation of Array Access Interface, you can access bean objects
2957
+     * like an array.
2958
+     * Unsets a value from the array/bean.
2959
+     *
2960
+     * Array functions do not reveal x-own-lists and list-alias because
2961
+     * you dont want duplicate entries in foreach-loops.
2962
+     * Also offers a slight performance improvement for array access.
2963
+     *
2964
+     * @param  mixed $offset property
2965
+     *
2966
+     * @return void
2967
+     */
2968
+    public function offsetUnset( $offset )
2969
+    {
2970
+        $this->__unset( $offset );
2971
+    }
2972
+
2973
+    /**
2974
+     * Implementation of Array Access Interface, you can access bean objects
2975
+     * like an array.
2976
+     * Returns value of a property.
2977
+     *
2978
+     * Array functions do not reveal x-own-lists and list-alias because
2979
+     * you dont want duplicate entries in foreach-loops.
2980
+     * Also offers a slight performance improvement for array access.
2981
+     *
2982
+     * @param  mixed $offset property
2983
+     *
2984
+     * @return mixed
2985
+     */
2986
+    public function &offsetGet( $offset )
2987
+    {
2988
+        return $this->__get( $offset );
2989
+    }
2990
+
2991
+    /**
2992
+     * Chainable method to cast a certain ID to a bean; for instance:
2993
+     * $person = $club->fetchAs('person')->member;
2994
+     * This will load a bean of type person using member_id as ID.
2995
+     *
2996
+     * @param  string $type preferred fetch type
2997
+     *
2998
+     * @return OODBBean
2999
+     */
3000
+    public function fetchAs( $type )
3001
+    {
3002
+        $this->fetchType = $type;
3003
+
3004
+        return $this;
3005
+    }
3006
+
3007
+    /**
3008
+     * Prepares to load a bean using the bean type specified by
3009
+     * another property.
3010
+     * Similar to fetchAs but uses a column instead of a direct value.
3011
+     *
3012
+     * Usage:
3013
+     *
3014
+     * <code>
3015
+     * $car = R::load( 'car', $id );
3016
+     * $engine = $car->poly('partType')->part;
3017
+     * </code>
3018
+     *
3019
+     * In the example above, we have a bean of type car that
3020
+     * may consists of several parts (i.e. chassis, wheels).
3021
+     * To obtain the 'engine' we access the property 'part'
3022
+     * using the type (i.e. engine) specified by the property
3023
+     * indicated by the argument of poly().
3024
+     * This essentially is a polymorph relation, hence the name.
3025
+     * In database this relation might look like this:
3026
+     *
3027
+     * partType | part_id
3028
+     * --------------------
3029
+     * engine   | 1020300
3030
+     * wheel    | 4820088
3031
+     * chassis  | 7823122
3032
+     *
3033
+     * @param string $field field name to use for mapping
3034
+     *
3035
+     * @return OODBBean
3036
+     */
3037
+    public function poly( $field )
3038
+    {
3039
+        return $this->fetchAs( $this->$field );
3040
+    }
3041
+
3042
+    /**
3043
+     * Traverses a bean property with the specified function.
3044
+     * Recursively iterates through the property invoking the
3045
+     * function for each bean along the way passing the bean to it.
3046
+     *
3047
+     * Can be used together with with, withCondition, alias and fetchAs.
3048
+     *
3049
+     * <code>
3050
+     * $task
3051
+     *    ->withCondition(' priority >= ? ', [ $priority ])
3052
+     *    ->traverse('ownTaskList', function( $t ) use ( &$todo ) {
3053
+     *       $todo[] = $t->descr;
3054
+     *    } );
3055
+     * </code>
3056
+     *
3057
+     * In the example, we create a to-do list by traversing a
3058
+     * hierarchical list of tasks while filtering out all tasks
3059
+     * having a low priority.
3060
+     *
3061
+     * @param string $property property
3062
+     * @param callable $function function
3063
+     * @param integer $maxDepth maximum depth for traversal
3064
+     *
3065
+     * @return OODBBean
3066
+     * @throws RedException
3067
+     */
3068
+    public function traverse( $property, $function, $maxDepth = NULL )
3069
+    {
3070
+        $this->via = NULL;
3071
+        if ( strpos( $property, 'shared' ) !== FALSE ) {
3072
+            throw new RedException( 'Traverse only works with (x)own-lists.' );
3073
+        }
3074
+
3075
+        if ( !is_null( $maxDepth ) ) {
3076
+            if ( !$maxDepth-- ) return $this;
3077
+        }
3078
+
3079
+        $oldFetchType = $this->fetchType;
3080
+        $oldAliasName = $this->aliasName;
3081
+        $oldWith      = $this->withSql;
3082
+        $oldBindings  = $this->withParams;
3083
+
3084
+        $beans = $this->$property;
3085
+
3086
+        if ( $beans === NULL ) return $this;
3087
+
3088
+        if ( !is_array( $beans ) ) $beans = array( $beans );
3089
+
3090
+        foreach( $beans as $bean ) {
3091
+            $function( $bean );
3092
+            $bean->fetchType  = $oldFetchType;
3093
+            $bean->aliasName  = $oldAliasName;
3094
+            $bean->withSql    = $oldWith;
3095
+            $bean->withParams = $oldBindings;
3096
+
3097
+            $bean->traverse( $property, $function, $maxDepth );
3098
+        }
3099
+
3100
+        return $this;
3101
+    }
3102
+
3103
+    /**
3104
+     * Implementation of Countable interface. Makes it possible to use
3105
+     * count() function on a bean. This method gets invoked if you use
3106
+     * the count() function on a bean. The count() method will return
3107
+     * the number of properties of the bean, this includes the id property.
3108
+     *
3109
+     * Usage:
3110
+     *
3111
+     * <code>
3112
+     * $bean = R::dispense('bean');
3113
+     * $bean->property1 = 1;
3114
+     * $bean->property2 = 2;
3115
+     * echo count($bean); //prints 3 (cause id is also a property)
3116
+     * </code>
3117
+     *
3118
+     * The example above will print the number 3 to stdout.
3119
+     * Although we have assigned values to just two properties, the
3120
+     * primary key id is also a property of the bean and together
3121
+     * that makes 3. Besides using the count() function, you can also
3122
+     * call this method using a method notation: $bean->count().
3123
+     *
3124
+     * @return integer
3125
+     */
3126
+    public function count()
3127
+    {
3128
+        return count( $this->properties );
3129
+    }
3130
+
3131
+    /**
3132
+     * Checks whether a bean is empty or not.
3133
+     * A bean is empty if it has no other properties than the id field OR
3134
+     * if all the other properties are 'empty()' (this might
3135
+     * include NULL and FALSE values).
3136
+     *
3137
+     * Usage:
3138
+     *
3139
+     * <code>
3140
+     * $newBean = R::dispense( 'bean' );
3141
+     * $newBean->isEmpty(); // TRUE
3142
+     * </code>
3143
+     *
3144
+     * The example above demonstrates that newly dispensed beans are
3145
+     * considered 'empty'.
3146
+     *
3147
+     * @return boolean
3148
+     */
3149
+    public function isEmpty()
3150
+    {
3151
+        $empty = TRUE;
3152
+        foreach ( $this->properties as $key => $value ) {
3153
+            if ( $key == 'id' ) {
3154
+                continue;
3155
+            }
3156
+            if ( !empty( $value ) ) {
3157
+                $empty = FALSE;
3158
+            }
3159
+        }
3160
+
3161
+        return $empty;
3162
+    }
3163
+
3164
+    /**
3165
+     * Chainable setter.
3166
+     * This method is actually the same as just setting a value
3167
+     * using a magic setter (->property = ...). The difference
3168
+     * is that you can chain these setters like this:
3169
+     *
3170
+     * Usage:
3171
+     *
3172
+     * <code>
3173
+     * $book->setAttr('title', 'mybook')->setAttr('author', 'me');
3174
+     * </code>
3175
+     *
3176
+     * This is the same as setting both properties $book->title and
3177
+     * $book->author. Sometimes a chained notation can improve the
3178
+     * readability of the code.
3179
+     *
3180
+     * @param string $property the property of the bean
3181
+     * @param mixed  $value    the value you want to set
3182
+     *
3183
+     * @return OODBBean
3184
+     */
3185
+    public function setAttr( $property, $value )
3186
+    {
3187
+        $this->$property = $value;
3188
+
3189
+        return $this;
3190
+    }
3191
+
3192
+    /**
3193
+     * Convience method.
3194
+     * Unsets all properties in the internal properties array.
3195
+     *
3196
+     * Usage:
3197
+     *
3198
+     * <code>
3199
+     * $bean->property = 1;
3200
+     * $bean->unsetAll( array( 'property' ) );
3201
+     * $bean->property; //NULL
3202
+     * </code>
3203
+     *
3204
+     * In the example above the 'property' of the bean will be
3205
+     * unset, resulting in the getter returning NULL instead of 1.
3206
+     *
3207
+     * @param array $properties properties you want to unset.
3208
+     *
3209
+     * @return OODBBean
3210
+     */
3211
+    public function unsetAll( $properties )
3212
+    {
3213
+        foreach ( $properties as $prop ) {
3214
+            if ( isset( $this->properties[$prop] ) ) {
3215
+                unset( $this->properties[$prop] );
3216
+            }
3217
+        }
3218
+        return $this;
3219
+    }
3220
+
3221
+    /**
3222
+     * Returns original (old) value of a property.
3223
+     * You can use this method to see what has changed in a
3224
+     * bean. The original value of a property is the value that
3225
+     * this property has had since the bean has been retrieved
3226
+     * from the databases.
3227
+     *
3228
+     * <code>
3229
+     * $book->title = 'new title';
3230
+     * $oldTitle = $book->old('title');
3231
+     * </code>
3232
+     *
3233
+     * The example shows how to use the old() method.
3234
+     * Here we set the title property of the bean to 'new title', then
3235
+     * we obtain the original value using old('title') and store it in
3236
+     * a variable $oldTitle.
3237
+     *
3238
+     * @param string $property name of the property you want the old value of
3239
+     *
3240
+     * @return mixed
3241
+     */
3242
+    public function old( $property )
3243
+    {
3244
+        $old = $this->getMeta( 'sys.orig', array() );
3245
+
3246
+        if ( array_key_exists( $property, $old ) ) {
3247
+            return $old[$property];
3248
+        }
3249
+
3250
+        return NULL;
3251
+    }
3252
+
3253
+    /**
3254
+     * Convenience method.
3255
+     *
3256
+     * Returns TRUE if the bean has been changed, or FALSE otherwise.
3257
+     * Same as $bean->getMeta('tainted');
3258
+     * Note that a bean becomes tainted as soon as you retrieve a list from
3259
+     * the bean. This is because the bean lists are arrays and the bean cannot
3260
+     * determine whether you have made modifications to a list so RedBeanPHP
3261
+     * will mark the whole bean as tainted.
3262
+     *
3263
+     * @return boolean
3264
+     */
3265
+    public function isTainted()
3266
+    {
3267
+        return $this->getMeta( 'tainted' );
3268
+    }
3269
+
3270
+    /**
3271
+     * Returns TRUE if the value of a certain property of the bean has been changed and
3272
+     * FALSE otherwise.
3273
+     *
3274
+     * Note that this method will return TRUE if applied to a loaded list.
3275
+     * Also note that this method keeps track of the bean's history regardless whether
3276
+     * it has been stored or not. Storing a bean does not undo it's history,
3277
+     * to clean the history of a bean use: clearHistory().
3278
+     *
3279
+     * @param string  $property name of the property you want the change-status of
3280
+     *
3281
+     * @return boolean
3282
+     */
3283
+    public function hasChanged( $property )
3284
+    {
3285
+        return ( array_key_exists( $property, $this->properties ) ) ?
3286
+            $this->old( $property ) != $this->properties[$property] : FALSE;
3287
+    }
3288
+
3289
+    /**
3290
+     * Returns TRUE if the specified list exists, has been loaded
3291
+     * and has been changed:
3292
+     * beans have been added or deleted.
3293
+     * This method will not tell you anything about
3294
+     * the state of the beans in the list.
3295
+     *
3296
+     * Usage:
3297
+     *
3298
+     * <code>
3299
+     * $book->hasListChanged( 'ownPage' ); // FALSE
3300
+     * array_pop( $book->ownPageList );
3301
+     * $book->hasListChanged( 'ownPage' ); // TRUE
3302
+     * </code>
3303
+     *
3304
+     * In the example, the first time we ask whether the
3305
+     * own-page list has been changed we get FALSE. Then we pop
3306
+     * a page from the list and the hasListChanged() method returns TRUE.
3307
+     *
3308
+     * @param string $property name of the list to check
3309
+     *
3310
+     * @return boolean
3311
+     */
3312
+    public function hasListChanged( $property )
3313
+    {
3314
+        if ( !array_key_exists( $property, $this->properties ) ) return FALSE;
3315
+        $diffAdded = array_diff_assoc( $this->properties[$property], $this->__info['sys.shadow.'.$property] );
3316
+        if ( count( $diffAdded ) ) return TRUE;
3317
+        $diffMissing = array_diff_assoc( $this->__info['sys.shadow.'.$property], $this->properties[$property] );
3318
+        if ( count( $diffMissing ) ) return TRUE;
3319
+        return FALSE;
3320
+    }
3321
+
3322
+    /**
3323
+     * Clears (syncs) the history of the bean.
3324
+     * Resets all shadow values of the bean to their current value.
3325
+     *
3326
+     * Usage:
3327
+     *
3328
+     * <code>
3329
+     * $book->title = 'book';
3330
+     * echo $book->hasChanged( 'title' ); //TRUE
3331
+     * R::store( $book );
3332
+     * echo $book->hasChanged( 'title' ); //TRUE
3333
+     * $book->clearHistory();
3334
+     * echo $book->hasChanged( 'title' ); //FALSE
3335
+     * </code>
3336
+     *
3337
+     * Note that even after store(), the history of the bean still
3338
+     * contains the act of changing the title of the book.
3339
+     * Only after invoking clearHistory() will the history of the bean
3340
+     * be cleared and will hasChanged() return FALSE.
3341
+     *
3342
+     * @return self
3343
+     */
3344
+    public function clearHistory()
3345
+    {
3346
+        $this->__info['sys.orig'] = array();
3347
+        foreach( $this->properties as $key => $value ) {
3348
+            if ( is_scalar($value) ) {
3349
+                $this->__info['sys.orig'][$key] = $value;
3350
+            } else {
3351
+                $this->__info['sys.shadow.'.$key] = $value;
3352
+            }
3353
+        }
3354
+        $this->__info[ 'changelist' ] = array();
3355
+        return $this;
3356
+    }
3357
+
3358
+    /**
3359
+     * Creates a N-M relation by linking an intermediate bean.
3360
+     * This method can be used to quickly connect beans using indirect
3361
+     * relations. For instance, given an album and a song you can connect the two
3362
+     * using a track with a number like this:
3363
+     *
3364
+     * Usage:
3365
+     *
3366
+     * <code>
3367
+     * $album->link('track', array('number'=>1))->song = $song;
3368
+     * </code>
3369
+     *
3370
+     * or:
3371
+     *
3372
+     * <code>
3373
+     * $album->link($trackBean)->song = $song;
3374
+     * </code>
3375
+     *
3376
+     * What this method does is adding the link bean to the own-list, in this case
3377
+     * ownTrack. If the first argument is a string and the second is an array or
3378
+     * a JSON string then the linking bean gets dispensed on-the-fly as seen in
3379
+     * example #1. After preparing the linking bean, the bean is returned thus
3380
+     * allowing the chained setter: ->song = $song.
3381
+     *
3382
+     * @param string|OODBBean $typeOrBean    type of bean to dispense or the full bean
3383
+     * @param string|array    $qualification JSON string or array (optional)
3384
+     *
3385
+     * @return OODBBean
3386
+     */
3387
+    public function link( $typeOrBean, $qualification = array() )
3388
+    {
3389
+        if ( is_string( $typeOrBean ) ) {
3390
+            $typeOrBean = AQueryWriter::camelsSnake( $typeOrBean );
3391
+            $bean = $this->beanHelper->getToolBox()->getRedBean()->dispense( $typeOrBean );
3392
+            if ( is_string( $qualification ) ) {
3393
+                $data = json_decode( $qualification, TRUE );
3394
+            } else {
3395
+                $data = $qualification;
3396
+            }
3397
+            foreach ( $data as $key => $value ) {
3398
+                $bean->$key = $value;
3399
+            }
3400
+        } else {
3401
+            $bean = $typeOrBean;
3402
+        }
3403
+        $list = 'own' . ucfirst( $bean->getMeta( 'type' ) );
3404
+        array_push( $this->$list, $bean );
3405
+        return $bean;
3406
+    }
3407
+
3408
+    /**
3409
+     * Returns a bean of the given type with the same ID of as
3410
+     * the current one. This only happens in a one-to-one relation.
3411
+     * This is as far as support for 1-1 goes in RedBeanPHP. This
3412
+     * method will only return a reference to the bean, changing it
3413
+     * and storing the bean will not update the related one-bean.
3414
+     *
3415
+     * Usage:
3416
+     *
3417
+     * <code>
3418
+     * $author = R::load( 'author', $id );
3419
+     * $biography = $author->one( 'bio' );
3420
+     * </code>
3421
+     *
3422
+     * The example loads the biography associated with the author
3423
+     * using a one-to-one relation. These relations are generally not
3424
+     * created (nor supported) by RedBeanPHP.
3425
+     *
3426
+     * @param  $type type of bean to load
3427
+     *
3428
+     * @return OODBBean
3429
+     */
3430
+    public function one( $type ) {
3431
+        return $this->beanHelper
3432
+            ->getToolBox()
3433
+            ->getRedBean()
3434
+            ->load( $type, $this->id );
3435
+    }
3436
+
3437
+    /**
3438
+     * Reloads the bean.
3439
+     * Returns the same bean freshly loaded from the database.
3440
+     * This method is equal to the following code:
3441
+     *
3442
+     * <code>
3443
+     * $id = $bean->id;
3444
+     * $type = $bean->getMeta( 'type' );
3445
+     * $bean = R::load( $type, $id );
3446
+     * </code>
3447
+     *
3448
+     * This is just a convenience method to reload beans
3449
+     * quickly.
3450
+     *
3451
+     * Usage:
3452
+     *
3453
+     * <code>
3454
+     * R::exec( ...update query... );
3455
+     * $book = $book->fresh();
3456
+     * </code>
3457
+     *
3458
+     * The code snippet above illustrates how to obtain changes
3459
+     * caused by an UPDATE query, simply by reloading the bean using
3460
+     * the fresh() method.
3461
+     *
3462
+     * @return OODBBean
3463
+     */
3464
+    public function fresh()
3465
+    {
3466
+        return $this->beanHelper
3467
+            ->getToolbox()
3468
+            ->getRedBean()
3469
+            ->load( $this->getMeta( 'type' ), $this->properties['id'] );
3470
+    }
3471
+
3472
+    /**
3473
+     * Registers a association renaming globally.
3474
+     * Use via() and link() to associate shared beans using a
3475
+     * 3rd bean that will act as an intermediate type. For instance
3476
+     * consider an employee and a project. We could associate employees
3477
+     * with projects using a sharedEmployeeList. But, maybe there is more
3478
+     * to the relationship than just the association. Maybe we want
3479
+     * to qualify the relation between a project and an employee with
3480
+     * a role: 'developer', 'designer', 'tester' and so on. In that case,
3481
+     * it might be better to introduce a new concept to reflect this:
3482
+     * the participant. However, we still want the flexibility to
3483
+     * query our employees in one go. This is where link() and via()
3484
+     * can help. You can still introduce the more applicable
3485
+     * concept (participant) and have your easy access to the shared beans.
3486
+     *
3487
+     * <code>
3488
+     * $Anna = R::dispense( 'employee' );
3489
+     * $Anna->badge   = 'Anna';
3490
+     * $project = R::dispense( 'project' );
3491
+     * $project->name = 'x';
3492
+     * $Anna->link( 'participant', array(
3493
+     *	 'arole' => 'developer'
3494
+     *	) )->project = $project;
3495
+     * R::storeAll( array( $project,  $Anna )  );
3496
+     * $employees = $project
3497
+     *	->with(' ORDER BY badge ASC ')
3498
+     *  ->via( 'participant' )
3499
+     *  ->sharedEmployee;
3500
+     * </code>
3501
+     *
3502
+     * This piece of code creates a project and an employee.
3503
+     * It then associates the two using a via-relation called
3504
+     * 'participant' ( employee <-> participant <-> project ).
3505
+     * So, there will be a table named 'participant' instead of
3506
+     * a table named 'employee_project'. Using the via() method, the
3507
+     * employees associated with the project are retrieved 'via'
3508
+     * the participant table (and an SQL snippet to order them by badge).
3509
+     *
3510
+     * @param string $via type you wish to use for shared lists
3511
+     *
3512
+     * @return OODBBean
3513
+     */
3514
+    public function via( $via )
3515
+    {
3516
+        $this->via = AQueryWriter::camelsSnake( $via );
3517
+
3518
+        return $this;
3519
+    }
3520
+
3521
+    /**
3522
+     * Counts all own beans of type $type.
3523
+     * Also works with alias(), with() and withCondition().
3524
+     * Own-beans or xOwn-beans (exclusively owned beans) are beans
3525
+     * that have been associated using a one-to-many relation. They can
3526
+     * be accessed through the ownXList where X is the type of the
3527
+     * associated beans.
3528
+     *
3529
+     * Usage:
3530
+     *
3531
+     * <code>
3532
+     * $Bill->alias( 'author' )
3533
+     *      ->countOwn( 'book' );
3534
+     * </code>
3535
+     *
3536
+     * The example above counts all the books associated with 'author'
3537
+     * $Bill.
3538
+     *
3539
+     * @param string $type the type of bean you want to count
3540
+     *
3541
+     * @return integer
3542
+     */
3543
+    public function countOwn( $type )
3544
+    {
3545
+        $type = $this->beau( $type );
3546
+        if ( $this->aliasName ) {
3547
+            $myFieldLink     = $this->aliasName . '_id';
3548
+            $this->aliasName = NULL;
3549
+        } else {
3550
+            $myFieldLink = $this->__info['type'] . '_id';
3551
+        }
3552
+        $count = 0;
3553
+        if ( $this->getID() ) {
3554
+            reset( $this->withParams );
3555
+            $joinSql = $this->parseJoin( $type );
3556
+            $firstKey = count( $this->withParams ) > 0
3557
+                ? key( $this->withParams )
3558
+                : 0;
3559
+            if ( is_int( $firstKey ) ) {
3560
+                $bindings = array_merge( array( $this->getID() ), $this->withParams );
3561
+                $count    = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), "{$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
3562
+            } else {
3563
+                $bindings           = $this->withParams;
3564
+                $bindings[':slot0'] = $this->getID();
3565
+                $count              = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), "{$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
3566
+            }
3567
+        }
3568
+        $this->clearModifiers();
3569
+        return (int) $count;
3570
+    }
3571
+
3572
+    /**
3573
+     * Counts all shared beans of type $type.
3574
+     * Also works with via(), with() and withCondition().
3575
+     * Shared beans are beans that have an many-to-many relation.
3576
+     * They can be accessed using the sharedXList, where X the
3577
+     * type of the shared bean.
3578
+     *
3579
+     * Usage:
3580
+     *
3581
+     * <code>
3582
+     * $book = R::dispense( 'book' );
3583
+     * $book->sharedPageList = R::dispense( 'page', 5 );
3584
+     * R::store( $book );
3585
+     * echo $book->countShared( 'page' );
3586
+     * </code>
3587
+     *
3588
+     * The code snippet above will output '5', because there
3589
+     * are 5 beans of type 'page' in the shared list.
3590
+     *
3591
+     * @param string $type type of bean you wish to count
3592
+     *
3593
+     * @return integer
3594
+     */
3595
+    public function countShared( $type )
3596
+    {
3597
+        $toolbox = $this->beanHelper->getToolbox();
3598
+        $redbean = $toolbox->getRedBean();
3599
+        $writer  = $toolbox->getWriter();
3600
+        if ( $this->via ) {
3601
+            $oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
3602
+            if ( $oldName !== $this->via ) {
3603
+                //set the new renaming rule
3604
+                $writer->renameAssocTable( $oldName, $this->via );
3605
+                $this->via = NULL;
3606
+            }
3607
+        }
3608
+        $type  = $this->beau( $type );
3609
+        $count = 0;
3610
+        if ( $this->getID() ) {
3611
+            $count = $redbean->getAssociationManager()->relatedCount( $this, $type, $this->withSql, $this->withParams );
3612
+        }
3613
+        $this->clearModifiers();
3614
+        return (integer) $count;
3615
+    }
3616
+
3617
+    /**
3618
+     * Iterates through the specified own-list and
3619
+     * fetches all properties (with their type) and
3620
+     * returns the references.
3621
+     * Use this method to quickly load indirectly related
3622
+     * beans in an own-list. Whenever you cannot use a
3623
+     * shared-list this method offers the same convenience
3624
+     * by aggregating the parent beans of all children in
3625
+     * the specified own-list.
3626
+     *
3627
+     * Example:
3628
+     *
3629
+     * <code>
3630
+     * $quest->aggr( 'xownQuestTarget', 'target', 'quest' );
3631
+     * </code>
3632
+     *
3633
+     * Loads (in batch) and returns references to all
3634
+     * quest beans residing in the $questTarget->target properties
3635
+     * of each element in the xownQuestTargetList.
3636
+     *
3637
+     * @param string $list     the list you wish to process
3638
+     * @param string $property the property to load
3639
+     * @param string $type     the type of bean residing in this property (optional)
3640
+     *
3641
+     * @return array
3642
+     */
3643
+    public function &aggr( $list, $property, $type = NULL )
3644
+    {
3645
+        $this->via = NULL;
3646
+        $ids = $beanIndex = $references = array();
3647
+
3648
+        if ( strlen( $list ) < 4 ) throw new RedException('Invalid own-list.');
3649
+        if ( strpos( $list, 'own') !== 0 ) throw new RedException('Only own-lists can be aggregated.');
3650
+        if ( !ctype_upper( substr( $list, 3, 1 ) ) ) throw new RedException('Invalid own-list.');
3651
+
3652
+        if ( is_null( $type ) ) $type = $property;
3653
+
3654
+        foreach( $this->$list as $bean ) {
3655
+            $field = $property . '_id';
3656
+            if ( isset( $bean->$field)  ) {
3657
+                $ids[] = $bean->$field;
3658
+                $beanIndex[$bean->$field] = $bean;
3659
+            }
3660
+        }
3661
+
3662
+        $beans = $this->beanHelper->getToolBox()->getRedBean()->batch( $type, $ids );
3663
+
3664
+        //now preload the beans as well
3665
+        foreach( $beans as $bean ) {
3666
+            $beanIndex[$bean->id]->setProperty( $property, $bean );
3667
+        }
3668
+
3669
+        foreach( $beanIndex as $indexedBean ) {
3670
+            $references[] = $indexedBean->$property;
3671
+        }
3672
+
3673
+        return $references;
3674
+    }
3675
+
3676
+    /**
3677
+     * Tests whether the database identities of two beans are equal.
3678
+     * Two beans are considered 'equal' if:
3679
+     *
3680
+     * a. the types of the beans match
3681
+     * b. the ids of the beans match
3682
+     *
3683
+     * Returns TRUE if the beans are considered equal according to this
3684
+     * specification and FALSE otherwise.
3685
+     *
3686
+     * Usage:
3687
+     *
3688
+     * <code>
3689
+     * $coffee->fetchAs( 'flavour' )->taste->equals(
3690
+     *    R::enum('flavour:mocca')
3691
+     * );
3692
+     * </code>
3693
+     *
3694
+     * The example above compares the flavour label 'mocca' with
3695
+     * the flavour label attachec to the $coffee bean. This illustrates
3696
+     * how to use equals() with RedBeanPHP-style enums.
3697
+     *
3698
+     * @param OODBBean $bean other bean
3699
+     *
3700
+     * @return boolean
3701
+     */
3702
+    public function equals(OODBBean $bean)
3703
+    {
3704
+        return (bool) (
3705
+                ( (string) $this->properties['id'] === (string) $bean->properties['id'] )
3706
+            && ( (string) $this->__info['type']   === (string) $bean->__info['type']   )
3707
+        );
3708
+    }
3709
+
3710
+    /**
3711
+     * Magic method jsonSerialize,
3712
+     * implementation for the \JsonSerializable interface,
3713
+     * this method gets called by json_encode and
3714
+     * facilitates a better JSON representation
3715
+     * of the bean. Exports the bean on JSON serialization,
3716
+     * for the JSON fans.
3717
+     *
3718
+     * Models can override jsonSerialize (issue #651) by
3719
+     * implementing a __jsonSerialize method which should return
3720
+     * an array. The __jsonSerialize override gets called with
3721
+     * the @ modifier to prevent errors or warnings.
3722
+     *
3723
+     * @see  http://php.net/manual/en/class.jsonserializable.php
3724
+     *
3725
+     * @return array
3726
+     */
3727
+    public function jsonSerialize()
3728
+    {
3729
+        $json = $this->__call( '@__jsonSerialize', array( ) );
3730
+
3731
+        if ( $json !== NULL ) {
3732
+            return $json;
3733
+        }
3734
+
3735
+        return $this->export();
3736
+    }
3737 3737
 }
3738 3738
 }
3739 3739
 
@@ -3756,57 +3756,57 @@  discard block
 block discarded – undo
3756 3756
  */
3757 3757
 abstract class Observable { //bracket must be here - otherwise coverage software does not understand.
3758 3758
 
3759
-	/**
3760
-	 * @var array
3761
-	 */
3762
-	private $observers = array();
3763
-
3764
-	/**
3765
-	 * Implementation of the Observer Pattern.
3766
-	 * Adds an event listener to the observable object.
3767
-	 * First argument should be the name of the event you wish to listen for.
3768
-	 * Second argument should be the object that wants to be notified in case
3769
-	 * the event occurs.
3770
-	 *
3771
-	 * @param string   $eventname event identifier
3772
-	 * @param Observer $observer  observer instance
3773
-	 *
3774
-	 * @return void
3775
-	 */
3776
-	public function addEventListener( $eventname, Observer $observer )
3777
-	{
3778
-		if ( !isset( $this->observers[$eventname] ) ) {
3779
-			$this->observers[$eventname] = array();
3780
-		}
3781
-
3782
-		if ( in_array( $observer, $this->observers[$eventname] ) ) {
3783
-			return;
3784
-		}
3785
-
3786
-		$this->observers[$eventname][] = $observer;
3787
-	}
3788
-
3789
-	/**
3790
-	 * Notifies listeners.
3791
-	 * Sends the signal $eventname, the event identifier and a message object
3792
-	 * to all observers that have been registered to receive notification for
3793
-	 * this event. Part of the observer pattern implementation in RedBeanPHP.
3794
-	 *
3795
-	 * @param string $eventname event you want signal
3796
-	 * @param mixed  $info      message object to send along
3797
-	 *
3798
-	 * @return void
3799
-	 */
3800
-	public function signal( $eventname, $info )
3801
-	{
3802
-		if ( !isset( $this->observers[$eventname] ) ) {
3803
-			$this->observers[$eventname] = array();
3804
-		}
3805
-
3806
-		foreach ( $this->observers[$eventname] as $observer ) {
3807
-			$observer->onEvent( $eventname, $info );
3808
-		}
3809
-	}
3759
+    /**
3760
+     * @var array
3761
+     */
3762
+    private $observers = array();
3763
+
3764
+    /**
3765
+     * Implementation of the Observer Pattern.
3766
+     * Adds an event listener to the observable object.
3767
+     * First argument should be the name of the event you wish to listen for.
3768
+     * Second argument should be the object that wants to be notified in case
3769
+     * the event occurs.
3770
+     *
3771
+     * @param string   $eventname event identifier
3772
+     * @param Observer $observer  observer instance
3773
+     *
3774
+     * @return void
3775
+     */
3776
+    public function addEventListener( $eventname, Observer $observer )
3777
+    {
3778
+        if ( !isset( $this->observers[$eventname] ) ) {
3779
+            $this->observers[$eventname] = array();
3780
+        }
3781
+
3782
+        if ( in_array( $observer, $this->observers[$eventname] ) ) {
3783
+            return;
3784
+        }
3785
+
3786
+        $this->observers[$eventname][] = $observer;
3787
+    }
3788
+
3789
+    /**
3790
+     * Notifies listeners.
3791
+     * Sends the signal $eventname, the event identifier and a message object
3792
+     * to all observers that have been registered to receive notification for
3793
+     * this event. Part of the observer pattern implementation in RedBeanPHP.
3794
+     *
3795
+     * @param string $eventname event you want signal
3796
+     * @param mixed  $info      message object to send along
3797
+     *
3798
+     * @return void
3799
+     */
3800
+    public function signal( $eventname, $info )
3801
+    {
3802
+        if ( !isset( $this->observers[$eventname] ) ) {
3803
+            $this->observers[$eventname] = array();
3804
+        }
3805
+
3806
+        foreach ( $this->observers[$eventname] as $observer ) {
3807
+            $observer->onEvent( $eventname, $info );
3808
+        }
3809
+    }
3810 3810
 }
3811 3811
 }
3812 3812
 
@@ -3830,18 +3830,18 @@  discard block
 block discarded – undo
3830 3830
  */
3831 3831
 interface Observer
3832 3832
 {
3833
-	/**
3834
-	 * An observer object needs to be capable of receiving
3835
-	 * notifications. Therefore the observer needs to implement the
3836
-	 * onEvent method with two parameters: the event identifier specifying the
3837
-	 * current event and a message object (in RedBeanPHP this can also be a bean).
3838
-	 *
3839
-	 * @param string $eventname event identifier
3840
-	 * @param mixed  $bean      a message sent along with the notification
3841
-	 *
3842
-	 * @return void
3843
-	 */
3844
-	public function onEvent( $eventname, $bean );
3833
+    /**
3834
+     * An observer object needs to be capable of receiving
3835
+     * notifications. Therefore the observer needs to implement the
3836
+     * onEvent method with two parameters: the event identifier specifying the
3837
+     * current event and a message object (in RedBeanPHP this can also be a bean).
3838
+     *
3839
+     * @param string $eventname event identifier
3840
+     * @param mixed  $bean      a message sent along with the notification
3841
+     *
3842
+     * @return void
3843
+     */
3844
+    public function onEvent( $eventname, $bean );
3845 3845
 }
3846 3846
 }
3847 3847
 
@@ -3864,188 +3864,188 @@  discard block
 block discarded – undo
3864 3864
  */
3865 3865
 interface Adapter
3866 3866
 {
3867
-	/**
3868
-	 * Should returns a string containing the most recent SQL query
3869
-	 * that has been processed by the adapter.
3870
-	 *
3871
-	 * @return string
3872
-	 */
3873
-	public function getSQL();
3874
-
3875
-	/**
3876
-	 * Executes an SQL Statement using an array of values to bind
3877
-	 * If $noevent is TRUE then this function will not signal its
3878
-	 * observers to notify about the SQL execution; this to prevent
3879
-	 * infinite recursion when using observers.
3880
-	 *
3881
-	 * @param string  $sql      string containing SQL code for database
3882
-	 * @param array   $bindings array of values to bind to parameters in query string
3883
-	 * @param boolean $noevent  no event firing
3884
-	 *
3885
-	 * @return void
3886
-	 */
3887
-	public function exec( $sql, $bindings = array(), $noevent = FALSE );
3888
-
3889
-	/**
3890
-	 * Executes an SQL Query and returns a resultset.
3891
-	 * This method returns a multi dimensional resultset similar to getAll
3892
-	 * The values array can be used to bind values to the place holders in the
3893
-	 * SQL query.
3894
-	 *
3895
-	 * @param string $sql      string containing SQL code for database
3896
-	 * @param array  $bindings array of values to bind to parameters in query string
3897
-	 *
3898
-	 * @return array
3899
-	 */
3900
-	public function get( $sql, $bindings = array() );
3901
-
3902
-	/**
3903
-	 * Executes an SQL Query and returns a resultset.
3904
-	 * This method returns a single row (one array) resultset.
3905
-	 * The values array can be used to bind values to the place holders in the
3906
-	 * SQL query.
3907
-	 *
3908
-	 * @param string $sql      string containing SQL code for database
3909
-	 * @param array  $bindings array of values to bind to parameters in query string
3910
-	 *
3911
-	 * @return array
3912
-	 */
3913
-	public function getRow( $sql, $bindings = array() );
3914
-
3915
-	/**
3916
-	 * Executes an SQL Query and returns a resultset.
3917
-	 * This method returns a single column (one array) resultset.
3918
-	 * The values array can be used to bind values to the place holders in the
3919
-	 * SQL query.
3920
-	 *
3921
-	 * @param string $sql      string containing SQL code for database
3922
-	 * @param array  $bindings array of values to bind to parameters in query string
3923
-	 *
3924
-	 * @return array
3925
-	 */
3926
-	public function getCol( $sql, $bindings = array() );
3927
-
3928
-	/**
3929
-	 * Executes an SQL Query and returns a resultset.
3930
-	 * This method returns a single cell, a scalar value as the resultset.
3931
-	 * The values array can be used to bind values to the place holders in the
3932
-	 * SQL query.
3933
-	 *
3934
-	 * @param string $sql      string containing SQL code for database
3935
-	 * @param array  $bindings array of values to bind to parameters in query string
3936
-	 *
3937
-	 * @return string
3938
-	 */
3939
-	public function getCell( $sql, $bindings = array() );
3940
-
3941
-	/**
3942
-	 * Executes the SQL query specified in $sql and takes
3943
-	 * the first two columns of the resultset. This function transforms the
3944
-	 * resultset into an associative array. Values from the the first column will
3945
-	 * serve as keys while the values of the second column will be used as values.
3946
-	 * The values array can be used to bind values to the place holders in the
3947
-	 * SQL query.
3948
-	 *
3949
-	 * @param string $sql      string containing SQL code for database
3950
-	 * @param array  $bindings array of values to bind to parameters in query string
3951
-	 *
3952
-	 * @return array
3953
-	 */
3954
-	public function getAssoc( $sql, $bindings = array() );
3955
-
3956
-	/**
3957
-	 * Executes the SQL query specified in $sql and indexes
3958
-	 * the row by the first column.
3959
-	 *
3960
-	 * @param string $sql      Sstring containing SQL code for databaseQL
3961
-	 * @param array  $bindings values to bind
3962
-	 *
3963
-	 * @return array
3964
-	 */
3965
-	public function getAssocRow( $sql, $bindings = array() );
3966
-
3967
-	/**
3968
-	 * Returns the latest insert ID.
3969
-	 *
3970
-	 * @return integer
3971
-	 */
3972
-	public function getInsertID();
3973
-
3974
-	/**
3975
-	 * Returns the number of rows that have been
3976
-	 * affected by the last update statement.
3977
-	 *
3978
-	 * @return integer
3979
-	 */
3980
-	public function getAffectedRows();
3981
-
3982
-	/**
3983
-	 * Returns a database agnostic Cursor object.
3984
-	 *
3985
-	 * @param string $sql      string containing SQL code for database
3986
-	 * @param array  $bindings array of values to bind to parameters in query string
3987
-	 *
3988
-	 * @return Cursor
3989
-	 */
3990
-	public function getCursor( $sql, $bindings = array() );
3991
-
3992
-	/**
3993
-	 * Returns the original database resource. This is useful if you want to
3994
-	 * perform operations on the driver directly instead of working with the
3995
-	 * adapter. RedBean will only access the adapter and never to talk
3996
-	 * directly to the driver though.
3997
-	 *
3998
-	 * @return mixed
3999
-	 */
4000
-	public function getDatabase();
4001
-
4002
-	/**
4003
-	 * This method is part of the RedBean Transaction Management
4004
-	 * mechanisms.
4005
-	 * Starts a transaction.
4006
-	 *
4007
-	 * @return void
4008
-	 */
4009
-	public function startTransaction();
4010
-
4011
-	/**
4012
-	 * This method is part of the RedBean Transaction Management
4013
-	 * mechanisms.
4014
-	 * Commits the transaction.
4015
-	 *
4016
-	 * @return void
4017
-	 */
4018
-	public function commit();
4019
-
4020
-	/**
4021
-	 * This method is part of the RedBean Transaction Management
4022
-	 * mechanisms.
4023
-	 * Rolls back the transaction.
4024
-	 *
4025
-	 * @return void
4026
-	 */
4027
-	public function rollback();
4028
-
4029
-	/**
4030
-	 * Closes database connection.
4031
-	 *
4032
-	 * @return void
4033
-	 */
4034
-	public function close();
4035
-
4036
-	/**
4037
-	 * Sets a driver specific option.
4038
-	 * Using this method you can access driver-specific functions.
4039
-	 * If the selected option exists the value will be passed and
4040
-	 * this method will return boolean TRUE, otherwise it will return
4041
-	 * boolean FALSE.
4042
-	 *
4043
-	 * @param string $optionKey   option key
4044
-	 * @param string $optionValue option value
4045
-	 *
4046
-	 * @return boolean
4047
-	 */
4048
-	public function setOption( $optionKey, $optionValue );
3867
+    /**
3868
+     * Should returns a string containing the most recent SQL query
3869
+     * that has been processed by the adapter.
3870
+     *
3871
+     * @return string
3872
+     */
3873
+    public function getSQL();
3874
+
3875
+    /**
3876
+     * Executes an SQL Statement using an array of values to bind
3877
+     * If $noevent is TRUE then this function will not signal its
3878
+     * observers to notify about the SQL execution; this to prevent
3879
+     * infinite recursion when using observers.
3880
+     *
3881
+     * @param string  $sql      string containing SQL code for database
3882
+     * @param array   $bindings array of values to bind to parameters in query string
3883
+     * @param boolean $noevent  no event firing
3884
+     *
3885
+     * @return void
3886
+     */
3887
+    public function exec( $sql, $bindings = array(), $noevent = FALSE );
3888
+
3889
+    /**
3890
+     * Executes an SQL Query and returns a resultset.
3891
+     * This method returns a multi dimensional resultset similar to getAll
3892
+     * The values array can be used to bind values to the place holders in the
3893
+     * SQL query.
3894
+     *
3895
+     * @param string $sql      string containing SQL code for database
3896
+     * @param array  $bindings array of values to bind to parameters in query string
3897
+     *
3898
+     * @return array
3899
+     */
3900
+    public function get( $sql, $bindings = array() );
3901
+
3902
+    /**
3903
+     * Executes an SQL Query and returns a resultset.
3904
+     * This method returns a single row (one array) resultset.
3905
+     * The values array can be used to bind values to the place holders in the
3906
+     * SQL query.
3907
+     *
3908
+     * @param string $sql      string containing SQL code for database
3909
+     * @param array  $bindings array of values to bind to parameters in query string
3910
+     *
3911
+     * @return array
3912
+     */
3913
+    public function getRow( $sql, $bindings = array() );
3914
+
3915
+    /**
3916
+     * Executes an SQL Query and returns a resultset.
3917
+     * This method returns a single column (one array) resultset.
3918
+     * The values array can be used to bind values to the place holders in the
3919
+     * SQL query.
3920
+     *
3921
+     * @param string $sql      string containing SQL code for database
3922
+     * @param array  $bindings array of values to bind to parameters in query string
3923
+     *
3924
+     * @return array
3925
+     */
3926
+    public function getCol( $sql, $bindings = array() );
3927
+
3928
+    /**
3929
+     * Executes an SQL Query and returns a resultset.
3930
+     * This method returns a single cell, a scalar value as the resultset.
3931
+     * The values array can be used to bind values to the place holders in the
3932
+     * SQL query.
3933
+     *
3934
+     * @param string $sql      string containing SQL code for database
3935
+     * @param array  $bindings array of values to bind to parameters in query string
3936
+     *
3937
+     * @return string
3938
+     */
3939
+    public function getCell( $sql, $bindings = array() );
3940
+
3941
+    /**
3942
+     * Executes the SQL query specified in $sql and takes
3943
+     * the first two columns of the resultset. This function transforms the
3944
+     * resultset into an associative array. Values from the the first column will
3945
+     * serve as keys while the values of the second column will be used as values.
3946
+     * The values array can be used to bind values to the place holders in the
3947
+     * SQL query.
3948
+     *
3949
+     * @param string $sql      string containing SQL code for database
3950
+     * @param array  $bindings array of values to bind to parameters in query string
3951
+     *
3952
+     * @return array
3953
+     */
3954
+    public function getAssoc( $sql, $bindings = array() );
3955
+
3956
+    /**
3957
+     * Executes the SQL query specified in $sql and indexes
3958
+     * the row by the first column.
3959
+     *
3960
+     * @param string $sql      Sstring containing SQL code for databaseQL
3961
+     * @param array  $bindings values to bind
3962
+     *
3963
+     * @return array
3964
+     */
3965
+    public function getAssocRow( $sql, $bindings = array() );
3966
+
3967
+    /**
3968
+     * Returns the latest insert ID.
3969
+     *
3970
+     * @return integer
3971
+     */
3972
+    public function getInsertID();
3973
+
3974
+    /**
3975
+     * Returns the number of rows that have been
3976
+     * affected by the last update statement.
3977
+     *
3978
+     * @return integer
3979
+     */
3980
+    public function getAffectedRows();
3981
+
3982
+    /**
3983
+     * Returns a database agnostic Cursor object.
3984
+     *
3985
+     * @param string $sql      string containing SQL code for database
3986
+     * @param array  $bindings array of values to bind to parameters in query string
3987
+     *
3988
+     * @return Cursor
3989
+     */
3990
+    public function getCursor( $sql, $bindings = array() );
3991
+
3992
+    /**
3993
+     * Returns the original database resource. This is useful if you want to
3994
+     * perform operations on the driver directly instead of working with the
3995
+     * adapter. RedBean will only access the adapter and never to talk
3996
+     * directly to the driver though.
3997
+     *
3998
+     * @return mixed
3999
+     */
4000
+    public function getDatabase();
4001
+
4002
+    /**
4003
+     * This method is part of the RedBean Transaction Management
4004
+     * mechanisms.
4005
+     * Starts a transaction.
4006
+     *
4007
+     * @return void
4008
+     */
4009
+    public function startTransaction();
4010
+
4011
+    /**
4012
+     * This method is part of the RedBean Transaction Management
4013
+     * mechanisms.
4014
+     * Commits the transaction.
4015
+     *
4016
+     * @return void
4017
+     */
4018
+    public function commit();
4019
+
4020
+    /**
4021
+     * This method is part of the RedBean Transaction Management
4022
+     * mechanisms.
4023
+     * Rolls back the transaction.
4024
+     *
4025
+     * @return void
4026
+     */
4027
+    public function rollback();
4028
+
4029
+    /**
4030
+     * Closes database connection.
4031
+     *
4032
+     * @return void
4033
+     */
4034
+    public function close();
4035
+
4036
+    /**
4037
+     * Sets a driver specific option.
4038
+     * Using this method you can access driver-specific functions.
4039
+     * If the selected option exists the value will be passed and
4040
+     * this method will return boolean TRUE, otherwise it will return
4041
+     * boolean FALSE.
4042
+     *
4043
+     * @param string $optionKey   option key
4044
+     * @param string $optionValue option value
4045
+     *
4046
+     * @return boolean
4047
+     */
4048
+    public function setOption( $optionKey, $optionValue );
4049 4049
 }
4050 4050
 }
4051 4051
 
@@ -4076,258 +4076,258 @@  discard block
 block discarded – undo
4076 4076
  */
4077 4077
 class DBAdapter extends Observable implements Adapter
4078 4078
 {
4079
-	/**
4080
-	 * @var Driver
4081
-	 */
4082
-	private $db = NULL;
4083
-
4084
-	/**
4085
-	 * @var string
4086
-	 */
4087
-	private $sql = '';
4088
-
4089
-	/**
4090
-	 * Constructor.
4091
-	 *
4092
-	 * Creates an instance of the RedBean Adapter Class.
4093
-	 * This class provides an interface for RedBean to work
4094
-	 * with ADO compatible DB instances.
4095
-	 *
4096
-	 * Usage:
4097
-	 *
4098
-	 * <code>
4099
-	 * $database = new RPDO( $dsn, $user, $pass );
4100
-	 * $adapter = new DBAdapter( $database );
4101
-	 * $writer = new PostgresWriter( $adapter );
4102
-	 * $oodb = new OODB( $writer, FALSE );
4103
-	 * $bean = $oodb->dispense( 'bean' );
4104
-	 * $bean->name = 'coffeeBean';
4105
-	 * $id = $oodb->store( $bean );
4106
-	 * $bean = $oodb->load( 'bean', $id );
4107
-	 * </code>
4108
-	 *
4109
-	 * The example above creates the 3 RedBeanPHP core objects:
4110
-	 * the Adapter, the Query Writer and the OODB instance and
4111
-	 * wires them together. The example also demonstrates some of
4112
-	 * the methods that can be used with OODB, as you see, they
4113
-	 * closely resemble their facade counterparts.
4114
-	 *
4115
-	 * The wiring process: create an RPDO instance using your database
4116
-	 * connection parameters. Create a database adapter from the RPDO
4117
-	 * object and pass that to the constructor of the writer. Next,
4118
-	 * create an OODB instance from the writer. Now you have an OODB
4119
-	 * object.
4120
-	 *
4121
-	 * @param Driver $database ADO Compatible DB Instance
4122
-	 */
4123
-	public function __construct( $database )
4124
-	{
4125
-		$this->db = $database;
4126
-	}
4127
-
4128
-	/**
4129
-	 * Returns a string containing the most recent SQL query
4130
-	 * processed by the database adapter, thus conforming to the
4131
-	 * interface:
4132
-	 *
4133
-	 * @see Adapter::getSQL
4134
-	 *
4135
-	 * Methods like get(), getRow() and exec() cause this SQL cache
4136
-	 * to get filled. If no SQL query has been processed yet this function
4137
-	 * will return an empty string.
4138
-	 *
4139
-	 * @return string
4140
-	 */
4141
-	public function getSQL()
4142
-	{
4143
-		return $this->sql;
4144
-	}
4079
+    /**
4080
+     * @var Driver
4081
+     */
4082
+    private $db = NULL;
4083
+
4084
+    /**
4085
+     * @var string
4086
+     */
4087
+    private $sql = '';
4088
+
4089
+    /**
4090
+     * Constructor.
4091
+     *
4092
+     * Creates an instance of the RedBean Adapter Class.
4093
+     * This class provides an interface for RedBean to work
4094
+     * with ADO compatible DB instances.
4095
+     *
4096
+     * Usage:
4097
+     *
4098
+     * <code>
4099
+     * $database = new RPDO( $dsn, $user, $pass );
4100
+     * $adapter = new DBAdapter( $database );
4101
+     * $writer = new PostgresWriter( $adapter );
4102
+     * $oodb = new OODB( $writer, FALSE );
4103
+     * $bean = $oodb->dispense( 'bean' );
4104
+     * $bean->name = 'coffeeBean';
4105
+     * $id = $oodb->store( $bean );
4106
+     * $bean = $oodb->load( 'bean', $id );
4107
+     * </code>
4108
+     *
4109
+     * The example above creates the 3 RedBeanPHP core objects:
4110
+     * the Adapter, the Query Writer and the OODB instance and
4111
+     * wires them together. The example also demonstrates some of
4112
+     * the methods that can be used with OODB, as you see, they
4113
+     * closely resemble their facade counterparts.
4114
+     *
4115
+     * The wiring process: create an RPDO instance using your database
4116
+     * connection parameters. Create a database adapter from the RPDO
4117
+     * object and pass that to the constructor of the writer. Next,
4118
+     * create an OODB instance from the writer. Now you have an OODB
4119
+     * object.
4120
+     *
4121
+     * @param Driver $database ADO Compatible DB Instance
4122
+     */
4123
+    public function __construct( $database )
4124
+    {
4125
+        $this->db = $database;
4126
+    }
4127
+
4128
+    /**
4129
+     * Returns a string containing the most recent SQL query
4130
+     * processed by the database adapter, thus conforming to the
4131
+     * interface:
4132
+     *
4133
+     * @see Adapter::getSQL
4134
+     *
4135
+     * Methods like get(), getRow() and exec() cause this SQL cache
4136
+     * to get filled. If no SQL query has been processed yet this function
4137
+     * will return an empty string.
4138
+     *
4139
+     * @return string
4140
+     */
4141
+    public function getSQL()
4142
+    {
4143
+        return $this->sql;
4144
+    }
4145
+
4146
+    /**
4147
+     * @see Adapter::exec
4148
+     */
4149
+    public function exec( $sql, $bindings = array(), $noevent = FALSE )
4150
+    {
4151
+        if ( !$noevent ) {
4152
+            $this->sql = $sql;
4153
+            $this->signal( 'sql_exec', $this );
4154
+        }
4155
+
4156
+        return $this->db->Execute( $sql, $bindings );
4157
+    }
4158
+
4159
+    /**
4160
+     * @see Adapter::get
4161
+     */
4162
+    public function get( $sql, $bindings = array() )
4163
+    {
4164
+        $this->sql = $sql;
4165
+        $this->signal( 'sql_exec', $this );
4166
+
4167
+        return $this->db->GetAll( $sql, $bindings );
4168
+    }
4169
+
4170
+    /**
4171
+     * @see Adapter::getRow
4172
+     */
4173
+    public function getRow( $sql, $bindings = array() )
4174
+    {
4175
+        $this->sql = $sql;
4176
+        $this->signal( 'sql_exec', $this );
4177
+
4178
+        return $this->db->GetRow( $sql, $bindings );
4179
+    }
4180
+
4181
+    /**
4182
+     * @see Adapter::getCol
4183
+     */
4184
+    public function getCol( $sql, $bindings = array() )
4185
+    {
4186
+        $this->sql = $sql;
4187
+        $this->signal( 'sql_exec', $this );
4188
+
4189
+        return $this->db->GetCol( $sql, $bindings );
4190
+    }
4191
+
4192
+    /**
4193
+     * @see Adapter::getAssoc
4194
+     */
4195
+    public function getAssoc( $sql, $bindings = array() )
4196
+    {
4197
+        $this->sql = $sql;
4198
+
4199
+        $this->signal( 'sql_exec', $this );
4200
+
4201
+        $rows  = $this->db->GetAll( $sql, $bindings );
4202
+
4203
+        if ( !$rows ) {
4204
+            return array();
4205
+        }
4206
+
4207
+        $assoc = array();
4208
+		
4209
+        foreach ( $rows as $row ) {
4210
+            if ( empty( $row ) ) continue;
4211
+
4212
+            $key   = array_shift( $row );
4213
+            switch ( count( $row ) ) {
4214
+                case 0:
4215
+                    $value = $key;
4216
+                    break;
4217
+                case 1:
4218
+                    $value = reset( $row );
4219
+                    break;
4220
+                default:
4221
+                    $value = $row;
4222
+            }
4223
+
4224
+            $assoc[$key] = $value;
4225
+        }
4226
+
4227
+        return $assoc;
4228
+    }
4229
+
4230
+    /**
4231
+     * @see Adapter::getAssocRow
4232
+     */
4233
+    public function getAssocRow($sql, $bindings = array())
4234
+    {
4235
+        $this->sql = $sql;
4236
+        $this->signal( 'sql_exec', $this );
4237
+
4238
+        return $this->db->GetAssocRow( $sql, $bindings );
4239
+    }
4240
+
4241
+    /**
4242
+     * @see Adapter::getCell
4243
+     */
4244
+    public function getCell( $sql, $bindings = array(), $noSignal = NULL )
4245
+    {
4246
+        $this->sql = $sql;
4247
+
4248
+        if ( !$noSignal ) $this->signal( 'sql_exec', $this );
4249
+
4250
+        return $this->db->GetOne( $sql, $bindings );
4251
+    }
4252
+
4253
+    /**
4254
+     * @see Adapter::getCursor
4255
+     */
4256
+    public function getCursor( $sql, $bindings = array() )
4257
+    {
4258
+        return $this->db->GetCursor( $sql, $bindings );
4259
+    }
4260
+
4261
+    /**
4262
+     * @see Adapter::getInsertID
4263
+     */
4264
+    public function getInsertID()
4265
+    {
4266
+        return $this->db->getInsertID();
4267
+    }
4268
+
4269
+    /**
4270
+     * @see Adapter::getAffectedRows
4271
+     */
4272
+    public function getAffectedRows()
4273
+    {
4274
+        return $this->db->Affected_Rows();
4275
+    }
4276
+
4277
+    /**
4278
+     * @see Adapter::getDatabase
4279
+     */
4280
+    public function getDatabase()
4281
+    {
4282
+        return $this->db;
4283
+    }
4284
+
4285
+    /**
4286
+     * @see Adapter::startTransaction
4287
+     */
4288
+    public function startTransaction()
4289
+    {
4290
+        $this->db->StartTrans();
4291
+    }
4292
+
4293
+    /**
4294
+     * @see Adapter::commit
4295
+     */
4296
+    public function commit()
4297
+    {
4298
+        $this->db->CommitTrans();
4299
+    }
4300
+
4301
+    /**
4302
+     * @see Adapter::rollback
4303
+     */
4304
+    public function rollback()
4305
+    {
4306
+        $this->db->FailTrans();
4307
+    }
4308
+
4309
+    /**
4310
+     * @see Adapter::close.
4311
+     */
4312
+    public function close()
4313
+    {
4314
+        $this->db->close();
4315
+    }
4316
+
4317
+    /**
4318
+     * @see Adapter::setOption
4319
+     */
4320
+    public function setOption( $optionKey, $optionValue ) {
4321
+        if ( method_exists( $this->db, $optionKey ) ) {
4322
+            call_user_func( array( $this->db, $optionKey ), $optionValue );
4323
+            return TRUE;
4324
+        }
4325
+        return FALSE;
4326
+    }
4327
+}
4328
+}
4145 4329
 
4146
-	/**
4147
-	 * @see Adapter::exec
4148
-	 */
4149
-	public function exec( $sql, $bindings = array(), $noevent = FALSE )
4150
-	{
4151
-		if ( !$noevent ) {
4152
-			$this->sql = $sql;
4153
-			$this->signal( 'sql_exec', $this );
4154
-		}
4155
-
4156
-		return $this->db->Execute( $sql, $bindings );
4157
-	}
4158
-
4159
-	/**
4160
-	 * @see Adapter::get
4161
-	 */
4162
-	public function get( $sql, $bindings = array() )
4163
-	{
4164
-		$this->sql = $sql;
4165
-		$this->signal( 'sql_exec', $this );
4166
-
4167
-		return $this->db->GetAll( $sql, $bindings );
4168
-	}
4169
-
4170
-	/**
4171
-	 * @see Adapter::getRow
4172
-	 */
4173
-	public function getRow( $sql, $bindings = array() )
4174
-	{
4175
-		$this->sql = $sql;
4176
-		$this->signal( 'sql_exec', $this );
4177
-
4178
-		return $this->db->GetRow( $sql, $bindings );
4179
-	}
4180
-
4181
-	/**
4182
-	 * @see Adapter::getCol
4183
-	 */
4184
-	public function getCol( $sql, $bindings = array() )
4185
-	{
4186
-		$this->sql = $sql;
4187
-		$this->signal( 'sql_exec', $this );
4188
-
4189
-		return $this->db->GetCol( $sql, $bindings );
4190
-	}
4191
-
4192
-	/**
4193
-	 * @see Adapter::getAssoc
4194
-	 */
4195
-	public function getAssoc( $sql, $bindings = array() )
4196
-	{
4197
-		$this->sql = $sql;
4198
-
4199
-		$this->signal( 'sql_exec', $this );
4200
-
4201
-		$rows  = $this->db->GetAll( $sql, $bindings );
4202
-
4203
-		if ( !$rows ) {
4204
-			return array();
4205
-		}
4206
-
4207
-		$assoc = array();
4208
-		
4209
-		foreach ( $rows as $row ) {
4210
-			if ( empty( $row ) ) continue;
4211
-
4212
-			$key   = array_shift( $row );
4213
-			switch ( count( $row ) ) {
4214
-				case 0:
4215
-					$value = $key;
4216
-					break;
4217
-				case 1:
4218
-					$value = reset( $row );
4219
-					break;
4220
-				default:
4221
-					$value = $row;
4222
-			}
4223
-
4224
-			$assoc[$key] = $value;
4225
-		}
4226
-
4227
-		return $assoc;
4228
-	}
4229
-
4230
-	/**
4231
-	 * @see Adapter::getAssocRow
4232
-	 */
4233
-	public function getAssocRow($sql, $bindings = array())
4234
-	{
4235
-		$this->sql = $sql;
4236
-		$this->signal( 'sql_exec', $this );
4237
-
4238
-		return $this->db->GetAssocRow( $sql, $bindings );
4239
-	}
4240
-
4241
-	/**
4242
-	 * @see Adapter::getCell
4243
-	 */
4244
-	public function getCell( $sql, $bindings = array(), $noSignal = NULL )
4245
-	{
4246
-		$this->sql = $sql;
4247
-
4248
-		if ( !$noSignal ) $this->signal( 'sql_exec', $this );
4249
-
4250
-		return $this->db->GetOne( $sql, $bindings );
4251
-	}
4252
-
4253
-	/**
4254
-	 * @see Adapter::getCursor
4255
-	 */
4256
-	public function getCursor( $sql, $bindings = array() )
4257
-	{
4258
-		return $this->db->GetCursor( $sql, $bindings );
4259
-	}
4260
-
4261
-	/**
4262
-	 * @see Adapter::getInsertID
4263
-	 */
4264
-	public function getInsertID()
4265
-	{
4266
-		return $this->db->getInsertID();
4267
-	}
4268
-
4269
-	/**
4270
-	 * @see Adapter::getAffectedRows
4271
-	 */
4272
-	public function getAffectedRows()
4273
-	{
4274
-		return $this->db->Affected_Rows();
4275
-	}
4276
-
4277
-	/**
4278
-	 * @see Adapter::getDatabase
4279
-	 */
4280
-	public function getDatabase()
4281
-	{
4282
-		return $this->db;
4283
-	}
4284
-
4285
-	/**
4286
-	 * @see Adapter::startTransaction
4287
-	 */
4288
-	public function startTransaction()
4289
-	{
4290
-		$this->db->StartTrans();
4291
-	}
4292
-
4293
-	/**
4294
-	 * @see Adapter::commit
4295
-	 */
4296
-	public function commit()
4297
-	{
4298
-		$this->db->CommitTrans();
4299
-	}
4300
-
4301
-	/**
4302
-	 * @see Adapter::rollback
4303
-	 */
4304
-	public function rollback()
4305
-	{
4306
-		$this->db->FailTrans();
4307
-	}
4308
-
4309
-	/**
4310
-	 * @see Adapter::close.
4311
-	 */
4312
-	public function close()
4313
-	{
4314
-		$this->db->close();
4315
-	}
4316
-
4317
-	/**
4318
-	 * @see Adapter::setOption
4319
-	 */
4320
-	public function setOption( $optionKey, $optionValue ) {
4321
-		if ( method_exists( $this->db, $optionKey ) ) {
4322
-			call_user_func( array( $this->db, $optionKey ), $optionValue );
4323
-			return TRUE;
4324
-		}
4325
-		return FALSE;
4326
-	}
4327
-}
4328
-}
4329
-
4330
-namespace RedBeanPHP {
4330
+namespace RedBeanPHP {
4331 4331
 
4332 4332
 /**
4333 4333
  * Database Cursor Interface.
@@ -4349,30 +4349,30 @@  discard block
 block discarded – undo
4349 4349
  */
4350 4350
 interface Cursor
4351 4351
 {
4352
-	/**
4353
-	 * Should retrieve the next row of the result set.
4354
-	 * This method is used to iterate over the result set.
4355
-	 *
4356
-	 * @return array
4357
-	 */
4358
-	public function getNextItem();
4352
+    /**
4353
+     * Should retrieve the next row of the result set.
4354
+     * This method is used to iterate over the result set.
4355
+     *
4356
+     * @return array
4357
+     */
4358
+    public function getNextItem();
4359 4359
 	
4360
-	/**
4361
-	 * Resets the cursor by closing it and re-executing the statement.
4362
-	 * This reloads fresh data from the database for the whole collection.
4363
-	 *
4364
-	 * @return void
4365
-	 */
4366
-	public function reset();
4367
-
4368
-	/**
4369
-	 * Closes the database cursor.
4370
-	 * Some databases require a cursor to be closed before executing
4371
-	 * another statement/opening a new cursor.
4372
-	 *
4373
-	 * @return void
4374
-	 */
4375
-	public function close();
4360
+    /**
4361
+     * Resets the cursor by closing it and re-executing the statement.
4362
+     * This reloads fresh data from the database for the whole collection.
4363
+     *
4364
+     * @return void
4365
+     */
4366
+    public function reset();
4367
+
4368
+    /**
4369
+     * Closes the database cursor.
4370
+     * Some databases require a cursor to be closed before executing
4371
+     * another statement/opening a new cursor.
4372
+     *
4373
+     * @return void
4374
+     */
4375
+    public function close();
4376 4376
 }
4377 4377
 }
4378 4378
 
@@ -4399,54 +4399,54 @@  discard block
 block discarded – undo
4399 4399
  */
4400 4400
 class PDOCursor implements Cursor
4401 4401
 {
4402
-	/**
4403
-	 * @var PDOStatement
4404
-	 */
4405
-	protected $res;
4406
-
4407
-	/**
4408
-	 * @var string
4409
-	 */
4410
-	protected $fetchStyle;
4411
-
4412
-	/**
4413
-	 * Constructor, creates a new instance of a PDO Database Cursor.
4414
-	 *
4415
-	 * @param PDOStatement $res        the PDO statement
4416
-	 * @param string       $fetchStyle fetch style constant to use
4417
-	 *
4418
-	 * @return void
4419
-	 */
4420
-	public function __construct( \PDOStatement $res, $fetchStyle )
4421
-	{
4422
-		$this->res = $res;
4423
-		$this->fetchStyle = $fetchStyle;
4424
-	}
4425
-
4426
-	/**
4427
-	 * @see Cursor::getNextItem
4428
-	 */
4429
-	public function getNextItem()
4430
-	{
4431
-		return $this->res->fetch();
4432
-	}
4402
+    /**
4403
+     * @var PDOStatement
4404
+     */
4405
+    protected $res;
4406
+
4407
+    /**
4408
+     * @var string
4409
+     */
4410
+    protected $fetchStyle;
4411
+
4412
+    /**
4413
+     * Constructor, creates a new instance of a PDO Database Cursor.
4414
+     *
4415
+     * @param PDOStatement $res        the PDO statement
4416
+     * @param string       $fetchStyle fetch style constant to use
4417
+     *
4418
+     * @return void
4419
+     */
4420
+    public function __construct( \PDOStatement $res, $fetchStyle )
4421
+    {
4422
+        $this->res = $res;
4423
+        $this->fetchStyle = $fetchStyle;
4424
+    }
4425
+
4426
+    /**
4427
+     * @see Cursor::getNextItem
4428
+     */
4429
+    public function getNextItem()
4430
+    {
4431
+        return $this->res->fetch();
4432
+    }
4433 4433
 	
4434
-	/**
4435
-	 * @see Cursor::reset
4436
-	 */
4437
-	public function reset()
4438
-	{
4439
-		$this->close();
4440
-		$this->res->execute();
4441
-	}
4442
-
4443
-	/**
4444
-	 * @see Cursor::close
4445
-	 */
4446
-	public function close()
4447
-	{
4448
-		$this->res->closeCursor();
4449
-	}
4434
+    /**
4435
+     * @see Cursor::reset
4436
+     */
4437
+    public function reset()
4438
+    {
4439
+        $this->close();
4440
+        $this->res->execute();
4441
+    }
4442
+
4443
+    /**
4444
+     * @see Cursor::close
4445
+     */
4446
+    public function close()
4447
+    {
4448
+        $this->res->closeCursor();
4449
+    }
4450 4450
 }
4451 4451
 }
4452 4452
 
@@ -4472,29 +4472,29 @@  discard block
 block discarded – undo
4472 4472
  */
4473 4473
 class NullCursor implements Cursor
4474 4474
 {
4475
-	/**
4476
-	 * @see Cursor::getNextItem
4477
-	 */
4478
-	public function getNextItem()
4479
-	{
4480
-		return NULL;
4481
-	}
4475
+    /**
4476
+     * @see Cursor::getNextItem
4477
+     */
4478
+    public function getNextItem()
4479
+    {
4480
+        return NULL;
4481
+    }
4482 4482
 	
4483
-	/**
4484
-	 * @see Cursor::reset
4485
-	 */
4486
-	public function reset()
4487
-	{
4488
-		return NULL;
4489
-	}
4490
-
4491
-	/**
4492
-	 * @see Cursor::close
4493
-	 */
4494
-	public function close()
4495
-	{
4496
-		return NULL;
4497
-	}
4483
+    /**
4484
+     * @see Cursor::reset
4485
+     */
4486
+    public function reset()
4487
+    {
4488
+        return NULL;
4489
+    }
4490
+
4491
+    /**
4492
+     * @see Cursor::close
4493
+     */
4494
+    public function close()
4495
+    {
4496
+        return NULL;
4497
+    }
4498 4498
 }
4499 4499
 }
4500 4500
 
@@ -4528,74 +4528,74 @@  discard block
 block discarded – undo
4528 4528
  */
4529 4529
 class BeanCollection
4530 4530
 {
4531
-	/**
4532
-	 * @var Cursor
4533
-	 */
4534
-	protected $cursor = NULL;
4535
-
4536
-	/**
4537
-	 * @var Repository
4538
-	 */
4539
-	protected $repository = NULL;
4540
-
4541
-	/**
4542
-	 * @var string
4543
-	 */
4544
-	protected $type = NULL;
4545
-
4546
-	/**
4547
-	 * Constructor, creates a new instance of the BeanCollection.
4548
-	 *
4549
-	 * @param string     $type       type of beans in this collection
4550
-	 * @param Repository $repository repository to use to generate bean objects
4551
-	 * @param Cursor     $cursor     cursor object to use
4552
-	 *
4553
-	 * @return void
4554
-	 */
4555
-	public function __construct( $type, Repository $repository, Cursor $cursor )
4556
-	{
4557
-		$this->type = $type;
4558
-		$this->cursor = $cursor;
4559
-		$this->repository = $repository;
4560
-	}
4561
-
4562
-	/**
4563
-	 * Returns the next bean in the collection.
4564
-	 * If called the first time, this will return the first bean in the collection.
4565
-	 * If there are no more beans left in the collection, this method
4566
-	 * will return NULL.
4567
-	 *
4568
-	 * @return OODBBean|NULL
4569
-	 */
4570
-	public function next()
4571
-	{
4572
-		$row = $this->cursor->getNextItem();
4573
-		if ( $row ) {
4574
-			$beans = $this->repository->convertToBeans( $this->type, array( $row ) );
4575
-			return reset( $beans );
4576
-		}
4577
-		return NULL;
4578
-	}
4531
+    /**
4532
+     * @var Cursor
4533
+     */
4534
+    protected $cursor = NULL;
4535
+
4536
+    /**
4537
+     * @var Repository
4538
+     */
4539
+    protected $repository = NULL;
4540
+
4541
+    /**
4542
+     * @var string
4543
+     */
4544
+    protected $type = NULL;
4545
+
4546
+    /**
4547
+     * Constructor, creates a new instance of the BeanCollection.
4548
+     *
4549
+     * @param string     $type       type of beans in this collection
4550
+     * @param Repository $repository repository to use to generate bean objects
4551
+     * @param Cursor     $cursor     cursor object to use
4552
+     *
4553
+     * @return void
4554
+     */
4555
+    public function __construct( $type, Repository $repository, Cursor $cursor )
4556
+    {
4557
+        $this->type = $type;
4558
+        $this->cursor = $cursor;
4559
+        $this->repository = $repository;
4560
+    }
4561
+
4562
+    /**
4563
+     * Returns the next bean in the collection.
4564
+     * If called the first time, this will return the first bean in the collection.
4565
+     * If there are no more beans left in the collection, this method
4566
+     * will return NULL.
4567
+     *
4568
+     * @return OODBBean|NULL
4569
+     */
4570
+    public function next()
4571
+    {
4572
+        $row = $this->cursor->getNextItem();
4573
+        if ( $row ) {
4574
+            $beans = $this->repository->convertToBeans( $this->type, array( $row ) );
4575
+            return reset( $beans );
4576
+        }
4577
+        return NULL;
4578
+    }
4579 4579
 	
4580
-	/**
4581
-	 * Resets the collection from the start, like a fresh() on a bean.
4582
-	 *
4583
-	 * @return void
4584
-	 */
4585
-	public function reset()
4586
-	{
4587
-		$this->cursor->reset();
4588
-	}
4589
-
4590
-	/**
4591
-	 * Closes the underlying cursor (needed for some databases).
4592
-	 *
4593
-	 * @return void
4594
-	 */
4595
-	public function close()
4596
-	{
4597
-		$this->cursor->close();
4598
-	}
4580
+    /**
4581
+     * Resets the collection from the start, like a fresh() on a bean.
4582
+     *
4583
+     * @return void
4584
+     */
4585
+    public function reset()
4586
+    {
4587
+        $this->cursor->reset();
4588
+    }
4589
+
4590
+    /**
4591
+     * Closes the underlying cursor (needed for some databases).
4592
+     *
4593
+     * @return void
4594
+     */
4595
+    public function close()
4596
+    {
4597
+        $this->cursor->close();
4598
+    }
4599 4599
 }
4600 4600
 }
4601 4601
 
@@ -4628,512 +4628,512 @@  discard block
 block discarded – undo
4628 4628
  */
4629 4629
 interface QueryWriter
4630 4630
 {
4631
-	/**
4632
-	 * SQL filter constants
4633
-	 */
4634
-	const C_SQLFILTER_READ  = 'r';
4635
-	const C_SQLFILTER_WRITE = 'w';
4636
-
4637
-	/**
4638
-	 * Query Writer constants.
4639
-	 */
4640
-	const C_SQLSTATE_NO_SUCH_TABLE                  = 1;
4641
-	const C_SQLSTATE_NO_SUCH_COLUMN                 = 2;
4642
-	const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3;
4643
-	const C_SQLSTATE_LOCK_TIMEOUT                   = 4;
4644
-
4645
-	/**
4646
-	 * Define data type regions
4647
-	 *
4648
-	 * 00 - 80: normal data types
4649
-	 * 80 - 99: special data types, only scan/code if requested
4650
-	 * 99     : specified by user, don't change
4651
-	 */
4652
-	const C_DATATYPE_RANGE_SPECIAL   = 80;
4653
-	const C_DATATYPE_RANGE_SPECIFIED = 99;
4654
-
4655
-	/**
4656
-	 * Define GLUE types for use with glueSQLCondition methods.
4657
-	 * Determines how to prefix a snippet of SQL before appending it
4658
-	 * to other SQL (or integrating it, mixing it otherwise).
4659
-	 *
4660
-	 * WHERE - glue as WHERE condition
4661
-	 * AND   - glue as AND condition
4662
-	 */
4663
-	const C_GLUE_WHERE = 1;
4664
-	const C_GLUE_AND   = 2;
4665
-
4666
-	/**
4667
-	 * Writes an SQL Snippet for a JOIN, returns the
4668
-	 * SQL snippet string.
4669
-	 *
4670
-	 * @note A default implementation is available in AQueryWriter
4671
-	 * unless a database uses very different SQL this should suffice.
4672
-	 *
4673
-	 * @param string $type       source type
4674
-	 * @param string $targetType target type (type to join)
4675
-	 * @param string $joinType   type of join (possible: 'LEFT', 'RIGHT' or 'INNER').
4676
-	 *
4677
-	 * @return string $joinSQLSnippet
4678
-	 */
4679
-	public function writeJoin( $type, $targetType, $joinType );
4680
-
4681
-	/**
4682
-	 * Glues an SQL snippet to the beginning of a WHERE clause.
4683
-	 * This ensures users don't have to add WHERE to their query snippets.
4684
-	 *
4685
-	 * The snippet gets prefixed with WHERE or AND
4686
-	 * if it starts with a condition.
4687
-	 *
4688
-	 * If the snippet does NOT start with a condition (or this function thinks so)
4689
-	 * the snippet is returned as-is.
4690
-	 *
4691
-	 * The GLUE type determines the prefix:
4692
-	 *
4693
-	 * * NONE  prefixes with WHERE
4694
-	 * * WHERE prefixes with WHERE and replaces AND if snippets starts with AND
4695
-	 * * AND   prefixes with AND
4696
-	 *
4697
-	 * This method will never replace WHERE with AND since a snippet should never
4698
-	 * begin with WHERE in the first place. OR is not supported.
4699
-	 *
4700
-	 * Only a limited set of clauses will be recognized as non-conditions.
4701
-	 * For instance beginning a snippet with complex statements like JOIN or UNION
4702
-	 * will not work. This is too complex for use in a snippet.
4703
-	 *
4704
-	 * @note A default implementation is available in AQueryWriter
4705
-	 * unless a database uses very different SQL this should suffice.
4706
-	 *
4707
-	 * @param string  $sql  SQL Snippet
4708
-	 * @param integer $glue the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND)
4709
-	 *
4710
-	 * @return string
4711
-	 */
4712
-	public function glueSQLCondition( $sql, $glue = NULL );
4713
-
4714
-	/**
4715
-	 * Determines if there is a LIMIT 1 clause in the SQL.
4716
-	 * If not, it will add a LIMIT 1. (used for findOne).
4717
-	 *
4718
-	 * @note A default implementation is available in AQueryWriter
4719
-	 * unless a database uses very different SQL this should suffice.
4720
-	 *
4721
-	 * @param string $sql query to scan and adjust
4722
-	 *
4723
-	 * @return string
4724
-	 */
4725
-	public function glueLimitOne( $sql );
4726
-
4727
-	/**
4728
-	 * Returns the tables that are in the database.
4729
-	 *
4730
-	 * @return array
4731
-	 */
4732
-	public function getTables();
4733
-
4734
-	/**
4735
-	 * This method will create a table for the bean.
4736
-	 * This methods accepts a type and infers the corresponding table name.
4737
-	 *
4738
-	 * @param string $type type of bean you want to create a table for
4739
-	 *
4740
-	 * @return void
4741
-	 */
4742
-	public function createTable( $type );
4743
-
4744
-	/**
4745
-	 * Returns an array containing all the columns of the specified type.
4746
-	 * The format of the return array looks like this:
4747
-	 * $field => $type where $field is the name of the column and $type
4748
-	 * is a database specific description of the datatype.
4749
-	 *
4750
-	 * This methods accepts a type and infers the corresponding table name.
4751
-	 *
4752
-	 * @param string $type type of bean you want to obtain a column list of
4753
-	 *
4754
-	 * @return array
4755
-	 */
4756
-	public function getColumns( $type );
4757
-
4758
-	/**
4759
-	 * Returns the Column Type Code (integer) that corresponds
4760
-	 * to the given value type. This method is used to determine the minimum
4761
-	 * column type required to represent the given value. There are two modes of
4762
-	 * operation: with or without special types. Scanning without special types
4763
-	 * requires the second parameter to be set to FALSE. This is useful when the
4764
-	 * column has already been created and prevents it from being modified to
4765
-	 * an incompatible type leading to data loss. Special types will be taken
4766
-	 * into account when a column does not exist yet (parameter is then set to TRUE).
4767
-	 *
4768
-	 * Special column types are determines by the AQueryWriter constant
4769
-	 * C_DATA_TYPE_ONLY_IF_NOT_EXISTS (usually 80). Another 'very special' type is type
4770
-	 * C_DATA_TYPE_MANUAL (usually 99) which represents a user specified type. Although
4771
-	 * no special treatment has been associated with the latter for now.
4772
-	 *
4773
-	 * @param string  $value                   value
4774
-	 * @param boolean $alsoScanSpecialForTypes take special types into account
4775
-	 *
4776
-	 * @return integer
4777
-	 */
4778
-	public function scanType( $value, $alsoScanSpecialForTypes = FALSE );
4779
-
4780
-	/**
4781
-	 * This method will add a column to a table.
4782
-	 * This methods accepts a type and infers the corresponding table name.
4783
-	 *
4784
-	 * @param string  $type   name of the table
4785
-	 * @param string  $column name of the column
4786
-	 * @param integer $field  data type for field
4787
-	 *
4788
-	 * @return void
4789
-	 */
4790
-	public function addColumn( $type, $column, $field );
4791
-
4792
-	/**
4793
-	 * Returns the Type Code for a Column Description.
4794
-	 * Given an SQL column description this method will return the corresponding
4795
-	 * code for the writer. If the include specials flag is set it will also
4796
-	 * return codes for special columns. Otherwise special columns will be identified
4797
-	 * as specified columns.
4798
-	 *
4799
-	 * @param string  $typedescription description
4800
-	 * @param boolean $includeSpecials whether you want to get codes for special columns as well
4801
-	 *
4802
-	 * @return integer
4803
-	 */
4804
-	public function code( $typedescription, $includeSpecials = FALSE );
4805
-
4806
-	/**
4807
-	 * This method will widen the column to the specified data type.
4808
-	 * This methods accepts a type and infers the corresponding table name.
4809
-	 *
4810
-	 * @param string  $type     type / table that needs to be adjusted
4811
-	 * @param string  $column   column that needs to be altered
4812
-	 * @param integer $datatype target data type
4813
-	 *
4814
-	 * @return void
4815
-	 */
4816
-	public function widenColumn( $type, $column, $datatype );
4817
-
4818
-	/**
4819
-	 * Selects records from the database.
4820
-	 * This methods selects the records from the database that match the specified
4821
-	 * type, conditions (optional) and additional SQL snippet (optional).
4822
-	 *
4823
-	 * @param string $type       name of the table you want to query
4824
-	 * @param array  $conditions criteria ( $column => array( $values ) )
4825
-	 * @param string $addSql     additional SQL snippet
4826
-	 * @param array  $bindings   bindings for SQL snippet
4827
-	 *
4828
-	 * @return array
4829
-	 */
4830
-	public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
4831
-
4832
-	/**
4833
-	 * Selects records from the database and returns a cursor.
4834
-	 * This methods selects the records from the database that match the specified
4835
-	 * type, conditions (optional) and additional SQL snippet (optional).
4836
-	 *
4837
-	 * @param string $type       name of the table you want to query
4838
-	 * @param array  $conditions criteria ( $column => array( $values ) )
4839
-	 * @param string $addSQL     additional SQL snippet
4840
-	 * @param array  $bindings   bindings for SQL snippet
4841
-	 *
4842
-	 * @return Cursor
4843
-	 */
4844
-	public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() );
4845
-
4846
-	/**
4847
-	 * Returns records through an intermediate type. This method is used to obtain records using a link table and
4848
-	 * allows the SQL snippets to reference columns in the link table for additional filtering or ordering.
4849
-	 *
4850
-	 * @param string $sourceType source type, the reference type you want to use to fetch related items on the other side
4851
-	 * @param string $destType   destination type, the target type you want to get beans of
4852
-	 * @param mixed  $linkID     ID to use for the link table
4853
-	 * @param string $addSql     Additional SQL snippet
4854
-	 * @param array  $bindings   Bindings for SQL snippet
4855
-	 *
4856
-	 * @return array
4857
-	 */
4858
-	public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() );
4859
-
4860
-	/**
4861
-	 * Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation.
4862
-	 *
4863
-	 * @param string $sourceType source type, the first part of the link you're looking for
4864
-	 * @param string $destType   destination type, the second part of the link you're looking for
4865
-	 * @param string $sourceID   ID for the source
4866
-	 * @param string $destID     ID for the destination
4867
-	 *
4868
-	 * @return array|null
4869
-	 */
4870
-	public function queryRecordLink( $sourceType, $destType, $sourceID, $destID );
4871
-
4872
-	/**
4873
-	 * Counts the number of records in the database that match the
4874
-	 * conditions and additional SQL.
4875
-	 *
4876
-	 * @param string $type       name of the table you want to query
4877
-	 * @param array  $conditions criteria ( $column => array( $values ) )
4878
-	 * @param string $addSQL     additional SQL snippet
4879
-	 * @param array  $bindings   bindings for SQL snippet
4880
-	 *
4881
-	 * @return integer
4882
-	 */
4883
-	public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
4884
-
4885
-	/**
4886
-	 * Returns the number of records linked through $linkType and satisfying the SQL in $addSQL/$bindings.
4887
-	 *
4888
-	 * @param string $sourceType source type
4889
-	 * @param string $targetType the thing you want to count
4890
-	 * @param mixed  $linkID     the of the source type
4891
-	 * @param string $addSQL     additional SQL snippet
4892
-	 * @param array  $bindings   bindings for SQL snippet
4893
-	 *
4894
-	 * @return integer
4895
-	 */
4896
-	public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() );
4897
-
4898
-	/**
4899
-	 * Returns all rows of specified type that have been tagged with one of the
4900
-	 * strings in the specified tag list array.
4901
-	 *
4902
-	 * Note that the additional SQL snippet can only be used for pagination,
4903
-	 * the SQL snippet will be appended to the end of the query.
4904
-	 *
4905
-	 * @param string  $type     the bean type you want to query
4906
-	 * @param array   $tagList  an array of strings, each string containing a tag title
4907
-	 * @param boolean $all      if TRUE only return records that have been associated with ALL the tags in the list
4908
-	 * @param string  $addSql   addition SQL snippet, for pagination
4909
-	 * @param array   $bindings parameter bindings for additional SQL snippet
4910
-	 *
4911
-	 * @return array
4912
-	 */
4913
-	public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() );
4914
-
4915
-	/**
4916
-	 * Like queryTagged but only counts.
4917
-	 *
4918
-	 * @param string  $type     the bean type you want to query
4919
-	 * @param array   $tagList  an array of strings, each string containing a tag title
4920
-	 * @param boolean $all      if TRUE only return records that have been associated with ALL the tags in the list
4921
-	 * @param string  $addSql   addition SQL snippet, for pagination
4922
-	 * @param array   $bindings parameter bindings for additional SQL snippet
4923
-	 *
4924
-	 * @return integer
4925
-	 */
4926
-	public function queryCountTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() );
4927
-
4928
-	/**
4929
-	 * Returns all parent rows or child rows of a specified row.
4930
-	 * Given a type specifier and a primary key id, this method returns either all child rows
4931
-	 * as defined by having <type>_id = id or all parent rows as defined per id = <type>_id
4932
-	 * taking into account an optional SQL snippet along with parameters.
4933
-	 *
4934
-	 * @param string  $type     the bean type you want to query rows for
4935
-	 * @param integer $id       id of the reference row
4936
-	 * @param boolean $up       TRUE to query parent rows, FALSE to query child rows
4937
-	 * @param string  $addSql   optional SQL snippet to embed in the query
4938
-	 * @param array   $bindings parameter bindings for additional SQL snippet
4939
-	 *
4940
-	 * @return array
4941
-	 */
4942
-	public function queryRecursiveCommonTableExpression( $type, $id, $up = TRUE, $addSql = NULL, $bindings = array() );
4943
-
4944
-	/**
4945
-	 * This method should update (or insert a record), it takes
4946
-	 * a table name, a list of update values ( $field => $value ) and an
4947
-	 * primary key ID (optional). If no primary key ID is provided, an
4948
-	 * INSERT will take place.
4949
-	 * Returns the new ID.
4950
-	 * This methods accepts a type and infers the corresponding table name.
4951
-	 *
4952
-	 * @param string  $type         name of the table to update
4953
-	 * @param array   $updatevalues list of update values
4954
-	 * @param integer $id           optional primary key ID value
4955
-	 *
4956
-	 * @return integer
4957
-	 */
4958
-	public function updateRecord( $type, $updatevalues, $id = NULL );
4959
-
4960
-	/**
4961
-	 * Deletes records from the database.
4962
-	 * @note $addSql is always prefixed with ' WHERE ' or ' AND .'
4963
-	 *
4964
-	 * @param string $type       name of the table you want to query
4965
-	 * @param array  $conditions criteria ( $column => array( $values ) )
4966
-	 * @param string $addSql     additional SQL
4967
-	 * @param array  $bindings   bindings
4968
-	 *
4969
-	 * @return void
4970
-	 */
4971
-	public function deleteRecord( $type, $conditions = array(), $addSql = '', $bindings = array() );
4972
-
4973
-	/**
4974
-	 * Deletes all links between $sourceType and $destType in an N-M relation.
4975
-	 *
4976
-	 * @param string $sourceType source type
4977
-	 * @param string $destType   destination type
4978
-	 * @param string $sourceID   source ID
4979
-	 *
4980
-	 * @return void
4981
-	 */
4982
-	public function deleteRelations( $sourceType, $destType, $sourceID );
4983
-
4984
-	/**
4985
-	 * @see QueryWriter::addUniqueConstaint
4986
-	 */
4987
-	public function addUniqueIndex( $type, $columns );
4988
-
4989
-	/**
4990
-	 * This method will add a UNIQUE constraint index to a table on columns $columns.
4991
-	 * This methods accepts a type and infers the corresponding table name.
4992
-	 *
4993
-	 * @param string $type               target bean type
4994
-	 * @param array  $columnsPartOfIndex columns to include in index
4995
-	 *
4996
-	 * @return void
4997
-	 */
4998
-	public function addUniqueConstraint( $type, $columns );
4999
-
5000
-	/**
5001
-	 * This method will check whether the SQL state is in the list of specified states
5002
-	 * and returns TRUE if it does appear in this list or FALSE if it
5003
-	 * does not. The purpose of this method is to translate the database specific state to
5004
-	 * a one of the constants defined in this class and then check whether it is in the list
5005
-	 * of standard states provided.
5006
-	 *
5007
-	 * @param string $state SQL state to consider
5008
-	 * @param array  $list  list of standardized SQL state constants to check against
5009
-	 * @param array  $extraDriverDetails Some databases communicate state information in a driver-specific format
5010
-	 *                                   rather than through the main sqlState code. For those databases, this extra
5011
-	 *                                   information can be used to determine the standardized state
5012
-	 *
5013
-	 * @return boolean
5014
-	 */
5015
-	public function sqlStateIn( $state, $list, $extraDriverDetails = array() );
5016
-
5017
-	/**
5018
-	 * This method will remove all beans of a certain type.
5019
-	 * This methods accepts a type and infers the corresponding table name.
5020
-	 *
5021
-	 * @param  string $type bean type
5022
-	 *
5023
-	 * @return void
5024
-	 */
5025
-	public function wipe( $type );
5026
-
5027
-	/**
5028
-	 * This method will add a foreign key from type and field to
5029
-	 * target type and target field.
5030
-	 * The foreign key is created without an action. On delete/update
5031
-	 * no action will be triggered. The FK is only used to allow database
5032
-	 * tools to generate pretty diagrams and to make it easy to add actions
5033
-	 * later on.
5034
-	 * This methods accepts a type and infers the corresponding table name.
5035
-	 *
5036
-	 *
5037
-	 * @param  string $type           type that will have a foreign key field
5038
-	 * @param  string $targetType     points to this type
5039
-	 * @param  string $property       field that contains the foreign key value
5040
-	 * @param  string $targetProperty field where the fk points to
5041
-	 * @param  string $isDep          whether target is dependent and should cascade on update/delete
5042
-	 *
5043
-	 * @return void
5044
-	 */
5045
-	public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE );
5046
-
5047
-	/**
5048
-	 * This method will add an index to a type and field with name
5049
-	 * $name.
5050
-	 * This methods accepts a type and infers the corresponding table name.
5051
-	 *
5052
-	 * @param string $type     type to add index to
5053
-	 * @param string $name     name of the new index
5054
-	 * @param string $property field to index
5055
-	 *
5056
-	 * @return void
5057
-	 */
5058
-	public function addIndex( $type, $name, $property );
5059
-
5060
-	/**
5061
-	 * Checks and filters a database structure element like a table of column
5062
-	 * for safe use in a query. A database structure has to conform to the
5063
-	 * RedBeanPHP DB security policy which basically means only alphanumeric
5064
-	 * symbols are allowed. This security policy is more strict than conventional
5065
-	 * SQL policies and does therefore not require database specific escaping rules.
5066
-	 *
5067
-	 * @param string  $databaseStructure name of the column/table to check
5068
-	 * @param boolean $noQuotes          TRUE to NOT put backticks or quotes around the string
5069
-	 *
5070
-	 * @return string
5071
-	 */
5072
-	public function esc( $databaseStructure, $dontQuote = FALSE );
5073
-
5074
-	/**
5075
-	 * Removes all tables and views from the database.
5076
-	 *
5077
-	 * @return void
5078
-	 */
5079
-	public function wipeAll();
5080
-
5081
-	/**
5082
-	 * Renames an association. For instance if you would like to refer to
5083
-	 * album_song as: track you can specify this by calling this method like:
5084
-	 *
5085
-	 * <code>
5086
-	 * renameAssociation('album_song','track')
5087
-	 * </code>
5088
-	 *
5089
-	 * This allows:
5090
-	 *
5091
-	 * <code>
5092
-	 * $album->sharedSong
5093
-	 * </code>
5094
-	 *
5095
-	 * to add/retrieve beans from track instead of album_song.
5096
-	 * Also works for exportAll().
5097
-	 *
5098
-	 * This method also accepts a single associative array as
5099
-	 * its first argument.
5100
-	 *
5101
-	 * @param string|array $fromType original type name, or array
5102
-	 * @param string       $toType   new type name (only if 1st argument is string)
5103
-	 *
5104
-	 * @return void
5105
-	 */
5106
-	public function renameAssocTable( $fromType, $toType = NULL );
5107
-
5108
-	/**
5109
-	 * Returns the format for link tables.
5110
-	 * Given an array containing two type names this method returns the
5111
-	 * name of the link table to be used to store and retrieve
5112
-	 * association records. For instance, given two types: person and
5113
-	 * project, the corresponding link table might be: 'person_project'.
5114
-	 *
5115
-	 * @param  array $types two types array($type1, $type2)
5116
-	 *
5117
-	 * @return string
5118
-	 */
5119
-	public function getAssocTable( $types );
5120
-
5121
-	/**
5122
-	 * Given a bean type and a property, this method
5123
-	 * tries to infer the fetch type using the foreign key
5124
-	 * definitions in the database.
5125
-	 * For instance: project, student -> person.
5126
-	 * If no fetchType can be inferred, this method will return NULL.
5127
-	 *
5128
-	 * @note QueryWriters do not have to implement this method,
5129
-	 * it's optional. A default version is available in AQueryWriter.
5130
-	 *
5131
-	 * @param $type     the source type to fetch a target type for
5132
-	 * @param $property the property to fetch the type of
5133
-	 *
5134
-	 * @return string|NULL
5135
-	 */
5136
-	public function inferFetchType( $type, $property );
4631
+    /**
4632
+     * SQL filter constants
4633
+     */
4634
+    const C_SQLFILTER_READ  = 'r';
4635
+    const C_SQLFILTER_WRITE = 'w';
4636
+
4637
+    /**
4638
+     * Query Writer constants.
4639
+     */
4640
+    const C_SQLSTATE_NO_SUCH_TABLE                  = 1;
4641
+    const C_SQLSTATE_NO_SUCH_COLUMN                 = 2;
4642
+    const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3;
4643
+    const C_SQLSTATE_LOCK_TIMEOUT                   = 4;
4644
+
4645
+    /**
4646
+     * Define data type regions
4647
+     *
4648
+     * 00 - 80: normal data types
4649
+     * 80 - 99: special data types, only scan/code if requested
4650
+     * 99     : specified by user, don't change
4651
+     */
4652
+    const C_DATATYPE_RANGE_SPECIAL   = 80;
4653
+    const C_DATATYPE_RANGE_SPECIFIED = 99;
4654
+
4655
+    /**
4656
+     * Define GLUE types for use with glueSQLCondition methods.
4657
+     * Determines how to prefix a snippet of SQL before appending it
4658
+     * to other SQL (or integrating it, mixing it otherwise).
4659
+     *
4660
+     * WHERE - glue as WHERE condition
4661
+     * AND   - glue as AND condition
4662
+     */
4663
+    const C_GLUE_WHERE = 1;
4664
+    const C_GLUE_AND   = 2;
4665
+
4666
+    /**
4667
+     * Writes an SQL Snippet for a JOIN, returns the
4668
+     * SQL snippet string.
4669
+     *
4670
+     * @note A default implementation is available in AQueryWriter
4671
+     * unless a database uses very different SQL this should suffice.
4672
+     *
4673
+     * @param string $type       source type
4674
+     * @param string $targetType target type (type to join)
4675
+     * @param string $joinType   type of join (possible: 'LEFT', 'RIGHT' or 'INNER').
4676
+     *
4677
+     * @return string $joinSQLSnippet
4678
+     */
4679
+    public function writeJoin( $type, $targetType, $joinType );
4680
+
4681
+    /**
4682
+     * Glues an SQL snippet to the beginning of a WHERE clause.
4683
+     * This ensures users don't have to add WHERE to their query snippets.
4684
+     *
4685
+     * The snippet gets prefixed with WHERE or AND
4686
+     * if it starts with a condition.
4687
+     *
4688
+     * If the snippet does NOT start with a condition (or this function thinks so)
4689
+     * the snippet is returned as-is.
4690
+     *
4691
+     * The GLUE type determines the prefix:
4692
+     *
4693
+     * * NONE  prefixes with WHERE
4694
+     * * WHERE prefixes with WHERE and replaces AND if snippets starts with AND
4695
+     * * AND   prefixes with AND
4696
+     *
4697
+     * This method will never replace WHERE with AND since a snippet should never
4698
+     * begin with WHERE in the first place. OR is not supported.
4699
+     *
4700
+     * Only a limited set of clauses will be recognized as non-conditions.
4701
+     * For instance beginning a snippet with complex statements like JOIN or UNION
4702
+     * will not work. This is too complex for use in a snippet.
4703
+     *
4704
+     * @note A default implementation is available in AQueryWriter
4705
+     * unless a database uses very different SQL this should suffice.
4706
+     *
4707
+     * @param string  $sql  SQL Snippet
4708
+     * @param integer $glue the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND)
4709
+     *
4710
+     * @return string
4711
+     */
4712
+    public function glueSQLCondition( $sql, $glue = NULL );
4713
+
4714
+    /**
4715
+     * Determines if there is a LIMIT 1 clause in the SQL.
4716
+     * If not, it will add a LIMIT 1. (used for findOne).
4717
+     *
4718
+     * @note A default implementation is available in AQueryWriter
4719
+     * unless a database uses very different SQL this should suffice.
4720
+     *
4721
+     * @param string $sql query to scan and adjust
4722
+     *
4723
+     * @return string
4724
+     */
4725
+    public function glueLimitOne( $sql );
4726
+
4727
+    /**
4728
+     * Returns the tables that are in the database.
4729
+     *
4730
+     * @return array
4731
+     */
4732
+    public function getTables();
4733
+
4734
+    /**
4735
+     * This method will create a table for the bean.
4736
+     * This methods accepts a type and infers the corresponding table name.
4737
+     *
4738
+     * @param string $type type of bean you want to create a table for
4739
+     *
4740
+     * @return void
4741
+     */
4742
+    public function createTable( $type );
4743
+
4744
+    /**
4745
+     * Returns an array containing all the columns of the specified type.
4746
+     * The format of the return array looks like this:
4747
+     * $field => $type where $field is the name of the column and $type
4748
+     * is a database specific description of the datatype.
4749
+     *
4750
+     * This methods accepts a type and infers the corresponding table name.
4751
+     *
4752
+     * @param string $type type of bean you want to obtain a column list of
4753
+     *
4754
+     * @return array
4755
+     */
4756
+    public function getColumns( $type );
4757
+
4758
+    /**
4759
+     * Returns the Column Type Code (integer) that corresponds
4760
+     * to the given value type. This method is used to determine the minimum
4761
+     * column type required to represent the given value. There are two modes of
4762
+     * operation: with or without special types. Scanning without special types
4763
+     * requires the second parameter to be set to FALSE. This is useful when the
4764
+     * column has already been created and prevents it from being modified to
4765
+     * an incompatible type leading to data loss. Special types will be taken
4766
+     * into account when a column does not exist yet (parameter is then set to TRUE).
4767
+     *
4768
+     * Special column types are determines by the AQueryWriter constant
4769
+     * C_DATA_TYPE_ONLY_IF_NOT_EXISTS (usually 80). Another 'very special' type is type
4770
+     * C_DATA_TYPE_MANUAL (usually 99) which represents a user specified type. Although
4771
+     * no special treatment has been associated with the latter for now.
4772
+     *
4773
+     * @param string  $value                   value
4774
+     * @param boolean $alsoScanSpecialForTypes take special types into account
4775
+     *
4776
+     * @return integer
4777
+     */
4778
+    public function scanType( $value, $alsoScanSpecialForTypes = FALSE );
4779
+
4780
+    /**
4781
+     * This method will add a column to a table.
4782
+     * This methods accepts a type and infers the corresponding table name.
4783
+     *
4784
+     * @param string  $type   name of the table
4785
+     * @param string  $column name of the column
4786
+     * @param integer $field  data type for field
4787
+     *
4788
+     * @return void
4789
+     */
4790
+    public function addColumn( $type, $column, $field );
4791
+
4792
+    /**
4793
+     * Returns the Type Code for a Column Description.
4794
+     * Given an SQL column description this method will return the corresponding
4795
+     * code for the writer. If the include specials flag is set it will also
4796
+     * return codes for special columns. Otherwise special columns will be identified
4797
+     * as specified columns.
4798
+     *
4799
+     * @param string  $typedescription description
4800
+     * @param boolean $includeSpecials whether you want to get codes for special columns as well
4801
+     *
4802
+     * @return integer
4803
+     */
4804
+    public function code( $typedescription, $includeSpecials = FALSE );
4805
+
4806
+    /**
4807
+     * This method will widen the column to the specified data type.
4808
+     * This methods accepts a type and infers the corresponding table name.
4809
+     *
4810
+     * @param string  $type     type / table that needs to be adjusted
4811
+     * @param string  $column   column that needs to be altered
4812
+     * @param integer $datatype target data type
4813
+     *
4814
+     * @return void
4815
+     */
4816
+    public function widenColumn( $type, $column, $datatype );
4817
+
4818
+    /**
4819
+     * Selects records from the database.
4820
+     * This methods selects the records from the database that match the specified
4821
+     * type, conditions (optional) and additional SQL snippet (optional).
4822
+     *
4823
+     * @param string $type       name of the table you want to query
4824
+     * @param array  $conditions criteria ( $column => array( $values ) )
4825
+     * @param string $addSql     additional SQL snippet
4826
+     * @param array  $bindings   bindings for SQL snippet
4827
+     *
4828
+     * @return array
4829
+     */
4830
+    public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
4831
+
4832
+    /**
4833
+     * Selects records from the database and returns a cursor.
4834
+     * This methods selects the records from the database that match the specified
4835
+     * type, conditions (optional) and additional SQL snippet (optional).
4836
+     *
4837
+     * @param string $type       name of the table you want to query
4838
+     * @param array  $conditions criteria ( $column => array( $values ) )
4839
+     * @param string $addSQL     additional SQL snippet
4840
+     * @param array  $bindings   bindings for SQL snippet
4841
+     *
4842
+     * @return Cursor
4843
+     */
4844
+    public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() );
4845
+
4846
+    /**
4847
+     * Returns records through an intermediate type. This method is used to obtain records using a link table and
4848
+     * allows the SQL snippets to reference columns in the link table for additional filtering or ordering.
4849
+     *
4850
+     * @param string $sourceType source type, the reference type you want to use to fetch related items on the other side
4851
+     * @param string $destType   destination type, the target type you want to get beans of
4852
+     * @param mixed  $linkID     ID to use for the link table
4853
+     * @param string $addSql     Additional SQL snippet
4854
+     * @param array  $bindings   Bindings for SQL snippet
4855
+     *
4856
+     * @return array
4857
+     */
4858
+    public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() );
4859
+
4860
+    /**
4861
+     * Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation.
4862
+     *
4863
+     * @param string $sourceType source type, the first part of the link you're looking for
4864
+     * @param string $destType   destination type, the second part of the link you're looking for
4865
+     * @param string $sourceID   ID for the source
4866
+     * @param string $destID     ID for the destination
4867
+     *
4868
+     * @return array|null
4869
+     */
4870
+    public function queryRecordLink( $sourceType, $destType, $sourceID, $destID );
4871
+
4872
+    /**
4873
+     * Counts the number of records in the database that match the
4874
+     * conditions and additional SQL.
4875
+     *
4876
+     * @param string $type       name of the table you want to query
4877
+     * @param array  $conditions criteria ( $column => array( $values ) )
4878
+     * @param string $addSQL     additional SQL snippet
4879
+     * @param array  $bindings   bindings for SQL snippet
4880
+     *
4881
+     * @return integer
4882
+     */
4883
+    public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
4884
+
4885
+    /**
4886
+     * Returns the number of records linked through $linkType and satisfying the SQL in $addSQL/$bindings.
4887
+     *
4888
+     * @param string $sourceType source type
4889
+     * @param string $targetType the thing you want to count
4890
+     * @param mixed  $linkID     the of the source type
4891
+     * @param string $addSQL     additional SQL snippet
4892
+     * @param array  $bindings   bindings for SQL snippet
4893
+     *
4894
+     * @return integer
4895
+     */
4896
+    public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() );
4897
+
4898
+    /**
4899
+     * Returns all rows of specified type that have been tagged with one of the
4900
+     * strings in the specified tag list array.
4901
+     *
4902
+     * Note that the additional SQL snippet can only be used for pagination,
4903
+     * the SQL snippet will be appended to the end of the query.
4904
+     *
4905
+     * @param string  $type     the bean type you want to query
4906
+     * @param array   $tagList  an array of strings, each string containing a tag title
4907
+     * @param boolean $all      if TRUE only return records that have been associated with ALL the tags in the list
4908
+     * @param string  $addSql   addition SQL snippet, for pagination
4909
+     * @param array   $bindings parameter bindings for additional SQL snippet
4910
+     *
4911
+     * @return array
4912
+     */
4913
+    public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() );
4914
+
4915
+    /**
4916
+     * Like queryTagged but only counts.
4917
+     *
4918
+     * @param string  $type     the bean type you want to query
4919
+     * @param array   $tagList  an array of strings, each string containing a tag title
4920
+     * @param boolean $all      if TRUE only return records that have been associated with ALL the tags in the list
4921
+     * @param string  $addSql   addition SQL snippet, for pagination
4922
+     * @param array   $bindings parameter bindings for additional SQL snippet
4923
+     *
4924
+     * @return integer
4925
+     */
4926
+    public function queryCountTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() );
4927
+
4928
+    /**
4929
+     * Returns all parent rows or child rows of a specified row.
4930
+     * Given a type specifier and a primary key id, this method returns either all child rows
4931
+     * as defined by having <type>_id = id or all parent rows as defined per id = <type>_id
4932
+     * taking into account an optional SQL snippet along with parameters.
4933
+     *
4934
+     * @param string  $type     the bean type you want to query rows for
4935
+     * @param integer $id       id of the reference row
4936
+     * @param boolean $up       TRUE to query parent rows, FALSE to query child rows
4937
+     * @param string  $addSql   optional SQL snippet to embed in the query
4938
+     * @param array   $bindings parameter bindings for additional SQL snippet
4939
+     *
4940
+     * @return array
4941
+     */
4942
+    public function queryRecursiveCommonTableExpression( $type, $id, $up = TRUE, $addSql = NULL, $bindings = array() );
4943
+
4944
+    /**
4945
+     * This method should update (or insert a record), it takes
4946
+     * a table name, a list of update values ( $field => $value ) and an
4947
+     * primary key ID (optional). If no primary key ID is provided, an
4948
+     * INSERT will take place.
4949
+     * Returns the new ID.
4950
+     * This methods accepts a type and infers the corresponding table name.
4951
+     *
4952
+     * @param string  $type         name of the table to update
4953
+     * @param array   $updatevalues list of update values
4954
+     * @param integer $id           optional primary key ID value
4955
+     *
4956
+     * @return integer
4957
+     */
4958
+    public function updateRecord( $type, $updatevalues, $id = NULL );
4959
+
4960
+    /**
4961
+     * Deletes records from the database.
4962
+     * @note $addSql is always prefixed with ' WHERE ' or ' AND .'
4963
+     *
4964
+     * @param string $type       name of the table you want to query
4965
+     * @param array  $conditions criteria ( $column => array( $values ) )
4966
+     * @param string $addSql     additional SQL
4967
+     * @param array  $bindings   bindings
4968
+     *
4969
+     * @return void
4970
+     */
4971
+    public function deleteRecord( $type, $conditions = array(), $addSql = '', $bindings = array() );
4972
+
4973
+    /**
4974
+     * Deletes all links between $sourceType and $destType in an N-M relation.
4975
+     *
4976
+     * @param string $sourceType source type
4977
+     * @param string $destType   destination type
4978
+     * @param string $sourceID   source ID
4979
+     *
4980
+     * @return void
4981
+     */
4982
+    public function deleteRelations( $sourceType, $destType, $sourceID );
4983
+
4984
+    /**
4985
+     * @see QueryWriter::addUniqueConstaint
4986
+     */
4987
+    public function addUniqueIndex( $type, $columns );
4988
+
4989
+    /**
4990
+     * This method will add a UNIQUE constraint index to a table on columns $columns.
4991
+     * This methods accepts a type and infers the corresponding table name.
4992
+     *
4993
+     * @param string $type               target bean type
4994
+     * @param array  $columnsPartOfIndex columns to include in index
4995
+     *
4996
+     * @return void
4997
+     */
4998
+    public function addUniqueConstraint( $type, $columns );
4999
+
5000
+    /**
5001
+     * This method will check whether the SQL state is in the list of specified states
5002
+     * and returns TRUE if it does appear in this list or FALSE if it
5003
+     * does not. The purpose of this method is to translate the database specific state to
5004
+     * a one of the constants defined in this class and then check whether it is in the list
5005
+     * of standard states provided.
5006
+     *
5007
+     * @param string $state SQL state to consider
5008
+     * @param array  $list  list of standardized SQL state constants to check against
5009
+     * @param array  $extraDriverDetails Some databases communicate state information in a driver-specific format
5010
+     *                                   rather than through the main sqlState code. For those databases, this extra
5011
+     *                                   information can be used to determine the standardized state
5012
+     *
5013
+     * @return boolean
5014
+     */
5015
+    public function sqlStateIn( $state, $list, $extraDriverDetails = array() );
5016
+
5017
+    /**
5018
+     * This method will remove all beans of a certain type.
5019
+     * This methods accepts a type and infers the corresponding table name.
5020
+     *
5021
+     * @param  string $type bean type
5022
+     *
5023
+     * @return void
5024
+     */
5025
+    public function wipe( $type );
5026
+
5027
+    /**
5028
+     * This method will add a foreign key from type and field to
5029
+     * target type and target field.
5030
+     * The foreign key is created without an action. On delete/update
5031
+     * no action will be triggered. The FK is only used to allow database
5032
+     * tools to generate pretty diagrams and to make it easy to add actions
5033
+     * later on.
5034
+     * This methods accepts a type and infers the corresponding table name.
5035
+     *
5036
+     *
5037
+     * @param  string $type           type that will have a foreign key field
5038
+     * @param  string $targetType     points to this type
5039
+     * @param  string $property       field that contains the foreign key value
5040
+     * @param  string $targetProperty field where the fk points to
5041
+     * @param  string $isDep          whether target is dependent and should cascade on update/delete
5042
+     *
5043
+     * @return void
5044
+     */
5045
+    public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE );
5046
+
5047
+    /**
5048
+     * This method will add an index to a type and field with name
5049
+     * $name.
5050
+     * This methods accepts a type and infers the corresponding table name.
5051
+     *
5052
+     * @param string $type     type to add index to
5053
+     * @param string $name     name of the new index
5054
+     * @param string $property field to index
5055
+     *
5056
+     * @return void
5057
+     */
5058
+    public function addIndex( $type, $name, $property );
5059
+
5060
+    /**
5061
+     * Checks and filters a database structure element like a table of column
5062
+     * for safe use in a query. A database structure has to conform to the
5063
+     * RedBeanPHP DB security policy which basically means only alphanumeric
5064
+     * symbols are allowed. This security policy is more strict than conventional
5065
+     * SQL policies and does therefore not require database specific escaping rules.
5066
+     *
5067
+     * @param string  $databaseStructure name of the column/table to check
5068
+     * @param boolean $noQuotes          TRUE to NOT put backticks or quotes around the string
5069
+     *
5070
+     * @return string
5071
+     */
5072
+    public function esc( $databaseStructure, $dontQuote = FALSE );
5073
+
5074
+    /**
5075
+     * Removes all tables and views from the database.
5076
+     *
5077
+     * @return void
5078
+     */
5079
+    public function wipeAll();
5080
+
5081
+    /**
5082
+     * Renames an association. For instance if you would like to refer to
5083
+     * album_song as: track you can specify this by calling this method like:
5084
+     *
5085
+     * <code>
5086
+     * renameAssociation('album_song','track')
5087
+     * </code>
5088
+     *
5089
+     * This allows:
5090
+     *
5091
+     * <code>
5092
+     * $album->sharedSong
5093
+     * </code>
5094
+     *
5095
+     * to add/retrieve beans from track instead of album_song.
5096
+     * Also works for exportAll().
5097
+     *
5098
+     * This method also accepts a single associative array as
5099
+     * its first argument.
5100
+     *
5101
+     * @param string|array $fromType original type name, or array
5102
+     * @param string       $toType   new type name (only if 1st argument is string)
5103
+     *
5104
+     * @return void
5105
+     */
5106
+    public function renameAssocTable( $fromType, $toType = NULL );
5107
+
5108
+    /**
5109
+     * Returns the format for link tables.
5110
+     * Given an array containing two type names this method returns the
5111
+     * name of the link table to be used to store and retrieve
5112
+     * association records. For instance, given two types: person and
5113
+     * project, the corresponding link table might be: 'person_project'.
5114
+     *
5115
+     * @param  array $types two types array($type1, $type2)
5116
+     *
5117
+     * @return string
5118
+     */
5119
+    public function getAssocTable( $types );
5120
+
5121
+    /**
5122
+     * Given a bean type and a property, this method
5123
+     * tries to infer the fetch type using the foreign key
5124
+     * definitions in the database.
5125
+     * For instance: project, student -> person.
5126
+     * If no fetchType can be inferred, this method will return NULL.
5127
+     *
5128
+     * @note QueryWriters do not have to implement this method,
5129
+     * it's optional. A default version is available in AQueryWriter.
5130
+     *
5131
+     * @param $type     the source type to fetch a target type for
5132
+     * @param $property the property to fetch the type of
5133
+     *
5134
+     * @return string|NULL
5135
+     */
5136
+    public function inferFetchType( $type, $property );
5137 5137
 }
5138 5138
 }
5139 5139
 
@@ -5163,969 +5163,969 @@  discard block
 block discarded – undo
5163 5163
  */
5164 5164
 abstract class AQueryWriter
5165 5165
 {
5166
-	/**
5167
-	 * Constant: Select Snippet 'FOR UPDATE'
5168
-	 */
5169
-	const C_SELECT_SNIPPET_FOR_UPDATE = 'FOR UPDATE';
5170
-	const C_DATA_TYPE_ONLY_IF_NOT_EXISTS = 80;
5171
-	const C_DATA_TYPE_MANUAL = 99;
5172
-
5173
-	/**
5174
-	 * @var array
5175
-	 */
5176
-	private static $sqlFilters = array();
5177
-
5178
-	/**
5179
-	 * @var boolean
5180
-	 */
5181
-	private static $flagSQLFilterSafeMode = FALSE;
5182
-
5183
-	/**
5184
-	 * @var boolean
5185
-	 */
5186
-	private static $flagNarrowFieldMode = TRUE;
5187
-
5188
-	/**
5189
-	 * @var boolean
5190
-	 */
5191
-	protected static $flagUseJSONColumns = FALSE;
5192
-
5193
-	/**
5194
-	 * @var array
5195
-	 */
5196
-	public static $renames = array();
5197
-
5198
-	/**
5199
-	 * @var DBAdapter
5200
-	 */
5201
-	protected $adapter;
5202
-
5203
-	/**
5204
-	 * @var string
5205
-	 */
5206
-	protected $defaultValue = 'NULL';
5207
-
5208
-	/**
5209
-	 * @var string
5210
-	 */
5211
-	protected $quoteCharacter = '';
5212
-
5213
-	/**
5214
-	 * @var boolean
5215
-	 */
5216
-	protected $flagUseCache = TRUE;
5217
-
5218
-	/**
5219
-	 * @var array
5220
-	 */
5221
-	protected $cache = array();
5222
-
5223
-	/**
5224
-	 * @var integer
5225
-	 */
5226
-	protected $maxCacheSizePerType = 20;
5227
-
5228
-	/**
5229
-	 * @var string
5230
-	 */
5231
-	protected $sqlSelectSnippet = '';
5232
-
5233
-	/**
5234
-	 * @var array
5235
-	 */
5236
-	public $typeno_sqltype = array();
5237
-
5238
-	/**
5239
-	 * @var bool
5240
-	 */
5241
-	protected static $noNuke = false;
5242
-
5243
-	/**
5244
-	 * Toggles support for automatic generation of JSON columns.
5245
-	 * Using JSON columns means that strings containing JSON will
5246
-	 * cause the column to be created (not modified) as a JSON column.
5247
-	 * However it might also trigger exceptions if this means the DB attempts to
5248
-	 * convert a non-json column to a JSON column. Returns the previous
5249
-	 * value of the flag.
5250
-	 *
5251
-	 * @param boolean $flag TRUE or FALSE
5252
-	 *
5253
-	 * @return boolean
5254
-	 */
5255
-	public static function useJSONColumns( $flag )
5256
-	{
5257
-		$old = self::$flagUseJSONColumns;
5258
-		self::$flagUseJSONColumns = $flag;
5259
-		return $old;
5260
-	}
5261
-
5262
-	/**
5263
-	 * Toggles support for nuke().
5264
-	 * Can be used to turn off the nuke() feature for security reasons.
5265
-	 * Returns the old flag value.
5266
-	 *
5267
-	 * @param boolean $flag TRUE or FALSE
5268
-	 *
5269
-	 * @return boolean
5270
-	 */
5271
-	public static function forbidNuke( $flag ) {
5272
-		$old = self::$noNuke;
5273
-		self::$noNuke = (bool) $flag;
5274
-		return $old;
5275
-	}
5276
-
5277
-	/**
5278
-	 * Checks whether a number can be treated like an int.
5279
-	 *
5280
-	 * @param  string $value string representation of a certain value
5281
-	 *
5282
-	 * @return boolean
5283
-	 */
5284
-	public static function canBeTreatedAsInt( $value )
5285
-	{
5286
-		return (bool) ( strval( $value ) === strval( intval( $value ) ) );
5287
-	}
5288
-
5289
-	/**
5290
-	 * @see QueryWriter::getAssocTableFormat
5291
-	 */
5292
-	public static function getAssocTableFormat( $types )
5293
-	{
5294
-		sort( $types );
5295
-
5296
-		$assoc = implode( '_', $types );
5297
-
5298
-		return ( isset( self::$renames[$assoc] ) ) ? self::$renames[$assoc] : $assoc;
5299
-	}
5300
-
5301
-	/**
5302
-	 * @see QueryWriter::renameAssociation
5303
-	 */
5304
-	public static function renameAssociation( $from, $to = NULL )
5305
-	{
5306
-		if ( is_array( $from ) ) {
5307
-			foreach ( $from as $key => $value ) self::$renames[$key] = $value;
5308
-
5309
-			return;
5310
-		}
5311
-
5312
-		self::$renames[$from] = $to;
5313
-	}
5314
-
5315
-	/**
5316
-	 * Globally available service method for RedBeanPHP.
5317
-	 * Converts a camel cased string to a snake cased string.
5318
-	 *
5319
-	 * @param string $camel camelCased string to converty to snake case
5320
-	 *
5321
-	 * @return string
5322
-	 */
5323
-	public static function camelsSnake( $camel )
5324
-	{
5325
-		return strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $camel ) );
5326
-	}
5327
-
5328
-	/**
5329
-	 * Clears renames.
5330
-	 *
5331
-	 * @return void
5332
-	 */
5333
-	public static function clearRenames()
5334
-	{
5335
-		self::$renames = array();
5336
-	}
5337
-
5338
-	/**
5339
-	 * Toggles 'Narrow Field Mode'.
5340
-	 * In Narrow Field mode the queryRecord method will
5341
-	 * narrow its selection field to
5342
-	 *
5343
-	 * SELECT {table}.*
5344
-	 *
5345
-	 * instead of
5346
-	 *
5347
-	 * SELECT *
5348
-	 *
5349
-	 * This is a better way of querying because it allows
5350
-	 * more flexibility (for instance joins). However if you need
5351
-	 * the wide selector for backward compatibility; use this method
5352
-	 * to turn OFF Narrow Field Mode by passing FALSE.
5353
-	 * Default is TRUE.
5354
-	 *
5355
-	 * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field
5356
-	 *
5357
-	 * @return void
5358
-	 */
5359
-	public static function setNarrowFieldMode( $narrowField )
5360
-	{
5361
-		self::$flagNarrowFieldMode = (boolean) $narrowField;
5362
-	}
5363
-
5364
-	/**
5365
-	 * Sets SQL filters.
5366
-	 * This is a lowlevel method to set the SQL filter array.
5367
-	 * The format of this array is:
5368
-	 *
5369
-	 * <code>
5370
-	 * array(
5371
-	 * 		'<MODE, i.e. 'r' for read, 'w' for write>' => array(
5372
-	 * 			'<TABLE NAME>' => array(
5373
-	 * 				'<COLUMN NAME>' => '<SQL>'
5374
-	 * 			)
5375
-	 * 		)
5376
-	 * )
5377
-	 * </code>
5378
-	 *
5379
-	 * Example:
5380
-	 *
5381
-	 * <code>
5382
-	 * array(
5383
-	 *   QueryWriter::C_SQLFILTER_READ => array(
5384
-	 * 	'book' => array(
5385
-	 * 		'title' => ' LOWER(book.title) '
5386
-	 * 	)
5387
-	 * )
5388
-	 * </code>
5389
-	 *
5390
-	 * Note that you can use constants instead of magical chars
5391
-	 * as keys for the uppermost array.
5392
-	 * This is a lowlevel method. For a more friendly method
5393
-	 * please take a look at the facade: R::bindFunc().
5394
-	 *
5395
-	 * @param array list of filters to set
5396
-	 *
5397
-	 * @return void
5398
-	 */
5399
-	public static function setSQLFilters( $sqlFilters, $safeMode = FALSE )
5400
-	{
5401
-		self::$flagSQLFilterSafeMode = (boolean) $safeMode;
5402
-		self::$sqlFilters = $sqlFilters;
5403
-	}
5404
-
5405
-	/**
5406
-	 * Returns current SQL Filters.
5407
-	 * This method returns the raw SQL filter array.
5408
-	 * This is a lowlevel method. For a more friendly method
5409
-	 * please take a look at the facade: R::bindFunc().
5410
-	 *
5411
-	 * @return array
5412
-	 */
5413
-	public static function getSQLFilters()
5414
-	{
5415
-		return self::$sqlFilters;
5416
-	}
5417
-
5418
-	/**
5419
-	 * Returns a cache key for the cache values passed.
5420
-	 * This method returns a fingerprint string to be used as a key to store
5421
-	 * data in the writer cache.
5422
-	 *
5423
-	 * @param array $keyValues key-value to generate key for
5424
-	 *
5425
-	 * @return string
5426
-	 */
5427
-	private function getCacheKey( $keyValues )
5428
-	{
5429
-		return json_encode( $keyValues );
5430
-	}
5431
-
5432
-	/**
5433
-	 * Returns the values associated with the provided cache tag and key.
5434
-	 *
5435
-	 * @param string $cacheTag cache tag to use for lookup
5436
-	 * @param string $key      key to use for lookup
5437
-	 *
5438
-	 * @return mixed
5439
-	 */
5440
-	private function getCached( $cacheTag, $key )
5441
-	{
5442
-		$sql = $this->adapter->getSQL();
5443
-		if ($this->updateCache()) {
5444
-			if ( isset( $this->cache[$cacheTag][$key] ) ) {
5445
-				return $this->cache[$cacheTag][$key];
5446
-			}
5447
-		}
5448
-
5449
-		return NULL;
5450
-	}
5451
-
5452
-	/**
5453
-	 * Checks if the previous query had a keep-cache tag.
5454
-	 * If so, the cache will persist, otherwise the cache will be flushed.
5455
-	 *
5456
-	 * Returns TRUE if the cache will remain and FALSE if a flush has
5457
-	 * been performed.
5458
-	 *
5459
-	 * @return boolean
5460
-	 */
5461
-	private function updateCache()
5462
-	{
5463
-		$sql = $this->adapter->getSQL();
5464
-		if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) {
5465
-			// If SQL has been taken place outside of this method then something else then
5466
-			// a select query might have happened! (or instruct to keep cache)
5467
-			$this->cache = array();
5468
-			return FALSE;
5469
-		}
5470
-		return TRUE;
5471
-	}
5472
-
5473
-	/**
5474
-	 * Stores data from the writer in the cache under a specific key and cache tag.
5475
-	 * A cache tag is used to make sure the cache remains consistent. In most cases the cache tag
5476
-	 * will be the bean type, this makes sure queries associated with a certain reference type will
5477
-	 * never contain conflicting data.
5478
-	 * Why not use the cache tag as a key? Well
5479
-	 * we need to make sure the cache contents fits the key (and key is based on the cache values).
5480
-	 * Otherwise it would be possible to store two different result sets under the same key (the cache tag).
5481
-	 *
5482
-	 * In previous versions you could only store one key-entry, I have changed this to
5483
-	 * improve caching efficiency (issue #400).
5484
-	 *
5485
-	 * @param string $cacheTag cache tag (secondary key)
5486
-	 * @param string $key      key to store values under
5487
-	 * @param array  $values   content to be stored
5488
-	 *
5489
-	 * @return void
5490
-	 */
5491
-	private function putResultInCache( $cacheTag, $key, $values )
5492
-	{
5493
-		if ( isset( $this->cache[$cacheTag] ) ) {
5494
-			if ( count( $this->cache[$cacheTag] ) > $this->maxCacheSizePerType ) array_shift( $this->cache[$cacheTag] );
5495
-		} else {
5496
-			$this->cache[$cacheTag] = array();
5497
-		}
5498
-		$this->cache[$cacheTag][$key] = $values;
5499
-	}
5500
-
5501
-	/**
5502
-	 * Creates an SQL snippet from a list of conditions of format:
5503
-	 *
5504
-	 * <code>
5505
-	 * array(
5506
-	 *    key => array(
5507
-	 *           value1, value2, value3 ....
5508
-	 *        )
5509
-	 * )
5510
-	 * </code>
5511
-	 *
5512
-	 * @param array  $conditions list of conditions
5513
-	 * @param array  $bindings   parameter bindings for SQL snippet
5514
-	 * @param string $addSql     additional SQL snippet to append to result
5515
-	 *
5516
-	 * @return string
5517
-	 */
5518
-	private function makeSQLFromConditions( $conditions, &$bindings, $addSql = '' )
5519
-	{
5520
-		reset( $bindings );
5521
-		$firstKey       = key( $bindings );
5522
-		$paramTypeIsNum = ( is_numeric( $firstKey ) );
5523
-		$counter        = 0;
5524
-
5525
-		$sqlConditions = array();
5526
-		foreach ( $conditions as $column => $values ) {
5527
-			if ( $values === NULL ) continue;
5528
-
5529
-			if ( is_array( $values ) ) {
5530
-				if ( empty( $values ) ) continue;
5531
-			} else {
5532
-				$values = array( $values );
5533
-			}
5534
-
5535
-			$checkOODB = reset( $values );
5536
-			if ( $checkOODB instanceof OODBBean && $checkOODB->getMeta( 'type' ) === $column && substr( $column, -3 ) != '_id' )
5537
-				$column = $column . '_id';
5538
-
5539
-
5540
-			$sql = $this->esc( $column );
5541
-			$sql .= ' IN ( ';
5542
-
5543
-			if ( $paramTypeIsNum ) {
5544
-				$sql .= implode( ',', array_fill( 0, count( $values ), '?' ) ) . ' ) ';
5545
-
5546
-				array_unshift($sqlConditions, $sql);
5547
-
5548
-				foreach ( $values as $k => $v ) {
5549
-					if ( $v instanceof OODBBean ) {
5550
-						$v = $v->id;
5551
-					}
5552
-					$values[$k] = strval( $v );
5553
-
5554
-					array_unshift( $bindings, $v );
5555
-				}
5556
-			} else {
5557
-
5558
-				$slots = array();
5559
-
5560
-				foreach( $values as $k => $v ) {
5561
-					if ( $v instanceof OODBBean ) {
5562
-						$v = $v->id;
5563
-					}
5564
-					$slot            = ':slot'.$counter++;
5565
-					$slots[]         = $slot;
5566
-					$bindings[$slot] = strval( $v );
5567
-				}
5568
-
5569
-				$sql .= implode( ',', $slots ).' ) ';
5570
-				$sqlConditions[] = $sql;
5571
-			}
5572
-		}
5573
-
5574
-		$sql = '';
5575
-		if ( !empty( $sqlConditions ) ) {
5576
-			$sql .= " WHERE ( " . implode( ' AND ', $sqlConditions ) . ") ";
5577
-		}
5578
-
5579
-		$addSql = $this->glueSQLCondition( $addSql, !empty( $sqlConditions ) ? QueryWriter::C_GLUE_AND : NULL );
5580
-		if ( $addSql ) $sql .= $addSql;
5581
-
5582
-		return $sql;
5583
-	}
5584
-
5585
-	/**
5586
-	 * Returns the table names and column names for a relational query.
5587
-	 *
5588
-	 * @param string  $sourceType type of the source bean
5589
-	 * @param string  $destType   type of the bean you want to obtain using the relation
5590
-	 * @param boolean $noQuote    TRUE if you want to omit quotes
5591
-	 *
5592
-	 * @return array
5593
-	 */
5594
-	private function getRelationalTablesAndColumns( $sourceType, $destType, $noQuote = FALSE )
5595
-	{
5596
-		$linkTable   = $this->esc( $this->getAssocTable( array( $sourceType, $destType ) ), $noQuote );
5597
-		$sourceCol   = $this->esc( $sourceType . '_id', $noQuote );
5598
-
5599
-		if ( $sourceType === $destType ) {
5600
-			$destCol = $this->esc( $destType . '2_id', $noQuote );
5601
-		} else {
5602
-			$destCol = $this->esc( $destType . '_id', $noQuote );
5603
-		}
5604
-
5605
-		$sourceTable = $this->esc( $sourceType, $noQuote );
5606
-		$destTable   = $this->esc( $destType, $noQuote );
5607
-
5608
-		return array( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol );
5609
-	}
5610
-
5611
-	/**
5612
-	 * Determines whether a string can be considered JSON or not.
5613
-	 * This is used by writers that support JSON columns. However
5614
-	 * we dont want that code duplicated over all JSON supporting
5615
-	 * Query Writers.
5616
-	 *
5617
-	 * @param string $value value to determine 'JSONness' of.
5618
-	 *
5619
-	 * @return boolean
5620
-	 */
5621
-	protected function isJSON( $value )
5622
-	{
5623
-		return (
5624
-			is_string($value) &&
5625
-			is_array(json_decode($value, TRUE)) &&
5626
-			(json_last_error() == JSON_ERROR_NONE)
5627
-		);
5628
-	}
5629
-
5630
-	/**
5631
-	 * Given a type and a property name this method
5632
-	 * returns the foreign key map section associated with this pair.
5633
-	 *
5634
-	 * @param string $type     name of the type
5635
-	 * @param string $property name of the property
5636
-	 *
5637
-	 * @return array|NULL
5638
-	 */
5639
-	protected function getForeignKeyForTypeProperty( $type, $property )
5640
-	{
5641
-		$property = $this->esc( $property, TRUE );
5642
-
5643
-		try {
5644
-			$map = $this->getKeyMapForType( $type );
5645
-		} catch ( SQLException $e ) {
5646
-			return NULL;
5647
-		}
5648
-
5649
-		foreach( $map as $key ) {
5650
-			if ( $key['from'] === $property ) return $key;
5651
-		}
5652
-		return NULL;
5653
-	}
5654
-
5655
-	/**
5656
-	 * Returns the foreign key map (FKM) for a type.
5657
-	 * A foreign key map describes the foreign keys in a table.
5658
-	 * A FKM always has the same structure:
5659
-	 *
5660
-	 * <code>
5661
-	 * array(
5662
-	 * 	'name'      => <name of the foreign key>
5663
-	 *    'from'      => <name of the column on the source table>
5664
-	 *    'table'     => <name of the target table>
5665
-	 *    'to'        => <name of the target column> (most of the time 'id')
5666
-	 *    'on_update' => <update rule: 'SET NULL','CASCADE' or 'RESTRICT'>
5667
-	 *    'on_delete' => <delete rule: 'SET NULL','CASCADE' or 'RESTRICT'>
5668
-	 * )
5669
-	 * </code>
5670
-	 *
5671
-	 * @note the keys in the result array are FKDLs, i.e. descriptive unique
5672
-	 * keys per source table. Also see: AQueryWriter::makeFKLabel for details.
5673
-	 *
5674
-	 * @param string $type the bean type you wish to obtain a key map of
5675
-	 *
5676
-	 * @return array
5677
-	 */
5678
-	protected function getKeyMapForType( $type )
5679
-	{
5680
-		return array();
5681
-	}
5682
-
5683
-	/**
5684
-	 * This method makes a key for a foreign key description array.
5685
-	 * This key is a readable string unique for every source table.
5686
-	 * This uniform key is called the FKDL Foreign Key Description Label.
5687
-	 * Note that the source table is not part of the FKDL because
5688
-	 * this key is supposed to be 'per source table'. If you wish to
5689
-	 * include a source table, prefix the key with 'on_table_<SOURCE>_'.
5690
-	 *
5691
-	 * @param string $from  the column of the key in the source table
5692
-	 * @param string $type  the type (table) where the key points to
5693
-	 * @param string $to    the target column of the foreign key (mostly just 'id')
5694
-	 *
5695
-	 * @return string
5696
-	 */
5697
-	protected function makeFKLabel($from, $type, $to)
5698
-	{
5699
-		return "from_{$from}_to_table_{$type}_col_{$to}";
5700
-	}
5701
-
5702
-	/**
5703
-	 * Returns an SQL Filter snippet for reading.
5704
-	 *
5705
-	 * @param string $type type of bean
5706
-	 *
5707
-	 * @return string
5708
-	 */
5709
-	protected function getSQLFilterSnippet( $type )
5710
-	{
5711
-		$existingCols = array();
5712
-		if (self::$flagSQLFilterSafeMode) {
5713
-			$existingCols = $this->getColumns( $type );
5714
-		}
5715
-
5716
-		$sqlFilters = array();
5717
-		if ( isset( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] ) ) {
5718
-			foreach( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] as $property => $sqlFilter ) {
5719
-				if ( !self::$flagSQLFilterSafeMode || isset( $existingCols[$property] ) ) {
5720
-					$sqlFilters[] = $sqlFilter.' AS '.$property.' ';
5721
-				}
5722
-			}
5723
-		}
5724
-		$sqlFilterStr = ( count($sqlFilters) ) ? ( ','.implode( ',', $sqlFilters ) ) : '';
5725
-		return $sqlFilterStr;
5726
-	}
5727
-
5728
-	/**
5729
-	 * Generates a list of parameters (slots) for an SQL snippet.
5730
-	 * This method calculates the correct number of slots to insert in the
5731
-	 * SQL snippet and determines the correct type of slot. If the bindings
5732
-	 * array contains named parameters this method will return named ones and
5733
-	 * update the keys in the value list accordingly (that's why we use the &).
5734
-	 *
5735
-	 * If you pass an offset the bindings will be re-added to the value list.
5736
-	 * Some databases cant handle duplicate parameter names in queries.
5737
-	 *
5738
-	 * @param array   &$valueList    list of values to generate slots for (gets modified if needed)
5739
-	 * @param array   $otherBindings list of additional bindings
5740
-	 * @param integer $offset        start counter at...
5741
-	 *
5742
-	 * @return string
5743
-	 */
5744
-	protected function getParametersForInClause( &$valueList, $otherBindings, $offset = 0 )
5745
-	{
5746
-		if ( is_array( $otherBindings ) && count( $otherBindings ) > 0 ) {
5747
-			reset( $otherBindings );
5748
-
5749
-			$key = key( $otherBindings );
5750
-
5751
-			if ( !is_numeric($key) ) {
5752
-				$filler  = array();
5753
-				$newList = (!$offset) ? array() : $valueList;
5754
-				$counter = $offset;
5755
-
5756
-				foreach( $valueList as $value ) {
5757
-					$slot           = ':slot' . ( $counter++ );
5758
-					$filler[]       = $slot;
5759
-					$newList[$slot] = $value;
5760
-				}
5761
-
5762
-				// Change the keys!
5763
-				$valueList = $newList;
5764
-
5765
-				return implode( ',', $filler );
5766
-			}
5767
-		}
5768
-
5769
-		return implode( ',', array_fill( 0, count( $valueList ), '?' ) );
5770
-	}
5771
-
5772
-	/**
5773
-	 * Adds a data type to the list of data types.
5774
-	 * Use this method to add a new column type definition to the writer.
5775
-	 * Used for UUID support.
5776
-	 *
5777
-	 * @param integer $dataTypeID    magic number constant assigned to this data type
5778
-	 * @param string  $SQLDefinition SQL column definition (i.e. INT(11))
5779
-	 *
5780
-	 * @return self
5781
-	 */
5782
-	protected function addDataType( $dataTypeID, $SQLDefinition )
5783
-	{
5784
-		$this->typeno_sqltype[ $dataTypeID ] = $SQLDefinition;
5785
-		$this->sqltype_typeno[ $SQLDefinition ] = $dataTypeID;
5786
-		return $this;
5787
-	}
5788
-
5789
-	/**
5790
-	 * Returns the sql that should follow an insert statement.
5791
-	 *
5792
-	 * @param string $table name
5793
-	 *
5794
-	 * @return string
5795
-	 */
5796
-	protected function getInsertSuffix( $table )
5797
-	{
5798
-		return '';
5799
-	}
5800
-
5801
-	/**
5802
-	 * Checks whether a value starts with zeros. In this case
5803
-	 * the value should probably be stored using a text datatype instead of a
5804
-	 * numerical type in order to preserve the zeros.
5805
-	 *
5806
-	 * @param string $value value to be checked.
5807
-	 *
5808
-	 * @return boolean
5809
-	 */
5810
-	protected function startsWithZeros( $value )
5811
-	{
5812
-		$value = strval( $value );
5813
-
5814
-		if ( strlen( $value ) > 1 && strpos( $value, '0' ) === 0 && strpos( $value, '0.' ) !== 0 ) {
5815
-			return TRUE;
5816
-		} else {
5817
-			return FALSE;
5818
-		}
5819
-	}
5820
-
5821
-	/**
5822
-	 * Inserts a record into the database using a series of insert columns
5823
-	 * and corresponding insertvalues. Returns the insert id.
5824
-	 *
5825
-	 * @param string $table         table to perform query on
5826
-	 * @param array  $insertcolumns columns to be inserted
5827
-	 * @param array  $insertvalues  values to be inserted
5828
-	 *
5829
-	 * @return integer
5830
-	 */
5831
-	protected function insertRecord( $type, $insertcolumns, $insertvalues )
5832
-	{
5833
-		$default = $this->defaultValue;
5834
-		$suffix  = $this->getInsertSuffix( $type );
5835
-		$table   = $this->esc( $type );
5836
-
5837
-		if ( count( $insertvalues ) > 0 && is_array( $insertvalues[0] ) && count( $insertvalues[0] ) > 0 ) {
5838
-
5839
-			$insertSlots = array();
5840
-			foreach ( $insertcolumns as $k => $v ) {
5841
-				$insertcolumns[$k] = $this->esc( $v );
5842
-
5843
-				if (isset(self::$sqlFilters['w'][$type][$v])) {
5844
-					$insertSlots[] = self::$sqlFilters['w'][$type][$v];
5845
-				} else {
5846
-					$insertSlots[] = '?';
5847
-				}
5848
-			}
5849
-
5850
-			$insertSQL = "INSERT INTO $table ( id, " . implode( ',', $insertcolumns ) . " ) VALUES
5166
+    /**
5167
+     * Constant: Select Snippet 'FOR UPDATE'
5168
+     */
5169
+    const C_SELECT_SNIPPET_FOR_UPDATE = 'FOR UPDATE';
5170
+    const C_DATA_TYPE_ONLY_IF_NOT_EXISTS = 80;
5171
+    const C_DATA_TYPE_MANUAL = 99;
5172
+
5173
+    /**
5174
+     * @var array
5175
+     */
5176
+    private static $sqlFilters = array();
5177
+
5178
+    /**
5179
+     * @var boolean
5180
+     */
5181
+    private static $flagSQLFilterSafeMode = FALSE;
5182
+
5183
+    /**
5184
+     * @var boolean
5185
+     */
5186
+    private static $flagNarrowFieldMode = TRUE;
5187
+
5188
+    /**
5189
+     * @var boolean
5190
+     */
5191
+    protected static $flagUseJSONColumns = FALSE;
5192
+
5193
+    /**
5194
+     * @var array
5195
+     */
5196
+    public static $renames = array();
5197
+
5198
+    /**
5199
+     * @var DBAdapter
5200
+     */
5201
+    protected $adapter;
5202
+
5203
+    /**
5204
+     * @var string
5205
+     */
5206
+    protected $defaultValue = 'NULL';
5207
+
5208
+    /**
5209
+     * @var string
5210
+     */
5211
+    protected $quoteCharacter = '';
5212
+
5213
+    /**
5214
+     * @var boolean
5215
+     */
5216
+    protected $flagUseCache = TRUE;
5217
+
5218
+    /**
5219
+     * @var array
5220
+     */
5221
+    protected $cache = array();
5222
+
5223
+    /**
5224
+     * @var integer
5225
+     */
5226
+    protected $maxCacheSizePerType = 20;
5227
+
5228
+    /**
5229
+     * @var string
5230
+     */
5231
+    protected $sqlSelectSnippet = '';
5232
+
5233
+    /**
5234
+     * @var array
5235
+     */
5236
+    public $typeno_sqltype = array();
5237
+
5238
+    /**
5239
+     * @var bool
5240
+     */
5241
+    protected static $noNuke = false;
5242
+
5243
+    /**
5244
+     * Toggles support for automatic generation of JSON columns.
5245
+     * Using JSON columns means that strings containing JSON will
5246
+     * cause the column to be created (not modified) as a JSON column.
5247
+     * However it might also trigger exceptions if this means the DB attempts to
5248
+     * convert a non-json column to a JSON column. Returns the previous
5249
+     * value of the flag.
5250
+     *
5251
+     * @param boolean $flag TRUE or FALSE
5252
+     *
5253
+     * @return boolean
5254
+     */
5255
+    public static function useJSONColumns( $flag )
5256
+    {
5257
+        $old = self::$flagUseJSONColumns;
5258
+        self::$flagUseJSONColumns = $flag;
5259
+        return $old;
5260
+    }
5261
+
5262
+    /**
5263
+     * Toggles support for nuke().
5264
+     * Can be used to turn off the nuke() feature for security reasons.
5265
+     * Returns the old flag value.
5266
+     *
5267
+     * @param boolean $flag TRUE or FALSE
5268
+     *
5269
+     * @return boolean
5270
+     */
5271
+    public static function forbidNuke( $flag ) {
5272
+        $old = self::$noNuke;
5273
+        self::$noNuke = (bool) $flag;
5274
+        return $old;
5275
+    }
5276
+
5277
+    /**
5278
+     * Checks whether a number can be treated like an int.
5279
+     *
5280
+     * @param  string $value string representation of a certain value
5281
+     *
5282
+     * @return boolean
5283
+     */
5284
+    public static function canBeTreatedAsInt( $value )
5285
+    {
5286
+        return (bool) ( strval( $value ) === strval( intval( $value ) ) );
5287
+    }
5288
+
5289
+    /**
5290
+     * @see QueryWriter::getAssocTableFormat
5291
+     */
5292
+    public static function getAssocTableFormat( $types )
5293
+    {
5294
+        sort( $types );
5295
+
5296
+        $assoc = implode( '_', $types );
5297
+
5298
+        return ( isset( self::$renames[$assoc] ) ) ? self::$renames[$assoc] : $assoc;
5299
+    }
5300
+
5301
+    /**
5302
+     * @see QueryWriter::renameAssociation
5303
+     */
5304
+    public static function renameAssociation( $from, $to = NULL )
5305
+    {
5306
+        if ( is_array( $from ) ) {
5307
+            foreach ( $from as $key => $value ) self::$renames[$key] = $value;
5308
+
5309
+            return;
5310
+        }
5311
+
5312
+        self::$renames[$from] = $to;
5313
+    }
5314
+
5315
+    /**
5316
+     * Globally available service method for RedBeanPHP.
5317
+     * Converts a camel cased string to a snake cased string.
5318
+     *
5319
+     * @param string $camel camelCased string to converty to snake case
5320
+     *
5321
+     * @return string
5322
+     */
5323
+    public static function camelsSnake( $camel )
5324
+    {
5325
+        return strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $camel ) );
5326
+    }
5327
+
5328
+    /**
5329
+     * Clears renames.
5330
+     *
5331
+     * @return void
5332
+     */
5333
+    public static function clearRenames()
5334
+    {
5335
+        self::$renames = array();
5336
+    }
5337
+
5338
+    /**
5339
+     * Toggles 'Narrow Field Mode'.
5340
+     * In Narrow Field mode the queryRecord method will
5341
+     * narrow its selection field to
5342
+     *
5343
+     * SELECT {table}.*
5344
+     *
5345
+     * instead of
5346
+     *
5347
+     * SELECT *
5348
+     *
5349
+     * This is a better way of querying because it allows
5350
+     * more flexibility (for instance joins). However if you need
5351
+     * the wide selector for backward compatibility; use this method
5352
+     * to turn OFF Narrow Field Mode by passing FALSE.
5353
+     * Default is TRUE.
5354
+     *
5355
+     * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field
5356
+     *
5357
+     * @return void
5358
+     */
5359
+    public static function setNarrowFieldMode( $narrowField )
5360
+    {
5361
+        self::$flagNarrowFieldMode = (boolean) $narrowField;
5362
+    }
5363
+
5364
+    /**
5365
+     * Sets SQL filters.
5366
+     * This is a lowlevel method to set the SQL filter array.
5367
+     * The format of this array is:
5368
+     *
5369
+     * <code>
5370
+     * array(
5371
+     * 		'<MODE, i.e. 'r' for read, 'w' for write>' => array(
5372
+     * 			'<TABLE NAME>' => array(
5373
+     * 				'<COLUMN NAME>' => '<SQL>'
5374
+     * 			)
5375
+     * 		)
5376
+     * )
5377
+     * </code>
5378
+     *
5379
+     * Example:
5380
+     *
5381
+     * <code>
5382
+     * array(
5383
+     *   QueryWriter::C_SQLFILTER_READ => array(
5384
+     * 	'book' => array(
5385
+     * 		'title' => ' LOWER(book.title) '
5386
+     * 	)
5387
+     * )
5388
+     * </code>
5389
+     *
5390
+     * Note that you can use constants instead of magical chars
5391
+     * as keys for the uppermost array.
5392
+     * This is a lowlevel method. For a more friendly method
5393
+     * please take a look at the facade: R::bindFunc().
5394
+     *
5395
+     * @param array list of filters to set
5396
+     *
5397
+     * @return void
5398
+     */
5399
+    public static function setSQLFilters( $sqlFilters, $safeMode = FALSE )
5400
+    {
5401
+        self::$flagSQLFilterSafeMode = (boolean) $safeMode;
5402
+        self::$sqlFilters = $sqlFilters;
5403
+    }
5404
+
5405
+    /**
5406
+     * Returns current SQL Filters.
5407
+     * This method returns the raw SQL filter array.
5408
+     * This is a lowlevel method. For a more friendly method
5409
+     * please take a look at the facade: R::bindFunc().
5410
+     *
5411
+     * @return array
5412
+     */
5413
+    public static function getSQLFilters()
5414
+    {
5415
+        return self::$sqlFilters;
5416
+    }
5417
+
5418
+    /**
5419
+     * Returns a cache key for the cache values passed.
5420
+     * This method returns a fingerprint string to be used as a key to store
5421
+     * data in the writer cache.
5422
+     *
5423
+     * @param array $keyValues key-value to generate key for
5424
+     *
5425
+     * @return string
5426
+     */
5427
+    private function getCacheKey( $keyValues )
5428
+    {
5429
+        return json_encode( $keyValues );
5430
+    }
5431
+
5432
+    /**
5433
+     * Returns the values associated with the provided cache tag and key.
5434
+     *
5435
+     * @param string $cacheTag cache tag to use for lookup
5436
+     * @param string $key      key to use for lookup
5437
+     *
5438
+     * @return mixed
5439
+     */
5440
+    private function getCached( $cacheTag, $key )
5441
+    {
5442
+        $sql = $this->adapter->getSQL();
5443
+        if ($this->updateCache()) {
5444
+            if ( isset( $this->cache[$cacheTag][$key] ) ) {
5445
+                return $this->cache[$cacheTag][$key];
5446
+            }
5447
+        }
5448
+
5449
+        return NULL;
5450
+    }
5451
+
5452
+    /**
5453
+     * Checks if the previous query had a keep-cache tag.
5454
+     * If so, the cache will persist, otherwise the cache will be flushed.
5455
+     *
5456
+     * Returns TRUE if the cache will remain and FALSE if a flush has
5457
+     * been performed.
5458
+     *
5459
+     * @return boolean
5460
+     */
5461
+    private function updateCache()
5462
+    {
5463
+        $sql = $this->adapter->getSQL();
5464
+        if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) {
5465
+            // If SQL has been taken place outside of this method then something else then
5466
+            // a select query might have happened! (or instruct to keep cache)
5467
+            $this->cache = array();
5468
+            return FALSE;
5469
+        }
5470
+        return TRUE;
5471
+    }
5472
+
5473
+    /**
5474
+     * Stores data from the writer in the cache under a specific key and cache tag.
5475
+     * A cache tag is used to make sure the cache remains consistent. In most cases the cache tag
5476
+     * will be the bean type, this makes sure queries associated with a certain reference type will
5477
+     * never contain conflicting data.
5478
+     * Why not use the cache tag as a key? Well
5479
+     * we need to make sure the cache contents fits the key (and key is based on the cache values).
5480
+     * Otherwise it would be possible to store two different result sets under the same key (the cache tag).
5481
+     *
5482
+     * In previous versions you could only store one key-entry, I have changed this to
5483
+     * improve caching efficiency (issue #400).
5484
+     *
5485
+     * @param string $cacheTag cache tag (secondary key)
5486
+     * @param string $key      key to store values under
5487
+     * @param array  $values   content to be stored
5488
+     *
5489
+     * @return void
5490
+     */
5491
+    private function putResultInCache( $cacheTag, $key, $values )
5492
+    {
5493
+        if ( isset( $this->cache[$cacheTag] ) ) {
5494
+            if ( count( $this->cache[$cacheTag] ) > $this->maxCacheSizePerType ) array_shift( $this->cache[$cacheTag] );
5495
+        } else {
5496
+            $this->cache[$cacheTag] = array();
5497
+        }
5498
+        $this->cache[$cacheTag][$key] = $values;
5499
+    }
5500
+
5501
+    /**
5502
+     * Creates an SQL snippet from a list of conditions of format:
5503
+     *
5504
+     * <code>
5505
+     * array(
5506
+     *    key => array(
5507
+     *           value1, value2, value3 ....
5508
+     *        )
5509
+     * )
5510
+     * </code>
5511
+     *
5512
+     * @param array  $conditions list of conditions
5513
+     * @param array  $bindings   parameter bindings for SQL snippet
5514
+     * @param string $addSql     additional SQL snippet to append to result
5515
+     *
5516
+     * @return string
5517
+     */
5518
+    private function makeSQLFromConditions( $conditions, &$bindings, $addSql = '' )
5519
+    {
5520
+        reset( $bindings );
5521
+        $firstKey       = key( $bindings );
5522
+        $paramTypeIsNum = ( is_numeric( $firstKey ) );
5523
+        $counter        = 0;
5524
+
5525
+        $sqlConditions = array();
5526
+        foreach ( $conditions as $column => $values ) {
5527
+            if ( $values === NULL ) continue;
5528
+
5529
+            if ( is_array( $values ) ) {
5530
+                if ( empty( $values ) ) continue;
5531
+            } else {
5532
+                $values = array( $values );
5533
+            }
5534
+
5535
+            $checkOODB = reset( $values );
5536
+            if ( $checkOODB instanceof OODBBean && $checkOODB->getMeta( 'type' ) === $column && substr( $column, -3 ) != '_id' )
5537
+                $column = $column . '_id';
5538
+
5539
+
5540
+            $sql = $this->esc( $column );
5541
+            $sql .= ' IN ( ';
5542
+
5543
+            if ( $paramTypeIsNum ) {
5544
+                $sql .= implode( ',', array_fill( 0, count( $values ), '?' ) ) . ' ) ';
5545
+
5546
+                array_unshift($sqlConditions, $sql);
5547
+
5548
+                foreach ( $values as $k => $v ) {
5549
+                    if ( $v instanceof OODBBean ) {
5550
+                        $v = $v->id;
5551
+                    }
5552
+                    $values[$k] = strval( $v );
5553
+
5554
+                    array_unshift( $bindings, $v );
5555
+                }
5556
+            } else {
5557
+
5558
+                $slots = array();
5559
+
5560
+                foreach( $values as $k => $v ) {
5561
+                    if ( $v instanceof OODBBean ) {
5562
+                        $v = $v->id;
5563
+                    }
5564
+                    $slot            = ':slot'.$counter++;
5565
+                    $slots[]         = $slot;
5566
+                    $bindings[$slot] = strval( $v );
5567
+                }
5568
+
5569
+                $sql .= implode( ',', $slots ).' ) ';
5570
+                $sqlConditions[] = $sql;
5571
+            }
5572
+        }
5573
+
5574
+        $sql = '';
5575
+        if ( !empty( $sqlConditions ) ) {
5576
+            $sql .= " WHERE ( " . implode( ' AND ', $sqlConditions ) . ") ";
5577
+        }
5578
+
5579
+        $addSql = $this->glueSQLCondition( $addSql, !empty( $sqlConditions ) ? QueryWriter::C_GLUE_AND : NULL );
5580
+        if ( $addSql ) $sql .= $addSql;
5581
+
5582
+        return $sql;
5583
+    }
5584
+
5585
+    /**
5586
+     * Returns the table names and column names for a relational query.
5587
+     *
5588
+     * @param string  $sourceType type of the source bean
5589
+     * @param string  $destType   type of the bean you want to obtain using the relation
5590
+     * @param boolean $noQuote    TRUE if you want to omit quotes
5591
+     *
5592
+     * @return array
5593
+     */
5594
+    private function getRelationalTablesAndColumns( $sourceType, $destType, $noQuote = FALSE )
5595
+    {
5596
+        $linkTable   = $this->esc( $this->getAssocTable( array( $sourceType, $destType ) ), $noQuote );
5597
+        $sourceCol   = $this->esc( $sourceType . '_id', $noQuote );
5598
+
5599
+        if ( $sourceType === $destType ) {
5600
+            $destCol = $this->esc( $destType . '2_id', $noQuote );
5601
+        } else {
5602
+            $destCol = $this->esc( $destType . '_id', $noQuote );
5603
+        }
5604
+
5605
+        $sourceTable = $this->esc( $sourceType, $noQuote );
5606
+        $destTable   = $this->esc( $destType, $noQuote );
5607
+
5608
+        return array( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol );
5609
+    }
5610
+
5611
+    /**
5612
+     * Determines whether a string can be considered JSON or not.
5613
+     * This is used by writers that support JSON columns. However
5614
+     * we dont want that code duplicated over all JSON supporting
5615
+     * Query Writers.
5616
+     *
5617
+     * @param string $value value to determine 'JSONness' of.
5618
+     *
5619
+     * @return boolean
5620
+     */
5621
+    protected function isJSON( $value )
5622
+    {
5623
+        return (
5624
+            is_string($value) &&
5625
+            is_array(json_decode($value, TRUE)) &&
5626
+            (json_last_error() == JSON_ERROR_NONE)
5627
+        );
5628
+    }
5629
+
5630
+    /**
5631
+     * Given a type and a property name this method
5632
+     * returns the foreign key map section associated with this pair.
5633
+     *
5634
+     * @param string $type     name of the type
5635
+     * @param string $property name of the property
5636
+     *
5637
+     * @return array|NULL
5638
+     */
5639
+    protected function getForeignKeyForTypeProperty( $type, $property )
5640
+    {
5641
+        $property = $this->esc( $property, TRUE );
5642
+
5643
+        try {
5644
+            $map = $this->getKeyMapForType( $type );
5645
+        } catch ( SQLException $e ) {
5646
+            return NULL;
5647
+        }
5648
+
5649
+        foreach( $map as $key ) {
5650
+            if ( $key['from'] === $property ) return $key;
5651
+        }
5652
+        return NULL;
5653
+    }
5654
+
5655
+    /**
5656
+     * Returns the foreign key map (FKM) for a type.
5657
+     * A foreign key map describes the foreign keys in a table.
5658
+     * A FKM always has the same structure:
5659
+     *
5660
+     * <code>
5661
+     * array(
5662
+     * 	'name'      => <name of the foreign key>
5663
+     *    'from'      => <name of the column on the source table>
5664
+     *    'table'     => <name of the target table>
5665
+     *    'to'        => <name of the target column> (most of the time 'id')
5666
+     *    'on_update' => <update rule: 'SET NULL','CASCADE' or 'RESTRICT'>
5667
+     *    'on_delete' => <delete rule: 'SET NULL','CASCADE' or 'RESTRICT'>
5668
+     * )
5669
+     * </code>
5670
+     *
5671
+     * @note the keys in the result array are FKDLs, i.e. descriptive unique
5672
+     * keys per source table. Also see: AQueryWriter::makeFKLabel for details.
5673
+     *
5674
+     * @param string $type the bean type you wish to obtain a key map of
5675
+     *
5676
+     * @return array
5677
+     */
5678
+    protected function getKeyMapForType( $type )
5679
+    {
5680
+        return array();
5681
+    }
5682
+
5683
+    /**
5684
+     * This method makes a key for a foreign key description array.
5685
+     * This key is a readable string unique for every source table.
5686
+     * This uniform key is called the FKDL Foreign Key Description Label.
5687
+     * Note that the source table is not part of the FKDL because
5688
+     * this key is supposed to be 'per source table'. If you wish to
5689
+     * include a source table, prefix the key with 'on_table_<SOURCE>_'.
5690
+     *
5691
+     * @param string $from  the column of the key in the source table
5692
+     * @param string $type  the type (table) where the key points to
5693
+     * @param string $to    the target column of the foreign key (mostly just 'id')
5694
+     *
5695
+     * @return string
5696
+     */
5697
+    protected function makeFKLabel($from, $type, $to)
5698
+    {
5699
+        return "from_{$from}_to_table_{$type}_col_{$to}";
5700
+    }
5701
+
5702
+    /**
5703
+     * Returns an SQL Filter snippet for reading.
5704
+     *
5705
+     * @param string $type type of bean
5706
+     *
5707
+     * @return string
5708
+     */
5709
+    protected function getSQLFilterSnippet( $type )
5710
+    {
5711
+        $existingCols = array();
5712
+        if (self::$flagSQLFilterSafeMode) {
5713
+            $existingCols = $this->getColumns( $type );
5714
+        }
5715
+
5716
+        $sqlFilters = array();
5717
+        if ( isset( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] ) ) {
5718
+            foreach( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] as $property => $sqlFilter ) {
5719
+                if ( !self::$flagSQLFilterSafeMode || isset( $existingCols[$property] ) ) {
5720
+                    $sqlFilters[] = $sqlFilter.' AS '.$property.' ';
5721
+                }
5722
+            }
5723
+        }
5724
+        $sqlFilterStr = ( count($sqlFilters) ) ? ( ','.implode( ',', $sqlFilters ) ) : '';
5725
+        return $sqlFilterStr;
5726
+    }
5727
+
5728
+    /**
5729
+     * Generates a list of parameters (slots) for an SQL snippet.
5730
+     * This method calculates the correct number of slots to insert in the
5731
+     * SQL snippet and determines the correct type of slot. If the bindings
5732
+     * array contains named parameters this method will return named ones and
5733
+     * update the keys in the value list accordingly (that's why we use the &).
5734
+     *
5735
+     * If you pass an offset the bindings will be re-added to the value list.
5736
+     * Some databases cant handle duplicate parameter names in queries.
5737
+     *
5738
+     * @param array   &$valueList    list of values to generate slots for (gets modified if needed)
5739
+     * @param array   $otherBindings list of additional bindings
5740
+     * @param integer $offset        start counter at...
5741
+     *
5742
+     * @return string
5743
+     */
5744
+    protected function getParametersForInClause( &$valueList, $otherBindings, $offset = 0 )
5745
+    {
5746
+        if ( is_array( $otherBindings ) && count( $otherBindings ) > 0 ) {
5747
+            reset( $otherBindings );
5748
+
5749
+            $key = key( $otherBindings );
5750
+
5751
+            if ( !is_numeric($key) ) {
5752
+                $filler  = array();
5753
+                $newList = (!$offset) ? array() : $valueList;
5754
+                $counter = $offset;
5755
+
5756
+                foreach( $valueList as $value ) {
5757
+                    $slot           = ':slot' . ( $counter++ );
5758
+                    $filler[]       = $slot;
5759
+                    $newList[$slot] = $value;
5760
+                }
5761
+
5762
+                // Change the keys!
5763
+                $valueList = $newList;
5764
+
5765
+                return implode( ',', $filler );
5766
+            }
5767
+        }
5768
+
5769
+        return implode( ',', array_fill( 0, count( $valueList ), '?' ) );
5770
+    }
5771
+
5772
+    /**
5773
+     * Adds a data type to the list of data types.
5774
+     * Use this method to add a new column type definition to the writer.
5775
+     * Used for UUID support.
5776
+     *
5777
+     * @param integer $dataTypeID    magic number constant assigned to this data type
5778
+     * @param string  $SQLDefinition SQL column definition (i.e. INT(11))
5779
+     *
5780
+     * @return self
5781
+     */
5782
+    protected function addDataType( $dataTypeID, $SQLDefinition )
5783
+    {
5784
+        $this->typeno_sqltype[ $dataTypeID ] = $SQLDefinition;
5785
+        $this->sqltype_typeno[ $SQLDefinition ] = $dataTypeID;
5786
+        return $this;
5787
+    }
5788
+
5789
+    /**
5790
+     * Returns the sql that should follow an insert statement.
5791
+     *
5792
+     * @param string $table name
5793
+     *
5794
+     * @return string
5795
+     */
5796
+    protected function getInsertSuffix( $table )
5797
+    {
5798
+        return '';
5799
+    }
5800
+
5801
+    /**
5802
+     * Checks whether a value starts with zeros. In this case
5803
+     * the value should probably be stored using a text datatype instead of a
5804
+     * numerical type in order to preserve the zeros.
5805
+     *
5806
+     * @param string $value value to be checked.
5807
+     *
5808
+     * @return boolean
5809
+     */
5810
+    protected function startsWithZeros( $value )
5811
+    {
5812
+        $value = strval( $value );
5813
+
5814
+        if ( strlen( $value ) > 1 && strpos( $value, '0' ) === 0 && strpos( $value, '0.' ) !== 0 ) {
5815
+            return TRUE;
5816
+        } else {
5817
+            return FALSE;
5818
+        }
5819
+    }
5820
+
5821
+    /**
5822
+     * Inserts a record into the database using a series of insert columns
5823
+     * and corresponding insertvalues. Returns the insert id.
5824
+     *
5825
+     * @param string $table         table to perform query on
5826
+     * @param array  $insertcolumns columns to be inserted
5827
+     * @param array  $insertvalues  values to be inserted
5828
+     *
5829
+     * @return integer
5830
+     */
5831
+    protected function insertRecord( $type, $insertcolumns, $insertvalues )
5832
+    {
5833
+        $default = $this->defaultValue;
5834
+        $suffix  = $this->getInsertSuffix( $type );
5835
+        $table   = $this->esc( $type );
5836
+
5837
+        if ( count( $insertvalues ) > 0 && is_array( $insertvalues[0] ) && count( $insertvalues[0] ) > 0 ) {
5838
+
5839
+            $insertSlots = array();
5840
+            foreach ( $insertcolumns as $k => $v ) {
5841
+                $insertcolumns[$k] = $this->esc( $v );
5842
+
5843
+                if (isset(self::$sqlFilters['w'][$type][$v])) {
5844
+                    $insertSlots[] = self::$sqlFilters['w'][$type][$v];
5845
+                } else {
5846
+                    $insertSlots[] = '?';
5847
+                }
5848
+            }
5849
+
5850
+            $insertSQL = "INSERT INTO $table ( id, " . implode( ',', $insertcolumns ) . " ) VALUES
5851 5851
 			( $default, " . implode( ',', $insertSlots ) . " ) $suffix";
5852 5852
 
5853
-			$ids = array();
5854
-			foreach ( $insertvalues as $i => $insertvalue ) {
5855
-				$ids[] = $this->adapter->getCell( $insertSQL, $insertvalue, $i );
5856
-			}
5857
-
5858
-			$result = count( $ids ) === 1 ? array_pop( $ids ) : $ids;
5859
-		} else {
5860
-			$result = $this->adapter->getCell( "INSERT INTO $table (id) VALUES($default) $suffix" );
5861
-		}
5862
-
5863
-		if ( $suffix ) return $result;
5864
-
5865
-		$last_id = $this->adapter->getInsertID();
5866
-
5867
-		return $last_id;
5868
-	}
5869
-
5870
-	/**
5871
-	 * Checks table name or column name.
5872
-	 *
5873
-	 * @param string $table table string
5874
-	 *
5875
-	 * @return string
5876
-	 */
5877
-	protected function check( $struct )
5878
-	{
5879
-		if ( !is_string( $struct ) || !preg_match( '/^[a-zA-Z0-9_]+$/', $struct ) ) {
5880
-			throw new RedException( 'Identifier does not conform to RedBeanPHP security policies.' );
5881
-		}
5882
-
5883
-		return $struct;
5884
-	}
5885
-
5886
-	/**
5887
-	 * Checks whether the specified type (i.e. table) already exists in the database.
5888
-	 * Not part of the Object Database interface!
5889
-	 *
5890
-	 * @param string $table table name
5891
-	 *
5892
-	 * @return boolean
5893
-	 */
5894
-	public function tableExists( $table )
5895
-	{
5896
-		$tables = $this->getTables();
5897
-
5898
-		return in_array( $table, $tables );
5899
-	}
5900
-
5901
-	/**
5902
-	 * @see QueryWriter::glueSQLCondition
5903
-	 */
5904
-	public function glueSQLCondition( $sql, $glue = NULL )
5905
-	{
5906
-		static $snippetCache = array();
5907
-
5908
-		if ( trim( $sql ) === '' ) {
5909
-			return $sql;
5910
-		}
5911
-
5912
-		$key = $glue . '|' . $sql;
5913
-
5914
-		if ( isset( $snippetCache[$key] ) ) {
5915
-			return $snippetCache[$key];
5916
-		}
5917
-
5918
-		$lsql = ltrim( $sql );
5919
-
5920
-		if ( preg_match( '/^(INNER|LEFT|RIGHT|JOIN|AND|OR|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) {
5921
-			if ( $glue === QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) {
5922
-				$snippetCache[$key] = ' WHERE ' . substr( $lsql, 3 );
5923
-			} else {
5924
-				$snippetCache[$key] = $sql;
5925
-			}
5926
-		} else {
5927
-			$snippetCache[$key] = ( ( $glue === QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql;
5928
-		}
5929
-
5930
-		return $snippetCache[$key];
5931
-	}
5932
-
5933
-	/**
5934
-	 * @see QueryWriter::glueLimitOne
5935
-	 */
5936
-	public function glueLimitOne( $sql = '')
5937
-	{
5938
-		return ( strpos( strtoupper( $sql ), 'LIMIT' ) === FALSE ) ? ( $sql . ' LIMIT 1 ' ) : $sql;
5939
-	}
5940
-
5941
-	/**
5942
-	 * @see QueryWriter::esc
5943
-	 */
5944
-	public function esc( $dbStructure, $dontQuote = FALSE )
5945
-	{
5946
-		$this->check( $dbStructure );
5947
-
5948
-		return ( $dontQuote ) ? $dbStructure : $this->quoteCharacter . $dbStructure . $this->quoteCharacter;
5949
-	}
5950
-
5951
-	/**
5952
-	 * @see QueryWriter::addColumn
5953
-	 */
5954
-	public function addColumn( $type, $column, $field )
5955
-	{
5956
-		$table  = $type;
5957
-		$type   = $field;
5958
-		$table  = $this->esc( $table );
5959
-		$column = $this->esc( $column );
5960
-
5961
-		$type = ( isset( $this->typeno_sqltype[$type] ) ) ? $this->typeno_sqltype[$type] : '';
5962
-
5963
-		$this->adapter->exec( "ALTER TABLE $table ADD $column $type " );
5964
-	}
5965
-
5966
-	/**
5967
-	 * @see QueryWriter::updateRecord
5968
-	 */
5969
-	public function updateRecord( $type, $updatevalues, $id = NULL )
5970
-	{
5971
-		$table = $type;
5972
-
5973
-		if ( !$id ) {
5974
-			$insertcolumns = $insertvalues = array();
5975
-
5976
-			foreach ( $updatevalues as $pair ) {
5977
-				$insertcolumns[] = $pair['property'];
5978
-				$insertvalues[]  = $pair['value'];
5979
-			}
5980
-
5981
-			//Otherwise psql returns string while MySQL/SQLite return numeric causing problems with additions (array_diff)
5982
-			return (string) $this->insertRecord( $table, $insertcolumns, array( $insertvalues ) );
5983
-		}
5984
-
5985
-		if ( $id && !count( $updatevalues ) ) {
5986
-			return $id;
5987
-		}
5988
-
5989
-		$table = $this->esc( $table );
5990
-		$sql   = "UPDATE $table SET ";
5991
-
5992
-		$p = $v = array();
5993
-
5994
-		foreach ( $updatevalues as $uv ) {
5995
-
5996
-			if ( isset( self::$sqlFilters['w'][$type][$uv['property']] ) ) {
5997
-				$p[] = " {$this->esc( $uv["property"] )} = ". self::$sqlFilters['w'][$type][$uv['property']];
5998
-			} else {
5999
-				$p[] = " {$this->esc( $uv["property"] )} = ? ";
6000
-			}
6001
-
6002
-			$v[] = $uv['value'];
6003
-		}
6004
-
6005
-		$sql .= implode( ',', $p ) . ' WHERE id = ? ';
6006
-
6007
-		$v[] = $id;
6008
-
6009
-		$this->adapter->exec( $sql, $v );
6010
-
6011
-		return $id;
6012
-	}
6013
-
6014
-	/**
6015
-	 * @see QueryWriter::writeJoin
6016
-	 */
6017
-	public function writeJoin( $type, $targetType, $leftRight = 'LEFT' )
6018
-	{
6019
-		if ( $leftRight !== 'LEFT' && $leftRight !== 'RIGHT' && $leftRight !== 'INNER' )
6020
-			throw new RedException( 'Invalid JOIN.' );
6021
-
6022
-		$table = $this->esc( $type );
6023
-		$targetTable = $this->esc( $targetType );
6024
-		$field = $this->esc( $targetType, TRUE );
6025
-		return " {$leftRight} JOIN {$targetTable} ON {$targetTable}.id = {$table}.{$field}_id ";
6026
-	}
6027
-
6028
-	/**
6029
-	 * Sets an SQL snippet to be used for the next queryRecord() operation.
6030
-	 * A select snippet will be inserted at the end of the SQL select statement and
6031
-	 * can be used to modify SQL-select commands to enable locking, for instance
6032
-	 * using the 'FOR UPDATE' snippet (this will generate an SQL query like:
6033
-	 * 'SELECT * FROM ... FOR UPDATE'. After the query has been executed the
6034
-	 * SQL snippet will be erased. Note that only the first upcoming direct or
6035
-	 * indirect invocation of queryRecord() through batch(), find() or load()
6036
-	 * will be affected. The SQL snippet will be cached.
6037
-	 *
6038
-	 * @param string $sql SQL snippet to use in SELECT statement.
6039
-	 *
6040
-	 * return self
6041
-	 */
6042
-	public function setSQLSelectSnippet( $sqlSelectSnippet = '' ) {
6043
-		$this->sqlSelectSnippet = $sqlSelectSnippet;
6044
-		return $this;
6045
-	}
6046
-
6047
-	/**
6048
-	 * @see QueryWriter::queryRecord
6049
-	 */
6050
-	public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
6051
-	{
6052
-		if ( $this->flagUseCache ) {
6053
-			$key = $this->getCacheKey( array( $conditions, trim("$addSql {$this->sqlSelectSnippet}"), $bindings, 'select' ) );
6054
-			if ( $cached = $this->getCached( $type, $key ) ) {
6055
-				return $cached;
6056
-			}
6057
-		}
6058
-
6059
-		$table = $this->esc( $type );
6060
-
6061
-		$sqlFilterStr = '';
6062
-		if ( count( self::$sqlFilters ) ) {
6063
-			$sqlFilterStr = $this->getSQLFilterSnippet( $type );
6064
-		}
5853
+            $ids = array();
5854
+            foreach ( $insertvalues as $i => $insertvalue ) {
5855
+                $ids[] = $this->adapter->getCell( $insertSQL, $insertvalue, $i );
5856
+            }
5857
+
5858
+            $result = count( $ids ) === 1 ? array_pop( $ids ) : $ids;
5859
+        } else {
5860
+            $result = $this->adapter->getCell( "INSERT INTO $table (id) VALUES($default) $suffix" );
5861
+        }
5862
+
5863
+        if ( $suffix ) return $result;
5864
+
5865
+        $last_id = $this->adapter->getInsertID();
5866
+
5867
+        return $last_id;
5868
+    }
5869
+
5870
+    /**
5871
+     * Checks table name or column name.
5872
+     *
5873
+     * @param string $table table string
5874
+     *
5875
+     * @return string
5876
+     */
5877
+    protected function check( $struct )
5878
+    {
5879
+        if ( !is_string( $struct ) || !preg_match( '/^[a-zA-Z0-9_]+$/', $struct ) ) {
5880
+            throw new RedException( 'Identifier does not conform to RedBeanPHP security policies.' );
5881
+        }
5882
+
5883
+        return $struct;
5884
+    }
5885
+
5886
+    /**
5887
+     * Checks whether the specified type (i.e. table) already exists in the database.
5888
+     * Not part of the Object Database interface!
5889
+     *
5890
+     * @param string $table table name
5891
+     *
5892
+     * @return boolean
5893
+     */
5894
+    public function tableExists( $table )
5895
+    {
5896
+        $tables = $this->getTables();
5897
+
5898
+        return in_array( $table, $tables );
5899
+    }
5900
+
5901
+    /**
5902
+     * @see QueryWriter::glueSQLCondition
5903
+     */
5904
+    public function glueSQLCondition( $sql, $glue = NULL )
5905
+    {
5906
+        static $snippetCache = array();
5907
+
5908
+        if ( trim( $sql ) === '' ) {
5909
+            return $sql;
5910
+        }
5911
+
5912
+        $key = $glue . '|' . $sql;
5913
+
5914
+        if ( isset( $snippetCache[$key] ) ) {
5915
+            return $snippetCache[$key];
5916
+        }
5917
+
5918
+        $lsql = ltrim( $sql );
5919
+
5920
+        if ( preg_match( '/^(INNER|LEFT|RIGHT|JOIN|AND|OR|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) {
5921
+            if ( $glue === QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) {
5922
+                $snippetCache[$key] = ' WHERE ' . substr( $lsql, 3 );
5923
+            } else {
5924
+                $snippetCache[$key] = $sql;
5925
+            }
5926
+        } else {
5927
+            $snippetCache[$key] = ( ( $glue === QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql;
5928
+        }
5929
+
5930
+        return $snippetCache[$key];
5931
+    }
5932
+
5933
+    /**
5934
+     * @see QueryWriter::glueLimitOne
5935
+     */
5936
+    public function glueLimitOne( $sql = '')
5937
+    {
5938
+        return ( strpos( strtoupper( $sql ), 'LIMIT' ) === FALSE ) ? ( $sql . ' LIMIT 1 ' ) : $sql;
5939
+    }
5940
+
5941
+    /**
5942
+     * @see QueryWriter::esc
5943
+     */
5944
+    public function esc( $dbStructure, $dontQuote = FALSE )
5945
+    {
5946
+        $this->check( $dbStructure );
5947
+
5948
+        return ( $dontQuote ) ? $dbStructure : $this->quoteCharacter . $dbStructure . $this->quoteCharacter;
5949
+    }
5950
+
5951
+    /**
5952
+     * @see QueryWriter::addColumn
5953
+     */
5954
+    public function addColumn( $type, $column, $field )
5955
+    {
5956
+        $table  = $type;
5957
+        $type   = $field;
5958
+        $table  = $this->esc( $table );
5959
+        $column = $this->esc( $column );
5960
+
5961
+        $type = ( isset( $this->typeno_sqltype[$type] ) ) ? $this->typeno_sqltype[$type] : '';
5962
+
5963
+        $this->adapter->exec( "ALTER TABLE $table ADD $column $type " );
5964
+    }
5965
+
5966
+    /**
5967
+     * @see QueryWriter::updateRecord
5968
+     */
5969
+    public function updateRecord( $type, $updatevalues, $id = NULL )
5970
+    {
5971
+        $table = $type;
5972
+
5973
+        if ( !$id ) {
5974
+            $insertcolumns = $insertvalues = array();
5975
+
5976
+            foreach ( $updatevalues as $pair ) {
5977
+                $insertcolumns[] = $pair['property'];
5978
+                $insertvalues[]  = $pair['value'];
5979
+            }
5980
+
5981
+            //Otherwise psql returns string while MySQL/SQLite return numeric causing problems with additions (array_diff)
5982
+            return (string) $this->insertRecord( $table, $insertcolumns, array( $insertvalues ) );
5983
+        }
5984
+
5985
+        if ( $id && !count( $updatevalues ) ) {
5986
+            return $id;
5987
+        }
5988
+
5989
+        $table = $this->esc( $table );
5990
+        $sql   = "UPDATE $table SET ";
5991
+
5992
+        $p = $v = array();
5993
+
5994
+        foreach ( $updatevalues as $uv ) {
5995
+
5996
+            if ( isset( self::$sqlFilters['w'][$type][$uv['property']] ) ) {
5997
+                $p[] = " {$this->esc( $uv["property"] )} = ". self::$sqlFilters['w'][$type][$uv['property']];
5998
+            } else {
5999
+                $p[] = " {$this->esc( $uv["property"] )} = ? ";
6000
+            }
6001
+
6002
+            $v[] = $uv['value'];
6003
+        }
6004
+
6005
+        $sql .= implode( ',', $p ) . ' WHERE id = ? ';
6006
+
6007
+        $v[] = $id;
6008
+
6009
+        $this->adapter->exec( $sql, $v );
6010
+
6011
+        return $id;
6012
+    }
6013
+
6014
+    /**
6015
+     * @see QueryWriter::writeJoin
6016
+     */
6017
+    public function writeJoin( $type, $targetType, $leftRight = 'LEFT' )
6018
+    {
6019
+        if ( $leftRight !== 'LEFT' && $leftRight !== 'RIGHT' && $leftRight !== 'INNER' )
6020
+            throw new RedException( 'Invalid JOIN.' );
6021
+
6022
+        $table = $this->esc( $type );
6023
+        $targetTable = $this->esc( $targetType );
6024
+        $field = $this->esc( $targetType, TRUE );
6025
+        return " {$leftRight} JOIN {$targetTable} ON {$targetTable}.id = {$table}.{$field}_id ";
6026
+    }
6027
+
6028
+    /**
6029
+     * Sets an SQL snippet to be used for the next queryRecord() operation.
6030
+     * A select snippet will be inserted at the end of the SQL select statement and
6031
+     * can be used to modify SQL-select commands to enable locking, for instance
6032
+     * using the 'FOR UPDATE' snippet (this will generate an SQL query like:
6033
+     * 'SELECT * FROM ... FOR UPDATE'. After the query has been executed the
6034
+     * SQL snippet will be erased. Note that only the first upcoming direct or
6035
+     * indirect invocation of queryRecord() through batch(), find() or load()
6036
+     * will be affected. The SQL snippet will be cached.
6037
+     *
6038
+     * @param string $sql SQL snippet to use in SELECT statement.
6039
+     *
6040
+     * return self
6041
+     */
6042
+    public function setSQLSelectSnippet( $sqlSelectSnippet = '' ) {
6043
+        $this->sqlSelectSnippet = $sqlSelectSnippet;
6044
+        return $this;
6045
+    }
6046
+
6047
+    /**
6048
+     * @see QueryWriter::queryRecord
6049
+     */
6050
+    public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
6051
+    {
6052
+        if ( $this->flagUseCache ) {
6053
+            $key = $this->getCacheKey( array( $conditions, trim("$addSql {$this->sqlSelectSnippet}"), $bindings, 'select' ) );
6054
+            if ( $cached = $this->getCached( $type, $key ) ) {
6055
+                return $cached;
6056
+            }
6057
+        }
6058
+
6059
+        $table = $this->esc( $type );
6060
+
6061
+        $sqlFilterStr = '';
6062
+        if ( count( self::$sqlFilters ) ) {
6063
+            $sqlFilterStr = $this->getSQLFilterSnippet( $type );
6064
+        }
6065 6065
 		
6066
-		if ( is_array ( $conditions ) && !empty ( $conditions ) ) {
6067
-			$sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
6068
-		} else {
6069
-			$sql = $this->glueSQLCondition( $addSql );
6070
-		}
6071
-
6072
-		$fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*';
6073
-		$sql   = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} {$this->sqlSelectSnippet} -- keep-cache";
6074
-		$this->sqlSelectSnippet = '';
6075
-		$rows  = $this->adapter->get( $sql, $bindings );
6076
-
6077
-		if ( $this->flagUseCache ) {
6078
-			$this->putResultInCache( $type, $key, $rows );
6079
-		}
6080
-
6081
-		return $rows;
6082
-	}
6083
-
6084
-	/**
6085
-	 * @see QueryWriter::queryRecordWithCursor
6086
-	 */
6087
-	public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() )
6088
-	{
6089
-		$table = $this->esc( $type );
6090
-
6091
-		$sqlFilterStr = '';
6092
-		if ( count( self::$sqlFilters ) ) {
6093
-			$sqlFilterStr = $this->getSQLFilterSnippet( $type );
6094
-		}
6095
-
6096
-		$fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*';
6097
-
6098
-		$sql = $this->glueSQLCondition( $addSql, NULL );
6099
-		$sql = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} -- keep-cache";
6100
-
6101
-		return $this->adapter->getCursor( $sql, $bindings );
6102
-	}
6103
-
6104
-	/**
6105
-	 * @see QueryWriter::queryRecordRelated
6106
-	 */
6107
-	public function queryRecordRelated( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() )
6108
-	{
6109
-		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
6110
-
6111
-		if ( $this->flagUseCache ) {
6112
-			$key = $this->getCacheKey( array( $sourceType, implode( ',', $linkIDs ), trim($addSql), $bindings, 'selectrelated' ) );
6113
-			if ( $cached = $this->getCached( $destType, $key ) ) {
6114
-				return $cached;
6115
-			}
6116
-		}
6117
-
6118
-		$addSql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE );
6119
-		$inClause = $this->getParametersForInClause( $linkIDs, $bindings );
6120
-
6121
-		$sqlFilterStr = '';
6122
-		if ( count( self::$sqlFilters ) ) {
6123
-			$sqlFilterStr = $this->getSQLFilterSnippet( $destType );
6124
-		}
6125
-
6126
-		if ( $sourceType === $destType ) {
6127
-			$inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases
6128
-			$sql = "
6066
+        if ( is_array ( $conditions ) && !empty ( $conditions ) ) {
6067
+            $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
6068
+        } else {
6069
+            $sql = $this->glueSQLCondition( $addSql );
6070
+        }
6071
+
6072
+        $fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*';
6073
+        $sql   = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} {$this->sqlSelectSnippet} -- keep-cache";
6074
+        $this->sqlSelectSnippet = '';
6075
+        $rows  = $this->adapter->get( $sql, $bindings );
6076
+
6077
+        if ( $this->flagUseCache ) {
6078
+            $this->putResultInCache( $type, $key, $rows );
6079
+        }
6080
+
6081
+        return $rows;
6082
+    }
6083
+
6084
+    /**
6085
+     * @see QueryWriter::queryRecordWithCursor
6086
+     */
6087
+    public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() )
6088
+    {
6089
+        $table = $this->esc( $type );
6090
+
6091
+        $sqlFilterStr = '';
6092
+        if ( count( self::$sqlFilters ) ) {
6093
+            $sqlFilterStr = $this->getSQLFilterSnippet( $type );
6094
+        }
6095
+
6096
+        $fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*';
6097
+
6098
+        $sql = $this->glueSQLCondition( $addSql, NULL );
6099
+        $sql = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} -- keep-cache";
6100
+
6101
+        return $this->adapter->getCursor( $sql, $bindings );
6102
+    }
6103
+
6104
+    /**
6105
+     * @see QueryWriter::queryRecordRelated
6106
+     */
6107
+    public function queryRecordRelated( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() )
6108
+    {
6109
+        list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
6110
+
6111
+        if ( $this->flagUseCache ) {
6112
+            $key = $this->getCacheKey( array( $sourceType, implode( ',', $linkIDs ), trim($addSql), $bindings, 'selectrelated' ) );
6113
+            if ( $cached = $this->getCached( $destType, $key ) ) {
6114
+                return $cached;
6115
+            }
6116
+        }
6117
+
6118
+        $addSql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE );
6119
+        $inClause = $this->getParametersForInClause( $linkIDs, $bindings );
6120
+
6121
+        $sqlFilterStr = '';
6122
+        if ( count( self::$sqlFilters ) ) {
6123
+            $sqlFilterStr = $this->getSQLFilterSnippet( $destType );
6124
+        }
6125
+
6126
+        if ( $sourceType === $destType ) {
6127
+            $inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases
6128
+            $sql = "
6129 6129
 			SELECT
6130 6130
 				{$destTable}.* {$sqlFilterStr} ,
6131 6131
 				COALESCE(
@@ -6138,9 +6138,9 @@  discard block
 block discarded – undo
6138 6138
 			{$addSql}
6139 6139
 			-- keep-cache";
6140 6140
 
6141
-			$linkIDs = array_merge( $linkIDs, $linkIDs );
6142
-		} else {
6143
-			$sql = "
6141
+            $linkIDs = array_merge( $linkIDs, $linkIDs );
6142
+        } else {
6143
+            $sql = "
6144 6144
 			SELECT
6145 6145
 				{$destTable}.* {$sqlFilterStr},
6146 6146
 				{$linkTable}.{$sourceCol} AS linked_by
@@ -6149,89 +6149,89 @@  discard block
 block discarded – undo
6149 6149
 			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) )
6150 6150
 			{$addSql}
6151 6151
 			-- keep-cache";
6152
-		}
6152
+        }
6153 6153
 
6154
-		$bindings = array_merge( $linkIDs, $bindings );
6154
+        $bindings = array_merge( $linkIDs, $bindings );
6155 6155
 
6156
-		$rows = $this->adapter->get( $sql, $bindings );
6156
+        $rows = $this->adapter->get( $sql, $bindings );
6157 6157
 
6158
-		if ( $this->flagUseCache ) {
6159
-			$this->putResultInCache( $destType, $key, $rows );
6160
-		}
6158
+        if ( $this->flagUseCache ) {
6159
+            $this->putResultInCache( $destType, $key, $rows );
6160
+        }
6161 6161
 
6162
-		return $rows;
6163
-	}
6162
+        return $rows;
6163
+    }
6164 6164
 
6165
-	/**
6166
-	 * @see QueryWriter::queryRecordLink
6167
-	 */
6168
-	public function queryRecordLink( $sourceType, $destType, $sourceID, $destID )
6169
-	{
6170
-		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
6171
-
6172
-		if ( $this->flagUseCache ) {
6173
-			$key = $this->getCacheKey( array( $sourceType, $destType, $sourceID, $destID, 'selectlink' ) );
6174
-			if ( $cached = $this->getCached( $linkTable, $key ) ) {
6175
-				return $cached;
6176
-			}
6177
-		}
6178
-
6179
-		$sqlFilterStr = '';
6180
-		if ( count( self::$sqlFilters ) ) {
6181
-			$sqlFilterStr = $this->getSQLFilterSnippet( $destType );
6182
-		}
6183
-
6184
-		if ( $sourceTable === $destTable ) {
6185
-			$sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
6165
+    /**
6166
+     * @see QueryWriter::queryRecordLink
6167
+     */
6168
+    public function queryRecordLink( $sourceType, $destType, $sourceID, $destID )
6169
+    {
6170
+        list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
6171
+
6172
+        if ( $this->flagUseCache ) {
6173
+            $key = $this->getCacheKey( array( $sourceType, $destType, $sourceID, $destID, 'selectlink' ) );
6174
+            if ( $cached = $this->getCached( $linkTable, $key ) ) {
6175
+                return $cached;
6176
+            }
6177
+        }
6178
+
6179
+        $sqlFilterStr = '';
6180
+        if ( count( self::$sqlFilters ) ) {
6181
+            $sqlFilterStr = $this->getSQLFilterSnippet( $destType );
6182
+        }
6183
+
6184
+        if ( $sourceTable === $destTable ) {
6185
+            $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
6186 6186
 				WHERE ( {$sourceCol} = ? AND {$destCol} = ? ) OR
6187 6187
 				 ( {$destCol} = ? AND {$sourceCol} = ? ) -- keep-cache";
6188
-			$row = $this->adapter->getRow( $sql, array( $sourceID, $destID, $sourceID, $destID ) );
6189
-		} else {
6190
-			$sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
6188
+            $row = $this->adapter->getRow( $sql, array( $sourceID, $destID, $sourceID, $destID ) );
6189
+        } else {
6190
+            $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
6191 6191
 				WHERE {$sourceCol} = ? AND {$destCol} = ? -- keep-cache";
6192
-			$row = $this->adapter->getRow( $sql, array( $sourceID, $destID ) );
6193
-		}
6194
-
6195
-		if ( $this->flagUseCache ) {
6196
-			$this->putResultInCache( $linkTable, $key, $row );
6197
-		}
6198
-
6199
-		return $row;
6200
-	}
6201
-
6202
-	/**
6203
-	 * Returns or counts all rows of specified type that have been tagged with one of the
6204
-	 * strings in the specified tag list array.
6205
-	 *
6206
-	 * Note that the additional SQL snippet can only be used for pagination,
6207
-	 * the SQL snippet will be appended to the end of the query.
6208
-	 *
6209
-	 * @param string  $type     the bean type you want to query
6210
-	 * @param array   $tagList  an array of strings, each string containing a tag title
6211
-	 * @param boolean $all      if TRUE only return records that have been associated with ALL the tags in the list
6212
-	 * @param string  $addSql   addition SQL snippet, for pagination
6213
-	 * @param array   $bindings parameter bindings for additional SQL snippet
6214
-	 * @param string  $wrap     SQL wrapper string (use %s for subquery)
6215
-	 *
6216
-	 * @return array
6217
-	 */
6218
-	private function queryTaggedGeneric( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array(), $wrap = '%s' )
6219
-	{
6220
-		if ( $this->flagUseCache ) {
6221
-			$key = $this->getCacheKey( array( implode( ',', $tagList ), $all, trim($addSql), $bindings, 'selectTagged' ) );
6222
-			if ( $cached = $this->getCached( $type, $key ) ) {
6223
-				return $cached;
6224
-			}
6225
-		}
6226
-
6227
-		$assocType = $this->getAssocTable( array( $type, 'tag' ) );
6228
-		$assocTable = $this->esc( $assocType );
6229
-		$assocField = $type . '_id';
6230
-		$table = $this->esc( $type );
6231
-		$slots = implode( ',', array_fill( 0, count( $tagList ), '?' ) );
6232
-		$score = ( $all ) ? count( $tagList ) : 1;
6233
-
6234
-		$sql = "
6192
+            $row = $this->adapter->getRow( $sql, array( $sourceID, $destID ) );
6193
+        }
6194
+
6195
+        if ( $this->flagUseCache ) {
6196
+            $this->putResultInCache( $linkTable, $key, $row );
6197
+        }
6198
+
6199
+        return $row;
6200
+    }
6201
+
6202
+    /**
6203
+     * Returns or counts all rows of specified type that have been tagged with one of the
6204
+     * strings in the specified tag list array.
6205
+     *
6206
+     * Note that the additional SQL snippet can only be used for pagination,
6207
+     * the SQL snippet will be appended to the end of the query.
6208
+     *
6209
+     * @param string  $type     the bean type you want to query
6210
+     * @param array   $tagList  an array of strings, each string containing a tag title
6211
+     * @param boolean $all      if TRUE only return records that have been associated with ALL the tags in the list
6212
+     * @param string  $addSql   addition SQL snippet, for pagination
6213
+     * @param array   $bindings parameter bindings for additional SQL snippet
6214
+     * @param string  $wrap     SQL wrapper string (use %s for subquery)
6215
+     *
6216
+     * @return array
6217
+     */
6218
+    private function queryTaggedGeneric( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array(), $wrap = '%s' )
6219
+    {
6220
+        if ( $this->flagUseCache ) {
6221
+            $key = $this->getCacheKey( array( implode( ',', $tagList ), $all, trim($addSql), $bindings, 'selectTagged' ) );
6222
+            if ( $cached = $this->getCached( $type, $key ) ) {
6223
+                return $cached;
6224
+            }
6225
+        }
6226
+
6227
+        $assocType = $this->getAssocTable( array( $type, 'tag' ) );
6228
+        $assocTable = $this->esc( $assocType );
6229
+        $assocField = $type . '_id';
6230
+        $table = $this->esc( $type );
6231
+        $slots = implode( ',', array_fill( 0, count( $tagList ), '?' ) );
6232
+        $score = ( $all ) ? count( $tagList ) : 1;
6233
+
6234
+        $sql = "
6235 6235
 			SELECT {$table}.* FROM {$table}
6236 6236
 			INNER JOIN {$assocTable} ON {$assocField} = {$table}.id
6237 6237
 			INNER JOIN tag ON {$assocTable}.tag_id = tag.id
@@ -6241,82 +6241,82 @@  discard block
 block discarded – undo
6241 6241
 			{$addSql}
6242 6242
 			-- keep-cache
6243 6243
 		";
6244
-		$sql = sprintf($wrap,$sql);
6245
-
6246
-		$bindings = array_merge( $tagList, array( $score ), $bindings );
6247
-		$rows = $this->adapter->get( $sql, $bindings );
6248
-
6249
-		if ( $this->flagUseCache ) {
6250
-			$this->putResultInCache( $type, $key, $rows );
6251
-		}
6252
-
6253
-		return $rows;
6254
-	}
6255
-
6256
-	/**
6257
-	 * @see QueryWriter::queryTagged
6258
-	 */
6259
-	public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() )
6260
-	{
6261
-		return $this->queryTaggedGeneric( $type, $tagList, $all, $addSql, $bindings );
6262
-	}
6263
-
6264
-	/**
6265
-	 * @see QueryWriter::queryCountTagged
6266
-	 */
6267
-	public function queryCountTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() )
6268
-	{
6269
-		$rows = $this->queryTaggedGeneric( $type, $tagList, $all, $addSql, $bindings, 'SELECT COUNT(*) AS counted FROM (%s) AS counting' );
6270
-		return intval($rows[0]['counted']);
6271
-	}
6272
-
6273
-	/**
6274
-	 * @see QueryWriter::queryRecordCount
6275
-	 */
6276
-	public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
6277
-	{
6278
-		if ( $this->flagUseCache ) {
6279
-			$key = $this->getCacheKey( array( $conditions, trim($addSql), $bindings, 'count' ) );
6280
-			if ( $cached = $this->getCached( $type, $key ) ) {
6281
-				return $cached;
6282
-			}
6283
-		}
6284
-
6285
-		$table  = $this->esc( $type );
6286
-
6287
-		if ( is_array ( $conditions ) && !empty ( $conditions ) ) {
6288
-			$sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
6289
-		} else {
6290
-			$sql = $this->glueSQLCondition( $addSql );
6291
-		}
6292
-
6293
-		$sql    = "SELECT COUNT(*) FROM {$table} {$sql} -- keep-cache";
6294
-		$count  = (int) $this->adapter->getCell( $sql, $bindings );
6295
-
6296
-		if ( $this->flagUseCache ) {
6297
-			$this->putResultInCache( $type, $key, $count );
6298
-		}
6299
-
6300
-		return $count;
6301
-	}
6302
-
6303
-	/**
6304
-	 * @see QueryWriter::queryRecordCountRelated
6305
-	 */
6306
-	public function queryRecordCountRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() )
6307
-	{
6308
-		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
6309
-
6310
-		if ( $this->flagUseCache ) {
6311
-			$cacheType = "#{$sourceType}/{$destType}";
6312
-			$key = $this->getCacheKey( array( $sourceType, $destType, $linkID, trim($addSql), $bindings, 'countrelated' ) );
6313
-			if ( $cached = $this->getCached( $cacheType, $key ) ) {
6314
-				return $cached;
6315
-			}
6316
-		}
6317
-
6318
-		if ( $sourceType === $destType ) {
6319
-			$sql = "
6244
+        $sql = sprintf($wrap,$sql);
6245
+
6246
+        $bindings = array_merge( $tagList, array( $score ), $bindings );
6247
+        $rows = $this->adapter->get( $sql, $bindings );
6248
+
6249
+        if ( $this->flagUseCache ) {
6250
+            $this->putResultInCache( $type, $key, $rows );
6251
+        }
6252
+
6253
+        return $rows;
6254
+    }
6255
+
6256
+    /**
6257
+     * @see QueryWriter::queryTagged
6258
+     */
6259
+    public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() )
6260
+    {
6261
+        return $this->queryTaggedGeneric( $type, $tagList, $all, $addSql, $bindings );
6262
+    }
6263
+
6264
+    /**
6265
+     * @see QueryWriter::queryCountTagged
6266
+     */
6267
+    public function queryCountTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() )
6268
+    {
6269
+        $rows = $this->queryTaggedGeneric( $type, $tagList, $all, $addSql, $bindings, 'SELECT COUNT(*) AS counted FROM (%s) AS counting' );
6270
+        return intval($rows[0]['counted']);
6271
+    }
6272
+
6273
+    /**
6274
+     * @see QueryWriter::queryRecordCount
6275
+     */
6276
+    public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
6277
+    {
6278
+        if ( $this->flagUseCache ) {
6279
+            $key = $this->getCacheKey( array( $conditions, trim($addSql), $bindings, 'count' ) );
6280
+            if ( $cached = $this->getCached( $type, $key ) ) {
6281
+                return $cached;
6282
+            }
6283
+        }
6284
+
6285
+        $table  = $this->esc( $type );
6286
+
6287
+        if ( is_array ( $conditions ) && !empty ( $conditions ) ) {
6288
+            $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
6289
+        } else {
6290
+            $sql = $this->glueSQLCondition( $addSql );
6291
+        }
6292
+
6293
+        $sql    = "SELECT COUNT(*) FROM {$table} {$sql} -- keep-cache";
6294
+        $count  = (int) $this->adapter->getCell( $sql, $bindings );
6295
+
6296
+        if ( $this->flagUseCache ) {
6297
+            $this->putResultInCache( $type, $key, $count );
6298
+        }
6299
+
6300
+        return $count;
6301
+    }
6302
+
6303
+    /**
6304
+     * @see QueryWriter::queryRecordCountRelated
6305
+     */
6306
+    public function queryRecordCountRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() )
6307
+    {
6308
+        list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
6309
+
6310
+        if ( $this->flagUseCache ) {
6311
+            $cacheType = "#{$sourceType}/{$destType}";
6312
+            $key = $this->getCacheKey( array( $sourceType, $destType, $linkID, trim($addSql), $bindings, 'countrelated' ) );
6313
+            if ( $cached = $this->getCached( $cacheType, $key ) ) {
6314
+                return $cached;
6315
+            }
6316
+        }
6317
+
6318
+        if ( $sourceType === $destType ) {
6319
+            $sql = "
6320 6320
 			SELECT COUNT(*) FROM {$linkTable}
6321 6321
 			INNER JOIN {$destTable} ON
6322 6322
 			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) OR
@@ -6324,40 +6324,40 @@  discard block
 block discarded – undo
6324 6324
 			{$addSql}
6325 6325
 			-- keep-cache";
6326 6326
 
6327
-			$bindings = array_merge( array( $linkID, $linkID ), $bindings );
6328
-		} else {
6329
-			$sql = "
6327
+            $bindings = array_merge( array( $linkID, $linkID ), $bindings );
6328
+        } else {
6329
+            $sql = "
6330 6330
 			SELECT COUNT(*) FROM {$linkTable}
6331 6331
 			INNER JOIN {$destTable} ON
6332 6332
 			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? )
6333 6333
 			{$addSql}
6334 6334
 			-- keep-cache";
6335 6335
 
6336
-			$bindings = array_merge( array( $linkID ), $bindings );
6337
-		}
6336
+            $bindings = array_merge( array( $linkID ), $bindings );
6337
+        }
6338 6338
 
6339
-		$count = (int) $this->adapter->getCell( $sql, $bindings );
6339
+        $count = (int) $this->adapter->getCell( $sql, $bindings );
6340 6340
 
6341
-		if ( $this->flagUseCache ) {
6342
-			$this->putResultInCache( $cacheType, $key, $count );
6343
-		}
6341
+        if ( $this->flagUseCache ) {
6342
+            $this->putResultInCache( $cacheType, $key, $count );
6343
+        }
6344 6344
 
6345
-		return $count;
6346
-	}
6345
+        return $count;
6346
+    }
6347 6347
 
6348
-	/**
6349
-	 * @see QueryWriter::queryRecursiveCommonTableExpression
6350
-	 */
6351
-	public function queryRecursiveCommonTableExpression( $type, $id, $up = TRUE, $addSql = NULL, $bindings = array() )
6352
-	{
6353
-		$alias     = $up ? 'parent' : 'child';
6354
-		$direction = $up ? " {$alias}.{$type}_id = {$type}.id " : " {$alias}.id = {$type}.{$type}_id ";
6348
+    /**
6349
+     * @see QueryWriter::queryRecursiveCommonTableExpression
6350
+     */
6351
+    public function queryRecursiveCommonTableExpression( $type, $id, $up = TRUE, $addSql = NULL, $bindings = array() )
6352
+    {
6353
+        $alias     = $up ? 'parent' : 'child';
6354
+        $direction = $up ? " {$alias}.{$type}_id = {$type}.id " : " {$alias}.id = {$type}.{$type}_id ";
6355 6355
 
6356
-		array_unshift( $bindings, $id );
6356
+        array_unshift( $bindings, $id );
6357 6357
 
6358
-		$sql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE );
6358
+        $sql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE );
6359 6359
 
6360
-		$rows = $this->adapter->get("
6360
+        $rows = $this->adapter->get("
6361 6361
 			WITH RECURSIVE tree AS
6362 6362
 			(
6363 6363
 				SELECT *
@@ -6367,178 +6367,178 @@  discard block
 block discarded – undo
6367 6367
 				INNER JOIN tree {$alias} ON {$direction}
6368 6368
 			)
6369 6369
 			SELECT * FROM tree {$sql};",
6370
-			$bindings
6371
-		);
6372
-
6373
-		return $rows;
6374
-	}
6375
-
6376
-	/**
6377
-	 * @see QueryWriter::deleteRecord
6378
-	 */
6379
-	public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
6380
-	{
6381
-		$table  = $this->esc( $type );
6382
-
6383
-		if ( is_array ( $conditions ) && !empty ( $conditions ) ) {
6384
-			$sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
6385
-		} else {
6386
-			$sql = $this->glueSQLCondition( $addSql );
6387
-		}
6370
+            $bindings
6371
+        );
6372
+
6373
+        return $rows;
6374
+    }
6375
+
6376
+    /**
6377
+     * @see QueryWriter::deleteRecord
6378
+     */
6379
+    public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
6380
+    {
6381
+        $table  = $this->esc( $type );
6382
+
6383
+        if ( is_array ( $conditions ) && !empty ( $conditions ) ) {
6384
+            $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
6385
+        } else {
6386
+            $sql = $this->glueSQLCondition( $addSql );
6387
+        }
6388 6388
 		
6389
-		$sql    = "DELETE FROM {$table} {$sql}";
6389
+        $sql    = "DELETE FROM {$table} {$sql}";
6390 6390
 
6391
-		$this->adapter->exec( $sql, $bindings );
6392
-	}
6391
+        $this->adapter->exec( $sql, $bindings );
6392
+    }
6393 6393
 
6394
-	/**
6395
-	 * @see QueryWriter::deleteRelations
6396
-	 */
6397
-	public function deleteRelations( $sourceType, $destType, $sourceID )
6398
-	{
6399
-		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
6394
+    /**
6395
+     * @see QueryWriter::deleteRelations
6396
+     */
6397
+    public function deleteRelations( $sourceType, $destType, $sourceID )
6398
+    {
6399
+        list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
6400 6400
 
6401
-		if ( $sourceTable === $destTable ) {
6402
-			$sql = "DELETE FROM {$linkTable}
6401
+        if ( $sourceTable === $destTable ) {
6402
+            $sql = "DELETE FROM {$linkTable}
6403 6403
 				WHERE ( {$sourceCol} = ? ) OR
6404 6404
 				( {$destCol} = ?  )
6405 6405
 			";
6406 6406
 
6407
-			$this->adapter->exec( $sql, array( $sourceID, $sourceID ) );
6408
-		} else {
6409
-			$sql = "DELETE FROM {$linkTable}
6407
+            $this->adapter->exec( $sql, array( $sourceID, $sourceID ) );
6408
+        } else {
6409
+            $sql = "DELETE FROM {$linkTable}
6410 6410
 				WHERE {$sourceCol} = ? ";
6411 6411
 
6412
-			$this->adapter->exec( $sql, array( $sourceID ) );
6413
-		}
6414
-	}
6415
-
6416
-	/**
6417
-	 * @see QueryWriter::widenColumn
6418
-	 */
6419
-	public function widenColumn( $type, $property, $dataType )
6420
-	{
6421
-		if ( !isset($this->typeno_sqltype[$dataType]) ) return FALSE;
6422
-
6423
-		$table   = $this->esc( $type );
6424
-		$column  = $this->esc( $property );
6425
-
6426
-		$newType = $this->typeno_sqltype[$dataType];
6427
-
6428
-		$this->adapter->exec( "ALTER TABLE $table CHANGE $column $column $newType " );
6429
-
6430
-		return TRUE;
6431
-	}
6432
-
6433
-	/**
6434
-	 * @see QueryWriter::wipe
6435
-	 */
6436
-	public function wipe( $type )
6437
-	{
6438
-		$table = $this->esc( $type );
6439
-
6440
-		$this->adapter->exec( "TRUNCATE $table " );
6441
-	}
6442
-
6443
-	/**
6444
-	 * @see QueryWriter::renameAssocTable
6445
-	 */
6446
-	public function renameAssocTable( $from, $to = NULL )
6447
-	{
6448
-		self::renameAssociation( $from, $to );
6449
-	}
6450
-
6451
-	/**
6452
-	 * @see QueryWriter::getAssocTable
6453
-	 */
6454
-	public function getAssocTable( $types )
6455
-	{
6456
-		return self::getAssocTableFormat( $types );
6457
-	}
6458
-
6459
-	/**
6460
-	 * Turns caching on or off. Default: off.
6461
-	 * If caching is turned on retrieval queries fired after eachother will
6462
-	 * use a result row cache.
6463
-	 *
6464
-	 * @param boolean
6465
-	 *
6466
-	 * @return void
6467
-	 */
6468
-	public function setUseCache( $yesNo )
6469
-	{
6470
-		$this->flushCache();
6471
-
6472
-		$this->flagUseCache = (bool) $yesNo;
6473
-	}
6474
-
6475
-	/**
6476
-	 * Flushes the Query Writer Cache.
6477
-	 * Clears the internal query cache array and returns its overall
6478
-	 * size.
6479
-	 *
6480
-	 * @return integer
6481
-	 */
6482
-	public function flushCache( $newMaxCacheSizePerType = NULL )
6483
-	{
6484
-		if ( !is_null( $newMaxCacheSizePerType ) && $newMaxCacheSizePerType > 0 ) {
6485
-			$this->maxCacheSizePerType = $newMaxCacheSizePerType;
6486
-		}
6487
-		$count = count( $this->cache, COUNT_RECURSIVE );
6488
-		$this->cache = array();
6489
-		return $count;
6490
-	}
6491
-
6492
-	/**
6493
-	 * @deprecated Use esc() instead.
6494
-	 *
6495
-	 * @param string  $column   column to be escaped
6496
-	 * @param boolean $noQuotes omit quotes
6497
-	 *
6498
-	 * @return string
6499
-	 */
6500
-	public function safeColumn( $column, $noQuotes = FALSE )
6501
-	{
6502
-		return $this->esc( $column, $noQuotes );
6503
-	}
6504
-
6505
-	/**
6506
-	 * @deprecated Use esc() instead.
6507
-	 *
6508
-	 * @param string  $table    table to be escaped
6509
-	 * @param boolean $noQuotes omit quotes
6510
-	 *
6511
-	 * @return string
6512
-	 */
6513
-	public function safeTable( $table, $noQuotes = FALSE )
6514
-	{
6515
-		return $this->esc( $table, $noQuotes );
6516
-	}
6517
-
6518
-	/**
6519
-	 * @see QueryWriter::inferFetchType
6520
-	 */
6521
-	public function inferFetchType( $type, $property )
6522
-	{
6523
-		$type = $this->esc( $type, TRUE );
6524
-		$field = $this->esc( $property, TRUE ) . '_id';
6525
-		$keys = $this->getKeyMapForType( $type );
6526
-
6527
-		foreach( $keys as $key ) {
6528
-			if (
6529
-				$key['from'] === $field
6530
-			) return $key['table'];
6531
-		}
6532
-		return NULL;
6533
-	}
6534
-
6535
-	/**
6536
-	 * @see QueryWriter::addUniqueConstraint
6537
-	 */
6538
-	public function addUniqueIndex( $type, $properties )
6539
-	{
6540
-		return $this->addUniqueConstraint( $type, $properties );
6541
-	}
6412
+            $this->adapter->exec( $sql, array( $sourceID ) );
6413
+        }
6414
+    }
6415
+
6416
+    /**
6417
+     * @see QueryWriter::widenColumn
6418
+     */
6419
+    public function widenColumn( $type, $property, $dataType )
6420
+    {
6421
+        if ( !isset($this->typeno_sqltype[$dataType]) ) return FALSE;
6422
+
6423
+        $table   = $this->esc( $type );
6424
+        $column  = $this->esc( $property );
6425
+
6426
+        $newType = $this->typeno_sqltype[$dataType];
6427
+
6428
+        $this->adapter->exec( "ALTER TABLE $table CHANGE $column $column $newType " );
6429
+
6430
+        return TRUE;
6431
+    }
6432
+
6433
+    /**
6434
+     * @see QueryWriter::wipe
6435
+     */
6436
+    public function wipe( $type )
6437
+    {
6438
+        $table = $this->esc( $type );
6439
+
6440
+        $this->adapter->exec( "TRUNCATE $table " );
6441
+    }
6442
+
6443
+    /**
6444
+     * @see QueryWriter::renameAssocTable
6445
+     */
6446
+    public function renameAssocTable( $from, $to = NULL )
6447
+    {
6448
+        self::renameAssociation( $from, $to );
6449
+    }
6450
+
6451
+    /**
6452
+     * @see QueryWriter::getAssocTable
6453
+     */
6454
+    public function getAssocTable( $types )
6455
+    {
6456
+        return self::getAssocTableFormat( $types );
6457
+    }
6458
+
6459
+    /**
6460
+     * Turns caching on or off. Default: off.
6461
+     * If caching is turned on retrieval queries fired after eachother will
6462
+     * use a result row cache.
6463
+     *
6464
+     * @param boolean
6465
+     *
6466
+     * @return void
6467
+     */
6468
+    public function setUseCache( $yesNo )
6469
+    {
6470
+        $this->flushCache();
6471
+
6472
+        $this->flagUseCache = (bool) $yesNo;
6473
+    }
6474
+
6475
+    /**
6476
+     * Flushes the Query Writer Cache.
6477
+     * Clears the internal query cache array and returns its overall
6478
+     * size.
6479
+     *
6480
+     * @return integer
6481
+     */
6482
+    public function flushCache( $newMaxCacheSizePerType = NULL )
6483
+    {
6484
+        if ( !is_null( $newMaxCacheSizePerType ) && $newMaxCacheSizePerType > 0 ) {
6485
+            $this->maxCacheSizePerType = $newMaxCacheSizePerType;
6486
+        }
6487
+        $count = count( $this->cache, COUNT_RECURSIVE );
6488
+        $this->cache = array();
6489
+        return $count;
6490
+    }
6491
+
6492
+    /**
6493
+     * @deprecated Use esc() instead.
6494
+     *
6495
+     * @param string  $column   column to be escaped
6496
+     * @param boolean $noQuotes omit quotes
6497
+     *
6498
+     * @return string
6499
+     */
6500
+    public function safeColumn( $column, $noQuotes = FALSE )
6501
+    {
6502
+        return $this->esc( $column, $noQuotes );
6503
+    }
6504
+
6505
+    /**
6506
+     * @deprecated Use esc() instead.
6507
+     *
6508
+     * @param string  $table    table to be escaped
6509
+     * @param boolean $noQuotes omit quotes
6510
+     *
6511
+     * @return string
6512
+     */
6513
+    public function safeTable( $table, $noQuotes = FALSE )
6514
+    {
6515
+        return $this->esc( $table, $noQuotes );
6516
+    }
6517
+
6518
+    /**
6519
+     * @see QueryWriter::inferFetchType
6520
+     */
6521
+    public function inferFetchType( $type, $property )
6522
+    {
6523
+        $type = $this->esc( $type, TRUE );
6524
+        $field = $this->esc( $property, TRUE ) . '_id';
6525
+        $keys = $this->getKeyMapForType( $type );
6526
+
6527
+        foreach( $keys as $key ) {
6528
+            if (
6529
+                $key['from'] === $field
6530
+            ) return $key['table'];
6531
+        }
6532
+        return NULL;
6533
+    }
6534
+
6535
+    /**
6536
+     * @see QueryWriter::addUniqueConstraint
6537
+     */
6538
+    public function addUniqueIndex( $type, $properties )
6539
+    {
6540
+        return $this->addUniqueConstraint( $type, $properties );
6541
+    }
6542 6542
 }
6543 6543
 }
6544 6544
 
@@ -6566,45 +6566,45 @@  discard block
 block discarded – undo
6566 6566
  */
6567 6567
 class MySQL extends AQueryWriter implements QueryWriter
6568 6568
 {
6569
-	/**
6570
-	 * Data types
6571
-	 */
6572
-	const C_DATATYPE_BOOL             = 0;
6573
-	const C_DATATYPE_UINT32           = 2;
6574
-	const C_DATATYPE_DOUBLE           = 3;
6575
-	const C_DATATYPE_TEXT7            = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible
6576
-	const C_DATATYPE_TEXT8            = 5;
6577
-	const C_DATATYPE_TEXT16           = 6;
6578
-	const C_DATATYPE_TEXT32           = 7;
6579
-	const C_DATATYPE_SPECIAL_DATE     = 80;
6580
-	const C_DATATYPE_SPECIAL_DATETIME = 81;
6581
-	const C_DATATYPE_SPECIAL_TIME     = 83;  //MySQL time column (only manual)
6582
-	const C_DATATYPE_SPECIAL_POINT    = 90;
6583
-	const C_DATATYPE_SPECIAL_LINESTRING = 91;
6584
-	const C_DATATYPE_SPECIAL_POLYGON    = 92;
6585
-	const C_DATATYPE_SPECIAL_MONEY      = 93;
6586
-	const C_DATATYPE_SPECIAL_JSON       = 94;  //JSON support (only manual)
6587
-
6588
-	const C_DATATYPE_SPECIFIED        = 99;
6589
-
6590
-	/**
6591
-	 * @var DBAdapter
6592
-	 */
6593
-	protected $adapter;
6594
-
6595
-	/**
6596
-	 * @var string
6597
-	 */
6598
-	protected $quoteCharacter = '`';
6599
-
6600
-	/**
6601
-	 * @see AQueryWriter::getKeyMapForType
6602
-	 */
6603
-	protected function getKeyMapForType( $type )
6604
-	{
6605
-		$databaseName = $this->adapter->getCell('SELECT DATABASE()');
6606
-		$table = $this->esc( $type, TRUE );
6607
-		$keys = $this->adapter->get('
6569
+    /**
6570
+     * Data types
6571
+     */
6572
+    const C_DATATYPE_BOOL             = 0;
6573
+    const C_DATATYPE_UINT32           = 2;
6574
+    const C_DATATYPE_DOUBLE           = 3;
6575
+    const C_DATATYPE_TEXT7            = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible
6576
+    const C_DATATYPE_TEXT8            = 5;
6577
+    const C_DATATYPE_TEXT16           = 6;
6578
+    const C_DATATYPE_TEXT32           = 7;
6579
+    const C_DATATYPE_SPECIAL_DATE     = 80;
6580
+    const C_DATATYPE_SPECIAL_DATETIME = 81;
6581
+    const C_DATATYPE_SPECIAL_TIME     = 83;  //MySQL time column (only manual)
6582
+    const C_DATATYPE_SPECIAL_POINT    = 90;
6583
+    const C_DATATYPE_SPECIAL_LINESTRING = 91;
6584
+    const C_DATATYPE_SPECIAL_POLYGON    = 92;
6585
+    const C_DATATYPE_SPECIAL_MONEY      = 93;
6586
+    const C_DATATYPE_SPECIAL_JSON       = 94;  //JSON support (only manual)
6587
+
6588
+    const C_DATATYPE_SPECIFIED        = 99;
6589
+
6590
+    /**
6591
+     * @var DBAdapter
6592
+     */
6593
+    protected $adapter;
6594
+
6595
+    /**
6596
+     * @var string
6597
+     */
6598
+    protected $quoteCharacter = '`';
6599
+
6600
+    /**
6601
+     * @see AQueryWriter::getKeyMapForType
6602
+     */
6603
+    protected function getKeyMapForType( $type )
6604
+    {
6605
+        $databaseName = $this->adapter->getCell('SELECT DATABASE()');
6606
+        $table = $this->esc( $type, TRUE );
6607
+        $keys = $this->adapter->get('
6608 6608
 			SELECT
6609 6609
 				information_schema.key_column_usage.constraint_name AS `name`,
6610 6610
 				information_schema.key_column_usage.referenced_table_name AS `table`,
@@ -6623,338 +6623,338 @@  discard block
 block discarded – undo
6623 6623
 				AND information_schema.key_column_usage.constraint_name != \'PRIMARY\'
6624 6624
 				AND information_schema.key_column_usage.referenced_table_name IS NOT NULL
6625 6625
 		', array( ':database' => $databaseName, ':table' => $table ) );
6626
-		$keyInfoList = array();
6627
-		foreach ( $keys as $k ) {
6628
-			$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
6629
-			$keyInfoList[$label] = array(
6630
-				'name'          => $k['name'],
6631
-				'from'          => $k['from'],
6632
-				'table'         => $k['table'],
6633
-				'to'            => $k['to'],
6634
-				'on_update'     => $k['on_update'],
6635
-				'on_delete'     => $k['on_delete']
6636
-			);
6637
-		}
6638
-		return $keyInfoList;
6639
-	}
6640
-
6641
-	/**
6642
-	 * Constructor
6643
-	 * Most of the time, you do not need to use this constructor,
6644
-	 * since the facade takes care of constructing and wiring the
6645
-	 * RedBeanPHP core objects. However if you would like to
6646
-	 * assemble an OODB instance yourself, this is how it works:
6647
-	 *
6648
-	 * Usage:
6649
-	 *
6650
-	 * <code>
6651
-	 * $database = new RPDO( $dsn, $user, $pass );
6652
-	 * $adapter = new DBAdapter( $database );
6653
-	 * $writer = new PostgresWriter( $adapter );
6654
-	 * $oodb = new OODB( $writer, FALSE );
6655
-	 * $bean = $oodb->dispense( 'bean' );
6656
-	 * $bean->name = 'coffeeBean';
6657
-	 * $id = $oodb->store( $bean );
6658
-	 * $bean = $oodb->load( 'bean', $id );
6659
-	 * </code>
6660
-	 *
6661
-	 * The example above creates the 3 RedBeanPHP core objects:
6662
-	 * the Adapter, the Query Writer and the OODB instance and
6663
-	 * wires them together. The example also demonstrates some of
6664
-	 * the methods that can be used with OODB, as you see, they
6665
-	 * closely resemble their facade counterparts.
6666
-	 *
6667
-	 * The wiring process: create an RPDO instance using your database
6668
-	 * connection parameters. Create a database adapter from the RPDO
6669
-	 * object and pass that to the constructor of the writer. Next,
6670
-	 * create an OODB instance from the writer. Now you have an OODB
6671
-	 * object.
6672
-	 *
6673
-	 * @param Adapter $adapter Database Adapter
6674
-	 */
6675
-	public function __construct( Adapter $adapter )
6676
-	{
6677
-		$this->typeno_sqltype = array(
6678
-			MySQL::C_DATATYPE_BOOL             => ' TINYINT(1) UNSIGNED ',
6679
-			MySQL::C_DATATYPE_UINT32           => ' INT(11) UNSIGNED ',
6680
-			MySQL::C_DATATYPE_DOUBLE           => ' DOUBLE ',
6681
-			MySQL::C_DATATYPE_TEXT7            => ' VARCHAR(191) ',
6682
-			MYSQL::C_DATATYPE_TEXT8	           => ' VARCHAR(255) ',
6683
-			MySQL::C_DATATYPE_TEXT16           => ' TEXT ',
6684
-			MySQL::C_DATATYPE_TEXT32           => ' LONGTEXT ',
6685
-			MySQL::C_DATATYPE_SPECIAL_DATE     => ' DATE ',
6686
-			MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
6687
-			MySQL::C_DATATYPE_SPECIAL_TIME     => ' TIME ',
6688
-			MySQL::C_DATATYPE_SPECIAL_POINT    => ' POINT ',
6689
-			MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ',
6690
-			MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ',
6691
-			MySQL::C_DATATYPE_SPECIAL_MONEY    => ' DECIMAL(10,2) ',
6692
-			MYSQL::C_DATATYPE_SPECIAL_JSON     => ' JSON '
6693
-		);
6694
-
6695
-		$this->sqltype_typeno = array();
6696
-
6697
-		foreach ( $this->typeno_sqltype as $k => $v ) {
6698
-			$this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
6699
-		}
6700
-
6701
-		$this->adapter = $adapter;
6702
-
6703
-		$this->encoding = $this->adapter->getDatabase()->getMysqlEncoding();
6704
-	}
6705
-
6706
-	/**
6707
-	 * This method returns the datatype to be used for primary key IDS and
6708
-	 * foreign keys. Returns one if the data type constants.
6709
-	 *
6710
-	 * @return integer
6711
-	 */
6712
-	public function getTypeForID()
6713
-	{
6714
-		return self::C_DATATYPE_UINT32;
6715
-	}
6716
-
6717
-	/**
6718
-	 * @see QueryWriter::getTables
6719
-	 */
6720
-	public function getTables()
6721
-	{
6722
-		return $this->adapter->getCol( 'show tables' );
6723
-	}
6724
-
6725
-	/**
6726
-	 * @see QueryWriter::createTable
6727
-	 */
6728
-	public function createTable( $table )
6729
-	{
6730
-		$table = $this->esc( $table );
6731
-
6732
-		$charset_collate = $this->adapter->getDatabase()->getMysqlEncoding( TRUE );
6733
-		$charset = $charset_collate['charset'];
6734
-		$collate = $charset_collate['collate'];
6626
+        $keyInfoList = array();
6627
+        foreach ( $keys as $k ) {
6628
+            $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
6629
+            $keyInfoList[$label] = array(
6630
+                'name'          => $k['name'],
6631
+                'from'          => $k['from'],
6632
+                'table'         => $k['table'],
6633
+                'to'            => $k['to'],
6634
+                'on_update'     => $k['on_update'],
6635
+                'on_delete'     => $k['on_delete']
6636
+            );
6637
+        }
6638
+        return $keyInfoList;
6639
+    }
6640
+
6641
+    /**
6642
+     * Constructor
6643
+     * Most of the time, you do not need to use this constructor,
6644
+     * since the facade takes care of constructing and wiring the
6645
+     * RedBeanPHP core objects. However if you would like to
6646
+     * assemble an OODB instance yourself, this is how it works:
6647
+     *
6648
+     * Usage:
6649
+     *
6650
+     * <code>
6651
+     * $database = new RPDO( $dsn, $user, $pass );
6652
+     * $adapter = new DBAdapter( $database );
6653
+     * $writer = new PostgresWriter( $adapter );
6654
+     * $oodb = new OODB( $writer, FALSE );
6655
+     * $bean = $oodb->dispense( 'bean' );
6656
+     * $bean->name = 'coffeeBean';
6657
+     * $id = $oodb->store( $bean );
6658
+     * $bean = $oodb->load( 'bean', $id );
6659
+     * </code>
6660
+     *
6661
+     * The example above creates the 3 RedBeanPHP core objects:
6662
+     * the Adapter, the Query Writer and the OODB instance and
6663
+     * wires them together. The example also demonstrates some of
6664
+     * the methods that can be used with OODB, as you see, they
6665
+     * closely resemble their facade counterparts.
6666
+     *
6667
+     * The wiring process: create an RPDO instance using your database
6668
+     * connection parameters. Create a database adapter from the RPDO
6669
+     * object and pass that to the constructor of the writer. Next,
6670
+     * create an OODB instance from the writer. Now you have an OODB
6671
+     * object.
6672
+     *
6673
+     * @param Adapter $adapter Database Adapter
6674
+     */
6675
+    public function __construct( Adapter $adapter )
6676
+    {
6677
+        $this->typeno_sqltype = array(
6678
+            MySQL::C_DATATYPE_BOOL             => ' TINYINT(1) UNSIGNED ',
6679
+            MySQL::C_DATATYPE_UINT32           => ' INT(11) UNSIGNED ',
6680
+            MySQL::C_DATATYPE_DOUBLE           => ' DOUBLE ',
6681
+            MySQL::C_DATATYPE_TEXT7            => ' VARCHAR(191) ',
6682
+            MYSQL::C_DATATYPE_TEXT8	           => ' VARCHAR(255) ',
6683
+            MySQL::C_DATATYPE_TEXT16           => ' TEXT ',
6684
+            MySQL::C_DATATYPE_TEXT32           => ' LONGTEXT ',
6685
+            MySQL::C_DATATYPE_SPECIAL_DATE     => ' DATE ',
6686
+            MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
6687
+            MySQL::C_DATATYPE_SPECIAL_TIME     => ' TIME ',
6688
+            MySQL::C_DATATYPE_SPECIAL_POINT    => ' POINT ',
6689
+            MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ',
6690
+            MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ',
6691
+            MySQL::C_DATATYPE_SPECIAL_MONEY    => ' DECIMAL(10,2) ',
6692
+            MYSQL::C_DATATYPE_SPECIAL_JSON     => ' JSON '
6693
+        );
6694
+
6695
+        $this->sqltype_typeno = array();
6696
+
6697
+        foreach ( $this->typeno_sqltype as $k => $v ) {
6698
+            $this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
6699
+        }
6700
+
6701
+        $this->adapter = $adapter;
6702
+
6703
+        $this->encoding = $this->adapter->getDatabase()->getMysqlEncoding();
6704
+    }
6705
+
6706
+    /**
6707
+     * This method returns the datatype to be used for primary key IDS and
6708
+     * foreign keys. Returns one if the data type constants.
6709
+     *
6710
+     * @return integer
6711
+     */
6712
+    public function getTypeForID()
6713
+    {
6714
+        return self::C_DATATYPE_UINT32;
6715
+    }
6716
+
6717
+    /**
6718
+     * @see QueryWriter::getTables
6719
+     */
6720
+    public function getTables()
6721
+    {
6722
+        return $this->adapter->getCol( 'show tables' );
6723
+    }
6724
+
6725
+    /**
6726
+     * @see QueryWriter::createTable
6727
+     */
6728
+    public function createTable( $table )
6729
+    {
6730
+        $table = $this->esc( $table );
6731
+
6732
+        $charset_collate = $this->adapter->getDatabase()->getMysqlEncoding( TRUE );
6733
+        $charset = $charset_collate['charset'];
6734
+        $collate = $charset_collate['collate'];
6735 6735
 		
6736
-		$sql   = "CREATE TABLE $table (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET={$charset} COLLATE={$collate} ";
6737
-
6738
-		$this->adapter->exec( $sql );
6739
-	}
6740
-
6741
-	/**
6742
-	 * @see QueryWriter::getColumns
6743
-	 */
6744
-	public function getColumns( $table )
6745
-	{
6746
-		$columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) );
6747
-
6748
-		$columns = array();
6749
-		foreach ( $columnsRaw as $r ) {
6750
-			$columns[$r['Field']] = $r['Type'];
6751
-		}
6752
-
6753
-		return $columns;
6754
-	}
6755
-
6756
-	/**
6757
-	 * @see QueryWriter::scanType
6758
-	 */
6759
-	public function scanType( $value, $flagSpecial = FALSE )
6760
-	{
6761
-		$this->svalue = $value;
6762
-
6763
-		if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL;
6764
-		if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7;
6765
-
6766
-		if ( $flagSpecial ) {
6767
-			if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) {
6768
-				return MySQL::C_DATATYPE_SPECIAL_MONEY;
6769
-			}
6770
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
6771
-				return MySQL::C_DATATYPE_SPECIAL_DATE;
6772
-			}
6773
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
6774
-				return MySQL::C_DATATYPE_SPECIAL_DATETIME;
6775
-			}
6776
-			if ( preg_match( '/^POINT\(/', $value ) ) {
6777
-				return MySQL::C_DATATYPE_SPECIAL_POINT;
6778
-			}
6779
-			if ( preg_match( '/^LINESTRING\(/', $value ) ) {
6780
-				return MySQL::C_DATATYPE_SPECIAL_LINESTRING;
6781
-			}
6782
-			if ( preg_match( '/^POLYGON\(/', $value ) ) {
6783
-				return MySQL::C_DATATYPE_SPECIAL_POLYGON;
6784
-			}
6785
-			if ( self::$flagUseJSONColumns && $this->isJSON( $value ) ) {
6786
-				return self::C_DATATYPE_SPECIAL_JSON;
6787
-			}
6788
-		}
6789
-
6790
-		//setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?).
6791
-		if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) {
6792
-			return MySQL::C_DATATYPE_BOOL;
6793
-		}
6794
-
6795
-		if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
6796
-
6797
-		if ( !$this->startsWithZeros( $value ) ) {
6798
-
6799
-			if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) {
6800
-				return MySQL::C_DATATYPE_UINT32;
6801
-			}
6802
-
6803
-			if ( is_numeric( $value ) ) {
6804
-				return MySQL::C_DATATYPE_DOUBLE;
6805
-			}
6806
-		}
6807
-
6808
-		if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) {
6809
-			return MySQL::C_DATATYPE_TEXT7;
6810
-		}
6811
-
6812
-		if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) {
6813
-			return MySQL::C_DATATYPE_TEXT8;
6814
-		}
6815
-
6816
-		if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) {
6817
-			return MySQL::C_DATATYPE_TEXT16;
6818
-		}
6819
-
6820
-		return MySQL::C_DATATYPE_TEXT32;
6821
-	}
6822
-
6823
-	/**
6824
-	 * @see QueryWriter::code
6825
-	 */
6826
-	public function code( $typedescription, $includeSpecials = FALSE )
6827
-	{
6828
-		if ( isset( $this->sqltype_typeno[$typedescription] ) ) {
6829
-			$r = $this->sqltype_typeno[$typedescription];
6830
-		} else {
6831
-			$r = self::C_DATATYPE_SPECIFIED;
6832
-		}
6833
-
6834
-		if ( $includeSpecials ) {
6835
-			return $r;
6836
-		}
6837
-
6838
-		if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
6839
-			return self::C_DATATYPE_SPECIFIED;
6840
-		}
6841
-
6842
-		return $r;
6843
-	}
6844
-
6845
-	/**
6846
-	 * @see QueryWriter::addUniqueIndex
6847
-	 */
6848
-	public function addUniqueConstraint( $type, $properties )
6849
-	{
6850
-		$tableNoQ = $this->esc( $type, TRUE );
6851
-		$columns = array();
6852
-		foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
6853
-		$table = $this->esc( $type );
6854
-		sort( $columns ); // Else we get multiple indexes due to order-effects
6855
-		$name = 'UQ_' . sha1( implode( ',', $columns ) );
6856
-		try {
6857
-			$sql = "ALTER TABLE $table
6736
+        $sql   = "CREATE TABLE $table (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET={$charset} COLLATE={$collate} ";
6737
+
6738
+        $this->adapter->exec( $sql );
6739
+    }
6740
+
6741
+    /**
6742
+     * @see QueryWriter::getColumns
6743
+     */
6744
+    public function getColumns( $table )
6745
+    {
6746
+        $columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) );
6747
+
6748
+        $columns = array();
6749
+        foreach ( $columnsRaw as $r ) {
6750
+            $columns[$r['Field']] = $r['Type'];
6751
+        }
6752
+
6753
+        return $columns;
6754
+    }
6755
+
6756
+    /**
6757
+     * @see QueryWriter::scanType
6758
+     */
6759
+    public function scanType( $value, $flagSpecial = FALSE )
6760
+    {
6761
+        $this->svalue = $value;
6762
+
6763
+        if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL;
6764
+        if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7;
6765
+
6766
+        if ( $flagSpecial ) {
6767
+            if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) {
6768
+                return MySQL::C_DATATYPE_SPECIAL_MONEY;
6769
+            }
6770
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
6771
+                return MySQL::C_DATATYPE_SPECIAL_DATE;
6772
+            }
6773
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
6774
+                return MySQL::C_DATATYPE_SPECIAL_DATETIME;
6775
+            }
6776
+            if ( preg_match( '/^POINT\(/', $value ) ) {
6777
+                return MySQL::C_DATATYPE_SPECIAL_POINT;
6778
+            }
6779
+            if ( preg_match( '/^LINESTRING\(/', $value ) ) {
6780
+                return MySQL::C_DATATYPE_SPECIAL_LINESTRING;
6781
+            }
6782
+            if ( preg_match( '/^POLYGON\(/', $value ) ) {
6783
+                return MySQL::C_DATATYPE_SPECIAL_POLYGON;
6784
+            }
6785
+            if ( self::$flagUseJSONColumns && $this->isJSON( $value ) ) {
6786
+                return self::C_DATATYPE_SPECIAL_JSON;
6787
+            }
6788
+        }
6789
+
6790
+        //setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?).
6791
+        if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) {
6792
+            return MySQL::C_DATATYPE_BOOL;
6793
+        }
6794
+
6795
+        if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
6796
+
6797
+        if ( !$this->startsWithZeros( $value ) ) {
6798
+
6799
+            if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) {
6800
+                return MySQL::C_DATATYPE_UINT32;
6801
+            }
6802
+
6803
+            if ( is_numeric( $value ) ) {
6804
+                return MySQL::C_DATATYPE_DOUBLE;
6805
+            }
6806
+        }
6807
+
6808
+        if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) {
6809
+            return MySQL::C_DATATYPE_TEXT7;
6810
+        }
6811
+
6812
+        if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) {
6813
+            return MySQL::C_DATATYPE_TEXT8;
6814
+        }
6815
+
6816
+        if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) {
6817
+            return MySQL::C_DATATYPE_TEXT16;
6818
+        }
6819
+
6820
+        return MySQL::C_DATATYPE_TEXT32;
6821
+    }
6822
+
6823
+    /**
6824
+     * @see QueryWriter::code
6825
+     */
6826
+    public function code( $typedescription, $includeSpecials = FALSE )
6827
+    {
6828
+        if ( isset( $this->sqltype_typeno[$typedescription] ) ) {
6829
+            $r = $this->sqltype_typeno[$typedescription];
6830
+        } else {
6831
+            $r = self::C_DATATYPE_SPECIFIED;
6832
+        }
6833
+
6834
+        if ( $includeSpecials ) {
6835
+            return $r;
6836
+        }
6837
+
6838
+        if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
6839
+            return self::C_DATATYPE_SPECIFIED;
6840
+        }
6841
+
6842
+        return $r;
6843
+    }
6844
+
6845
+    /**
6846
+     * @see QueryWriter::addUniqueIndex
6847
+     */
6848
+    public function addUniqueConstraint( $type, $properties )
6849
+    {
6850
+        $tableNoQ = $this->esc( $type, TRUE );
6851
+        $columns = array();
6852
+        foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
6853
+        $table = $this->esc( $type );
6854
+        sort( $columns ); // Else we get multiple indexes due to order-effects
6855
+        $name = 'UQ_' . sha1( implode( ',', $columns ) );
6856
+        try {
6857
+            $sql = "ALTER TABLE $table
6858 6858
 						 ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")";
6859
-			$this->adapter->exec( $sql );
6860
-		} catch ( SQLException $e ) {
6861
-			//do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways!
6862
-			return FALSE;
6863
-		}
6864
-		return TRUE;
6865
-	}
6866
-
6867
-	/**
6868
-	 * @see QueryWriter::addIndex
6869
-	 */
6870
-	public function addIndex( $type, $name, $property )
6871
-	{
6872
-		try {
6873
-			$table  = $this->esc( $type );
6874
-			$name   = preg_replace( '/\W/', '', $name );
6875
-			$column = $this->esc( $property );
6876
-			$this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
6877
-			return TRUE;
6878
-		} catch ( SQLException $e ) {
6879
-			return FALSE;
6880
-		}
6881
-	}
6882
-
6883
-	/**
6884
-	 * @see QueryWriter::addFK
6885
-	 * @return bool
6886
-	 */
6887
-	public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
6888
-	{
6889
-		$table = $this->esc( $type );
6890
-		$targetTable = $this->esc( $targetType );
6891
-		$targetTableNoQ = $this->esc( $targetType, TRUE );
6892
-		$field = $this->esc( $property );
6893
-		$fieldNoQ = $this->esc( $property, TRUE );
6894
-		$targetField = $this->esc( $targetProperty );
6895
-		$targetFieldNoQ = $this->esc( $targetProperty, TRUE );
6896
-		$tableNoQ = $this->esc( $type, TRUE );
6897
-		$fieldNoQ = $this->esc( $property, TRUE );
6898
-		if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
6899
-
6900
-		//Widen the column if it's incapable of representing a foreign key (at least INT).
6901
-		$columns = $this->getColumns( $tableNoQ );
6902
-		$idType = $this->getTypeForID();
6903
-		if ( $this->code( $columns[$fieldNoQ] ) !==  $idType ) {
6904
-			$this->widenColumn( $type, $property, $idType );
6905
-		}
6906
-
6907
-		$fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ);
6908
-		$cName = 'c_'.$fkName;
6909
-		try {
6910
-			$this->adapter->exec( "
6859
+            $this->adapter->exec( $sql );
6860
+        } catch ( SQLException $e ) {
6861
+            //do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways!
6862
+            return FALSE;
6863
+        }
6864
+        return TRUE;
6865
+    }
6866
+
6867
+    /**
6868
+     * @see QueryWriter::addIndex
6869
+     */
6870
+    public function addIndex( $type, $name, $property )
6871
+    {
6872
+        try {
6873
+            $table  = $this->esc( $type );
6874
+            $name   = preg_replace( '/\W/', '', $name );
6875
+            $column = $this->esc( $property );
6876
+            $this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
6877
+            return TRUE;
6878
+        } catch ( SQLException $e ) {
6879
+            return FALSE;
6880
+        }
6881
+    }
6882
+
6883
+    /**
6884
+     * @see QueryWriter::addFK
6885
+     * @return bool
6886
+     */
6887
+    public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
6888
+    {
6889
+        $table = $this->esc( $type );
6890
+        $targetTable = $this->esc( $targetType );
6891
+        $targetTableNoQ = $this->esc( $targetType, TRUE );
6892
+        $field = $this->esc( $property );
6893
+        $fieldNoQ = $this->esc( $property, TRUE );
6894
+        $targetField = $this->esc( $targetProperty );
6895
+        $targetFieldNoQ = $this->esc( $targetProperty, TRUE );
6896
+        $tableNoQ = $this->esc( $type, TRUE );
6897
+        $fieldNoQ = $this->esc( $property, TRUE );
6898
+        if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
6899
+
6900
+        //Widen the column if it's incapable of representing a foreign key (at least INT).
6901
+        $columns = $this->getColumns( $tableNoQ );
6902
+        $idType = $this->getTypeForID();
6903
+        if ( $this->code( $columns[$fieldNoQ] ) !==  $idType ) {
6904
+            $this->widenColumn( $type, $property, $idType );
6905
+        }
6906
+
6907
+        $fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ);
6908
+        $cName = 'c_'.$fkName;
6909
+        try {
6910
+            $this->adapter->exec( "
6911 6911
 				ALTER TABLE {$table}
6912 6912
 				ADD CONSTRAINT $cName
6913 6913
 				FOREIGN KEY $fkName ( `{$fieldNoQ}` ) REFERENCES `{$targetTableNoQ}`
6914 6914
 				(`{$targetFieldNoQ}`) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';');
6915
-		} catch ( SQLException $e ) {
6916
-			// Failure of fk-constraints is not a problem
6917
-		}
6918
-		return TRUE;
6919
-	}
6920
-
6921
-	/**
6922
-	 * @see QueryWriter::sqlStateIn
6923
-	 */
6924
-	public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
6925
-	{
6926
-		$stateMap = array(
6927
-			'42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
6928
-			'42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
6929
-			'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,
6930
-		);
6931
-
6932
-		if ( $state == 'HY000' && !empty( $extraDriverDetails[1] ) ) {
6933
-			$driverCode = $extraDriverDetails[1];
6934
-
6935
-			if ( $driverCode == '1205' && in_array( QueryWriter::C_SQLSTATE_LOCK_TIMEOUT, $list ) ) {
6936
-				return TRUE;
6937
-			}
6938
-		}
6939
-
6940
-		return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
6941
-	}
6942
-
6943
-	/**
6944
-	 * @see QueryWriter::wipeAll
6945
-	 */
6946
-	public function wipeAll()
6947
-	{
6948
-		if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
6949
-		$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' );
6950
-
6951
-		foreach ( $this->getTables() as $t ) {
6952
-			try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
6953
-			try { $this->adapter->exec( "DROP VIEW IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
6954
-		}
6955
-
6956
-		$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' );
6957
-	}
6915
+        } catch ( SQLException $e ) {
6916
+            // Failure of fk-constraints is not a problem
6917
+        }
6918
+        return TRUE;
6919
+    }
6920
+
6921
+    /**
6922
+     * @see QueryWriter::sqlStateIn
6923
+     */
6924
+    public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
6925
+    {
6926
+        $stateMap = array(
6927
+            '42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
6928
+            '42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
6929
+            '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,
6930
+        );
6931
+
6932
+        if ( $state == 'HY000' && !empty( $extraDriverDetails[1] ) ) {
6933
+            $driverCode = $extraDriverDetails[1];
6934
+
6935
+            if ( $driverCode == '1205' && in_array( QueryWriter::C_SQLSTATE_LOCK_TIMEOUT, $list ) ) {
6936
+                return TRUE;
6937
+            }
6938
+        }
6939
+
6940
+        return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
6941
+    }
6942
+
6943
+    /**
6944
+     * @see QueryWriter::wipeAll
6945
+     */
6946
+    public function wipeAll()
6947
+    {
6948
+        if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
6949
+        $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' );
6950
+
6951
+        foreach ( $this->getTables() as $t ) {
6952
+            try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
6953
+            try { $this->adapter->exec( "DROP VIEW IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
6954
+        }
6955
+
6956
+        $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' );
6957
+    }
6958 6958
 }
6959 6959
 }
6960 6960
 
@@ -6982,446 +6982,446 @@  discard block
 block discarded – undo
6982 6982
  */
6983 6983
 class SQLiteT extends AQueryWriter implements QueryWriter
6984 6984
 {
6985
-	/**
6986
-	 * Data types
6987
-	 */
6988
-	const C_DATATYPE_INTEGER   = 0;
6989
-	const C_DATATYPE_NUMERIC   = 1;
6990
-	const C_DATATYPE_TEXT      = 2;
6991
-	const C_DATATYPE_SPECIFIED = 99;
6992
-
6993
-	/**
6994
-	 * @var DBAdapter
6995
-	 */
6996
-	protected $adapter;
6997
-
6998
-	/**
6999
-	 * @var string
7000
-	 */
7001
-	protected $quoteCharacter = '`';
7002
-
7003
-	/**
7004
-	 * Gets all information about a table (from a type).
7005
-	 *
7006
-	 * Format:
7007
-	 * array(
7008
-	 *    name => name of the table
7009
-	 *    columns => array( name => datatype )
7010
-	 *    indexes => array() raw index information rows from PRAGMA query
7011
-	 *    keys => array() raw key information rows from PRAGMA query
7012
-	 * )
7013
-	 *
7014
-	 * @param string $type type you want to get info of
7015
-	 *
7016
-	 * @return array
7017
-	 */
7018
-	protected function getTable( $type )
7019
-	{
7020
-		$tableName = $this->esc( $type, TRUE );
7021
-		$columns   = $this->getColumns( $type );
7022
-		$indexes   = $this->getIndexes( $type );
7023
-		$keys      = $this->getKeyMapForType( $type );
7024
-
7025
-		$table = array(
7026
-			'columns' => $columns,
7027
-			'indexes' => $indexes,
7028
-			'keys' => $keys,
7029
-			'name' => $tableName
7030
-		);
7031
-
7032
-		$this->tableArchive[$tableName] = $table;
7033
-
7034
-		return $table;
7035
-	}
7036
-
7037
-	/**
7038
-	 * Puts a table. Updates the table structure.
7039
-	 * In SQLite we can't change columns, drop columns, change or add foreign keys so we
7040
-	 * have a table-rebuild function. You simply load your table with getTable(), modify it and
7041
-	 * then store it with putTable()...
7042
-	 *
7043
-	 * @param array $tableMap information array
7044
-	 *
7045
-	 * @return void
7046
-	 */
7047
-	protected function putTable( $tableMap )
7048
-	{
7049
-		$table = $tableMap['name'];
7050
-		$q     = array();
7051
-		$q[]   = "DROP TABLE IF EXISTS tmp_backup;";
7052
-
7053
-		$oldColumnNames = array_keys( $this->getColumns( $table ) );
7054
-
7055
-		foreach ( $oldColumnNames as $k => $v ) $oldColumnNames[$k] = "`$v`";
7056
-
7057
-		$q[] = "CREATE TEMPORARY TABLE tmp_backup(" . implode( ",", $oldColumnNames ) . ");";
7058
-		$q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
7059
-		$q[] = "PRAGMA foreign_keys = 0 ";
7060
-		$q[] = "DROP TABLE `$table`;";
7061
-
7062
-		$newTableDefStr = '';
7063
-		foreach ( $tableMap['columns'] as $column => $type ) {
7064
-			if ( $column != 'id' ) {
7065
-				$newTableDefStr .= ",`$column` $type";
7066
-			}
7067
-		}
7068
-
7069
-		$fkDef = '';
7070
-		foreach ( $tableMap['keys'] as $key ) {
7071
-			$fkDef .= ", FOREIGN KEY(`{$key['from']}`)
6985
+    /**
6986
+     * Data types
6987
+     */
6988
+    const C_DATATYPE_INTEGER   = 0;
6989
+    const C_DATATYPE_NUMERIC   = 1;
6990
+    const C_DATATYPE_TEXT      = 2;
6991
+    const C_DATATYPE_SPECIFIED = 99;
6992
+
6993
+    /**
6994
+     * @var DBAdapter
6995
+     */
6996
+    protected $adapter;
6997
+
6998
+    /**
6999
+     * @var string
7000
+     */
7001
+    protected $quoteCharacter = '`';
7002
+
7003
+    /**
7004
+     * Gets all information about a table (from a type).
7005
+     *
7006
+     * Format:
7007
+     * array(
7008
+     *    name => name of the table
7009
+     *    columns => array( name => datatype )
7010
+     *    indexes => array() raw index information rows from PRAGMA query
7011
+     *    keys => array() raw key information rows from PRAGMA query
7012
+     * )
7013
+     *
7014
+     * @param string $type type you want to get info of
7015
+     *
7016
+     * @return array
7017
+     */
7018
+    protected function getTable( $type )
7019
+    {
7020
+        $tableName = $this->esc( $type, TRUE );
7021
+        $columns   = $this->getColumns( $type );
7022
+        $indexes   = $this->getIndexes( $type );
7023
+        $keys      = $this->getKeyMapForType( $type );
7024
+
7025
+        $table = array(
7026
+            'columns' => $columns,
7027
+            'indexes' => $indexes,
7028
+            'keys' => $keys,
7029
+            'name' => $tableName
7030
+        );
7031
+
7032
+        $this->tableArchive[$tableName] = $table;
7033
+
7034
+        return $table;
7035
+    }
7036
+
7037
+    /**
7038
+     * Puts a table. Updates the table structure.
7039
+     * In SQLite we can't change columns, drop columns, change or add foreign keys so we
7040
+     * have a table-rebuild function. You simply load your table with getTable(), modify it and
7041
+     * then store it with putTable()...
7042
+     *
7043
+     * @param array $tableMap information array
7044
+     *
7045
+     * @return void
7046
+     */
7047
+    protected function putTable( $tableMap )
7048
+    {
7049
+        $table = $tableMap['name'];
7050
+        $q     = array();
7051
+        $q[]   = "DROP TABLE IF EXISTS tmp_backup;";
7052
+
7053
+        $oldColumnNames = array_keys( $this->getColumns( $table ) );
7054
+
7055
+        foreach ( $oldColumnNames as $k => $v ) $oldColumnNames[$k] = "`$v`";
7056
+
7057
+        $q[] = "CREATE TEMPORARY TABLE tmp_backup(" . implode( ",", $oldColumnNames ) . ");";
7058
+        $q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
7059
+        $q[] = "PRAGMA foreign_keys = 0 ";
7060
+        $q[] = "DROP TABLE `$table`;";
7061
+
7062
+        $newTableDefStr = '';
7063
+        foreach ( $tableMap['columns'] as $column => $type ) {
7064
+            if ( $column != 'id' ) {
7065
+                $newTableDefStr .= ",`$column` $type";
7066
+            }
7067
+        }
7068
+
7069
+        $fkDef = '';
7070
+        foreach ( $tableMap['keys'] as $key ) {
7071
+            $fkDef .= ", FOREIGN KEY(`{$key['from']}`)
7072 7072
 						 REFERENCES `{$key['table']}`(`{$key['to']}`)
7073 7073
 						 ON DELETE {$key['on_delete']} ON UPDATE {$key['on_update']}";
7074
-		}
7075
-
7076
-		$q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT  $newTableDefStr  $fkDef );";
7077
-
7078
-		foreach ( $tableMap['indexes'] as $name => $index ) {
7079
-			if ( strpos( $name, 'UQ_' ) === 0 ) {
7080
-				$cols = explode( '__', substr( $name, strlen( 'UQ_' . $table ) ) );
7081
-				foreach ( $cols as $k => $v ) $cols[$k] = "`$v`";
7082
-				$q[] = "CREATE UNIQUE INDEX $name ON `$table` (" . implode( ',', $cols ) . ")";
7083
-			} else $q[] = "CREATE INDEX $name ON `$table` ({$index['name']}) ";
7084
-		}
7085
-
7086
-		$q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
7087
-		$q[] = "DROP TABLE tmp_backup;";
7088
-		$q[] = "PRAGMA foreign_keys = 1 ";
7089
-
7090
-		foreach ( $q as $sq ) $this->adapter->exec( $sq );
7091
-	}
7092
-
7093
-	/**
7094
-	 * Returns the an array describing the indexes for type $type.
7095
-	 *
7096
-	 * @param string $type type to describe indexes of
7097
-	 *
7098
-	 * @return array
7099
-	 */
7100
-	protected function getIndexes( $type )
7101
-	{
7102
-		$table   = $this->esc( $type, TRUE );
7103
-		$indexes = $this->adapter->get( "PRAGMA index_list('$table')" );
7104
-
7105
-		$indexInfoList = array();
7106
-		foreach ( $indexes as $i ) {
7107
-			$indexInfoList[$i['name']] = $this->adapter->getRow( "PRAGMA index_info('{$i['name']}') " );
7108
-
7109
-			$indexInfoList[$i['name']]['unique'] = $i['unique'];
7110
-		}
7111
-
7112
-		return $indexInfoList;
7113
-	}
7114
-
7115
-	/**
7116
-	 * Adds a foreign key to a type.
7117
-	 * Note: cant put this in try-catch because that can hide the fact
7118
-	 * that database has been damaged.
7119
-	 *
7120
-	 * @param  string  $type        type you want to modify table of
7121
-	 * @param  string  $targetType  target type
7122
-	 * @param  string  $field       field of the type that needs to get the fk
7123
-	 * @param  string  $targetField field where the fk needs to point to
7124
-	 * @param  integer $buildopt    0 = NO ACTION, 1 = ON DELETE CASCADE
7125
-	 *
7126
-	 * @return boolean
7127
-	 */
7128
-	protected function buildFK( $type, $targetType, $property, $targetProperty, $constraint = FALSE )
7129
-	{
7130
-		$table           = $this->esc( $type, TRUE );
7131
-		$targetTable     = $this->esc( $targetType, TRUE );
7132
-		$column          = $this->esc( $property, TRUE );
7133
-		$targetColumn    = $this->esc( $targetProperty, TRUE );
7134
-
7135
-		$tables = $this->getTables();
7136
-		if ( !in_array( $targetTable, $tables ) ) return FALSE;
7137
-
7138
-		if ( !is_null( $this->getForeignKeyForTypeProperty( $table, $column ) ) ) return FALSE;
7139
-		$t = $this->getTable( $table );
7140
-		$consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' );
7141
-		$label   = 'from_' . $column . '_to_table_' . $targetTable . '_col_' . $targetColumn;
7142
-		$t['keys'][$label] = array(
7143
-			'table'     => $targetTable,
7144
-			'from'      => $column,
7145
-			'to'        => $targetColumn,
7146
-			'on_update' => $consSQL,
7147
-			'on_delete' => $consSQL
7148
-		);
7149
-		$this->putTable( $t );
7150
-		return TRUE;
7151
-	}
7152
-
7153
-	/**
7154
-	 * @see AQueryWriter::getKeyMapForType
7155
-	 */
7156
-	protected function getKeyMapForType( $type )
7157
-	{
7158
-		$table = $this->esc( $type, TRUE );
7159
-		$keys  = $this->adapter->get( "PRAGMA foreign_key_list('$table')" );
7160
-		$keyInfoList = array();
7161
-		foreach ( $keys as $k ) {
7162
-			$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
7163
-			$keyInfoList[$label] = array(
7164
-				'name'          => $label,
7165
-				'from'          => $k['from'],
7166
-				'table'         => $k['table'],
7167
-				'to'            => $k['to'],
7168
-				'on_update'     => $k['on_update'],
7169
-				'on_delete'     => $k['on_delete']
7170
-			);
7171
-		}
7172
-		return $keyInfoList;
7173
-	}
7174
-
7175
-	/**
7176
-	 * Constructor
7177
-	 * Most of the time, you do not need to use this constructor,
7178
-	 * since the facade takes care of constructing and wiring the
7179
-	 * RedBeanPHP core objects. However if you would like to
7180
-	 * assemble an OODB instance yourself, this is how it works:
7181
-	 *
7182
-	 * Usage:
7183
-	 *
7184
-	 * <code>
7185
-	 * $database = new RPDO( $dsn, $user, $pass );
7186
-	 * $adapter = new DBAdapter( $database );
7187
-	 * $writer = new PostgresWriter( $adapter );
7188
-	 * $oodb = new OODB( $writer, FALSE );
7189
-	 * $bean = $oodb->dispense( 'bean' );
7190
-	 * $bean->name = 'coffeeBean';
7191
-	 * $id = $oodb->store( $bean );
7192
-	 * $bean = $oodb->load( 'bean', $id );
7193
-	 * </code>
7194
-	 *
7195
-	 * The example above creates the 3 RedBeanPHP core objects:
7196
-	 * the Adapter, the Query Writer and the OODB instance and
7197
-	 * wires them together. The example also demonstrates some of
7198
-	 * the methods that can be used with OODB, as you see, they
7199
-	 * closely resemble their facade counterparts.
7200
-	 *
7201
-	 * The wiring process: create an RPDO instance using your database
7202
-	 * connection parameters. Create a database adapter from the RPDO
7203
-	 * object and pass that to the constructor of the writer. Next,
7204
-	 * create an OODB instance from the writer. Now you have an OODB
7205
-	 * object.
7206
-	 *
7207
-	 * @param Adapter $adapter Database Adapter
7208
-	 */
7209
-	public function __construct( Adapter $adapter )
7210
-	{
7211
-		$this->typeno_sqltype = array(
7212
-			SQLiteT::C_DATATYPE_INTEGER => 'INTEGER',
7213
-			SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC',
7214
-			SQLiteT::C_DATATYPE_TEXT    => 'TEXT',
7215
-		);
7216
-
7217
-		$this->sqltype_typeno = array();
7218
-
7219
-		foreach ( $this->typeno_sqltype as $k => $v ) {
7220
-			$this->sqltype_typeno[$v] = $k;
7221
-		}
7222
-
7223
-		$this->adapter = $adapter;
7224
-		$this->adapter->setOption( 'setInitQuery', ' PRAGMA foreign_keys = 1 ' );
7225
-	}
7226
-
7227
-	/**
7228
-	 * This method returns the datatype to be used for primary key IDS and
7229
-	 * foreign keys. Returns one if the data type constants.
7230
-	 *
7231
-	 * @return integer $const data type to be used for IDS.
7232
-	 */
7233
-	public function getTypeForID()
7234
-	{
7235
-		return self::C_DATATYPE_INTEGER;
7236
-	}
7237
-
7238
-	/**
7239
-	 * @see QueryWriter::scanType
7240
-	 */
7241
-	public function scanType( $value, $flagSpecial = FALSE )
7242
-	{
7243
-		$this->svalue = $value;
7244
-
7245
-		if ( $value === NULL ) return self::C_DATATYPE_INTEGER;
7246
-		if ( $value === INF ) return self::C_DATATYPE_TEXT;
7247
-
7248
-		if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
7249
-
7250
-		if ( $value === TRUE || $value === FALSE )  return self::C_DATATYPE_INTEGER;
7251
-
7252
-		if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER;
7253
-
7254
-		if ( ( is_numeric( $value ) && $value < 2147483648 && $value > -2147483648)
7255
-			|| preg_match( '/\d{4}\-\d\d\-\d\d/', $value )
7256
-			|| preg_match( '/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', $value )
7257
-		) {
7258
-			return self::C_DATATYPE_NUMERIC;
7259
-		}
7260
-
7261
-		return self::C_DATATYPE_TEXT;
7262
-	}
7263
-
7264
-	/**
7265
-	 * @see QueryWriter::addColumn
7266
-	 */
7267
-	public function addColumn( $table, $column, $type )
7268
-	{
7269
-		$column = $this->check( $column );
7270
-		$table  = $this->check( $table );
7271
-		$type   = $this->typeno_sqltype[$type];
7272
-
7273
-		$this->adapter->exec( "ALTER TABLE `$table` ADD `$column` $type " );
7274
-	}
7275
-
7276
-	/**
7277
-	 * @see QueryWriter::code
7278
-	 */
7279
-	public function code( $typedescription, $includeSpecials = FALSE )
7280
-	{
7281
-		$r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99 );
7282
-
7283
-		return $r;
7284
-	}
7285
-
7286
-	/**
7287
-	 * @see QueryWriter::widenColumn
7288
-	 */
7289
-	public function widenColumn( $type, $column, $datatype )
7290
-	{
7291
-		$t = $this->getTable( $type );
7292
-
7293
-		$t['columns'][$column] = $this->typeno_sqltype[$datatype];
7294
-
7295
-		$this->putTable( $t );
7296
-	}
7297
-
7298
-	/**
7299
-	 * @see QueryWriter::getTables();
7300
-	 */
7301
-	public function getTables()
7302
-	{
7303
-		return $this->adapter->getCol( "SELECT name FROM sqlite_master
7074
+        }
7075
+
7076
+        $q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT  $newTableDefStr  $fkDef );";
7077
+
7078
+        foreach ( $tableMap['indexes'] as $name => $index ) {
7079
+            if ( strpos( $name, 'UQ_' ) === 0 ) {
7080
+                $cols = explode( '__', substr( $name, strlen( 'UQ_' . $table ) ) );
7081
+                foreach ( $cols as $k => $v ) $cols[$k] = "`$v`";
7082
+                $q[] = "CREATE UNIQUE INDEX $name ON `$table` (" . implode( ',', $cols ) . ")";
7083
+            } else $q[] = "CREATE INDEX $name ON `$table` ({$index['name']}) ";
7084
+        }
7085
+
7086
+        $q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
7087
+        $q[] = "DROP TABLE tmp_backup;";
7088
+        $q[] = "PRAGMA foreign_keys = 1 ";
7089
+
7090
+        foreach ( $q as $sq ) $this->adapter->exec( $sq );
7091
+    }
7092
+
7093
+    /**
7094
+     * Returns the an array describing the indexes for type $type.
7095
+     *
7096
+     * @param string $type type to describe indexes of
7097
+     *
7098
+     * @return array
7099
+     */
7100
+    protected function getIndexes( $type )
7101
+    {
7102
+        $table   = $this->esc( $type, TRUE );
7103
+        $indexes = $this->adapter->get( "PRAGMA index_list('$table')" );
7104
+
7105
+        $indexInfoList = array();
7106
+        foreach ( $indexes as $i ) {
7107
+            $indexInfoList[$i['name']] = $this->adapter->getRow( "PRAGMA index_info('{$i['name']}') " );
7108
+
7109
+            $indexInfoList[$i['name']]['unique'] = $i['unique'];
7110
+        }
7111
+
7112
+        return $indexInfoList;
7113
+    }
7114
+
7115
+    /**
7116
+     * Adds a foreign key to a type.
7117
+     * Note: cant put this in try-catch because that can hide the fact
7118
+     * that database has been damaged.
7119
+     *
7120
+     * @param  string  $type        type you want to modify table of
7121
+     * @param  string  $targetType  target type
7122
+     * @param  string  $field       field of the type that needs to get the fk
7123
+     * @param  string  $targetField field where the fk needs to point to
7124
+     * @param  integer $buildopt    0 = NO ACTION, 1 = ON DELETE CASCADE
7125
+     *
7126
+     * @return boolean
7127
+     */
7128
+    protected function buildFK( $type, $targetType, $property, $targetProperty, $constraint = FALSE )
7129
+    {
7130
+        $table           = $this->esc( $type, TRUE );
7131
+        $targetTable     = $this->esc( $targetType, TRUE );
7132
+        $column          = $this->esc( $property, TRUE );
7133
+        $targetColumn    = $this->esc( $targetProperty, TRUE );
7134
+
7135
+        $tables = $this->getTables();
7136
+        if ( !in_array( $targetTable, $tables ) ) return FALSE;
7137
+
7138
+        if ( !is_null( $this->getForeignKeyForTypeProperty( $table, $column ) ) ) return FALSE;
7139
+        $t = $this->getTable( $table );
7140
+        $consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' );
7141
+        $label   = 'from_' . $column . '_to_table_' . $targetTable . '_col_' . $targetColumn;
7142
+        $t['keys'][$label] = array(
7143
+            'table'     => $targetTable,
7144
+            'from'      => $column,
7145
+            'to'        => $targetColumn,
7146
+            'on_update' => $consSQL,
7147
+            'on_delete' => $consSQL
7148
+        );
7149
+        $this->putTable( $t );
7150
+        return TRUE;
7151
+    }
7152
+
7153
+    /**
7154
+     * @see AQueryWriter::getKeyMapForType
7155
+     */
7156
+    protected function getKeyMapForType( $type )
7157
+    {
7158
+        $table = $this->esc( $type, TRUE );
7159
+        $keys  = $this->adapter->get( "PRAGMA foreign_key_list('$table')" );
7160
+        $keyInfoList = array();
7161
+        foreach ( $keys as $k ) {
7162
+            $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
7163
+            $keyInfoList[$label] = array(
7164
+                'name'          => $label,
7165
+                'from'          => $k['from'],
7166
+                'table'         => $k['table'],
7167
+                'to'            => $k['to'],
7168
+                'on_update'     => $k['on_update'],
7169
+                'on_delete'     => $k['on_delete']
7170
+            );
7171
+        }
7172
+        return $keyInfoList;
7173
+    }
7174
+
7175
+    /**
7176
+     * Constructor
7177
+     * Most of the time, you do not need to use this constructor,
7178
+     * since the facade takes care of constructing and wiring the
7179
+     * RedBeanPHP core objects. However if you would like to
7180
+     * assemble an OODB instance yourself, this is how it works:
7181
+     *
7182
+     * Usage:
7183
+     *
7184
+     * <code>
7185
+     * $database = new RPDO( $dsn, $user, $pass );
7186
+     * $adapter = new DBAdapter( $database );
7187
+     * $writer = new PostgresWriter( $adapter );
7188
+     * $oodb = new OODB( $writer, FALSE );
7189
+     * $bean = $oodb->dispense( 'bean' );
7190
+     * $bean->name = 'coffeeBean';
7191
+     * $id = $oodb->store( $bean );
7192
+     * $bean = $oodb->load( 'bean', $id );
7193
+     * </code>
7194
+     *
7195
+     * The example above creates the 3 RedBeanPHP core objects:
7196
+     * the Adapter, the Query Writer and the OODB instance and
7197
+     * wires them together. The example also demonstrates some of
7198
+     * the methods that can be used with OODB, as you see, they
7199
+     * closely resemble their facade counterparts.
7200
+     *
7201
+     * The wiring process: create an RPDO instance using your database
7202
+     * connection parameters. Create a database adapter from the RPDO
7203
+     * object and pass that to the constructor of the writer. Next,
7204
+     * create an OODB instance from the writer. Now you have an OODB
7205
+     * object.
7206
+     *
7207
+     * @param Adapter $adapter Database Adapter
7208
+     */
7209
+    public function __construct( Adapter $adapter )
7210
+    {
7211
+        $this->typeno_sqltype = array(
7212
+            SQLiteT::C_DATATYPE_INTEGER => 'INTEGER',
7213
+            SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC',
7214
+            SQLiteT::C_DATATYPE_TEXT    => 'TEXT',
7215
+        );
7216
+
7217
+        $this->sqltype_typeno = array();
7218
+
7219
+        foreach ( $this->typeno_sqltype as $k => $v ) {
7220
+            $this->sqltype_typeno[$v] = $k;
7221
+        }
7222
+
7223
+        $this->adapter = $adapter;
7224
+        $this->adapter->setOption( 'setInitQuery', ' PRAGMA foreign_keys = 1 ' );
7225
+    }
7226
+
7227
+    /**
7228
+     * This method returns the datatype to be used for primary key IDS and
7229
+     * foreign keys. Returns one if the data type constants.
7230
+     *
7231
+     * @return integer $const data type to be used for IDS.
7232
+     */
7233
+    public function getTypeForID()
7234
+    {
7235
+        return self::C_DATATYPE_INTEGER;
7236
+    }
7237
+
7238
+    /**
7239
+     * @see QueryWriter::scanType
7240
+     */
7241
+    public function scanType( $value, $flagSpecial = FALSE )
7242
+    {
7243
+        $this->svalue = $value;
7244
+
7245
+        if ( $value === NULL ) return self::C_DATATYPE_INTEGER;
7246
+        if ( $value === INF ) return self::C_DATATYPE_TEXT;
7247
+
7248
+        if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
7249
+
7250
+        if ( $value === TRUE || $value === FALSE )  return self::C_DATATYPE_INTEGER;
7251
+
7252
+        if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER;
7253
+
7254
+        if ( ( is_numeric( $value ) && $value < 2147483648 && $value > -2147483648)
7255
+            || preg_match( '/\d{4}\-\d\d\-\d\d/', $value )
7256
+            || preg_match( '/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', $value )
7257
+        ) {
7258
+            return self::C_DATATYPE_NUMERIC;
7259
+        }
7260
+
7261
+        return self::C_DATATYPE_TEXT;
7262
+    }
7263
+
7264
+    /**
7265
+     * @see QueryWriter::addColumn
7266
+     */
7267
+    public function addColumn( $table, $column, $type )
7268
+    {
7269
+        $column = $this->check( $column );
7270
+        $table  = $this->check( $table );
7271
+        $type   = $this->typeno_sqltype[$type];
7272
+
7273
+        $this->adapter->exec( "ALTER TABLE `$table` ADD `$column` $type " );
7274
+    }
7275
+
7276
+    /**
7277
+     * @see QueryWriter::code
7278
+     */
7279
+    public function code( $typedescription, $includeSpecials = FALSE )
7280
+    {
7281
+        $r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99 );
7282
+
7283
+        return $r;
7284
+    }
7285
+
7286
+    /**
7287
+     * @see QueryWriter::widenColumn
7288
+     */
7289
+    public function widenColumn( $type, $column, $datatype )
7290
+    {
7291
+        $t = $this->getTable( $type );
7292
+
7293
+        $t['columns'][$column] = $this->typeno_sqltype[$datatype];
7294
+
7295
+        $this->putTable( $t );
7296
+    }
7297
+
7298
+    /**
7299
+     * @see QueryWriter::getTables();
7300
+     */
7301
+    public function getTables()
7302
+    {
7303
+        return $this->adapter->getCol( "SELECT name FROM sqlite_master
7304 7304
 			WHERE type='table' AND name!='sqlite_sequence';" );
7305
-	}
7306
-
7307
-	/**
7308
-	 * @see QueryWriter::createTable
7309
-	 */
7310
-	public function createTable( $table )
7311
-	{
7312
-		$table = $this->esc( $table );
7313
-
7314
-		$sql   = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) ";
7315
-
7316
-		$this->adapter->exec( $sql );
7317
-	}
7318
-
7319
-	/**
7320
-	 * @see QueryWriter::getColumns
7321
-	 */
7322
-	public function getColumns( $table )
7323
-	{
7324
-		$table      = $this->esc( $table, TRUE );
7325
-
7326
-		$columnsRaw = $this->adapter->get( "PRAGMA table_info('$table')" );
7327
-
7328
-		$columns    = array();
7329
-		foreach ( $columnsRaw as $r ) $columns[$r['name']] = $r['type'];
7330
-
7331
-		return $columns;
7332
-	}
7333
-
7334
-	/**
7335
-	 * @see QueryWriter::addUniqueIndex
7336
-	 */
7337
-	public function addUniqueConstraint( $type, $properties )
7338
-	{
7339
-		$tableNoQ = $this->esc( $type, TRUE );
7340
-		$name  = 'UQ_' . $this->esc( $type, TRUE ) . implode( '__', $properties );
7341
-		$t     = $this->getTable( $type );
7342
-		$t['indexes'][$name] = array( 'name' => $name );
7343
-		try {
7344
-			$this->putTable( $t );
7345
-		} catch( SQLException $e ) {
7346
-			return FALSE;
7347
-		}
7348
-		return TRUE;
7349
-	}
7350
-
7351
-	/**
7352
-	 * @see QueryWriter::sqlStateIn
7353
-	 */
7354
-	public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
7355
-	{
7356
-		$stateMap = array(
7357
-			'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
7358
-		);
7359
-		if ( $state == 'HY000'
7360
-		&& isset($extraDriverDetails[1])
7361
-		&& $extraDriverDetails[1] == 1
7362
-		&& ( in_array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, $list )
7363
-			|| in_array( QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, $list )
7364
-		)) {
7365
-			return TRUE;
7366
-		}
7367
-		return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
7368
-	}
7369
-
7370
-	/**
7371
-	 * @see QueryWriter::addIndex
7372
-	 */
7373
-	public function addIndex( $type, $name, $column )
7374
-	{
7375
-		$columns = $this->getColumns( $type );
7376
-		if ( !isset( $columns[$column] ) ) return FALSE;
7377
-
7378
-		$table  = $this->esc( $type );
7379
-		$name   = preg_replace( '/\W/', '', $name );
7380
-		$column = $this->esc( $column, TRUE );
7381
-
7382
-		try {
7383
-			$t = $this->getTable( $type );
7384
-			$t['indexes'][$name] = array( 'name' => $column );
7385
-			$this->putTable( $t );
7386
-			return TRUE;
7387
-		} catch( SQLException $exception ) {
7388
-			return FALSE;
7389
-		}
7390
-	}
7391
-
7392
-	/**
7393
-	 * @see QueryWriter::wipe
7394
-	 */
7395
-	public function wipe( $type )
7396
-	{
7397
-		$table = $this->esc( $type );
7398
-
7399
-		$this->adapter->exec( "DELETE FROM $table " );
7400
-	}
7401
-
7402
-	/**
7403
-	 * @see QueryWriter::addFK
7404
-	 */
7405
-	public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
7406
-	{
7407
-		return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDep );
7408
-	}
7409
-
7410
-	/**
7411
-	 * @see QueryWriter::wipeAll
7412
-	 */
7413
-	public function wipeAll()
7414
-	{
7415
-		if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
7416
-		$this->adapter->exec( 'PRAGMA foreign_keys = 0 ' );
7417
-
7418
-		foreach ( $this->getTables() as $t ) {
7419
-			try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
7420
-			try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
7421
-		}
7422
-
7423
-		$this->adapter->exec( 'PRAGMA foreign_keys = 1 ' );
7424
-	}
7305
+    }
7306
+
7307
+    /**
7308
+     * @see QueryWriter::createTable
7309
+     */
7310
+    public function createTable( $table )
7311
+    {
7312
+        $table = $this->esc( $table );
7313
+
7314
+        $sql   = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) ";
7315
+
7316
+        $this->adapter->exec( $sql );
7317
+    }
7318
+
7319
+    /**
7320
+     * @see QueryWriter::getColumns
7321
+     */
7322
+    public function getColumns( $table )
7323
+    {
7324
+        $table      = $this->esc( $table, TRUE );
7325
+
7326
+        $columnsRaw = $this->adapter->get( "PRAGMA table_info('$table')" );
7327
+
7328
+        $columns    = array();
7329
+        foreach ( $columnsRaw as $r ) $columns[$r['name']] = $r['type'];
7330
+
7331
+        return $columns;
7332
+    }
7333
+
7334
+    /**
7335
+     * @see QueryWriter::addUniqueIndex
7336
+     */
7337
+    public function addUniqueConstraint( $type, $properties )
7338
+    {
7339
+        $tableNoQ = $this->esc( $type, TRUE );
7340
+        $name  = 'UQ_' . $this->esc( $type, TRUE ) . implode( '__', $properties );
7341
+        $t     = $this->getTable( $type );
7342
+        $t['indexes'][$name] = array( 'name' => $name );
7343
+        try {
7344
+            $this->putTable( $t );
7345
+        } catch( SQLException $e ) {
7346
+            return FALSE;
7347
+        }
7348
+        return TRUE;
7349
+    }
7350
+
7351
+    /**
7352
+     * @see QueryWriter::sqlStateIn
7353
+     */
7354
+    public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
7355
+    {
7356
+        $stateMap = array(
7357
+            '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
7358
+        );
7359
+        if ( $state == 'HY000'
7360
+        && isset($extraDriverDetails[1])
7361
+        && $extraDriverDetails[1] == 1
7362
+        && ( in_array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, $list )
7363
+            || in_array( QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, $list )
7364
+        )) {
7365
+            return TRUE;
7366
+        }
7367
+        return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
7368
+    }
7369
+
7370
+    /**
7371
+     * @see QueryWriter::addIndex
7372
+     */
7373
+    public function addIndex( $type, $name, $column )
7374
+    {
7375
+        $columns = $this->getColumns( $type );
7376
+        if ( !isset( $columns[$column] ) ) return FALSE;
7377
+
7378
+        $table  = $this->esc( $type );
7379
+        $name   = preg_replace( '/\W/', '', $name );
7380
+        $column = $this->esc( $column, TRUE );
7381
+
7382
+        try {
7383
+            $t = $this->getTable( $type );
7384
+            $t['indexes'][$name] = array( 'name' => $column );
7385
+            $this->putTable( $t );
7386
+            return TRUE;
7387
+        } catch( SQLException $exception ) {
7388
+            return FALSE;
7389
+        }
7390
+    }
7391
+
7392
+    /**
7393
+     * @see QueryWriter::wipe
7394
+     */
7395
+    public function wipe( $type )
7396
+    {
7397
+        $table = $this->esc( $type );
7398
+
7399
+        $this->adapter->exec( "DELETE FROM $table " );
7400
+    }
7401
+
7402
+    /**
7403
+     * @see QueryWriter::addFK
7404
+     */
7405
+    public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
7406
+    {
7407
+        return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDep );
7408
+    }
7409
+
7410
+    /**
7411
+     * @see QueryWriter::wipeAll
7412
+     */
7413
+    public function wipeAll()
7414
+    {
7415
+        if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
7416
+        $this->adapter->exec( 'PRAGMA foreign_keys = 0 ' );
7417
+
7418
+        foreach ( $this->getTables() as $t ) {
7419
+            try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
7420
+            try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
7421
+        }
7422
+
7423
+        $this->adapter->exec( 'PRAGMA foreign_keys = 1 ' );
7424
+    }
7425 7425
 }
7426 7426
 }
7427 7427
 
@@ -7449,59 +7449,59 @@  discard block
 block discarded – undo
7449 7449
  */
7450 7450
 class PostgreSQL extends AQueryWriter implements QueryWriter
7451 7451
 {
7452
-	/**
7453
-	 * Data types
7454
-	 */
7455
-	const C_DATATYPE_INTEGER          = 0;
7456
-	const C_DATATYPE_DOUBLE           = 1;
7457
-	const C_DATATYPE_TEXT             = 3;
7458
-	const C_DATATYPE_SPECIAL_DATE     = 80;
7459
-	const C_DATATYPE_SPECIAL_DATETIME = 81;
7460
-	const C_DATATYPE_SPECIAL_TIME     = 82; //TIME (no zone) only manual
7461
-	const C_DATATYPE_SPECIAL_TIMEZ    = 83; //TIME (plus zone) only manual
7462
-	const C_DATATYPE_SPECIAL_POINT    = 90;
7463
-	const C_DATATYPE_SPECIAL_LSEG     = 91;
7464
-	const C_DATATYPE_SPECIAL_CIRCLE   = 92;
7465
-	const C_DATATYPE_SPECIAL_MONEY    = 93;
7466
-	const C_DATATYPE_SPECIAL_POLYGON  = 94;
7467
-	const C_DATATYPE_SPECIAL_MONEY2   = 95; //Numbers only money, i.e. fixed point numeric
7468
-	const C_DATATYPE_SPECIAL_JSON     = 96; //JSON support (only manual)
7469
-	const C_DATATYPE_SPECIFIED        = 99;
7470
-
7471
-	/**
7472
-	 * @var DBAdapter
7473
-	 */
7474
-	protected $adapter;
7475
-
7476
-	/**
7477
-	 * @var string
7478
-	 */
7479
-	protected $quoteCharacter = '"';
7480
-
7481
-	/**
7482
-	 * @var string
7483
-	 */
7484
-	protected $defaultValue = 'DEFAULT';
7485
-
7486
-	/**
7487
-	 * Returns the insert suffix SQL Snippet
7488
-	 *
7489
-	 * @param string $table table
7490
-	 *
7491
-	 * @return  string $sql SQL Snippet
7492
-	 */
7493
-	protected function getInsertSuffix( $table )
7494
-	{
7495
-		return 'RETURNING id ';
7496
-	}
7497
-
7498
-	/**
7499
-	 * @see AQueryWriter::getKeyMapForType
7500
-	 */
7501
-	protected function getKeyMapForType( $type )
7502
-	{
7503
-		$table = $this->esc( $type, TRUE );
7504
-		$keys = $this->adapter->get( '
7452
+    /**
7453
+     * Data types
7454
+     */
7455
+    const C_DATATYPE_INTEGER          = 0;
7456
+    const C_DATATYPE_DOUBLE           = 1;
7457
+    const C_DATATYPE_TEXT             = 3;
7458
+    const C_DATATYPE_SPECIAL_DATE     = 80;
7459
+    const C_DATATYPE_SPECIAL_DATETIME = 81;
7460
+    const C_DATATYPE_SPECIAL_TIME     = 82; //TIME (no zone) only manual
7461
+    const C_DATATYPE_SPECIAL_TIMEZ    = 83; //TIME (plus zone) only manual
7462
+    const C_DATATYPE_SPECIAL_POINT    = 90;
7463
+    const C_DATATYPE_SPECIAL_LSEG     = 91;
7464
+    const C_DATATYPE_SPECIAL_CIRCLE   = 92;
7465
+    const C_DATATYPE_SPECIAL_MONEY    = 93;
7466
+    const C_DATATYPE_SPECIAL_POLYGON  = 94;
7467
+    const C_DATATYPE_SPECIAL_MONEY2   = 95; //Numbers only money, i.e. fixed point numeric
7468
+    const C_DATATYPE_SPECIAL_JSON     = 96; //JSON support (only manual)
7469
+    const C_DATATYPE_SPECIFIED        = 99;
7470
+
7471
+    /**
7472
+     * @var DBAdapter
7473
+     */
7474
+    protected $adapter;
7475
+
7476
+    /**
7477
+     * @var string
7478
+     */
7479
+    protected $quoteCharacter = '"';
7480
+
7481
+    /**
7482
+     * @var string
7483
+     */
7484
+    protected $defaultValue = 'DEFAULT';
7485
+
7486
+    /**
7487
+     * Returns the insert suffix SQL Snippet
7488
+     *
7489
+     * @param string $table table
7490
+     *
7491
+     * @return  string $sql SQL Snippet
7492
+     */
7493
+    protected function getInsertSuffix( $table )
7494
+    {
7495
+        return 'RETURNING id ';
7496
+    }
7497
+
7498
+    /**
7499
+     * @see AQueryWriter::getKeyMapForType
7500
+     */
7501
+    protected function getKeyMapForType( $type )
7502
+    {
7503
+        $table = $this->esc( $type, TRUE );
7504
+        $keys = $this->adapter->get( '
7505 7505
 			SELECT
7506 7506
 			information_schema.key_column_usage.constraint_name AS "name",
7507 7507
 			information_schema.key_column_usage.column_name AS "from",
@@ -7509,339 +7509,339 @@  discard block
 block discarded – undo
7509 7509
 			information_schema.constraint_column_usage.column_name AS "to",
7510 7510
 			information_schema.referential_constraints.update_rule AS "on_update",
7511 7511
 			information_schema.referential_constraints.delete_rule AS "on_delete"
7512
-				FROM information_schema.key_column_usage
7513
-			INNER JOIN information_schema.constraint_table_usage
7514
-				ON (
7515
-					information_schema.key_column_usage.constraint_name = information_schema.constraint_table_usage.constraint_name
7516
-					AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_table_usage.constraint_schema
7517
-					AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_table_usage.constraint_catalog
7518
-				)
7519
-			INNER JOIN information_schema.constraint_column_usage
7520
-				ON (
7521
-					information_schema.key_column_usage.constraint_name = information_schema.constraint_column_usage.constraint_name
7522
-					AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_column_usage.constraint_schema
7523
-					AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_column_usage.constraint_catalog
7524
-				)
7525
-			INNER JOIN information_schema.referential_constraints
7526
-				ON (
7527
-					information_schema.key_column_usage.constraint_name = information_schema.referential_constraints.constraint_name
7528
-					AND information_schema.key_column_usage.constraint_schema = information_schema.referential_constraints.constraint_schema
7529
-					AND information_schema.key_column_usage.constraint_catalog = information_schema.referential_constraints.constraint_catalog
7530
-				)
7531
-			WHERE
7532
-				information_schema.key_column_usage.table_catalog = current_database()
7533
-				AND information_schema.key_column_usage.table_schema = ANY( current_schemas( FALSE ) )
7534
-				AND information_schema.key_column_usage.table_name = ?
7535
-		', array( $type ) );
7536
-		$keyInfoList = array();
7537
-		foreach ( $keys as $k ) {
7538
-			$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
7539
-			$keyInfoList[$label] = array(
7540
-				'name'          => $k['name'],
7541
-				'from'          => $k['from'],
7542
-				'table'         => $k['table'],
7543
-				'to'            => $k['to'],
7544
-				'on_update'     => $k['on_update'],
7545
-				'on_delete'     => $k['on_delete']
7546
-			);
7547
-		}
7548
-		return $keyInfoList;
7549
-	}
7550
-
7551
-	/**
7552
-	 * Constructor
7553
-	 * Most of the time, you do not need to use this constructor,
7554
-	 * since the facade takes care of constructing and wiring the
7555
-	 * RedBeanPHP core objects. However if you would like to
7556
-	 * assemble an OODB instance yourself, this is how it works:
7557
-	 *
7558
-	 * Usage:
7559
-	 *
7560
-	 * <code>
7561
-	 * $database = new RPDO( $dsn, $user, $pass );
7562
-	 * $adapter = new DBAdapter( $database );
7563
-	 * $writer = new PostgresWriter( $adapter );
7564
-	 * $oodb = new OODB( $writer, FALSE );
7565
-	 * $bean = $oodb->dispense( 'bean' );
7566
-	 * $bean->name = 'coffeeBean';
7567
-	 * $id = $oodb->store( $bean );
7568
-	 * $bean = $oodb->load( 'bean', $id );
7569
-	 * </code>
7570
-	 *
7571
-	 * The example above creates the 3 RedBeanPHP core objects:
7572
-	 * the Adapter, the Query Writer and the OODB instance and
7573
-	 * wires them together. The example also demonstrates some of
7574
-	 * the methods that can be used with OODB, as you see, they
7575
-	 * closely resemble their facade counterparts.
7576
-	 *
7577
-	 * The wiring process: create an RPDO instance using your database
7578
-	 * connection parameters. Create a database adapter from the RPDO
7579
-	 * object and pass that to the constructor of the writer. Next,
7580
-	 * create an OODB instance from the writer. Now you have an OODB
7581
-	 * object.
7582
-	 *
7583
-	 * @param Adapter $adapter Database Adapter
7584
-	 */
7585
-	public function __construct( Adapter $adapter )
7586
-	{
7587
-		$this->typeno_sqltype = array(
7588
-			self::C_DATATYPE_INTEGER          => ' integer ',
7589
-			self::C_DATATYPE_DOUBLE           => ' double precision ',
7590
-			self::C_DATATYPE_TEXT             => ' text ',
7591
-			self::C_DATATYPE_SPECIAL_DATE     => ' date ',
7592
-			self::C_DATATYPE_SPECIAL_TIME     => ' time ',
7593
-			self::C_DATATYPE_SPECIAL_TIMEZ    => ' time with time zone ',
7594
-			self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ',
7595
-			self::C_DATATYPE_SPECIAL_POINT    => ' point ',
7596
-			self::C_DATATYPE_SPECIAL_LSEG     => ' lseg ',
7597
-			self::C_DATATYPE_SPECIAL_CIRCLE   => ' circle ',
7598
-			self::C_DATATYPE_SPECIAL_MONEY    => ' money ',
7599
-			self::C_DATATYPE_SPECIAL_MONEY2   => ' numeric(10,2) ',
7600
-			self::C_DATATYPE_SPECIAL_POLYGON  => ' polygon ',
7601
-			self::C_DATATYPE_SPECIAL_JSON     => ' json ',
7602
-		);
7603
-
7604
-		$this->sqltype_typeno = array();
7605
-
7606
-		foreach ( $this->typeno_sqltype as $k => $v ) {
7607
-			$this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
7608
-		}
7609
-
7610
-		$this->adapter = $adapter;
7611
-	}
7612
-
7613
-	/**
7614
-	 * This method returns the datatype to be used for primary key IDS and
7615
-	 * foreign keys. Returns one if the data type constants.
7616
-	 *
7617
-	 * @return integer
7618
-	 */
7619
-	public function getTypeForID()
7620
-	{
7621
-		return self::C_DATATYPE_INTEGER;
7622
-	}
7623
-
7624
-	/**
7625
-	 * @see QueryWriter::getTables
7626
-	 */
7627
-	public function getTables()
7628
-	{
7629
-		return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' );
7630
-	}
7631
-
7632
-	/**
7633
-	 * @see QueryWriter::createTable
7634
-	 */
7635
-	public function createTable( $table )
7636
-	{
7637
-		$table = $this->esc( $table );
7638
-
7639
-		$this->adapter->exec( " CREATE TABLE $table (id SERIAL PRIMARY KEY); " );
7640
-	}
7641
-
7642
-	/**
7643
-	 * @see QueryWriter::getColumns
7644
-	 */
7645
-	public function getColumns( $table )
7646
-	{
7647
-		$table      = $this->esc( $table, TRUE );
7648
-
7649
-		$columnsRaw = $this->adapter->get( "SELECT column_name, data_type FROM information_schema.columns WHERE table_name='$table' AND table_schema = ANY( current_schemas( FALSE ) )" );
7650
-
7651
-		$columns = array();
7652
-		foreach ( $columnsRaw as $r ) {
7653
-			$columns[$r['column_name']] = $r['data_type'];
7654
-		}
7655
-
7656
-		return $columns;
7657
-	}
7658
-
7659
-	/**
7660
-	 * @see QueryWriter::scanType
7661
-	 */
7662
-	public function scanType( $value, $flagSpecial = FALSE )
7663
-	{
7664
-		$this->svalue = $value;
7665
-
7666
-		if ( $value === INF ) return self::C_DATATYPE_TEXT;
7667
-
7668
-		if ( $flagSpecial && $value ) {
7669
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
7670
-				return PostgreSQL::C_DATATYPE_SPECIAL_DATE;
7671
-			}
7672
-
7673
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) {
7674
-				return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME;
7675
-			}
7676
-
7677
-			if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) {
7678
-				return PostgreSQL::C_DATATYPE_SPECIAL_POINT;
7679
-			}
7680
-
7681
-			if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) {
7682
-				return PostgreSQL::C_DATATYPE_SPECIAL_LSEG;
7683
-			}
7684
-
7685
-			if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) {
7686
-				return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE;
7687
-			}
7688
-
7689
-			if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) {
7690
-				return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON;
7691
-			}
7692
-
7693
-			if ( preg_match( '/^\-?(\$|€|¥|£)[\d,\.]+$/', $value ) ) {
7694
-				return PostgreSQL::C_DATATYPE_SPECIAL_MONEY;
7695
-			}
7696
-
7697
-			if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) {
7698
-				return PostgreSQL::C_DATATYPE_SPECIAL_MONEY2;
7699
-			}
7700
-			if ( self::$flagUseJSONColumns && $this->isJSON( $value ) ) {
7701
-				return self::C_DATATYPE_SPECIAL_JSON;
7702
-			}
7703
-		}
7704
-
7705
-		if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
7706
-
7707
-		if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
7708
-
7709
-		if ( $value === FALSE || $value === TRUE || $value === NULL || ( is_numeric( $value )
7710
-				&& AQueryWriter::canBeTreatedAsInt( $value )
7711
-				&& $value < 2147483648
7712
-				&& $value > -2147483648 )
7713
-		) {
7714
-			return self::C_DATATYPE_INTEGER;
7715
-		} elseif ( is_numeric( $value ) ) {
7716
-			return self::C_DATATYPE_DOUBLE;
7717
-		} else {
7718
-			return self::C_DATATYPE_TEXT;
7719
-		}
7720
-	}
7721
-
7722
-	/**
7723
-	 * @see QueryWriter::code
7724
-	 */
7725
-	public function code( $typedescription, $includeSpecials = FALSE )
7726
-	{
7727
-		$r = ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99;
7728
-
7729
-		if ( $includeSpecials ) return $r;
7730
-
7731
-		if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
7732
-			return self::C_DATATYPE_SPECIFIED;
7733
-		}
7734
-
7735
-		return $r;
7736
-	}
7737
-
7738
-	/**
7739
-	 * @see QueryWriter::widenColumn
7740
-	 */
7741
-	public function widenColumn( $type, $column, $datatype )
7742
-	{
7743
-		$table   = $type;
7744
-		$type    = $datatype;
7745
-
7746
-		$table   = $this->esc( $table );
7747
-		$column  = $this->esc( $column );
7748
-
7749
-		$newtype = $this->typeno_sqltype[$type];
7750
-
7751
-		$this->adapter->exec( "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype " );
7752
-	}
7753
-
7754
-	/**
7755
-	 * @see QueryWriter::addUniqueIndex
7756
-	 */
7757
-	public function addUniqueConstraint( $type, $properties )
7758
-	{
7759
-		$tableNoQ = $this->esc( $type, TRUE );
7760
-		$columns = array();
7761
-		foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
7762
-		$table = $this->esc( $type );
7763
-		sort( $columns ); //else we get multiple indexes due to order-effects
7764
-		$name = "UQ_" . sha1( $table . implode( ',', $columns ) );
7765
-		$sql = "ALTER TABLE {$table}
7512
+				FROM information_schema.key_column_usage
7513
+			INNER JOIN information_schema.constraint_table_usage
7514
+				ON (
7515
+					information_schema.key_column_usage.constraint_name = information_schema.constraint_table_usage.constraint_name
7516
+					AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_table_usage.constraint_schema
7517
+					AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_table_usage.constraint_catalog
7518
+				)
7519
+			INNER JOIN information_schema.constraint_column_usage
7520
+				ON (
7521
+					information_schema.key_column_usage.constraint_name = information_schema.constraint_column_usage.constraint_name
7522
+					AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_column_usage.constraint_schema
7523
+					AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_column_usage.constraint_catalog
7524
+				)
7525
+			INNER JOIN information_schema.referential_constraints
7526
+				ON (
7527
+					information_schema.key_column_usage.constraint_name = information_schema.referential_constraints.constraint_name
7528
+					AND information_schema.key_column_usage.constraint_schema = information_schema.referential_constraints.constraint_schema
7529
+					AND information_schema.key_column_usage.constraint_catalog = information_schema.referential_constraints.constraint_catalog
7530
+				)
7531
+			WHERE
7532
+				information_schema.key_column_usage.table_catalog = current_database()
7533
+				AND information_schema.key_column_usage.table_schema = ANY( current_schemas( FALSE ) )
7534
+				AND information_schema.key_column_usage.table_name = ?
7535
+		', array( $type ) );
7536
+        $keyInfoList = array();
7537
+        foreach ( $keys as $k ) {
7538
+            $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
7539
+            $keyInfoList[$label] = array(
7540
+                'name'          => $k['name'],
7541
+                'from'          => $k['from'],
7542
+                'table'         => $k['table'],
7543
+                'to'            => $k['to'],
7544
+                'on_update'     => $k['on_update'],
7545
+                'on_delete'     => $k['on_delete']
7546
+            );
7547
+        }
7548
+        return $keyInfoList;
7549
+    }
7550
+
7551
+    /**
7552
+     * Constructor
7553
+     * Most of the time, you do not need to use this constructor,
7554
+     * since the facade takes care of constructing and wiring the
7555
+     * RedBeanPHP core objects. However if you would like to
7556
+     * assemble an OODB instance yourself, this is how it works:
7557
+     *
7558
+     * Usage:
7559
+     *
7560
+     * <code>
7561
+     * $database = new RPDO( $dsn, $user, $pass );
7562
+     * $adapter = new DBAdapter( $database );
7563
+     * $writer = new PostgresWriter( $adapter );
7564
+     * $oodb = new OODB( $writer, FALSE );
7565
+     * $bean = $oodb->dispense( 'bean' );
7566
+     * $bean->name = 'coffeeBean';
7567
+     * $id = $oodb->store( $bean );
7568
+     * $bean = $oodb->load( 'bean', $id );
7569
+     * </code>
7570
+     *
7571
+     * The example above creates the 3 RedBeanPHP core objects:
7572
+     * the Adapter, the Query Writer and the OODB instance and
7573
+     * wires them together. The example also demonstrates some of
7574
+     * the methods that can be used with OODB, as you see, they
7575
+     * closely resemble their facade counterparts.
7576
+     *
7577
+     * The wiring process: create an RPDO instance using your database
7578
+     * connection parameters. Create a database adapter from the RPDO
7579
+     * object and pass that to the constructor of the writer. Next,
7580
+     * create an OODB instance from the writer. Now you have an OODB
7581
+     * object.
7582
+     *
7583
+     * @param Adapter $adapter Database Adapter
7584
+     */
7585
+    public function __construct( Adapter $adapter )
7586
+    {
7587
+        $this->typeno_sqltype = array(
7588
+            self::C_DATATYPE_INTEGER          => ' integer ',
7589
+            self::C_DATATYPE_DOUBLE           => ' double precision ',
7590
+            self::C_DATATYPE_TEXT             => ' text ',
7591
+            self::C_DATATYPE_SPECIAL_DATE     => ' date ',
7592
+            self::C_DATATYPE_SPECIAL_TIME     => ' time ',
7593
+            self::C_DATATYPE_SPECIAL_TIMEZ    => ' time with time zone ',
7594
+            self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ',
7595
+            self::C_DATATYPE_SPECIAL_POINT    => ' point ',
7596
+            self::C_DATATYPE_SPECIAL_LSEG     => ' lseg ',
7597
+            self::C_DATATYPE_SPECIAL_CIRCLE   => ' circle ',
7598
+            self::C_DATATYPE_SPECIAL_MONEY    => ' money ',
7599
+            self::C_DATATYPE_SPECIAL_MONEY2   => ' numeric(10,2) ',
7600
+            self::C_DATATYPE_SPECIAL_POLYGON  => ' polygon ',
7601
+            self::C_DATATYPE_SPECIAL_JSON     => ' json ',
7602
+        );
7603
+
7604
+        $this->sqltype_typeno = array();
7605
+
7606
+        foreach ( $this->typeno_sqltype as $k => $v ) {
7607
+            $this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
7608
+        }
7609
+
7610
+        $this->adapter = $adapter;
7611
+    }
7612
+
7613
+    /**
7614
+     * This method returns the datatype to be used for primary key IDS and
7615
+     * foreign keys. Returns one if the data type constants.
7616
+     *
7617
+     * @return integer
7618
+     */
7619
+    public function getTypeForID()
7620
+    {
7621
+        return self::C_DATATYPE_INTEGER;
7622
+    }
7623
+
7624
+    /**
7625
+     * @see QueryWriter::getTables
7626
+     */
7627
+    public function getTables()
7628
+    {
7629
+        return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' );
7630
+    }
7631
+
7632
+    /**
7633
+     * @see QueryWriter::createTable
7634
+     */
7635
+    public function createTable( $table )
7636
+    {
7637
+        $table = $this->esc( $table );
7638
+
7639
+        $this->adapter->exec( " CREATE TABLE $table (id SERIAL PRIMARY KEY); " );
7640
+    }
7641
+
7642
+    /**
7643
+     * @see QueryWriter::getColumns
7644
+     */
7645
+    public function getColumns( $table )
7646
+    {
7647
+        $table      = $this->esc( $table, TRUE );
7648
+
7649
+        $columnsRaw = $this->adapter->get( "SELECT column_name, data_type FROM information_schema.columns WHERE table_name='$table' AND table_schema = ANY( current_schemas( FALSE ) )" );
7650
+
7651
+        $columns = array();
7652
+        foreach ( $columnsRaw as $r ) {
7653
+            $columns[$r['column_name']] = $r['data_type'];
7654
+        }
7655
+
7656
+        return $columns;
7657
+    }
7658
+
7659
+    /**
7660
+     * @see QueryWriter::scanType
7661
+     */
7662
+    public function scanType( $value, $flagSpecial = FALSE )
7663
+    {
7664
+        $this->svalue = $value;
7665
+
7666
+        if ( $value === INF ) return self::C_DATATYPE_TEXT;
7667
+
7668
+        if ( $flagSpecial && $value ) {
7669
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
7670
+                return PostgreSQL::C_DATATYPE_SPECIAL_DATE;
7671
+            }
7672
+
7673
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) {
7674
+                return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME;
7675
+            }
7676
+
7677
+            if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) {
7678
+                return PostgreSQL::C_DATATYPE_SPECIAL_POINT;
7679
+            }
7680
+
7681
+            if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) {
7682
+                return PostgreSQL::C_DATATYPE_SPECIAL_LSEG;
7683
+            }
7684
+
7685
+            if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) {
7686
+                return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE;
7687
+            }
7688
+
7689
+            if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) {
7690
+                return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON;
7691
+            }
7692
+
7693
+            if ( preg_match( '/^\-?(\$|€|¥|£)[\d,\.]+$/', $value ) ) {
7694
+                return PostgreSQL::C_DATATYPE_SPECIAL_MONEY;
7695
+            }
7696
+
7697
+            if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) {
7698
+                return PostgreSQL::C_DATATYPE_SPECIAL_MONEY2;
7699
+            }
7700
+            if ( self::$flagUseJSONColumns && $this->isJSON( $value ) ) {
7701
+                return self::C_DATATYPE_SPECIAL_JSON;
7702
+            }
7703
+        }
7704
+
7705
+        if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
7706
+
7707
+        if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
7708
+
7709
+        if ( $value === FALSE || $value === TRUE || $value === NULL || ( is_numeric( $value )
7710
+                && AQueryWriter::canBeTreatedAsInt( $value )
7711
+                && $value < 2147483648
7712
+                && $value > -2147483648 )
7713
+        ) {
7714
+            return self::C_DATATYPE_INTEGER;
7715
+        } elseif ( is_numeric( $value ) ) {
7716
+            return self::C_DATATYPE_DOUBLE;
7717
+        } else {
7718
+            return self::C_DATATYPE_TEXT;
7719
+        }
7720
+    }
7721
+
7722
+    /**
7723
+     * @see QueryWriter::code
7724
+     */
7725
+    public function code( $typedescription, $includeSpecials = FALSE )
7726
+    {
7727
+        $r = ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99;
7728
+
7729
+        if ( $includeSpecials ) return $r;
7730
+
7731
+        if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
7732
+            return self::C_DATATYPE_SPECIFIED;
7733
+        }
7734
+
7735
+        return $r;
7736
+    }
7737
+
7738
+    /**
7739
+     * @see QueryWriter::widenColumn
7740
+     */
7741
+    public function widenColumn( $type, $column, $datatype )
7742
+    {
7743
+        $table   = $type;
7744
+        $type    = $datatype;
7745
+
7746
+        $table   = $this->esc( $table );
7747
+        $column  = $this->esc( $column );
7748
+
7749
+        $newtype = $this->typeno_sqltype[$type];
7750
+
7751
+        $this->adapter->exec( "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype " );
7752
+    }
7753
+
7754
+    /**
7755
+     * @see QueryWriter::addUniqueIndex
7756
+     */
7757
+    public function addUniqueConstraint( $type, $properties )
7758
+    {
7759
+        $tableNoQ = $this->esc( $type, TRUE );
7760
+        $columns = array();
7761
+        foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
7762
+        $table = $this->esc( $type );
7763
+        sort( $columns ); //else we get multiple indexes due to order-effects
7764
+        $name = "UQ_" . sha1( $table . implode( ',', $columns ) );
7765
+        $sql = "ALTER TABLE {$table}
7766 7766
                 ADD CONSTRAINT $name UNIQUE (" . implode( ',', $columns ) . ")";
7767
-		try {
7768
-			$this->adapter->exec( $sql );
7769
-		} catch( SQLException $e ) {
7770
-			return FALSE;
7771
-		}
7772
-		return TRUE;
7773
-	}
7774
-
7775
-	/**
7776
-	 * @see QueryWriter::sqlStateIn
7777
-	 */
7778
-	public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
7779
-	{
7780
-		$stateMap = array(
7781
-			'42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
7782
-			'42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
7783
-			'23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,
7784
-			'55P03' => QueryWriter::C_SQLSTATE_LOCK_TIMEOUT
7785
-		);
7786
-		return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
7787
-	}
7788
-
7789
-	/**
7790
-	 * @see QueryWriter::addIndex
7791
-	 */
7792
-	public function addIndex( $type, $name, $property )
7793
-	{
7794
-		$table  = $this->esc( $type );
7795
-		$name   = preg_replace( '/\W/', '', $name );
7796
-		$column = $this->esc( $property );
7797
-
7798
-		try {
7799
-			$this->adapter->exec( "CREATE INDEX {$name} ON $table ({$column}) " );
7800
-			return TRUE;
7801
-		} catch ( SQLException $e ) {
7802
-			return FALSE;
7803
-		}
7804
-	}
7805
-
7806
-	/**
7807
-	 * @see QueryWriter::addFK
7808
-	 */
7809
-	public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
7810
-	{
7811
-		$table = $this->esc( $type );
7812
-		$targetTable = $this->esc( $targetType );
7813
-		$field = $this->esc( $property );
7814
-		$targetField = $this->esc( $targetProperty );
7815
-		$tableNoQ = $this->esc( $type, TRUE );
7816
-		$fieldNoQ = $this->esc( $property, TRUE );
7817
-		if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
7818
-		try{
7819
-			$delRule = ( $isDep ? 'CASCADE' : 'SET NULL' );
7820
-			$this->adapter->exec( "ALTER TABLE {$table}
7767
+        try {
7768
+            $this->adapter->exec( $sql );
7769
+        } catch( SQLException $e ) {
7770
+            return FALSE;
7771
+        }
7772
+        return TRUE;
7773
+    }
7774
+
7775
+    /**
7776
+     * @see QueryWriter::sqlStateIn
7777
+     */
7778
+    public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
7779
+    {
7780
+        $stateMap = array(
7781
+            '42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
7782
+            '42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
7783
+            '23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,
7784
+            '55P03' => QueryWriter::C_SQLSTATE_LOCK_TIMEOUT
7785
+        );
7786
+        return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
7787
+    }
7788
+
7789
+    /**
7790
+     * @see QueryWriter::addIndex
7791
+     */
7792
+    public function addIndex( $type, $name, $property )
7793
+    {
7794
+        $table  = $this->esc( $type );
7795
+        $name   = preg_replace( '/\W/', '', $name );
7796
+        $column = $this->esc( $property );
7797
+
7798
+        try {
7799
+            $this->adapter->exec( "CREATE INDEX {$name} ON $table ({$column}) " );
7800
+            return TRUE;
7801
+        } catch ( SQLException $e ) {
7802
+            return FALSE;
7803
+        }
7804
+    }
7805
+
7806
+    /**
7807
+     * @see QueryWriter::addFK
7808
+     */
7809
+    public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
7810
+    {
7811
+        $table = $this->esc( $type );
7812
+        $targetTable = $this->esc( $targetType );
7813
+        $field = $this->esc( $property );
7814
+        $targetField = $this->esc( $targetProperty );
7815
+        $tableNoQ = $this->esc( $type, TRUE );
7816
+        $fieldNoQ = $this->esc( $property, TRUE );
7817
+        if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
7818
+        try{
7819
+            $delRule = ( $isDep ? 'CASCADE' : 'SET NULL' );
7820
+            $this->adapter->exec( "ALTER TABLE {$table}
7821 7821
 				ADD FOREIGN KEY ( {$field} ) REFERENCES  {$targetTable}
7822 7822
 				({$targetField}) ON DELETE {$delRule} ON UPDATE {$delRule} DEFERRABLE ;" );
7823
-			return TRUE;
7824
-		} catch ( SQLException $e ) {
7825
-			return FALSE;
7826
-		}
7827
-	}
7828
-
7829
-	/**
7830
-	 * @see QueryWriter::wipeAll
7831
-	 */
7832
-	public function wipeAll()
7833
-	{
7834
-		if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
7835
-		$this->adapter->exec( 'SET CONSTRAINTS ALL DEFERRED' );
7836
-
7837
-		foreach ( $this->getTables() as $t ) {
7838
-			$t = $this->esc( $t );
7839
-			//Some plugins (PostGIS have unremovable tables/views), avoid exceptions.
7840
-			try { $this->adapter->exec( "DROP TABLE IF EXISTS $t CASCADE " ); }catch( \Exception $e ) {}
7841
-		}
7842
-
7843
-		$this->adapter->exec( 'SET CONSTRAINTS ALL IMMEDIATE' );
7844
-	}
7823
+            return TRUE;
7824
+        } catch ( SQLException $e ) {
7825
+            return FALSE;
7826
+        }
7827
+    }
7828
+
7829
+    /**
7830
+     * @see QueryWriter::wipeAll
7831
+     */
7832
+    public function wipeAll()
7833
+    {
7834
+        if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
7835
+        $this->adapter->exec( 'SET CONSTRAINTS ALL DEFERRED' );
7836
+
7837
+        foreach ( $this->getTables() as $t ) {
7838
+            $t = $this->esc( $t );
7839
+            //Some plugins (PostGIS have unremovable tables/views), avoid exceptions.
7840
+            try { $this->adapter->exec( "DROP TABLE IF EXISTS $t CASCADE " ); }catch( \Exception $e ) {}
7841
+        }
7842
+
7843
+        $this->adapter->exec( 'SET CONSTRAINTS ALL IMMEDIATE' );
7844
+    }
7845 7845
 }
7846 7846
 }
7847 7847
 
@@ -7868,361 +7868,361 @@  discard block
 block discarded – undo
7868 7868
  */
7869 7869
 class CUBRID extends AQueryWriter implements QueryWriter
7870 7870
 {
7871
-	/**
7872
-	 * Data types
7873
-	 */
7874
-	const C_DATATYPE_INTEGER          = 0;
7875
-	const C_DATATYPE_DOUBLE           = 1;
7876
-	const C_DATATYPE_STRING           = 2;
7877
-	const C_DATATYPE_SPECIAL_DATE     = 80;
7878
-	const C_DATATYPE_SPECIAL_DATETIME = 81;
7879
-	const C_DATATYPE_SPECIFIED        = 99;
7880
-
7881
-	/**
7882
-	 * @var DBAdapter
7883
-	 */
7884
-	protected $adapter;
7885
-
7886
-	/**
7887
-	 * @var string
7888
-	 */
7889
-	protected $quoteCharacter = '`';
7890
-
7891
-	/**
7892
-	 * This method adds a foreign key from type and field to
7893
-	 * target type and target field.
7894
-	 * The foreign key is created without an action. On delete/update
7895
-	 * no action will be triggered. The FK is only used to allow database
7896
-	 * tools to generate pretty diagrams and to make it easy to add actions
7897
-	 * later on.
7898
-	 * This methods accepts a type and infers the corresponding table name.
7899
-	 *
7900
-	 * @param  string  $type           type that will have a foreign key field
7901
-	 * @param  string  $targetType     points to this type
7902
-	 * @param  string  $property       field that contains the foreign key value
7903
-	 * @param  string  $targetProperty field where the fk points to
7904
-	 * @param  boolean $isDep          is dependent
7905
-	 *
7906
-	 * @return bool
7907
-	 */
7908
-	protected function buildFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
7909
-	{
7910
-		$table           = $this->esc( $type );
7911
-		$tableNoQ        = $this->esc( $type, TRUE );
7912
-		$targetTable     = $this->esc( $targetType );
7913
-		$targetTableNoQ  = $this->esc( $targetType, TRUE );
7914
-		$column          = $this->esc( $property );
7915
-		$columnNoQ       = $this->esc( $property, TRUE );
7916
-		$targetColumn    = $this->esc( $targetProperty );
7917
-		if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $columnNoQ ) ) ) return FALSE;
7918
-		$needsToDropFK   = FALSE;
7919
-		$casc = ( $isDep ? 'CASCADE' : 'SET NULL' );
7920
-		$sql  = "ALTER TABLE $table ADD CONSTRAINT FOREIGN KEY($column) REFERENCES $targetTable($targetColumn) ON DELETE $casc ";
7921
-		try {
7922
-			$this->adapter->exec( $sql );
7923
-		} catch( SQLException $e ) {
7924
-			return FALSE;
7925
-		}
7926
-		return TRUE;
7927
-	}
7928
-
7929
-	/**
7930
-	 * @see AQueryWriter::getKeyMapForType
7931
-	 */
7932
-	protected function getKeyMapForType( $type  )
7933
-	{
7934
-		$sqlCode = $this->adapter->get("SHOW CREATE TABLE `{$type}`");
7935
-		if (!isset($sqlCode[0])) return array();
7936
-		$matches = array();
7937
-		preg_match_all( '/CONSTRAINT\s+\[([\w_]+)\]\s+FOREIGN\s+KEY\s+\(\[([\w_]+)\]\)\s+REFERENCES\s+\[([\w_]+)\](\s+ON\s+DELETE\s+(CASCADE|SET\sNULL|RESTRICT|NO\sACTION)\s+ON\s+UPDATE\s+(SET\sNULL|RESTRICT|NO\sACTION))?/', $sqlCode[0]['CREATE TABLE'], $matches );
7938
-		$list = array();
7939
-		if (!isset($matches[0])) return $list;
7940
-		$max = count($matches[0]);
7941
-		for($i = 0; $i < $max; $i++) {
7942
-			$label = $this->makeFKLabel( $matches[2][$i], $matches[3][$i], 'id' );
7943
-			$list[ $label ] = array(
7944
-				'name' => $matches[1][$i],
7945
-				'from' => $matches[2][$i],
7946
-				'table' => $matches[3][$i],
7947
-				'to' => 'id',
7948
-				'on_update' => $matches[6][$i],
7949
-				'on_delete' => $matches[5][$i]
7950
-			);
7951
-		}
7952
-		return $list;
7953
-	}
7954
-
7955
-	/**
7956
-	 * Constructor
7957
-	 * Most of the time, you do not need to use this constructor,
7958
-	 * since the facade takes care of constructing and wiring the
7959
-	 * RedBeanPHP core objects. However if you would like to
7960
-	 * assemble an OODB instance yourself, this is how it works:
7961
-	 *
7962
-	 * Usage:
7963
-	 *
7964
-	 * <code>
7965
-	 * $database = new RPDO( $dsn, $user, $pass );
7966
-	 * $adapter = new DBAdapter( $database );
7967
-	 * $writer = new PostgresWriter( $adapter );
7968
-	 * $oodb = new OODB( $writer, FALSE );
7969
-	 * $bean = $oodb->dispense( 'bean' );
7970
-	 * $bean->name = 'coffeeBean';
7971
-	 * $id = $oodb->store( $bean );
7972
-	 * $bean = $oodb->load( 'bean', $id );
7973
-	 * </code>
7974
-	 *
7975
-	 * The example above creates the 3 RedBeanPHP core objects:
7976
-	 * the Adapter, the Query Writer and the OODB instance and
7977
-	 * wires them together. The example also demonstrates some of
7978
-	 * the methods that can be used with OODB, as you see, they
7979
-	 * closely resemble their facade counterparts.
7980
-	 *
7981
-	 * The wiring process: create an RPDO instance using your database
7982
-	 * connection parameters. Create a database adapter from the RPDO
7983
-	 * object and pass that to the constructor of the writer. Next,
7984
-	 * create an OODB instance from the writer. Now you have an OODB
7985
-	 * object.
7986
-	 *
7987
-	 * @param Adapter $adapter Database Adapter
7988
-	 */
7989
-	public function __construct( Adapter $adapter )
7990
-	{
7991
-		$this->typeno_sqltype = array(
7992
-			CUBRID::C_DATATYPE_INTEGER          => ' INTEGER ',
7993
-			CUBRID::C_DATATYPE_DOUBLE           => ' DOUBLE ',
7994
-			CUBRID::C_DATATYPE_STRING           => ' STRING ',
7995
-			CUBRID::C_DATATYPE_SPECIAL_DATE     => ' DATE ',
7996
-			CUBRID::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
7997
-		);
7998
-
7999
-		$this->sqltype_typeno = array();
8000
-
8001
-		foreach ( $this->typeno_sqltype as $k => $v ) {
8002
-			$this->sqltype_typeno[trim( ( $v ) )] = $k;
8003
-		}
8004
-
8005
-		$this->sqltype_typeno['STRING(1073741823)'] = self::C_DATATYPE_STRING;
8006
-
8007
-		$this->adapter = $adapter;
8008
-	}
8009
-
8010
-	/**
8011
-	 * This method returns the datatype to be used for primary key IDS and
8012
-	 * foreign keys. Returns one if the data type constants.
8013
-	 *
8014
-	 * @return integer
8015
-	 */
8016
-	public function getTypeForID()
8017
-	{
8018
-		return self::C_DATATYPE_INTEGER;
8019
-	}
8020
-
8021
-	/**
8022
-	 * @see QueryWriter::getTables
8023
-	 */
8024
-	public function getTables()
8025
-	{
8026
-		$rows = $this->adapter->getCol( "SELECT class_name FROM db_class WHERE is_system_class = 'NO';" );
8027
-
8028
-		return $rows;
8029
-	}
8030
-
8031
-	/**
8032
-	 * @see QueryWriter::createTable
8033
-	 */
8034
-	public function createTable( $table )
8035
-	{
8036
-		$sql  = 'CREATE TABLE '
8037
-			. $this->esc( $table )
8038
-			. ' ("id" integer AUTO_INCREMENT, CONSTRAINT "pk_'
8039
-			. $this->esc( $table, TRUE )
8040
-			. '_id" PRIMARY KEY("id"))';
8041
-
8042
-		$this->adapter->exec( $sql );
8043
-	}
8044
-
8045
-	/**
8046
-	 * @see QueryWriter::getColumns
8047
-	 */
8048
-	public function getColumns( $table )
8049
-	{
8050
-		$table = $this->esc( $table );
8051
-
8052
-		$columnsRaw = $this->adapter->get( "SHOW COLUMNS FROM $table" );
8053
-
8054
-		$columns = array();
8055
-		foreach ( $columnsRaw as $r ) {
8056
-			$columns[$r['Field']] = $r['Type'];
8057
-		}
8058
-
8059
-		return $columns;
8060
-	}
8061
-
8062
-	/**
8063
-	 * @see QueryWriter::scanType
8064
-	 */
8065
-	public function scanType( $value, $flagSpecial = FALSE )
8066
-	{
8067
-		$this->svalue = $value;
8068
-
8069
-		if ( is_null( $value ) ) {
8070
-			return self::C_DATATYPE_INTEGER;
8071
-		}
8072
-
8073
-		if ( $flagSpecial ) {
8074
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
8075
-				return self::C_DATATYPE_SPECIAL_DATE;
8076
-			}
8077
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
8078
-				return self::C_DATATYPE_SPECIAL_DATETIME;
8079
-			}
8080
-		}
8081
-
8082
-		$value = strval( $value );
8083
-
8084
-		if ( !$this->startsWithZeros( $value ) ) {
8085
-			if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= -2147483647 && $value <= 2147483647 ) {
8086
-				return self::C_DATATYPE_INTEGER;
8087
-			}
8088
-			if ( is_numeric( $value ) ) {
8089
-				return self::C_DATATYPE_DOUBLE;
8090
-			}
8091
-		}
8092
-
8093
-		return self::C_DATATYPE_STRING;
8094
-	}
8095
-
8096
-	/**
8097
-	 * @see QueryWriter::code
8098
-	 */
8099
-	public function code( $typedescription, $includeSpecials = FALSE )
8100
-	{
8101
-		$r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : self::C_DATATYPE_SPECIFIED );
8102
-
8103
-		if ( $includeSpecials ) {
8104
-			return $r;
8105
-		}
8106
-
8107
-		if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
8108
-			return self::C_DATATYPE_SPECIFIED;
8109
-		}
8110
-
8111
-		return $r;
8112
-	}
8113
-
8114
-	/**
8115
-	 * @see QueryWriter::addColumn
8116
-	 */
8117
-	public function addColumn( $type, $column, $field )
8118
-	{
8119
-		$table  = $type;
8120
-		$type   = $field;
8121
-
8122
-		$table  = $this->esc( $table );
8123
-		$column = $this->esc( $column );
8124
-
8125
-		$type   = array_key_exists( $type, $this->typeno_sqltype ) ? $this->typeno_sqltype[$type] : '';
8126
-
8127
-		$this->adapter->exec( "ALTER TABLE $table ADD COLUMN $column $type " );
8128
-	}
8129
-
8130
-	/**
8131
-	 * @see QueryWriter::addUniqueIndex
8132
-	 */
8133
-	public function addUniqueConstraint( $type, $properties )
8134
-	{
8135
-		$tableNoQ = $this->esc( $type, TRUE );
8136
-		$columns = array();
8137
-		foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
8138
-		$table = $this->esc( $type );
8139
-		sort( $columns ); // else we get multiple indexes due to order-effects
8140
-		$name = 'UQ_' . sha1( implode( ',', $columns ) );
8141
-		$sql = "ALTER TABLE $table ADD CONSTRAINT UNIQUE $name (" . implode( ',', $columns ) . ")";
8142
-		try {
8143
-			$this->adapter->exec( $sql );
8144
-		} catch( SQLException $e ) {
8145
-			return FALSE;
8146
-		}
8147
-		return TRUE;
8148
-	}
8149
-
8150
-	/**
8151
-	 * @see QueryWriter::sqlStateIn
8152
-	 */
8153
-	public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
8154
-	{
8155
-		return ( $state == 'HY000' ) ? ( count( array_diff( array(
8156
-				QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,
8157
-				QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
8158
-				QueryWriter::C_SQLSTATE_NO_SUCH_TABLE
8159
-			), $list ) ) !== 3 ) : FALSE;
8160
-	}
8161
-
8162
-	/**
8163
-	 * @see QueryWriter::addIndex
8164
-	 */
8165
-	public function addIndex( $type, $name, $column )
8166
-	{
8167
-		try {
8168
-			$table  = $this->esc( $type );
8169
-			$name   = preg_replace( '/\W/', '', $name );
8170
-			$column = $this->esc( $column );
8171
-			$this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
8172
-			return TRUE;
8173
-		} catch ( SQLException $e ) {
8174
-			return FALSE;
8175
-		}
8176
-	}
8177
-
8178
-	/**
8179
-	 * @see QueryWriter::addFK
8180
-	 */
8181
-	public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
8182
-	{
8183
-		return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDependent );
8184
-	}
8185
-
8186
-	/**
8187
-	 * @see QueryWriter::wipeAll
8188
-	 */
8189
-	public function wipeAll()
8190
-	{
8191
-		if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
8192
-		foreach ( $this->getTables() as $t ) {
8193
-			foreach ( $this->getKeyMapForType( $t ) as $k ) {
8194
-				$this->adapter->exec( "ALTER TABLE \"$t\" DROP FOREIGN KEY \"{$k['name']}\"" );
8195
-			}
8196
-		}
8197
-		foreach ( $this->getTables() as $t ) {
8198
-			$this->adapter->exec( "DROP TABLE \"$t\"" );
8199
-		}
8200
-	}
8201
-
8202
-	/**
8203
-	 * @see QueryWriter::esc
8204
-	 */
8205
-	public function esc( $dbStructure, $noQuotes = FALSE )
8206
-	{
8207
-		return parent::esc( strtolower( $dbStructure ), $noQuotes );
8208
-	}
8209
-
8210
-	/**
8211
-	 * @see QueryWriter::inferFetchType
8212
-	 */
8213
-	public function inferFetchType( $type, $property )
8214
-	{
8215
-		$table = $this->esc( $type, TRUE );
8216
-		$field = $this->esc( $property, TRUE ) . '_id';
8217
-		$keys = $this->getKeyMapForType( $table );
8218
-
8219
-		foreach( $keys as $key ) {
8220
-			if (
8221
-				$key['from'] === $field
8222
-			) return $key['table'];
8223
-		}
8224
-		return NULL;
8225
-	}
7871
+    /**
7872
+     * Data types
7873
+     */
7874
+    const C_DATATYPE_INTEGER          = 0;
7875
+    const C_DATATYPE_DOUBLE           = 1;
7876
+    const C_DATATYPE_STRING           = 2;
7877
+    const C_DATATYPE_SPECIAL_DATE     = 80;
7878
+    const C_DATATYPE_SPECIAL_DATETIME = 81;
7879
+    const C_DATATYPE_SPECIFIED        = 99;
7880
+
7881
+    /**
7882
+     * @var DBAdapter
7883
+     */
7884
+    protected $adapter;
7885
+
7886
+    /**
7887
+     * @var string
7888
+     */
7889
+    protected $quoteCharacter = '`';
7890
+
7891
+    /**
7892
+     * This method adds a foreign key from type and field to
7893
+     * target type and target field.
7894
+     * The foreign key is created without an action. On delete/update
7895
+     * no action will be triggered. The FK is only used to allow database
7896
+     * tools to generate pretty diagrams and to make it easy to add actions
7897
+     * later on.
7898
+     * This methods accepts a type and infers the corresponding table name.
7899
+     *
7900
+     * @param  string  $type           type that will have a foreign key field
7901
+     * @param  string  $targetType     points to this type
7902
+     * @param  string  $property       field that contains the foreign key value
7903
+     * @param  string  $targetProperty field where the fk points to
7904
+     * @param  boolean $isDep          is dependent
7905
+     *
7906
+     * @return bool
7907
+     */
7908
+    protected function buildFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
7909
+    {
7910
+        $table           = $this->esc( $type );
7911
+        $tableNoQ        = $this->esc( $type, TRUE );
7912
+        $targetTable     = $this->esc( $targetType );
7913
+        $targetTableNoQ  = $this->esc( $targetType, TRUE );
7914
+        $column          = $this->esc( $property );
7915
+        $columnNoQ       = $this->esc( $property, TRUE );
7916
+        $targetColumn    = $this->esc( $targetProperty );
7917
+        if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $columnNoQ ) ) ) return FALSE;
7918
+        $needsToDropFK   = FALSE;
7919
+        $casc = ( $isDep ? 'CASCADE' : 'SET NULL' );
7920
+        $sql  = "ALTER TABLE $table ADD CONSTRAINT FOREIGN KEY($column) REFERENCES $targetTable($targetColumn) ON DELETE $casc ";
7921
+        try {
7922
+            $this->adapter->exec( $sql );
7923
+        } catch( SQLException $e ) {
7924
+            return FALSE;
7925
+        }
7926
+        return TRUE;
7927
+    }
7928
+
7929
+    /**
7930
+     * @see AQueryWriter::getKeyMapForType
7931
+     */
7932
+    protected function getKeyMapForType( $type  )
7933
+    {
7934
+        $sqlCode = $this->adapter->get("SHOW CREATE TABLE `{$type}`");
7935
+        if (!isset($sqlCode[0])) return array();
7936
+        $matches = array();
7937
+        preg_match_all( '/CONSTRAINT\s+\[([\w_]+)\]\s+FOREIGN\s+KEY\s+\(\[([\w_]+)\]\)\s+REFERENCES\s+\[([\w_]+)\](\s+ON\s+DELETE\s+(CASCADE|SET\sNULL|RESTRICT|NO\sACTION)\s+ON\s+UPDATE\s+(SET\sNULL|RESTRICT|NO\sACTION))?/', $sqlCode[0]['CREATE TABLE'], $matches );
7938
+        $list = array();
7939
+        if (!isset($matches[0])) return $list;
7940
+        $max = count($matches[0]);
7941
+        for($i = 0; $i < $max; $i++) {
7942
+            $label = $this->makeFKLabel( $matches[2][$i], $matches[3][$i], 'id' );
7943
+            $list[ $label ] = array(
7944
+                'name' => $matches[1][$i],
7945
+                'from' => $matches[2][$i],
7946
+                'table' => $matches[3][$i],
7947
+                'to' => 'id',
7948
+                'on_update' => $matches[6][$i],
7949
+                'on_delete' => $matches[5][$i]
7950
+            );
7951
+        }
7952
+        return $list;
7953
+    }
7954
+
7955
+    /**
7956
+     * Constructor
7957
+     * Most of the time, you do not need to use this constructor,
7958
+     * since the facade takes care of constructing and wiring the
7959
+     * RedBeanPHP core objects. However if you would like to
7960
+     * assemble an OODB instance yourself, this is how it works:
7961
+     *
7962
+     * Usage:
7963
+     *
7964
+     * <code>
7965
+     * $database = new RPDO( $dsn, $user, $pass );
7966
+     * $adapter = new DBAdapter( $database );
7967
+     * $writer = new PostgresWriter( $adapter );
7968
+     * $oodb = new OODB( $writer, FALSE );
7969
+     * $bean = $oodb->dispense( 'bean' );
7970
+     * $bean->name = 'coffeeBean';
7971
+     * $id = $oodb->store( $bean );
7972
+     * $bean = $oodb->load( 'bean', $id );
7973
+     * </code>
7974
+     *
7975
+     * The example above creates the 3 RedBeanPHP core objects:
7976
+     * the Adapter, the Query Writer and the OODB instance and
7977
+     * wires them together. The example also demonstrates some of
7978
+     * the methods that can be used with OODB, as you see, they
7979
+     * closely resemble their facade counterparts.
7980
+     *
7981
+     * The wiring process: create an RPDO instance using your database
7982
+     * connection parameters. Create a database adapter from the RPDO
7983
+     * object and pass that to the constructor of the writer. Next,
7984
+     * create an OODB instance from the writer. Now you have an OODB
7985
+     * object.
7986
+     *
7987
+     * @param Adapter $adapter Database Adapter
7988
+     */
7989
+    public function __construct( Adapter $adapter )
7990
+    {
7991
+        $this->typeno_sqltype = array(
7992
+            CUBRID::C_DATATYPE_INTEGER          => ' INTEGER ',
7993
+            CUBRID::C_DATATYPE_DOUBLE           => ' DOUBLE ',
7994
+            CUBRID::C_DATATYPE_STRING           => ' STRING ',
7995
+            CUBRID::C_DATATYPE_SPECIAL_DATE     => ' DATE ',
7996
+            CUBRID::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
7997
+        );
7998
+
7999
+        $this->sqltype_typeno = array();
8000
+
8001
+        foreach ( $this->typeno_sqltype as $k => $v ) {
8002
+            $this->sqltype_typeno[trim( ( $v ) )] = $k;
8003
+        }
8004
+
8005
+        $this->sqltype_typeno['STRING(1073741823)'] = self::C_DATATYPE_STRING;
8006
+
8007
+        $this->adapter = $adapter;
8008
+    }
8009
+
8010
+    /**
8011
+     * This method returns the datatype to be used for primary key IDS and
8012
+     * foreign keys. Returns one if the data type constants.
8013
+     *
8014
+     * @return integer
8015
+     */
8016
+    public function getTypeForID()
8017
+    {
8018
+        return self::C_DATATYPE_INTEGER;
8019
+    }
8020
+
8021
+    /**
8022
+     * @see QueryWriter::getTables
8023
+     */
8024
+    public function getTables()
8025
+    {
8026
+        $rows = $this->adapter->getCol( "SELECT class_name FROM db_class WHERE is_system_class = 'NO';" );
8027
+
8028
+        return $rows;
8029
+    }
8030
+
8031
+    /**
8032
+     * @see QueryWriter::createTable
8033
+     */
8034
+    public function createTable( $table )
8035
+    {
8036
+        $sql  = 'CREATE TABLE '
8037
+            . $this->esc( $table )
8038
+            . ' ("id" integer AUTO_INCREMENT, CONSTRAINT "pk_'
8039
+            . $this->esc( $table, TRUE )
8040
+            . '_id" PRIMARY KEY("id"))';
8041
+
8042
+        $this->adapter->exec( $sql );
8043
+    }
8044
+
8045
+    /**
8046
+     * @see QueryWriter::getColumns
8047
+     */
8048
+    public function getColumns( $table )
8049
+    {
8050
+        $table = $this->esc( $table );
8051
+
8052
+        $columnsRaw = $this->adapter->get( "SHOW COLUMNS FROM $table" );
8053
+
8054
+        $columns = array();
8055
+        foreach ( $columnsRaw as $r ) {
8056
+            $columns[$r['Field']] = $r['Type'];
8057
+        }
8058
+
8059
+        return $columns;
8060
+    }
8061
+
8062
+    /**
8063
+     * @see QueryWriter::scanType
8064
+     */
8065
+    public function scanType( $value, $flagSpecial = FALSE )
8066
+    {
8067
+        $this->svalue = $value;
8068
+
8069
+        if ( is_null( $value ) ) {
8070
+            return self::C_DATATYPE_INTEGER;
8071
+        }
8072
+
8073
+        if ( $flagSpecial ) {
8074
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
8075
+                return self::C_DATATYPE_SPECIAL_DATE;
8076
+            }
8077
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
8078
+                return self::C_DATATYPE_SPECIAL_DATETIME;
8079
+            }
8080
+        }
8081
+
8082
+        $value = strval( $value );
8083
+
8084
+        if ( !$this->startsWithZeros( $value ) ) {
8085
+            if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= -2147483647 && $value <= 2147483647 ) {
8086
+                return self::C_DATATYPE_INTEGER;
8087
+            }
8088
+            if ( is_numeric( $value ) ) {
8089
+                return self::C_DATATYPE_DOUBLE;
8090
+            }
8091
+        }
8092
+
8093
+        return self::C_DATATYPE_STRING;
8094
+    }
8095
+
8096
+    /**
8097
+     * @see QueryWriter::code
8098
+     */
8099
+    public function code( $typedescription, $includeSpecials = FALSE )
8100
+    {
8101
+        $r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : self::C_DATATYPE_SPECIFIED );
8102
+
8103
+        if ( $includeSpecials ) {
8104
+            return $r;
8105
+        }
8106
+
8107
+        if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
8108
+            return self::C_DATATYPE_SPECIFIED;
8109
+        }
8110
+
8111
+        return $r;
8112
+    }
8113
+
8114
+    /**
8115
+     * @see QueryWriter::addColumn
8116
+     */
8117
+    public function addColumn( $type, $column, $field )
8118
+    {
8119
+        $table  = $type;
8120
+        $type   = $field;
8121
+
8122
+        $table  = $this->esc( $table );
8123
+        $column = $this->esc( $column );
8124
+
8125
+        $type   = array_key_exists( $type, $this->typeno_sqltype ) ? $this->typeno_sqltype[$type] : '';
8126
+
8127
+        $this->adapter->exec( "ALTER TABLE $table ADD COLUMN $column $type " );
8128
+    }
8129
+
8130
+    /**
8131
+     * @see QueryWriter::addUniqueIndex
8132
+     */
8133
+    public function addUniqueConstraint( $type, $properties )
8134
+    {
8135
+        $tableNoQ = $this->esc( $type, TRUE );
8136
+        $columns = array();
8137
+        foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
8138
+        $table = $this->esc( $type );
8139
+        sort( $columns ); // else we get multiple indexes due to order-effects
8140
+        $name = 'UQ_' . sha1( implode( ',', $columns ) );
8141
+        $sql = "ALTER TABLE $table ADD CONSTRAINT UNIQUE $name (" . implode( ',', $columns ) . ")";
8142
+        try {
8143
+            $this->adapter->exec( $sql );
8144
+        } catch( SQLException $e ) {
8145
+            return FALSE;
8146
+        }
8147
+        return TRUE;
8148
+    }
8149
+
8150
+    /**
8151
+     * @see QueryWriter::sqlStateIn
8152
+     */
8153
+    public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
8154
+    {
8155
+        return ( $state == 'HY000' ) ? ( count( array_diff( array(
8156
+                QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,
8157
+                QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
8158
+                QueryWriter::C_SQLSTATE_NO_SUCH_TABLE
8159
+            ), $list ) ) !== 3 ) : FALSE;
8160
+    }
8161
+
8162
+    /**
8163
+     * @see QueryWriter::addIndex
8164
+     */
8165
+    public function addIndex( $type, $name, $column )
8166
+    {
8167
+        try {
8168
+            $table  = $this->esc( $type );
8169
+            $name   = preg_replace( '/\W/', '', $name );
8170
+            $column = $this->esc( $column );
8171
+            $this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
8172
+            return TRUE;
8173
+        } catch ( SQLException $e ) {
8174
+            return FALSE;
8175
+        }
8176
+    }
8177
+
8178
+    /**
8179
+     * @see QueryWriter::addFK
8180
+     */
8181
+    public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
8182
+    {
8183
+        return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDependent );
8184
+    }
8185
+
8186
+    /**
8187
+     * @see QueryWriter::wipeAll
8188
+     */
8189
+    public function wipeAll()
8190
+    {
8191
+        if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
8192
+        foreach ( $this->getTables() as $t ) {
8193
+            foreach ( $this->getKeyMapForType( $t ) as $k ) {
8194
+                $this->adapter->exec( "ALTER TABLE \"$t\" DROP FOREIGN KEY \"{$k['name']}\"" );
8195
+            }
8196
+        }
8197
+        foreach ( $this->getTables() as $t ) {
8198
+            $this->adapter->exec( "DROP TABLE \"$t\"" );
8199
+        }
8200
+    }
8201
+
8202
+    /**
8203
+     * @see QueryWriter::esc
8204
+     */
8205
+    public function esc( $dbStructure, $noQuotes = FALSE )
8206
+    {
8207
+        return parent::esc( strtolower( $dbStructure ), $noQuotes );
8208
+    }
8209
+
8210
+    /**
8211
+     * @see QueryWriter::inferFetchType
8212
+     */
8213
+    public function inferFetchType( $type, $property )
8214
+    {
8215
+        $table = $this->esc( $type, TRUE );
8216
+        $field = $this->esc( $property, TRUE ) . '_id';
8217
+        $keys = $this->getKeyMapForType( $table );
8218
+
8219
+        foreach( $keys as $key ) {
8220
+            if (
8221
+                $key['from'] === $field
8222
+            ) return $key['table'];
8223
+        }
8224
+        return NULL;
8225
+    }
8226 8226
 }
8227 8227
 }
8228 8228
 
@@ -8265,65 +8265,65 @@  discard block
 block discarded – undo
8265 8265
  */
8266 8266
 class SQL extends RedException
8267 8267
 {
8268
-	/**
8269
-	 * @var string
8270
-	 */
8271
-	private $sqlState;
8272
-
8273
-	/**
8274
-	 * @var array
8275
-	 */
8276
-	private $driverDetails = array();
8277
-
8278
-	/**
8279
-	 * @return array
8280
-	 */
8281
-	public function getDriverDetails()
8282
-	{
8283
-		return $this->driverDetails;
8284
-	}
8285
-
8286
-	/**
8287
-	 * @param array $driverDetails
8288
-	 */
8289
-	public function setDriverDetails($driverDetails)
8290
-	{
8291
-		$this->driverDetails = $driverDetails;
8292
-	}
8293
-
8294
-	/**
8295
-	 * Returns an ANSI-92 compliant SQL state.
8296
-	 *
8297
-	 * @return string
8298
-	 */
8299
-	public function getSQLState()
8300
-	{
8301
-		return $this->sqlState;
8302
-	}
8303
-
8304
-	/**
8305
-	 * Returns the raw SQL STATE, possibly compliant with
8306
-	 * ANSI SQL error codes - but this depends on database driver.
8307
-	 *
8308
-	 * @param string $sqlState SQL state error code
8309
-	 *
8310
-	 * @return void
8311
-	 */
8312
-	public function setSQLState( $sqlState )
8313
-	{
8314
-		$this->sqlState = $sqlState;
8315
-	}
8316
-
8317
-	/**
8318
-	 * To String prints both code and SQL state.
8319
-	 *
8320
-	 * @return string
8321
-	 */
8322
-	public function __toString()
8323
-	{
8324
-		return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n".
8325
-				'trace: ' . $this->getTraceAsString();
8326
-	}
8268
+    /**
8269
+     * @var string
8270
+     */
8271
+    private $sqlState;
8272
+
8273
+    /**
8274
+     * @var array
8275
+     */
8276
+    private $driverDetails = array();
8277
+
8278
+    /**
8279
+     * @return array
8280
+     */
8281
+    public function getDriverDetails()
8282
+    {
8283
+        return $this->driverDetails;
8284
+    }
8285
+
8286
+    /**
8287
+     * @param array $driverDetails
8288
+     */
8289
+    public function setDriverDetails($driverDetails)
8290
+    {
8291
+        $this->driverDetails = $driverDetails;
8292
+    }
8293
+
8294
+    /**
8295
+     * Returns an ANSI-92 compliant SQL state.
8296
+     *
8297
+     * @return string
8298
+     */
8299
+    public function getSQLState()
8300
+    {
8301
+        return $this->sqlState;
8302
+    }
8303
+
8304
+    /**
8305
+     * Returns the raw SQL STATE, possibly compliant with
8306
+     * ANSI SQL error codes - but this depends on database driver.
8307
+     *
8308
+     * @param string $sqlState SQL state error code
8309
+     *
8310
+     * @return void
8311
+     */
8312
+    public function setSQLState( $sqlState )
8313
+    {
8314
+        $this->sqlState = $sqlState;
8315
+    }
8316
+
8317
+    /**
8318
+     * To String prints both code and SQL state.
8319
+     *
8320
+     * @return string
8321
+     */
8322
+    public function __toString()
8323
+    {
8324
+        return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n".
8325
+                'trace: ' . $this->getTraceAsString();
8326
+    }
8327 8327
 }
8328 8328
 }
8329 8329
 
@@ -8358,659 +8358,659 @@  discard block
 block discarded – undo
8358 8358
  */
8359 8359
 abstract class Repository
8360 8360
 {
8361
-	/**
8362
-	 * @var array
8363
-	 */
8364
-	protected $stash = NULL;
8361
+    /**
8362
+     * @var array
8363
+     */
8364
+    protected $stash = NULL;
8365 8365
 
8366
-	/*
8366
+    /*
8367 8367
 	 * @var integer
8368 8368
 	 */
8369
-	protected $nesting = 0;
8370
-
8371
-	/**
8372
-	 * @var DBAdapter
8373
-	 */
8374
-	protected $writer;
8375
-
8376
-	/**
8377
-	 * @var boolean
8378
-	 */
8379
-	protected $partialBeans = FALSE;
8380
-
8381
-	/**
8382
-	 * Toggles 'partial bean mode'. If this mode has been
8383
-	 * selected the repository will only update the fields of a bean that
8384
-	 * have been changed rather than the entire bean.
8385
-	 * Pass the value TRUE to select 'partial mode' for all beans.
8386
-	 * Pass the value FALSE to disable 'partial mode'.
8387
-	 * Pass an array of bean types if you wish to use partial mode only
8388
-	 * for some types.
8389
-	 * This method will return the previous value.
8390
-	 *
8391
-	 * @param boolean|array $yesNoBeans List of type names or 'all'
8392
-	 *
8393
-	 * @return mixed
8394
-	 */
8395
-	public function usePartialBeans( $yesNoBeans )
8396
-	{
8397
-		$oldValue = $this->partialBeans;
8398
-		$this->partialBeans = $yesNoBeans;
8399
-		return $oldValue;
8400
-	}
8401
-
8402
-	/**
8403
-	 * Fully processes a bean and updates the associated records in the database.
8404
-	 * First the bean properties will be grouped as 'embedded' bean,
8405
-	 * addition, deleted 'trash can' or residue. Next, the different groups
8406
-	 * of beans will be processed accordingly and the reference bean (i.e.
8407
-	 * the one that was passed to the method as an argument) will be stored.
8408
-	 * Each type of list (own/shared) has 3 bean processors: 
8409
-	 *
8410
-	 * - trashCanProcessor : removes the bean or breaks its association with the current bean
8411
-	 * - additionProcessor : associates the bean with the current one
8412
-	 * - residueProcessor  : manages beans in lists that 'remain' but may need to be updated
8413
-	 * 
8414
-	 * This method first groups the beans and then calls the
8415
-	 * internal processing methods.
8416
-	 *
8417
-	 * @param OODBBean $bean bean to process
8418
-	 *
8419
-	 * @return void
8420
-	 */
8421
-	protected function storeBeanWithLists( OODBBean $bean )
8422
-	{
8423
-		$sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups
8424
-		foreach ( $bean as $property => $value ) {
8425
-			$value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value;
8426
-			if ( $value instanceof OODBBean ) {
8427
-				$this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value );
8428
-				$bean->setMeta("sys.typeof.{$property}", $value->getMeta('type'));
8429
-			} elseif ( is_array( $value ) ) {
8430
-				foreach($value as &$item) {
8431
-					$item = ( $item instanceof SimpleModel ) ? $item->unbox() : $item;
8432
-				}
8433
-				$originals = $bean->moveMeta( 'sys.shadow.' . $property, array() );
8434
-				if ( strpos( $property, 'own' ) === 0 ) {
8435
-					list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue );
8436
-					$listName = lcfirst( substr( $property, 3 ) );
8437
-					if ($bean->moveMeta( 'sys.exclusive-'.  $listName ) ) {
8438
-						OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE );
8439
-						OODBBean::setMetaAll( $ownAdditions, 'sys.buildcommand.fkdependson', $bean->getMeta( 'type' ) );
8440
-					}
8441
-					unset( $bean->$property );
8442
-				} elseif ( strpos( $property, 'shared' ) === 0 ) {
8443
-					list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue );
8444
-					unset( $bean->$property );
8445
-				}
8446
-			}
8447
-		}
8448
-		$this->storeBean( $bean );
8449
-		$this->processTrashcan( $bean, $ownTrashcan );
8450
-		$this->processAdditions( $bean, $ownAdditions );
8451
-		$this->processResidue( $ownresidue );
8452
-		$this->processSharedTrashcan( $bean, $sharedTrashcan );
8453
-		$this->processSharedAdditions( $bean, $sharedAdditions );
8454
-		$this->processSharedResidue( $bean, $sharedresidue );
8455
-	}
8456
-
8457
-	/**
8458
-	 * Process groups. Internal function. Processes different kind of groups for
8459
-	 * storage function. Given a list of original beans and a list of current beans,
8460
-	 * this function calculates which beans remain in the list (residue), which
8461
-	 * have been deleted (are in the trashcan) and which beans have been added
8462
-	 * (additions).
8463
-	 *
8464
-	 * @param  array $originals originals
8465
-	 * @param  array $current   the current beans
8466
-	 * @param  array $additions beans that have been added
8467
-	 * @param  array $trashcan  beans that have been deleted
8468
-	 * @param  array $residue   beans that have been left untouched
8469
-	 *
8470
-	 * @return array
8471
-	 */
8472
-	protected function processGroups( $originals, $current, $additions, $trashcan, $residue )
8473
-	{
8474
-		return array(
8475
-			array_merge( $additions, array_diff( $current, $originals ) ),
8476
-			array_merge( $trashcan, array_diff( $originals, $current ) ),
8477
-			array_merge( $residue, array_intersect( $current, $originals ) )
8478
-		);
8479
-	}
8480
-
8481
-	/**
8482
-	 * Processes a list of beans from a bean.
8483
-	 * A bean may contain lists. This
8484
-	 * method handles shared addition lists; i.e.
8485
-	 * the $bean->sharedObject properties.
8486
-	 * Shared beans will be associated with eachother using the
8487
-	 * Association Manager.
8488
-	 *
8489
-	 * @param OODBBean $bean            the bean
8490
-	 * @param array    $sharedAdditions list with shared additions
8491
-	 *
8492
-	 * @return void
8493
-	 */
8494
-	protected function processSharedAdditions( $bean, $sharedAdditions )
8495
-	{
8496
-		foreach ( $sharedAdditions as $addition ) {
8497
-			if ( $addition instanceof OODBBean ) {
8498
-				$this->oodb->getAssociationManager()->associate( $addition, $bean );
8499
-			} else {
8500
-				throw new RedException( 'Array may only contain OODBBeans' );
8501
-			}
8502
-		}
8503
-	}
8504
-
8505
-	/**
8506
-	 * Processes a list of beans from a bean.
8507
-	 * A bean may contain lists. This
8508
-	 * method handles own lists; i.e.
8509
-	 * the $bean->ownObject properties.
8510
-	 * A residue is a bean in an own-list that stays
8511
-	 * where it is. This method checks if there have been any
8512
-	 * modification to this bean, in that case
8513
-	 * the bean is stored once again, otherwise the bean will be left untouched.
8514
-	 *
8515
-	 * @param array    $ownresidue list to process
8516
-	 *
8517
-	 * @return void
8518
-	 */
8519
-	protected function processResidue( $ownresidue )
8520
-	{
8521
-		foreach ( $ownresidue as $residue ) {
8522
-			if ( $residue->getMeta( 'tainted' ) ) {
8523
-				$this->store( $residue );
8524
-			}
8525
-		}
8526
-	}
8527
-
8528
-	/**
8529
-	 * Processes a list of beans from a bean. A bean may contain lists. This
8530
-	 * method handles own lists; i.e. the $bean->ownObject properties.
8531
-	 * A trash can bean is a bean in an own-list that has been removed
8532
-	 * (when checked with the shadow). This method
8533
-	 * checks if the bean is also in the dependency list. If it is the bean will be removed.
8534
-	 * If not, the connection between the bean and the owner bean will be broken by
8535
-	 * setting the ID to NULL.
8536
-	 *
8537
-	 * @param OODBBean $bean bean   to process
8538
-	 * @param array    $ownTrashcan list to process
8539
-	 *
8540
-	 * @return void
8541
-	 */
8542
-	protected function processTrashcan( $bean, $ownTrashcan )
8543
-	{
8544
-		foreach ( $ownTrashcan as $trash ) {
8545
-
8546
-			$myFieldLink = $bean->getMeta( 'type' ) . '_id';
8547
-			$alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) );
8548
-			if ( $alias ) $myFieldLink = $alias . '_id';
8549
-
8550
-			if ( $trash->getMeta( 'sys.garbage' ) === TRUE ) {
8551
-				$this->trash( $trash );
8552
-			} else {
8553
-				$trash->$myFieldLink = NULL;
8554
-				$this->store( $trash );
8555
-			}
8556
-		}
8557
-	}
8558
-
8559
-	/**
8560
-	 * Unassociates the list items in the trashcan.
8561
-	 * This bean processor processes the beans in the shared trash can.
8562
-	 * This group of beans has been deleted from a shared list.
8563
-	 * The affected beans will no longer be associated with the bean
8564
-	 * that contains the shared list.
8565
-	 *
8566
-	 * @param OODBBean $bean           bean to process
8567
-	 * @param array    $sharedTrashcan list to process
8568
-	 *
8569
-	 * @return void
8570
-	 */
8571
-	protected function processSharedTrashcan( $bean, $sharedTrashcan )
8572
-	{
8573
-		foreach ( $sharedTrashcan as $trash ) {
8574
-			$this->oodb->getAssociationManager()->unassociate( $trash, $bean );
8575
-		}
8576
-	}
8577
-
8578
-	/**
8579
-	 * Stores all the beans in the residue group.
8580
-	 * This bean processor processes the beans in the shared residue
8581
-	 * group. This group of beans 'remains' in the list but might need
8582
-	 * to be updated or synced. The affected beans will be stored
8583
-	 * to perform the required database queries.
8584
-	 *
8585
-	 * @param OODBBean $bean          bean to process
8586
-	 * @param array    $sharedresidue list to process
8587
-	 *
8588
-	 * @return void
8589
-	 */
8590
-	protected function processSharedResidue( $bean, $sharedresidue )
8591
-	{
8592
-		foreach ( $sharedresidue as $residue ) {
8593
-			$this->store( $residue );
8594
-		}
8595
-	}
8596
-
8597
-	/**
8598
-	 * Determines whether the bean has 'loaded lists' or
8599
-	 * 'loaded embedded beans' that need to be processed
8600
-	 * by the store() method.
8601
-	 *
8602
-	 * @param OODBBean $bean bean to be examined
8603
-	 *
8604
-	 * @return boolean
8605
-	 */
8606
-	protected function hasListsOrObjects( OODBBean $bean )
8607
-	{
8608
-		$processLists = FALSE;
8609
-		foreach ( $bean as $value ) {
8610
-			if ( is_array( $value ) || is_object( $value ) ) {
8611
-				$processLists = TRUE;
8612
-				break;
8613
-			}
8614
-		}
8615
-
8616
-		return $processLists;
8617
-	}
8618
-
8619
-	/**
8620
-	 * Converts an embedded bean to an ID, removes the bean property and
8621
-	 * stores the bean in the embedded beans array. The id will be
8622
-	 * assigned to the link field property, i.e. 'bean_id'.
8623
-	 *
8624
-	 * @param array    $embeddedBeans destination array for embedded bean
8625
-	 * @param OODBBean $bean          target bean to process
8626
-	 * @param string   $property      property that contains the embedded bean
8627
-	 * @param OODBBean $value         embedded bean itself
8628
-	 *
8629
-	 * @return void
8630
-	 */
8631
-	protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value )
8632
-	{
8633
-		$linkField = $property . '_id';
8634
-		if ( !$value->id || $value->getMeta( 'tainted' ) ) {
8635
-			$this->store( $value );
8636
-		}
8637
-		$id = $value->id;
8638
-		if ($bean->$linkField != $id) $bean->$linkField = $id;
8639
-		$bean->setMeta( 'cast.' . $linkField, 'id' );
8640
-		$embeddedBeans[$linkField] = $value;
8641
-		unset( $bean->$property );
8642
-	}
8643
-
8644
-	/**
8645
-	 * Constructor, requires a query writer and OODB.
8646
-	 * Creates a new instance of the bean respository class.
8647
-	 *
8648
-	 * @param OODB        $oodb   instance of object database
8649
-	 * @param QueryWriter $writer the Query Writer to use for this repository
8650
-	 *
8651
-	 * @return void
8652
-	 */
8653
-	public function __construct( OODB $oodb, QueryWriter $writer )
8654
-	{
8655
-		$this->writer = $writer;
8656
-		$this->oodb = $oodb;
8657
-	}
8658
-
8659
-	/**
8660
-	 * Checks whether a OODBBean bean is valid.
8661
-	 * If the type is not valid or the ID is not valid it will
8662
-	 * throw an exception: Security. To be valid a bean
8663
-	 * must abide to the following rules:
8664
-	 *
8665
-	 * - It must have an primary key id property named: id
8666
-	 * - It must have a type
8667
-	 * - The type must conform to the RedBeanPHP naming policy
8668
-	 * - All properties must be valid
8669
-	 * - All values must be valid
8670
-	 *
8671
-	 * @param OODBBean $bean the bean that needs to be checked
8672
-	 *
8673
-	 * @return void
8674
-	 */
8675
-	public function check( OODBBean $bean )
8676
-	{
8677
-		//Is all meta information present?
8678
-		if ( !isset( $bean->id ) ) {
8679
-			throw new RedException( 'Bean has incomplete Meta Information id ' );
8680
-		}
8681
-		if ( !( $bean->getMeta( 'type' ) ) ) {
8682
-			throw new RedException( 'Bean has incomplete Meta Information II' );
8683
-		}
8684
-		//Pattern of allowed characters
8685
-		$pattern = '/[^a-z0-9_]/i';
8686
-		//Does the type contain invalid characters?
8687
-		if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) {
8688
-			throw new RedException( 'Bean Type is invalid' );
8689
-		}
8690
-		//Are the properties and values valid?
8691
-		foreach ( $bean as $prop => $value ) {
8692
-			if (
8693
-				is_array( $value )
8694
-				|| ( is_object( $value ) )
8695
-			) {
8696
-				throw new RedException( "Invalid Bean value: property $prop" );
8697
-			} else if (
8698
-				strlen( $prop ) < 1
8699
-				|| preg_match( $pattern, $prop )
8700
-			) {
8701
-				throw new RedException( "Invalid Bean property: property $prop" );
8702
-			}
8703
-		}
8704
-	}
8705
-
8706
-	/**
8707
-	 * Searches the database for a bean that matches conditions $conditions and sql $addSQL
8708
-	 * and returns an array containing all the beans that have been found.
8709
-	 *
8710
-	 * Conditions need to take form:
8711
-	 *
8712
-	 * <code>
8713
-	 * array(
8714
-	 *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
8715
-	 *    'PROPERTY' => array( POSSIBLE VALUES... )
8716
-	 * );
8717
-	 * </code>
8718
-	 *
8719
-	 * All conditions are glued together using the AND-operator, while all value lists
8720
-	 * are glued using IN-operators thus acting as OR-conditions.
8721
-	 *
8722
-	 * Note that you can use property names; the columns will be extracted using the
8723
-	 * appropriate bean formatter.
8724
-	 *
8725
-	 * @param string $type       type of beans you are looking for
8726
-	 * @param array  $conditions list of conditions
8727
-	 * @param string $sql        SQL to be used in query
8728
-	 * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
8729
-	 *
8730
-	 * @return array
8731
-	 */
8732
-	public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
8733
-	{
8734
-		//for backward compatibility, allow mismatch arguments:
8735
-		if ( is_array( $sql ) ) {
8736
-			if ( isset( $sql[1] ) ) {
8737
-				$bindings = $sql[1];
8738
-			}
8739
-			$sql = $sql[0];
8740
-		}
8741
-		try {
8742
-			$beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) );
8743
-
8744
-			return $beans;
8745
-		} catch ( SQLException $exception ) {
8746
-			$this->handleException( $exception );
8747
-		}
8748
-
8749
-		return array();
8750
-	}
8751
-
8752
-	/**
8753
-	 * Finds a BeanCollection.
8754
-	 * Given a type, an SQL snippet and optionally some parameter bindings
8755
-	 * this methods returns a BeanCollection for your query.
8756
-	 *
8757
-	 * The BeanCollection represents a collection of beans and
8758
-	 * makes it possible to use database cursors. The BeanCollection
8759
-	 * has a method next() to obtain the first, next and last bean
8760
-	 * in the collection. The BeanCollection does not implement the array
8761
-	 * interface nor does it try to act like an array because it cannot go
8762
-	 * backward or rewind itself.
8763
-	 *
8764
-	 * @param string $type     type of beans you are looking for
8765
-	 * @param string $sql      SQL to be used in query
8766
-	 * @param array  $bindings whether you prefer to use a WHERE clause or not (TRUE = not)
8767
-	 *
8768
-	 * @return BeanCollection
8769
-	 */
8770
-	public function findCollection( $type, $sql, $bindings = array() )
8771
-	{
8772
-		try {
8773
-			$cursor = $this->writer->queryRecordWithCursor( $type, $sql, $bindings );
8774
-			return new BeanCollection( $type, $this, $cursor );
8775
-		} catch ( SQLException $exception ) {
8776
-			$this->handleException( $exception );
8777
-		}
8778
-		return new BeanCollection( $type, $this, new NullCursor );
8779
-	}
8780
-
8781
-	/**
8782
-	 * Stores a bean in the database. This method takes a
8783
-	 * OODBBean Bean Object $bean and stores it
8784
-	 * in the database. If the database schema is not compatible
8785
-	 * with this bean and RedBean runs in fluid mode the schema
8786
-	 * will be altered to store the bean correctly.
8787
-	 * If the database schema is not compatible with this bean and
8788
-	 * RedBean runs in frozen mode it will throw an exception.
8789
-	 * This function returns the primary key ID of the inserted
8790
-	 * bean.
8791
-	 *
8792
-	 * The return value is an integer if possible. If it is not possible to
8793
-	 * represent the value as an integer a string will be returned. We use
8794
-	 * explicit casts instead of functions to preserve performance
8795
-	 * (0.13 vs 0.28 for 10000 iterations on Core i3).
8796
-	 *
8797
-	 * @param OODBBean|SimpleModel $bean bean to store
8798
-	 *
8799
-	 * @return integer|string
8800
-	 */
8801
-	public function store( $bean )
8802
-	{
8803
-		$processLists = $this->hasListsOrObjects( $bean );
8804
-		if ( !$processLists && !$bean->getMeta( 'tainted' ) ) {
8805
-			return $bean->getID(); //bail out!
8806
-		}
8807
-		$this->oodb->signal( 'update', $bean );
8808
-		$processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model!
8809
-		if ( $processLists ) {
8810
-			$this->storeBeanWithLists( $bean );
8811
-		} else {
8812
-			$this->storeBean( $bean );
8813
-		}
8814
-		$this->oodb->signal( 'after_update', $bean );
8815
-
8816
-		return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id;
8817
-	}
8818
-
8819
-	/**
8820
-	 * Returns an array of beans. Pass a type and a series of ids and
8821
-	 * this method will bring you the corresponding beans.
8822
-	 *
8823
-	 * important note: Because this method loads beans using the load()
8824
-	 * function (but faster) it will return empty beans with ID 0 for
8825
-	 * every bean that could not be located. The resulting beans will have the
8826
-	 * passed IDs as their keys.
8827
-	 *
8828
-	 * @param string $type type of beans
8829
-	 * @param array  $ids  ids to load
8830
-	 *
8831
-	 * @return array
8832
-	 */
8833
-	public function batch( $type, $ids )
8834
-	{
8835
-		if ( !$ids ) {
8836
-			return array();
8837
-		}
8838
-		$collection = array();
8839
-		try {
8840
-			$rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) );
8841
-		} catch ( SQLException $e ) {
8842
-			$this->handleException( $e );
8843
-			$rows = FALSE;
8844
-		}
8845
-		$this->stash[$this->nesting] = array();
8846
-		if ( !$rows ) {
8847
-			return array();
8848
-		}
8849
-		foreach ( $rows as $row ) {
8850
-			$this->stash[$this->nesting][$row['id']] = $row;
8851
-		}
8852
-		foreach ( $ids as $id ) {
8853
-			$collection[$id] = $this->load( $type, $id );
8854
-		}
8855
-		$this->stash[$this->nesting] = NULL;
8856
-
8857
-		return $collection;
8858
-	}
8859
-
8860
-	/**
8861
-	 * This is a convenience method; it converts database rows
8862
-	 * (arrays) into beans. Given a type and a set of rows this method
8863
-	 * will return an array of beans of the specified type loaded with
8864
-	 * the data fields provided by the result set from the database.
8865
-	 *
8866
-	 * New in 4.3.2: meta mask. The meta mask is a special mask to send
8867
-	 * data from raw result rows to the meta store of the bean. This is
8868
-	 * useful for bundling additional information with custom queries.
8869
-	 * Values of every column whos name starts with $mask will be
8870
-	 * transferred to the meta section of the bean under key 'data.bundle'.
8871
-	 *
8872
-	 * @param string $type type of beans you would like to have
8873
-	 * @param array  $rows rows from the database result
8874
-	 * @param string $mask meta mask to apply (optional)
8875
-	 *
8876
-	 * @return array
8877
-	 */
8878
-	public function convertToBeans( $type, $rows, $mask = NULL )
8879
-	{
8880
-		$masklen = 0;
8881
-		if ( $mask !== NULL ) $masklen = mb_strlen( $mask );
8882
-
8883
-		$collection                  = array();
8884
-		$this->stash[$this->nesting] = array();
8885
-		foreach ( $rows as $row ) {
8886
-			$meta = array();
8887
-			if ( !is_null( $mask ) ) {
8888
-				foreach( $row as $key => $value ) {
8889
-					if ( strpos( $key, $mask ) === 0 ) {
8890
-						unset( $row[$key] );
8891
-						$meta[$key] = $value;
8892
-					}
8893
-				}
8894
-			}
8895
-
8896
-			$id                               = $row['id'];
8897
-			$this->stash[$this->nesting][$id] = $row;
8898
-			$collection[$id]                  = $this->load( $type, $id );
8899
-
8900
-			if ( $mask !== NULL ) {
8901
-				$collection[$id]->setMeta( 'data.bundle', $meta );
8902
-			}
8903
-		}
8904
-		$this->stash[$this->nesting] = NULL;
8905
-
8906
-		return $collection;
8907
-	}
8908
-
8909
-	/**
8910
-	 * Counts the number of beans of type $type.
8911
-	 * This method accepts a second argument to modify the count-query.
8912
-	 * A third argument can be used to provide bindings for the SQL snippet.
8913
-	 *
8914
-	 * @param string $type     type of bean we are looking for
8915
-	 * @param string $addSQL   additional SQL snippet
8916
-	 * @param array  $bindings parameters to bind to SQL
8917
-	 *
8918
-	 * @return integer
8919
-	 */
8920
-	public function count( $type, $addSQL = '', $bindings = array() )
8921
-	{
8922
-		$type = AQueryWriter::camelsSnake( $type );
8923
-		if ( count( explode( '_', $type ) ) > 2 ) {
8924
-			throw new RedException( 'Invalid type for count.' );
8925
-		}
8926
-
8927
-		try {
8928
-			return (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings );
8929
-		} catch ( SQLException $exception ) {
8930
-			if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array(
8931
-				 QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
8932
-				 QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ),
8933
-				 $exception->getDriverDetails() ) ) {
8934
-				throw $exception;
8935
-			}
8936
-		}
8937
-
8938
-		return 0;
8939
-	}
8940
-
8941
-	/**
8942
-	 * Removes a bean from the database.
8943
-	 * This function will remove the specified OODBBean
8944
-	 * Bean Object from the database.
8945
-	 *
8946
-	 * @param OODBBean|SimpleModel $bean bean you want to remove from database
8947
-	 *
8948
-	 * @return void
8949
-	 */
8950
-	public function trash( $bean )
8951
-	{
8952
-		$this->oodb->signal( 'delete', $bean );
8953
-		foreach ( $bean as $property => $value ) {
8954
-			if ( $value instanceof OODBBean ) {
8955
-				unset( $bean->$property );
8956
-			}
8957
-			if ( is_array( $value ) ) {
8958
-				if ( strpos( $property, 'own' ) === 0 ) {
8959
-					unset( $bean->$property );
8960
-				} elseif ( strpos( $property, 'shared' ) === 0 ) {
8961
-					unset( $bean->$property );
8962
-				}
8963
-			}
8964
-		}
8965
-		try {
8966
-			$this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL );
8967
-		} catch ( SQLException $exception ) {
8968
-			$this->handleException( $exception );
8969
-		}
8970
-		$bean->id = 0;
8971
-		$this->oodb->signal( 'after_delete', $bean );
8972
-	}
8973
-
8974
-	/**
8975
-	 * Checks whether the specified table already exists in the database.
8976
-	 * Not part of the Object Database interface!
8977
-	 *
8978
-	 * @deprecated Use AQueryWriter::typeExists() instead.
8979
-	 *
8980
-	 * @param string $table table name
8981
-	 *
8982
-	 * @return boolean
8983
-	 */
8984
-	public function tableExists( $table )
8985
-	{
8986
-		return $this->writer->tableExists( $table );
8987
-	}
8988
-
8989
-	/**
8990
-	 * Trash all beans of a given type.
8991
-	 * Wipes an entire type of bean. After this operation there
8992
-	 * will be no beans left of the specified type.
8993
-	 * This method will ignore exceptions caused by database
8994
-	 * tables that do not exist.
8995
-	 *
8996
-	 * @param string $type type of bean you wish to delete all instances of
8997
-	 *
8998
-	 * @return boolean
8999
-	 */
9000
-	public function wipe( $type )
9001
-	{
9002
-		try {
9003
-			$this->writer->wipe( $type );
9004
-
9005
-			return TRUE;
9006
-		} catch ( SQLException $exception ) {
9007
-			if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ), $exception->getDriverDetails() ) ) {
9008
-				throw $exception;
9009
-			}
9010
-
9011
-			return FALSE;
9012
-		}
9013
-	}
8369
+    protected $nesting = 0;
8370
+
8371
+    /**
8372
+     * @var DBAdapter
8373
+     */
8374
+    protected $writer;
8375
+
8376
+    /**
8377
+     * @var boolean
8378
+     */
8379
+    protected $partialBeans = FALSE;
8380
+
8381
+    /**
8382
+     * Toggles 'partial bean mode'. If this mode has been
8383
+     * selected the repository will only update the fields of a bean that
8384
+     * have been changed rather than the entire bean.
8385
+     * Pass the value TRUE to select 'partial mode' for all beans.
8386
+     * Pass the value FALSE to disable 'partial mode'.
8387
+     * Pass an array of bean types if you wish to use partial mode only
8388
+     * for some types.
8389
+     * This method will return the previous value.
8390
+     *
8391
+     * @param boolean|array $yesNoBeans List of type names or 'all'
8392
+     *
8393
+     * @return mixed
8394
+     */
8395
+    public function usePartialBeans( $yesNoBeans )
8396
+    {
8397
+        $oldValue = $this->partialBeans;
8398
+        $this->partialBeans = $yesNoBeans;
8399
+        return $oldValue;
8400
+    }
8401
+
8402
+    /**
8403
+     * Fully processes a bean and updates the associated records in the database.
8404
+     * First the bean properties will be grouped as 'embedded' bean,
8405
+     * addition, deleted 'trash can' or residue. Next, the different groups
8406
+     * of beans will be processed accordingly and the reference bean (i.e.
8407
+     * the one that was passed to the method as an argument) will be stored.
8408
+     * Each type of list (own/shared) has 3 bean processors: 
8409
+     *
8410
+     * - trashCanProcessor : removes the bean or breaks its association with the current bean
8411
+     * - additionProcessor : associates the bean with the current one
8412
+     * - residueProcessor  : manages beans in lists that 'remain' but may need to be updated
8413
+     * 
8414
+     * This method first groups the beans and then calls the
8415
+     * internal processing methods.
8416
+     *
8417
+     * @param OODBBean $bean bean to process
8418
+     *
8419
+     * @return void
8420
+     */
8421
+    protected function storeBeanWithLists( OODBBean $bean )
8422
+    {
8423
+        $sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups
8424
+        foreach ( $bean as $property => $value ) {
8425
+            $value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value;
8426
+            if ( $value instanceof OODBBean ) {
8427
+                $this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value );
8428
+                $bean->setMeta("sys.typeof.{$property}", $value->getMeta('type'));
8429
+            } elseif ( is_array( $value ) ) {
8430
+                foreach($value as &$item) {
8431
+                    $item = ( $item instanceof SimpleModel ) ? $item->unbox() : $item;
8432
+                }
8433
+                $originals = $bean->moveMeta( 'sys.shadow.' . $property, array() );
8434
+                if ( strpos( $property, 'own' ) === 0 ) {
8435
+                    list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue );
8436
+                    $listName = lcfirst( substr( $property, 3 ) );
8437
+                    if ($bean->moveMeta( 'sys.exclusive-'.  $listName ) ) {
8438
+                        OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE );
8439
+                        OODBBean::setMetaAll( $ownAdditions, 'sys.buildcommand.fkdependson', $bean->getMeta( 'type' ) );
8440
+                    }
8441
+                    unset( $bean->$property );
8442
+                } elseif ( strpos( $property, 'shared' ) === 0 ) {
8443
+                    list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue );
8444
+                    unset( $bean->$property );
8445
+                }
8446
+            }
8447
+        }
8448
+        $this->storeBean( $bean );
8449
+        $this->processTrashcan( $bean, $ownTrashcan );
8450
+        $this->processAdditions( $bean, $ownAdditions );
8451
+        $this->processResidue( $ownresidue );
8452
+        $this->processSharedTrashcan( $bean, $sharedTrashcan );
8453
+        $this->processSharedAdditions( $bean, $sharedAdditions );
8454
+        $this->processSharedResidue( $bean, $sharedresidue );
8455
+    }
8456
+
8457
+    /**
8458
+     * Process groups. Internal function. Processes different kind of groups for
8459
+     * storage function. Given a list of original beans and a list of current beans,
8460
+     * this function calculates which beans remain in the list (residue), which
8461
+     * have been deleted (are in the trashcan) and which beans have been added
8462
+     * (additions).
8463
+     *
8464
+     * @param  array $originals originals
8465
+     * @param  array $current   the current beans
8466
+     * @param  array $additions beans that have been added
8467
+     * @param  array $trashcan  beans that have been deleted
8468
+     * @param  array $residue   beans that have been left untouched
8469
+     *
8470
+     * @return array
8471
+     */
8472
+    protected function processGroups( $originals, $current, $additions, $trashcan, $residue )
8473
+    {
8474
+        return array(
8475
+            array_merge( $additions, array_diff( $current, $originals ) ),
8476
+            array_merge( $trashcan, array_diff( $originals, $current ) ),
8477
+            array_merge( $residue, array_intersect( $current, $originals ) )
8478
+        );
8479
+    }
8480
+
8481
+    /**
8482
+     * Processes a list of beans from a bean.
8483
+     * A bean may contain lists. This
8484
+     * method handles shared addition lists; i.e.
8485
+     * the $bean->sharedObject properties.
8486
+     * Shared beans will be associated with eachother using the
8487
+     * Association Manager.
8488
+     *
8489
+     * @param OODBBean $bean            the bean
8490
+     * @param array    $sharedAdditions list with shared additions
8491
+     *
8492
+     * @return void
8493
+     */
8494
+    protected function processSharedAdditions( $bean, $sharedAdditions )
8495
+    {
8496
+        foreach ( $sharedAdditions as $addition ) {
8497
+            if ( $addition instanceof OODBBean ) {
8498
+                $this->oodb->getAssociationManager()->associate( $addition, $bean );
8499
+            } else {
8500
+                throw new RedException( 'Array may only contain OODBBeans' );
8501
+            }
8502
+        }
8503
+    }
8504
+
8505
+    /**
8506
+     * Processes a list of beans from a bean.
8507
+     * A bean may contain lists. This
8508
+     * method handles own lists; i.e.
8509
+     * the $bean->ownObject properties.
8510
+     * A residue is a bean in an own-list that stays
8511
+     * where it is. This method checks if there have been any
8512
+     * modification to this bean, in that case
8513
+     * the bean is stored once again, otherwise the bean will be left untouched.
8514
+     *
8515
+     * @param array    $ownresidue list to process
8516
+     *
8517
+     * @return void
8518
+     */
8519
+    protected function processResidue( $ownresidue )
8520
+    {
8521
+        foreach ( $ownresidue as $residue ) {
8522
+            if ( $residue->getMeta( 'tainted' ) ) {
8523
+                $this->store( $residue );
8524
+            }
8525
+        }
8526
+    }
8527
+
8528
+    /**
8529
+     * Processes a list of beans from a bean. A bean may contain lists. This
8530
+     * method handles own lists; i.e. the $bean->ownObject properties.
8531
+     * A trash can bean is a bean in an own-list that has been removed
8532
+     * (when checked with the shadow). This method
8533
+     * checks if the bean is also in the dependency list. If it is the bean will be removed.
8534
+     * If not, the connection between the bean and the owner bean will be broken by
8535
+     * setting the ID to NULL.
8536
+     *
8537
+     * @param OODBBean $bean bean   to process
8538
+     * @param array    $ownTrashcan list to process
8539
+     *
8540
+     * @return void
8541
+     */
8542
+    protected function processTrashcan( $bean, $ownTrashcan )
8543
+    {
8544
+        foreach ( $ownTrashcan as $trash ) {
8545
+
8546
+            $myFieldLink = $bean->getMeta( 'type' ) . '_id';
8547
+            $alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) );
8548
+            if ( $alias ) $myFieldLink = $alias . '_id';
8549
+
8550
+            if ( $trash->getMeta( 'sys.garbage' ) === TRUE ) {
8551
+                $this->trash( $trash );
8552
+            } else {
8553
+                $trash->$myFieldLink = NULL;
8554
+                $this->store( $trash );
8555
+            }
8556
+        }
8557
+    }
8558
+
8559
+    /**
8560
+     * Unassociates the list items in the trashcan.
8561
+     * This bean processor processes the beans in the shared trash can.
8562
+     * This group of beans has been deleted from a shared list.
8563
+     * The affected beans will no longer be associated with the bean
8564
+     * that contains the shared list.
8565
+     *
8566
+     * @param OODBBean $bean           bean to process
8567
+     * @param array    $sharedTrashcan list to process
8568
+     *
8569
+     * @return void
8570
+     */
8571
+    protected function processSharedTrashcan( $bean, $sharedTrashcan )
8572
+    {
8573
+        foreach ( $sharedTrashcan as $trash ) {
8574
+            $this->oodb->getAssociationManager()->unassociate( $trash, $bean );
8575
+        }
8576
+    }
8577
+
8578
+    /**
8579
+     * Stores all the beans in the residue group.
8580
+     * This bean processor processes the beans in the shared residue
8581
+     * group. This group of beans 'remains' in the list but might need
8582
+     * to be updated or synced. The affected beans will be stored
8583
+     * to perform the required database queries.
8584
+     *
8585
+     * @param OODBBean $bean          bean to process
8586
+     * @param array    $sharedresidue list to process
8587
+     *
8588
+     * @return void
8589
+     */
8590
+    protected function processSharedResidue( $bean, $sharedresidue )
8591
+    {
8592
+        foreach ( $sharedresidue as $residue ) {
8593
+            $this->store( $residue );
8594
+        }
8595
+    }
8596
+
8597
+    /**
8598
+     * Determines whether the bean has 'loaded lists' or
8599
+     * 'loaded embedded beans' that need to be processed
8600
+     * by the store() method.
8601
+     *
8602
+     * @param OODBBean $bean bean to be examined
8603
+     *
8604
+     * @return boolean
8605
+     */
8606
+    protected function hasListsOrObjects( OODBBean $bean )
8607
+    {
8608
+        $processLists = FALSE;
8609
+        foreach ( $bean as $value ) {
8610
+            if ( is_array( $value ) || is_object( $value ) ) {
8611
+                $processLists = TRUE;
8612
+                break;
8613
+            }
8614
+        }
8615
+
8616
+        return $processLists;
8617
+    }
8618
+
8619
+    /**
8620
+     * Converts an embedded bean to an ID, removes the bean property and
8621
+     * stores the bean in the embedded beans array. The id will be
8622
+     * assigned to the link field property, i.e. 'bean_id'.
8623
+     *
8624
+     * @param array    $embeddedBeans destination array for embedded bean
8625
+     * @param OODBBean $bean          target bean to process
8626
+     * @param string   $property      property that contains the embedded bean
8627
+     * @param OODBBean $value         embedded bean itself
8628
+     *
8629
+     * @return void
8630
+     */
8631
+    protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value )
8632
+    {
8633
+        $linkField = $property . '_id';
8634
+        if ( !$value->id || $value->getMeta( 'tainted' ) ) {
8635
+            $this->store( $value );
8636
+        }
8637
+        $id = $value->id;
8638
+        if ($bean->$linkField != $id) $bean->$linkField = $id;
8639
+        $bean->setMeta( 'cast.' . $linkField, 'id' );
8640
+        $embeddedBeans[$linkField] = $value;
8641
+        unset( $bean->$property );
8642
+    }
8643
+
8644
+    /**
8645
+     * Constructor, requires a query writer and OODB.
8646
+     * Creates a new instance of the bean respository class.
8647
+     *
8648
+     * @param OODB        $oodb   instance of object database
8649
+     * @param QueryWriter $writer the Query Writer to use for this repository
8650
+     *
8651
+     * @return void
8652
+     */
8653
+    public function __construct( OODB $oodb, QueryWriter $writer )
8654
+    {
8655
+        $this->writer = $writer;
8656
+        $this->oodb = $oodb;
8657
+    }
8658
+
8659
+    /**
8660
+     * Checks whether a OODBBean bean is valid.
8661
+     * If the type is not valid or the ID is not valid it will
8662
+     * throw an exception: Security. To be valid a bean
8663
+     * must abide to the following rules:
8664
+     *
8665
+     * - It must have an primary key id property named: id
8666
+     * - It must have a type
8667
+     * - The type must conform to the RedBeanPHP naming policy
8668
+     * - All properties must be valid
8669
+     * - All values must be valid
8670
+     *
8671
+     * @param OODBBean $bean the bean that needs to be checked
8672
+     *
8673
+     * @return void
8674
+     */
8675
+    public function check( OODBBean $bean )
8676
+    {
8677
+        //Is all meta information present?
8678
+        if ( !isset( $bean->id ) ) {
8679
+            throw new RedException( 'Bean has incomplete Meta Information id ' );
8680
+        }
8681
+        if ( !( $bean->getMeta( 'type' ) ) ) {
8682
+            throw new RedException( 'Bean has incomplete Meta Information II' );
8683
+        }
8684
+        //Pattern of allowed characters
8685
+        $pattern = '/[^a-z0-9_]/i';
8686
+        //Does the type contain invalid characters?
8687
+        if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) {
8688
+            throw new RedException( 'Bean Type is invalid' );
8689
+        }
8690
+        //Are the properties and values valid?
8691
+        foreach ( $bean as $prop => $value ) {
8692
+            if (
8693
+                is_array( $value )
8694
+                || ( is_object( $value ) )
8695
+            ) {
8696
+                throw new RedException( "Invalid Bean value: property $prop" );
8697
+            } else if (
8698
+                strlen( $prop ) < 1
8699
+                || preg_match( $pattern, $prop )
8700
+            ) {
8701
+                throw new RedException( "Invalid Bean property: property $prop" );
8702
+            }
8703
+        }
8704
+    }
8705
+
8706
+    /**
8707
+     * Searches the database for a bean that matches conditions $conditions and sql $addSQL
8708
+     * and returns an array containing all the beans that have been found.
8709
+     *
8710
+     * Conditions need to take form:
8711
+     *
8712
+     * <code>
8713
+     * array(
8714
+     *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
8715
+     *    'PROPERTY' => array( POSSIBLE VALUES... )
8716
+     * );
8717
+     * </code>
8718
+     *
8719
+     * All conditions are glued together using the AND-operator, while all value lists
8720
+     * are glued using IN-operators thus acting as OR-conditions.
8721
+     *
8722
+     * Note that you can use property names; the columns will be extracted using the
8723
+     * appropriate bean formatter.
8724
+     *
8725
+     * @param string $type       type of beans you are looking for
8726
+     * @param array  $conditions list of conditions
8727
+     * @param string $sql        SQL to be used in query
8728
+     * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
8729
+     *
8730
+     * @return array
8731
+     */
8732
+    public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
8733
+    {
8734
+        //for backward compatibility, allow mismatch arguments:
8735
+        if ( is_array( $sql ) ) {
8736
+            if ( isset( $sql[1] ) ) {
8737
+                $bindings = $sql[1];
8738
+            }
8739
+            $sql = $sql[0];
8740
+        }
8741
+        try {
8742
+            $beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) );
8743
+
8744
+            return $beans;
8745
+        } catch ( SQLException $exception ) {
8746
+            $this->handleException( $exception );
8747
+        }
8748
+
8749
+        return array();
8750
+    }
8751
+
8752
+    /**
8753
+     * Finds a BeanCollection.
8754
+     * Given a type, an SQL snippet and optionally some parameter bindings
8755
+     * this methods returns a BeanCollection for your query.
8756
+     *
8757
+     * The BeanCollection represents a collection of beans and
8758
+     * makes it possible to use database cursors. The BeanCollection
8759
+     * has a method next() to obtain the first, next and last bean
8760
+     * in the collection. The BeanCollection does not implement the array
8761
+     * interface nor does it try to act like an array because it cannot go
8762
+     * backward or rewind itself.
8763
+     *
8764
+     * @param string $type     type of beans you are looking for
8765
+     * @param string $sql      SQL to be used in query
8766
+     * @param array  $bindings whether you prefer to use a WHERE clause or not (TRUE = not)
8767
+     *
8768
+     * @return BeanCollection
8769
+     */
8770
+    public function findCollection( $type, $sql, $bindings = array() )
8771
+    {
8772
+        try {
8773
+            $cursor = $this->writer->queryRecordWithCursor( $type, $sql, $bindings );
8774
+            return new BeanCollection( $type, $this, $cursor );
8775
+        } catch ( SQLException $exception ) {
8776
+            $this->handleException( $exception );
8777
+        }
8778
+        return new BeanCollection( $type, $this, new NullCursor );
8779
+    }
8780
+
8781
+    /**
8782
+     * Stores a bean in the database. This method takes a
8783
+     * OODBBean Bean Object $bean and stores it
8784
+     * in the database. If the database schema is not compatible
8785
+     * with this bean and RedBean runs in fluid mode the schema
8786
+     * will be altered to store the bean correctly.
8787
+     * If the database schema is not compatible with this bean and
8788
+     * RedBean runs in frozen mode it will throw an exception.
8789
+     * This function returns the primary key ID of the inserted
8790
+     * bean.
8791
+     *
8792
+     * The return value is an integer if possible. If it is not possible to
8793
+     * represent the value as an integer a string will be returned. We use
8794
+     * explicit casts instead of functions to preserve performance
8795
+     * (0.13 vs 0.28 for 10000 iterations on Core i3).
8796
+     *
8797
+     * @param OODBBean|SimpleModel $bean bean to store
8798
+     *
8799
+     * @return integer|string
8800
+     */
8801
+    public function store( $bean )
8802
+    {
8803
+        $processLists = $this->hasListsOrObjects( $bean );
8804
+        if ( !$processLists && !$bean->getMeta( 'tainted' ) ) {
8805
+            return $bean->getID(); //bail out!
8806
+        }
8807
+        $this->oodb->signal( 'update', $bean );
8808
+        $processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model!
8809
+        if ( $processLists ) {
8810
+            $this->storeBeanWithLists( $bean );
8811
+        } else {
8812
+            $this->storeBean( $bean );
8813
+        }
8814
+        $this->oodb->signal( 'after_update', $bean );
8815
+
8816
+        return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id;
8817
+    }
8818
+
8819
+    /**
8820
+     * Returns an array of beans. Pass a type and a series of ids and
8821
+     * this method will bring you the corresponding beans.
8822
+     *
8823
+     * important note: Because this method loads beans using the load()
8824
+     * function (but faster) it will return empty beans with ID 0 for
8825
+     * every bean that could not be located. The resulting beans will have the
8826
+     * passed IDs as their keys.
8827
+     *
8828
+     * @param string $type type of beans
8829
+     * @param array  $ids  ids to load
8830
+     *
8831
+     * @return array
8832
+     */
8833
+    public function batch( $type, $ids )
8834
+    {
8835
+        if ( !$ids ) {
8836
+            return array();
8837
+        }
8838
+        $collection = array();
8839
+        try {
8840
+            $rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) );
8841
+        } catch ( SQLException $e ) {
8842
+            $this->handleException( $e );
8843
+            $rows = FALSE;
8844
+        }
8845
+        $this->stash[$this->nesting] = array();
8846
+        if ( !$rows ) {
8847
+            return array();
8848
+        }
8849
+        foreach ( $rows as $row ) {
8850
+            $this->stash[$this->nesting][$row['id']] = $row;
8851
+        }
8852
+        foreach ( $ids as $id ) {
8853
+            $collection[$id] = $this->load( $type, $id );
8854
+        }
8855
+        $this->stash[$this->nesting] = NULL;
8856
+
8857
+        return $collection;
8858
+    }
8859
+
8860
+    /**
8861
+     * This is a convenience method; it converts database rows
8862
+     * (arrays) into beans. Given a type and a set of rows this method
8863
+     * will return an array of beans of the specified type loaded with
8864
+     * the data fields provided by the result set from the database.
8865
+     *
8866
+     * New in 4.3.2: meta mask. The meta mask is a special mask to send
8867
+     * data from raw result rows to the meta store of the bean. This is
8868
+     * useful for bundling additional information with custom queries.
8869
+     * Values of every column whos name starts with $mask will be
8870
+     * transferred to the meta section of the bean under key 'data.bundle'.
8871
+     *
8872
+     * @param string $type type of beans you would like to have
8873
+     * @param array  $rows rows from the database result
8874
+     * @param string $mask meta mask to apply (optional)
8875
+     *
8876
+     * @return array
8877
+     */
8878
+    public function convertToBeans( $type, $rows, $mask = NULL )
8879
+    {
8880
+        $masklen = 0;
8881
+        if ( $mask !== NULL ) $masklen = mb_strlen( $mask );
8882
+
8883
+        $collection                  = array();
8884
+        $this->stash[$this->nesting] = array();
8885
+        foreach ( $rows as $row ) {
8886
+            $meta = array();
8887
+            if ( !is_null( $mask ) ) {
8888
+                foreach( $row as $key => $value ) {
8889
+                    if ( strpos( $key, $mask ) === 0 ) {
8890
+                        unset( $row[$key] );
8891
+                        $meta[$key] = $value;
8892
+                    }
8893
+                }
8894
+            }
8895
+
8896
+            $id                               = $row['id'];
8897
+            $this->stash[$this->nesting][$id] = $row;
8898
+            $collection[$id]                  = $this->load( $type, $id );
8899
+
8900
+            if ( $mask !== NULL ) {
8901
+                $collection[$id]->setMeta( 'data.bundle', $meta );
8902
+            }
8903
+        }
8904
+        $this->stash[$this->nesting] = NULL;
8905
+
8906
+        return $collection;
8907
+    }
8908
+
8909
+    /**
8910
+     * Counts the number of beans of type $type.
8911
+     * This method accepts a second argument to modify the count-query.
8912
+     * A third argument can be used to provide bindings for the SQL snippet.
8913
+     *
8914
+     * @param string $type     type of bean we are looking for
8915
+     * @param string $addSQL   additional SQL snippet
8916
+     * @param array  $bindings parameters to bind to SQL
8917
+     *
8918
+     * @return integer
8919
+     */
8920
+    public function count( $type, $addSQL = '', $bindings = array() )
8921
+    {
8922
+        $type = AQueryWriter::camelsSnake( $type );
8923
+        if ( count( explode( '_', $type ) ) > 2 ) {
8924
+            throw new RedException( 'Invalid type for count.' );
8925
+        }
8926
+
8927
+        try {
8928
+            return (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings );
8929
+        } catch ( SQLException $exception ) {
8930
+            if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array(
8931
+                    QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
8932
+                    QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ),
8933
+                    $exception->getDriverDetails() ) ) {
8934
+                throw $exception;
8935
+            }
8936
+        }
8937
+
8938
+        return 0;
8939
+    }
8940
+
8941
+    /**
8942
+     * Removes a bean from the database.
8943
+     * This function will remove the specified OODBBean
8944
+     * Bean Object from the database.
8945
+     *
8946
+     * @param OODBBean|SimpleModel $bean bean you want to remove from database
8947
+     *
8948
+     * @return void
8949
+     */
8950
+    public function trash( $bean )
8951
+    {
8952
+        $this->oodb->signal( 'delete', $bean );
8953
+        foreach ( $bean as $property => $value ) {
8954
+            if ( $value instanceof OODBBean ) {
8955
+                unset( $bean->$property );
8956
+            }
8957
+            if ( is_array( $value ) ) {
8958
+                if ( strpos( $property, 'own' ) === 0 ) {
8959
+                    unset( $bean->$property );
8960
+                } elseif ( strpos( $property, 'shared' ) === 0 ) {
8961
+                    unset( $bean->$property );
8962
+                }
8963
+            }
8964
+        }
8965
+        try {
8966
+            $this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL );
8967
+        } catch ( SQLException $exception ) {
8968
+            $this->handleException( $exception );
8969
+        }
8970
+        $bean->id = 0;
8971
+        $this->oodb->signal( 'after_delete', $bean );
8972
+    }
8973
+
8974
+    /**
8975
+     * Checks whether the specified table already exists in the database.
8976
+     * Not part of the Object Database interface!
8977
+     *
8978
+     * @deprecated Use AQueryWriter::typeExists() instead.
8979
+     *
8980
+     * @param string $table table name
8981
+     *
8982
+     * @return boolean
8983
+     */
8984
+    public function tableExists( $table )
8985
+    {
8986
+        return $this->writer->tableExists( $table );
8987
+    }
8988
+
8989
+    /**
8990
+     * Trash all beans of a given type.
8991
+     * Wipes an entire type of bean. After this operation there
8992
+     * will be no beans left of the specified type.
8993
+     * This method will ignore exceptions caused by database
8994
+     * tables that do not exist.
8995
+     *
8996
+     * @param string $type type of bean you wish to delete all instances of
8997
+     *
8998
+     * @return boolean
8999
+     */
9000
+    public function wipe( $type )
9001
+    {
9002
+        try {
9003
+            $this->writer->wipe( $type );
9004
+
9005
+            return TRUE;
9006
+        } catch ( SQLException $exception ) {
9007
+            if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ), $exception->getDriverDetails() ) ) {
9008
+                throw $exception;
9009
+            }
9010
+
9011
+            return FALSE;
9012
+        }
9013
+    }
9014 9014
 }
9015 9015
 }
9016 9016
 
@@ -9043,327 +9043,327 @@  discard block
 block discarded – undo
9043 9043
  */
9044 9044
 class Fluid extends Repository
9045 9045
 {
9046
-	/**
9047
-	 * Figures out the desired type given the cast string ID.
9048
-	 * Given a cast ID, this method will return the associated
9049
-	 * type (INT(10) or VARCHAR for instance). The returned type
9050
-	 * can be processed by the Query Writer to build the specified
9051
-	 * column for you in the database. The Cast ID is actually just
9052
-	 * a superset of the QueryWriter types. In addition to default
9053
-	 * Query Writer column types you can pass the following 'cast types':
9054
-	 * 'id' and 'string'. These will map to Query Writer specific
9055
-	 * column types (probably INT and VARCHAR).
9056
-	 *
9057
-	 * @param string $cast cast identifier
9058
-	 *
9059
-	 * @return integer
9060
-	 */
9061
-	private function getTypeFromCast( $cast )
9062
-	{
9063
-		if ( $cast == 'string' ) {
9064
-			$typeno = $this->writer->scanType( 'STRING' );
9065
-		} elseif ( $cast == 'id' ) {
9066
-			$typeno = $this->writer->getTypeForID();
9067
-		} elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) {
9068
-			$typeno = $this->writer->sqltype_typeno[$cast];
9069
-		} else {
9070
-			throw new RedException( 'Invalid Cast' );
9071
-		}
9072
-
9073
-		return $typeno;
9074
-	}
9075
-
9076
-	/**
9077
-	 * Orders the Query Writer to create a table if it does not exist already and
9078
-	 * adds a note in the build report about the creation.
9079
-	 *
9080
-	 * @param OODBBean $bean bean to update report of
9081
-	 * @param string         $table table to check and create if not exists
9082
-	 *
9083
-	 * @return void
9084
-	 */
9085
-	private function createTableIfNotExists( OODBBean $bean, $table )
9086
-	{
9087
-		//Does table exist? If not, create
9088
-		if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) {
9089
-			$this->writer->createTable( $table );
9090
-			$bean->setMeta( 'buildreport.flags.created', TRUE );
9091
-		}
9092
-	}
9093
-
9094
-	/**
9095
-	 * Modifies the table to fit the bean data.
9096
-	 * Given a property and a value and the bean, this method will
9097
-	 * adjust the table structure to fit the requirements of the property and value.
9098
-	 * This may include adding a new column or widening an existing column to hold a larger
9099
-	 * or different kind of value. This method employs the writer to adjust the table
9100
-	 * structure in the database. Schema updates are recorded in meta properties of the bean.
9101
-	 *
9102
-	 * This method will also apply indexes, unique constraints and foreign keys.
9103
-	 *
9104
-	 * @param OODBBean $bean     bean to get cast data from and store meta in
9105
-	 * @param string   $property property to store
9106
-	 * @param mixed    $value    value to store
9107
-	 *
9108
-	 * @return void
9109
-	 */
9110
-	private function modifySchema( OODBBean $bean, $property, $value, &$columns = NULL )
9111
-	{
9112
-		$doFKStuff = FALSE;
9113
-		$table   = $bean->getMeta( 'type' );
9114
-		if ($columns === NULL) {
9115
-			$columns = $this->writer->getColumns( $table );
9116
-		}
9117
-		$columnNoQ = $this->writer->esc( $property, TRUE );
9118
-		if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) {
9119
-			if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types
9120
-				$cast   = $bean->getMeta( "cast.$property" );
9121
-				$typeno = $this->getTypeFromCast( $cast );
9122
-			} else {
9123
-				$cast   = FALSE;
9124
-				$typeno = $this->writer->scanType( $value, TRUE );
9125
-			}
9126
-			if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ?
9127
-				if ( !$cast ) { //rescan without taking into account special types >80
9128
-					$typeno = $this->writer->scanType( $value, FALSE );
9129
-				}
9130
-				$sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] );
9131
-				if ( $typeno > $sqlt ) { //no, we have to widen the database column type
9132
-					$this->writer->widenColumn( $table, $property, $typeno );
9133
-					$bean->setMeta( 'buildreport.flags.widen', TRUE );
9134
-					$doFKStuff = TRUE;
9135
-				}
9136
-			} else {
9137
-				$this->writer->addColumn( $table, $property, $typeno );
9138
-				$bean->setMeta( 'buildreport.flags.addcolumn', TRUE );
9139
-				$doFKStuff = TRUE;
9140
-			}
9141
-			if ($doFKStuff) {
9142
-				if (strrpos($columnNoQ, '_id')===(strlen($columnNoQ)-3)) {
9143
-					$destinationColumnNoQ = substr($columnNoQ, 0, strlen($columnNoQ)-3);
9144
-					$indexName = "index_foreignkey_{$table}_{$destinationColumnNoQ}";
9145
-					$this->writer->addIndex($table, $indexName, $columnNoQ);
9146
-					$typeof = $bean->getMeta("sys.typeof.{$destinationColumnNoQ}", $destinationColumnNoQ);
9147
-					$isLink = $bean->getMeta( 'sys.buildcommand.unique', FALSE );
9148
-					//Make FK CASCADING if part of exclusive list (dependson=typeof) or if link bean
9149
-					$isDep = ( $bean->moveMeta( 'sys.buildcommand.fkdependson' ) === $typeof || is_array( $isLink ) );
9150
-					$result = $this->writer->addFK( $table, $typeof, $columnNoQ, 'id', $isDep );
9151
-					//If this is a link bean and all unique columns have been added already, then apply unique constraint
9152
-					if ( is_array( $isLink ) && !count( array_diff( $isLink, array_keys( $this->writer->getColumns( $table ) ) ) ) ) {
9153
-						$this->writer->addUniqueConstraint( $table, $bean->moveMeta('sys.buildcommand.unique') );
9154
-						$bean->setMeta("sys.typeof.{$destinationColumnNoQ}", NULL);
9155
-					}
9156
-				}
9157
-			}
9158
-		}
9159
-	}
9160
-
9161
-	/**
9162
-	 * Part of the store() functionality.
9163
-	 * Handles all new additions after the bean has been saved.
9164
-	 * Stores addition bean in own-list, extracts the id and
9165
-	 * adds a foreign key. Also adds a constraint in case the type is
9166
-	 * in the dependent list.
9167
-	 *
9168
-	 * Note that this method raises a custom exception if the bean
9169
-	 * is not an instance of OODBBean. Therefore it does not use
9170
-	 * a type hint. This allows the user to take action in case
9171
-	 * invalid objects are passed in the list.
9172
-	 *
9173
-	 * @param OODBBean $bean         bean to process
9174
-	 * @param array    $ownAdditions list of addition beans in own-list
9175
-	 *
9176
-	 * @return void
9177
-	 */
9178
-	protected function processAdditions( $bean, $ownAdditions )
9179
-	{
9180
-		$beanType = $bean->getMeta( 'type' );
9181
-
9182
-		foreach ( $ownAdditions as $addition ) {
9183
-			if ( $addition instanceof OODBBean ) {
9184
-
9185
-				$myFieldLink = $beanType . '_id';
9186
-				$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
9187
-				if ( $alias ) $myFieldLink = $alias . '_id';
9188
-
9189
-				$addition->$myFieldLink = $bean->id;
9190
-				$addition->setMeta( 'cast.' . $myFieldLink, 'id' );
9191
-
9192
-				if ($alias) {
9193
-					$addition->setMeta( "sys.typeof.{$alias}", $beanType );
9194
-				} else {
9195
-					$addition->setMeta( "sys.typeof.{$beanType}", $beanType );
9196
-				}
9197
-
9198
-				$this->store( $addition );
9199
-			} else {
9200
-				throw new RedException( 'Array may only contain OODBBeans' );
9201
-			}
9202
-		}
9203
-	}
9204
-
9205
-	/**
9206
-	 * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
9207
-	 * method. When all lists and embedded beans (parent objects) have been processed and
9208
-	 * removed from the original bean the bean is passed to this method to be stored
9209
-	 * in the database.
9210
-	 *
9211
-	 * @param OODBBean $bean the clean bean
9212
-	 *
9213
-	 * @return void
9214
-	 */
9215
-	protected function storeBean( OODBBean $bean )
9216
-	{
9217
-		if ( $bean->getMeta( 'changed' ) ) {
9218
-			$this->check( $bean );
9219
-			$table = $bean->getMeta( 'type' );
9220
-			$this->createTableIfNotExists( $bean, $table );
9221
-
9222
-			$updateValues = array();
9223
-
9224
-			$partial = ( $this->partialBeans === TRUE || ( is_array( $this->partialBeans ) && in_array( $table, $this->partialBeans ) ) );
9225
-			if ( $partial ) {
9226
-				$mask = $bean->getMeta( 'changelist' );
9227
-				$bean->setMeta( 'changelist', array() );
9228
-			}
9229
-
9230
-			$columnCache = NULL;
9231
-			foreach ( $bean as $property => $value ) {
9232
-				if ( $partial && !in_array( $property, $mask ) ) continue;
9233
-				if ( $property !== 'id' ) {
9234
-					$this->modifySchema( $bean, $property, $value, $columnCache );
9235
-				}
9236
-				if ( $property !== 'id' ) {
9237
-					$updateValues[] = array( 'property' => $property, 'value' => $value );
9238
-				}
9239
-			}
9240
-
9241
-			$bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id );
9242
-			$bean->setMeta( 'changed', FALSE );
9243
-		}
9244
-		$bean->setMeta( 'tainted', FALSE );
9245
-	}
9246
-
9247
-	/**
9248
-	 * Exception handler.
9249
-	 * Fluid and Frozen mode have different ways of handling
9250
-	 * exceptions. Fluid mode (using the fluid repository) ignores
9251
-	 * exceptions caused by the following:
9252
-	 *
9253
-	 * - missing tables
9254
-	 * - missing column
9255
-	 *
9256
-	 * In these situations, the repository will behave as if
9257
-	 * no beans could be found. This is because in fluid mode
9258
-	 * it might happen to query a table or column that has not been
9259
-	 * created yet. In frozen mode, this is not supposed to happen
9260
-	 * and the corresponding exceptions will be thrown.
9261
-	 *
9262
-	 * @param \Exception $exception exception
9263
-	 *
9264
-	 * @return void
9265
-	 */
9266
-	protected function handleException( \Exception $exception )
9267
-	{
9268
-		if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
9269
-			array(
9270
-				QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
9271
-				QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ),
9272
-				$exception->getDriverDetails() )
9273
-		) {
9274
-			throw $exception;
9275
-		}
9276
-	}
9277
-
9278
-	/**
9279
-	 * Dispenses a new bean (a OODBBean Bean Object)
9280
-	 * of the specified type. Always
9281
-	 * use this function to get an empty bean object. Never
9282
-	 * instantiate a OODBBean yourself because it needs
9283
-	 * to be configured before you can use it with RedBean. This
9284
-	 * function applies the appropriate initialization /
9285
-	 * configuration for you.
9286
-	 *
9287
-	 * @param string  $type              type of bean you want to dispense
9288
-	 * @param string  $number            number of beans you would like to get
9289
-	 * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
9290
-	 *
9291
-	 * @return OODBBean
9292
-	 */
9293
-	public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
9294
-	{
9295
-		$OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
9296
-		$beans = array();
9297
-		for ( $i = 0; $i < $number; $i++ ) {
9298
-			$bean = new $OODBBEAN;
9299
-			$bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
9300
-			$this->check( $bean );
9301
-			$this->oodb->signal( 'dispense', $bean );
9302
-			$beans[] = $bean;
9303
-		}
9304
-
9305
-		return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
9306
-	}
9307
-
9308
-	/**
9309
-	 * Loads a bean from the object database.
9310
-	 * It searches for a OODBBean Bean Object in the
9311
-	 * database. It does not matter how this bean has been stored.
9312
-	 * RedBean uses the primary key ID $id and the string $type
9313
-	 * to find the bean. The $type specifies what kind of bean you
9314
-	 * are looking for; this is the same type as used with the
9315
-	 * dispense() function. If RedBean finds the bean it will return
9316
-	 * the OODB Bean object; if it cannot find the bean
9317
-	 * RedBean will return a new bean of type $type and with
9318
-	 * primary key ID 0. In the latter case it acts basically the
9319
-	 * same as dispense().
9320
-	 *
9321
-	 * Important note:
9322
-	 * If the bean cannot be found in the database a new bean of
9323
-	 * the specified type will be generated and returned.
9324
-	 *
9325
-	 * @param string  $type type of bean you want to load
9326
-	 * @param integer $id   ID of the bean you want to load
9327
-	 *
9328
-	 * @return OODBBean
9329
-	 */
9330
-	public function load( $type, $id )
9331
-	{
9332
-		$rows = array();
9333
-		$bean = $this->dispense( $type );
9334
-		if ( isset( $this->stash[$this->nesting][$id] ) ) {
9335
-			$row = $this->stash[$this->nesting][$id];
9336
-		} else {
9337
-			try {
9338
-				$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
9339
-			} catch ( SQLException $exception ) {
9340
-				if (
9341
-					$this->writer->sqlStateIn(
9342
-						$exception->getSQLState(),
9343
-						array(
9344
-							QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
9345
-							QueryWriter::C_SQLSTATE_NO_SUCH_TABLE
9346
-						),
9347
-						$exception->getDriverDetails()
9348
-					)
9349
-				) {
9350
-					$rows = array();
9351
-				} else {
9352
-					throw $exception;
9353
-				}
9354
-			}
9355
-			if ( !count( $rows ) ) {
9356
-				return $bean;
9357
-			}
9358
-			$row = array_pop( $rows );
9359
-		}
9360
-		$bean->importRow( $row );
9361
-		$this->nesting++;
9362
-		$this->oodb->signal( 'open', $bean );
9363
-		$this->nesting--;
9364
-
9365
-		return $bean->setMeta( 'tainted', FALSE );
9366
-	}
9046
+    /**
9047
+     * Figures out the desired type given the cast string ID.
9048
+     * Given a cast ID, this method will return the associated
9049
+     * type (INT(10) or VARCHAR for instance). The returned type
9050
+     * can be processed by the Query Writer to build the specified
9051
+     * column for you in the database. The Cast ID is actually just
9052
+     * a superset of the QueryWriter types. In addition to default
9053
+     * Query Writer column types you can pass the following 'cast types':
9054
+     * 'id' and 'string'. These will map to Query Writer specific
9055
+     * column types (probably INT and VARCHAR).
9056
+     *
9057
+     * @param string $cast cast identifier
9058
+     *
9059
+     * @return integer
9060
+     */
9061
+    private function getTypeFromCast( $cast )
9062
+    {
9063
+        if ( $cast == 'string' ) {
9064
+            $typeno = $this->writer->scanType( 'STRING' );
9065
+        } elseif ( $cast == 'id' ) {
9066
+            $typeno = $this->writer->getTypeForID();
9067
+        } elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) {
9068
+            $typeno = $this->writer->sqltype_typeno[$cast];
9069
+        } else {
9070
+            throw new RedException( 'Invalid Cast' );
9071
+        }
9072
+
9073
+        return $typeno;
9074
+    }
9075
+
9076
+    /**
9077
+     * Orders the Query Writer to create a table if it does not exist already and
9078
+     * adds a note in the build report about the creation.
9079
+     *
9080
+     * @param OODBBean $bean bean to update report of
9081
+     * @param string         $table table to check and create if not exists
9082
+     *
9083
+     * @return void
9084
+     */
9085
+    private function createTableIfNotExists( OODBBean $bean, $table )
9086
+    {
9087
+        //Does table exist? If not, create
9088
+        if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) {
9089
+            $this->writer->createTable( $table );
9090
+            $bean->setMeta( 'buildreport.flags.created', TRUE );
9091
+        }
9092
+    }
9093
+
9094
+    /**
9095
+     * Modifies the table to fit the bean data.
9096
+     * Given a property and a value and the bean, this method will
9097
+     * adjust the table structure to fit the requirements of the property and value.
9098
+     * This may include adding a new column or widening an existing column to hold a larger
9099
+     * or different kind of value. This method employs the writer to adjust the table
9100
+     * structure in the database. Schema updates are recorded in meta properties of the bean.
9101
+     *
9102
+     * This method will also apply indexes, unique constraints and foreign keys.
9103
+     *
9104
+     * @param OODBBean $bean     bean to get cast data from and store meta in
9105
+     * @param string   $property property to store
9106
+     * @param mixed    $value    value to store
9107
+     *
9108
+     * @return void
9109
+     */
9110
+    private function modifySchema( OODBBean $bean, $property, $value, &$columns = NULL )
9111
+    {
9112
+        $doFKStuff = FALSE;
9113
+        $table   = $bean->getMeta( 'type' );
9114
+        if ($columns === NULL) {
9115
+            $columns = $this->writer->getColumns( $table );
9116
+        }
9117
+        $columnNoQ = $this->writer->esc( $property, TRUE );
9118
+        if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) {
9119
+            if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types
9120
+                $cast   = $bean->getMeta( "cast.$property" );
9121
+                $typeno = $this->getTypeFromCast( $cast );
9122
+            } else {
9123
+                $cast   = FALSE;
9124
+                $typeno = $this->writer->scanType( $value, TRUE );
9125
+            }
9126
+            if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ?
9127
+                if ( !$cast ) { //rescan without taking into account special types >80
9128
+                    $typeno = $this->writer->scanType( $value, FALSE );
9129
+                }
9130
+                $sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] );
9131
+                if ( $typeno > $sqlt ) { //no, we have to widen the database column type
9132
+                    $this->writer->widenColumn( $table, $property, $typeno );
9133
+                    $bean->setMeta( 'buildreport.flags.widen', TRUE );
9134
+                    $doFKStuff = TRUE;
9135
+                }
9136
+            } else {
9137
+                $this->writer->addColumn( $table, $property, $typeno );
9138
+                $bean->setMeta( 'buildreport.flags.addcolumn', TRUE );
9139
+                $doFKStuff = TRUE;
9140
+            }
9141
+            if ($doFKStuff) {
9142
+                if (strrpos($columnNoQ, '_id')===(strlen($columnNoQ)-3)) {
9143
+                    $destinationColumnNoQ = substr($columnNoQ, 0, strlen($columnNoQ)-3);
9144
+                    $indexName = "index_foreignkey_{$table}_{$destinationColumnNoQ}";
9145
+                    $this->writer->addIndex($table, $indexName, $columnNoQ);
9146
+                    $typeof = $bean->getMeta("sys.typeof.{$destinationColumnNoQ}", $destinationColumnNoQ);
9147
+                    $isLink = $bean->getMeta( 'sys.buildcommand.unique', FALSE );
9148
+                    //Make FK CASCADING if part of exclusive list (dependson=typeof) or if link bean
9149
+                    $isDep = ( $bean->moveMeta( 'sys.buildcommand.fkdependson' ) === $typeof || is_array( $isLink ) );
9150
+                    $result = $this->writer->addFK( $table, $typeof, $columnNoQ, 'id', $isDep );
9151
+                    //If this is a link bean and all unique columns have been added already, then apply unique constraint
9152
+                    if ( is_array( $isLink ) && !count( array_diff( $isLink, array_keys( $this->writer->getColumns( $table ) ) ) ) ) {
9153
+                        $this->writer->addUniqueConstraint( $table, $bean->moveMeta('sys.buildcommand.unique') );
9154
+                        $bean->setMeta("sys.typeof.{$destinationColumnNoQ}", NULL);
9155
+                    }
9156
+                }
9157
+            }
9158
+        }
9159
+    }
9160
+
9161
+    /**
9162
+     * Part of the store() functionality.
9163
+     * Handles all new additions after the bean has been saved.
9164
+     * Stores addition bean in own-list, extracts the id and
9165
+     * adds a foreign key. Also adds a constraint in case the type is
9166
+     * in the dependent list.
9167
+     *
9168
+     * Note that this method raises a custom exception if the bean
9169
+     * is not an instance of OODBBean. Therefore it does not use
9170
+     * a type hint. This allows the user to take action in case
9171
+     * invalid objects are passed in the list.
9172
+     *
9173
+     * @param OODBBean $bean         bean to process
9174
+     * @param array    $ownAdditions list of addition beans in own-list
9175
+     *
9176
+     * @return void
9177
+     */
9178
+    protected function processAdditions( $bean, $ownAdditions )
9179
+    {
9180
+        $beanType = $bean->getMeta( 'type' );
9181
+
9182
+        foreach ( $ownAdditions as $addition ) {
9183
+            if ( $addition instanceof OODBBean ) {
9184
+
9185
+                $myFieldLink = $beanType . '_id';
9186
+                $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
9187
+                if ( $alias ) $myFieldLink = $alias . '_id';
9188
+
9189
+                $addition->$myFieldLink = $bean->id;
9190
+                $addition->setMeta( 'cast.' . $myFieldLink, 'id' );
9191
+
9192
+                if ($alias) {
9193
+                    $addition->setMeta( "sys.typeof.{$alias}", $beanType );
9194
+                } else {
9195
+                    $addition->setMeta( "sys.typeof.{$beanType}", $beanType );
9196
+                }
9197
+
9198
+                $this->store( $addition );
9199
+            } else {
9200
+                throw new RedException( 'Array may only contain OODBBeans' );
9201
+            }
9202
+        }
9203
+    }
9204
+
9205
+    /**
9206
+     * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
9207
+     * method. When all lists and embedded beans (parent objects) have been processed and
9208
+     * removed from the original bean the bean is passed to this method to be stored
9209
+     * in the database.
9210
+     *
9211
+     * @param OODBBean $bean the clean bean
9212
+     *
9213
+     * @return void
9214
+     */
9215
+    protected function storeBean( OODBBean $bean )
9216
+    {
9217
+        if ( $bean->getMeta( 'changed' ) ) {
9218
+            $this->check( $bean );
9219
+            $table = $bean->getMeta( 'type' );
9220
+            $this->createTableIfNotExists( $bean, $table );
9221
+
9222
+            $updateValues = array();
9223
+
9224
+            $partial = ( $this->partialBeans === TRUE || ( is_array( $this->partialBeans ) && in_array( $table, $this->partialBeans ) ) );
9225
+            if ( $partial ) {
9226
+                $mask = $bean->getMeta( 'changelist' );
9227
+                $bean->setMeta( 'changelist', array() );
9228
+            }
9229
+
9230
+            $columnCache = NULL;
9231
+            foreach ( $bean as $property => $value ) {
9232
+                if ( $partial && !in_array( $property, $mask ) ) continue;
9233
+                if ( $property !== 'id' ) {
9234
+                    $this->modifySchema( $bean, $property, $value, $columnCache );
9235
+                }
9236
+                if ( $property !== 'id' ) {
9237
+                    $updateValues[] = array( 'property' => $property, 'value' => $value );
9238
+                }
9239
+            }
9240
+
9241
+            $bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id );
9242
+            $bean->setMeta( 'changed', FALSE );
9243
+        }
9244
+        $bean->setMeta( 'tainted', FALSE );
9245
+    }
9246
+
9247
+    /**
9248
+     * Exception handler.
9249
+     * Fluid and Frozen mode have different ways of handling
9250
+     * exceptions. Fluid mode (using the fluid repository) ignores
9251
+     * exceptions caused by the following:
9252
+     *
9253
+     * - missing tables
9254
+     * - missing column
9255
+     *
9256
+     * In these situations, the repository will behave as if
9257
+     * no beans could be found. This is because in fluid mode
9258
+     * it might happen to query a table or column that has not been
9259
+     * created yet. In frozen mode, this is not supposed to happen
9260
+     * and the corresponding exceptions will be thrown.
9261
+     *
9262
+     * @param \Exception $exception exception
9263
+     *
9264
+     * @return void
9265
+     */
9266
+    protected function handleException( \Exception $exception )
9267
+    {
9268
+        if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
9269
+            array(
9270
+                QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
9271
+                QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ),
9272
+                $exception->getDriverDetails() )
9273
+        ) {
9274
+            throw $exception;
9275
+        }
9276
+    }
9277
+
9278
+    /**
9279
+     * Dispenses a new bean (a OODBBean Bean Object)
9280
+     * of the specified type. Always
9281
+     * use this function to get an empty bean object. Never
9282
+     * instantiate a OODBBean yourself because it needs
9283
+     * to be configured before you can use it with RedBean. This
9284
+     * function applies the appropriate initialization /
9285
+     * configuration for you.
9286
+     *
9287
+     * @param string  $type              type of bean you want to dispense
9288
+     * @param string  $number            number of beans you would like to get
9289
+     * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
9290
+     *
9291
+     * @return OODBBean
9292
+     */
9293
+    public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
9294
+    {
9295
+        $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
9296
+        $beans = array();
9297
+        for ( $i = 0; $i < $number; $i++ ) {
9298
+            $bean = new $OODBBEAN;
9299
+            $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
9300
+            $this->check( $bean );
9301
+            $this->oodb->signal( 'dispense', $bean );
9302
+            $beans[] = $bean;
9303
+        }
9304
+
9305
+        return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
9306
+    }
9307
+
9308
+    /**
9309
+     * Loads a bean from the object database.
9310
+     * It searches for a OODBBean Bean Object in the
9311
+     * database. It does not matter how this bean has been stored.
9312
+     * RedBean uses the primary key ID $id and the string $type
9313
+     * to find the bean. The $type specifies what kind of bean you
9314
+     * are looking for; this is the same type as used with the
9315
+     * dispense() function. If RedBean finds the bean it will return
9316
+     * the OODB Bean object; if it cannot find the bean
9317
+     * RedBean will return a new bean of type $type and with
9318
+     * primary key ID 0. In the latter case it acts basically the
9319
+     * same as dispense().
9320
+     *
9321
+     * Important note:
9322
+     * If the bean cannot be found in the database a new bean of
9323
+     * the specified type will be generated and returned.
9324
+     *
9325
+     * @param string  $type type of bean you want to load
9326
+     * @param integer $id   ID of the bean you want to load
9327
+     *
9328
+     * @return OODBBean
9329
+     */
9330
+    public function load( $type, $id )
9331
+    {
9332
+        $rows = array();
9333
+        $bean = $this->dispense( $type );
9334
+        if ( isset( $this->stash[$this->nesting][$id] ) ) {
9335
+            $row = $this->stash[$this->nesting][$id];
9336
+        } else {
9337
+            try {
9338
+                $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
9339
+            } catch ( SQLException $exception ) {
9340
+                if (
9341
+                    $this->writer->sqlStateIn(
9342
+                        $exception->getSQLState(),
9343
+                        array(
9344
+                            QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
9345
+                            QueryWriter::C_SQLSTATE_NO_SUCH_TABLE
9346
+                        ),
9347
+                        $exception->getDriverDetails()
9348
+                    )
9349
+                ) {
9350
+                    $rows = array();
9351
+                } else {
9352
+                    throw $exception;
9353
+                }
9354
+            }
9355
+            if ( !count( $rows ) ) {
9356
+                return $bean;
9357
+            }
9358
+            $row = array_pop( $rows );
9359
+        }
9360
+        $bean->importRow( $row );
9361
+        $this->nesting++;
9362
+        $this->oodb->signal( 'open', $bean );
9363
+        $this->nesting--;
9364
+
9365
+        return $bean->setMeta( 'tainted', FALSE );
9366
+    }
9367 9367
 }
9368 9368
 }
9369 9369
 
@@ -9396,180 +9396,180 @@  discard block
 block discarded – undo
9396 9396
  */
9397 9397
 class Frozen extends Repository
9398 9398
 {
9399
-	/**
9400
-	 * Exception handler.
9401
-	 * Fluid and Frozen mode have different ways of handling
9402
-	 * exceptions. Fluid mode (using the fluid repository) ignores
9403
-	 * exceptions caused by the following:
9404
-	 *
9405
-	 * - missing tables
9406
-	 * - missing column
9407
-	 *
9408
-	 * In these situations, the repository will behave as if
9409
-	 * no beans could be found. This is because in fluid mode
9410
-	 * it might happen to query a table or column that has not been
9411
-	 * created yet. In frozen mode, this is not supposed to happen
9412
-	 * and the corresponding exceptions will be thrown.
9413
-	 *
9414
-	 * @param \Exception $exception exception
9415
-	 *
9416
-	 * @return void
9417
-	 */
9418
-	protected function handleException( \Exception $exception )
9419
-	{
9420
-		throw $exception;
9421
-	}
9422
-
9423
-	/**
9424
-	 * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
9425
-	 * method. When all lists and embedded beans (parent objects) have been processed and
9426
-	 * removed from the original bean the bean is passed to this method to be stored
9427
-	 * in the database.
9428
-	 *
9429
-	 * @param OODBBean $bean the clean bean
9430
-	 *
9431
-	 * @return void
9432
-	 */
9433
-	protected function storeBean( OODBBean $bean )
9434
-	{
9435
-		if ( $bean->getMeta( 'changed' ) ) {
9436
-
9437
-			list( $properties, $table ) = $bean->getPropertiesAndType();
9438
-			$id = $properties['id'];
9439
-			unset($properties['id']);
9440
-			$updateValues = array();
9441
-			$k1 = 'property';
9442
-			$k2 = 'value';
9443
-
9444
-			$partial = ( $this->partialBeans === TRUE || ( is_array( $this->partialBeans ) && in_array( $table, $this->partialBeans ) ) );
9445
-			if ( $partial ) {
9446
-				$mask = $bean->getMeta( 'changelist' );
9447
-				$bean->setMeta( 'changelist', array() );
9448
-			}
9449
-
9450
-			foreach( $properties as $key => $value ) {
9451
-				if ( $partial && !in_array( $key, $mask ) ) continue;
9452
-				$updateValues[] = array( $k1 => $key, $k2 => $value );
9453
-			}
9454
-			$bean->id = $this->writer->updateRecord( $table, $updateValues, $id );
9455
-			$bean->setMeta( 'changed', FALSE );
9456
-		}
9457
-		$bean->setMeta( 'tainted', FALSE );
9458
-	}
9459
-
9460
-	/**
9461
-	 * Part of the store() functionality.
9462
-	 * Handles all new additions after the bean has been saved.
9463
-	 * Stores addition bean in own-list, extracts the id and
9464
-	 * adds a foreign key. Also adds a constraint in case the type is
9465
-	 * in the dependent list.
9466
-	 *
9467
-	 * Note that this method raises a custom exception if the bean
9468
-	 * is not an instance of OODBBean. Therefore it does not use
9469
-	 * a type hint. This allows the user to take action in case
9470
-	 * invalid objects are passed in the list.
9471
-	 *
9472
-	 * @param OODBBean $bean         bean to process
9473
-	 * @param array    $ownAdditions list of addition beans in own-list
9474
-	 *
9475
-	 * @return void
9476
-	 * @throws RedException
9477
-	 */
9478
-	protected function processAdditions( $bean, $ownAdditions )
9479
-	{
9480
-		$beanType = $bean->getMeta( 'type' );
9481
-
9482
-		$cachedIndex = array();
9483
-		foreach ( $ownAdditions as $addition ) {
9484
-			if ( $addition instanceof OODBBean ) {
9485
-
9486
-				$myFieldLink = $beanType . '_id';
9487
-				$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
9488
-				if ( $alias ) $myFieldLink = $alias . '_id';
9489
-
9490
-				$addition->$myFieldLink = $bean->id;
9491
-				$addition->setMeta( 'cast.' . $myFieldLink, 'id' );
9492
-				$this->store( $addition );
9493
-
9494
-			} else {
9495
-				throw new RedException( 'Array may only contain OODBBeans' );
9496
-			}
9497
-		}
9498
-	}
9499
-
9500
-	/**
9501
-	 * Dispenses a new bean (a OODBBean Bean Object)
9502
-	 * of the specified type. Always
9503
-	 * use this function to get an empty bean object. Never
9504
-	 * instantiate a OODBBean yourself because it needs
9505
-	 * to be configured before you can use it with RedBean. This
9506
-	 * function applies the appropriate initialization /
9507
-	 * configuration for you.
9508
-	 *
9509
-	 * @param string  $type              type of bean you want to dispense
9510
-	 * @param int  $number            number of beans you would like to get
9511
-	 * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
9512
-	 *
9513
-	 * @return OODBBean
9514
-	 */
9515
-	public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
9516
-	{
9517
-		$OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
9518
-		$beans = array();
9519
-		for ( $i = 0; $i < $number; $i++ ) {
9520
-			/** @var \RedBeanPHP\OODBBean $bean */
9521
-			$bean = new $OODBBEAN;
9522
-			$bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
9523
-			$this->oodb->signal( 'dispense', $bean );
9524
-			$beans[] = $bean;
9525
-		}
9526
-
9527
-		return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
9528
-	}
9529
-
9530
-	/**
9531
-	 * Loads a bean from the object database.
9532
-	 * It searches for a OODBBean Bean Object in the
9533
-	 * database. It does not matter how this bean has been stored.
9534
-	 * RedBean uses the primary key ID $id and the string $type
9535
-	 * to find the bean. The $type specifies what kind of bean you
9536
-	 * are looking for; this is the same type as used with the
9537
-	 * dispense() function. If RedBean finds the bean it will return
9538
-	 * the OODB Bean object; if it cannot find the bean
9539
-	 * RedBean will return a new bean of type $type and with
9540
-	 * primary key ID 0. In the latter case it acts basically the
9541
-	 * same as dispense().
9542
-	 *
9543
-	 * Important note:
9544
-	 * If the bean cannot be found in the database a new bean of
9545
-	 * the specified type will be generated and returned.
9546
-	 *
9547
-	 * @param string  $type type of bean you want to load
9548
-	 * @param integer $id   ID of the bean you want to load
9549
-	 *
9550
-	 * @return OODBBean
9551
-	 * @throws SQLException
9552
-	 */
9553
-	public function load( $type, $id )
9554
-	{
9555
-		$rows = array();
9556
-		$bean = $this->dispense( $type );
9557
-		if ( isset( $this->stash[$this->nesting][$id] ) ) {
9558
-			$row = $this->stash[$this->nesting][$id];
9559
-		} else {
9560
-			$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
9561
-			if ( !count( $rows ) ) {
9562
-				return $bean;
9563
-			}
9564
-			$row = array_pop( $rows );
9565
-		}
9566
-		$bean->importRow( $row );
9567
-		$this->nesting++;
9568
-		$this->oodb->signal( 'open', $bean );
9569
-		$this->nesting--;
9570
-
9571
-		return $bean->setMeta( 'tainted', FALSE );
9572
-	}
9399
+    /**
9400
+     * Exception handler.
9401
+     * Fluid and Frozen mode have different ways of handling
9402
+     * exceptions. Fluid mode (using the fluid repository) ignores
9403
+     * exceptions caused by the following:
9404
+     *
9405
+     * - missing tables
9406
+     * - missing column
9407
+     *
9408
+     * In these situations, the repository will behave as if
9409
+     * no beans could be found. This is because in fluid mode
9410
+     * it might happen to query a table or column that has not been
9411
+     * created yet. In frozen mode, this is not supposed to happen
9412
+     * and the corresponding exceptions will be thrown.
9413
+     *
9414
+     * @param \Exception $exception exception
9415
+     *
9416
+     * @return void
9417
+     */
9418
+    protected function handleException( \Exception $exception )
9419
+    {
9420
+        throw $exception;
9421
+    }
9422
+
9423
+    /**
9424
+     * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
9425
+     * method. When all lists and embedded beans (parent objects) have been processed and
9426
+     * removed from the original bean the bean is passed to this method to be stored
9427
+     * in the database.
9428
+     *
9429
+     * @param OODBBean $bean the clean bean
9430
+     *
9431
+     * @return void
9432
+     */
9433
+    protected function storeBean( OODBBean $bean )
9434
+    {
9435
+        if ( $bean->getMeta( 'changed' ) ) {
9436
+
9437
+            list( $properties, $table ) = $bean->getPropertiesAndType();
9438
+            $id = $properties['id'];
9439
+            unset($properties['id']);
9440
+            $updateValues = array();
9441
+            $k1 = 'property';
9442
+            $k2 = 'value';
9443
+
9444
+            $partial = ( $this->partialBeans === TRUE || ( is_array( $this->partialBeans ) && in_array( $table, $this->partialBeans ) ) );
9445
+            if ( $partial ) {
9446
+                $mask = $bean->getMeta( 'changelist' );
9447
+                $bean->setMeta( 'changelist', array() );
9448
+            }
9449
+
9450
+            foreach( $properties as $key => $value ) {
9451
+                if ( $partial && !in_array( $key, $mask ) ) continue;
9452
+                $updateValues[] = array( $k1 => $key, $k2 => $value );
9453
+            }
9454
+            $bean->id = $this->writer->updateRecord( $table, $updateValues, $id );
9455
+            $bean->setMeta( 'changed', FALSE );
9456
+        }
9457
+        $bean->setMeta( 'tainted', FALSE );
9458
+    }
9459
+
9460
+    /**
9461
+     * Part of the store() functionality.
9462
+     * Handles all new additions after the bean has been saved.
9463
+     * Stores addition bean in own-list, extracts the id and
9464
+     * adds a foreign key. Also adds a constraint in case the type is
9465
+     * in the dependent list.
9466
+     *
9467
+     * Note that this method raises a custom exception if the bean
9468
+     * is not an instance of OODBBean. Therefore it does not use
9469
+     * a type hint. This allows the user to take action in case
9470
+     * invalid objects are passed in the list.
9471
+     *
9472
+     * @param OODBBean $bean         bean to process
9473
+     * @param array    $ownAdditions list of addition beans in own-list
9474
+     *
9475
+     * @return void
9476
+     * @throws RedException
9477
+     */
9478
+    protected function processAdditions( $bean, $ownAdditions )
9479
+    {
9480
+        $beanType = $bean->getMeta( 'type' );
9481
+
9482
+        $cachedIndex = array();
9483
+        foreach ( $ownAdditions as $addition ) {
9484
+            if ( $addition instanceof OODBBean ) {
9485
+
9486
+                $myFieldLink = $beanType . '_id';
9487
+                $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
9488
+                if ( $alias ) $myFieldLink = $alias . '_id';
9489
+
9490
+                $addition->$myFieldLink = $bean->id;
9491
+                $addition->setMeta( 'cast.' . $myFieldLink, 'id' );
9492
+                $this->store( $addition );
9493
+
9494
+            } else {
9495
+                throw new RedException( 'Array may only contain OODBBeans' );
9496
+            }
9497
+        }
9498
+    }
9499
+
9500
+    /**
9501
+     * Dispenses a new bean (a OODBBean Bean Object)
9502
+     * of the specified type. Always
9503
+     * use this function to get an empty bean object. Never
9504
+     * instantiate a OODBBean yourself because it needs
9505
+     * to be configured before you can use it with RedBean. This
9506
+     * function applies the appropriate initialization /
9507
+     * configuration for you.
9508
+     *
9509
+     * @param string  $type              type of bean you want to dispense
9510
+     * @param int  $number            number of beans you would like to get
9511
+     * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
9512
+     *
9513
+     * @return OODBBean
9514
+     */
9515
+    public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
9516
+    {
9517
+        $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
9518
+        $beans = array();
9519
+        for ( $i = 0; $i < $number; $i++ ) {
9520
+            /** @var \RedBeanPHP\OODBBean $bean */
9521
+            $bean = new $OODBBEAN;
9522
+            $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
9523
+            $this->oodb->signal( 'dispense', $bean );
9524
+            $beans[] = $bean;
9525
+        }
9526
+
9527
+        return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
9528
+    }
9529
+
9530
+    /**
9531
+     * Loads a bean from the object database.
9532
+     * It searches for a OODBBean Bean Object in the
9533
+     * database. It does not matter how this bean has been stored.
9534
+     * RedBean uses the primary key ID $id and the string $type
9535
+     * to find the bean. The $type specifies what kind of bean you
9536
+     * are looking for; this is the same type as used with the
9537
+     * dispense() function. If RedBean finds the bean it will return
9538
+     * the OODB Bean object; if it cannot find the bean
9539
+     * RedBean will return a new bean of type $type and with
9540
+     * primary key ID 0. In the latter case it acts basically the
9541
+     * same as dispense().
9542
+     *
9543
+     * Important note:
9544
+     * If the bean cannot be found in the database a new bean of
9545
+     * the specified type will be generated and returned.
9546
+     *
9547
+     * @param string  $type type of bean you want to load
9548
+     * @param integer $id   ID of the bean you want to load
9549
+     *
9550
+     * @return OODBBean
9551
+     * @throws SQLException
9552
+     */
9553
+    public function load( $type, $id )
9554
+    {
9555
+        $rows = array();
9556
+        $bean = $this->dispense( $type );
9557
+        if ( isset( $this->stash[$this->nesting][$id] ) ) {
9558
+            $row = $this->stash[$this->nesting][$id];
9559
+        } else {
9560
+            $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
9561
+            if ( !count( $rows ) ) {
9562
+                return $bean;
9563
+            }
9564
+            $row = array_pop( $rows );
9565
+        }
9566
+        $bean->importRow( $row );
9567
+        $this->nesting++;
9568
+        $this->oodb->signal( 'open', $bean );
9569
+        $this->nesting--;
9570
+
9571
+        return $bean->setMeta( 'tainted', FALSE );
9572
+    }
9573 9573
 }
9574 9574
 }
9575 9575
 
@@ -9602,549 +9602,549 @@  discard block
 block discarded – undo
9602 9602
  */
9603 9603
 class OODB extends Observable
9604 9604
 {
9605
-	/**
9606
-	 * @var array
9607
-	 */
9608
-	private static $sqlFilters = array();
9609
-
9610
-	/**
9611
-	 * @var array
9612
-	 */
9613
-	protected $chillList = array();
9614
-
9615
-	/**
9616
-	 * @var array
9617
-	 */
9618
-	protected $stash = NULL;
9619
-
9620
-	/*
9605
+    /**
9606
+     * @var array
9607
+     */
9608
+    private static $sqlFilters = array();
9609
+
9610
+    /**
9611
+     * @var array
9612
+     */
9613
+    protected $chillList = array();
9614
+
9615
+    /**
9616
+     * @var array
9617
+     */
9618
+    protected $stash = NULL;
9619
+
9620
+    /*
9621 9621
 	 * @var integer
9622 9622
 	 */
9623
-	protected $nesting = 0;
9624
-
9625
-	/**
9626
-	 * @var DBAdapter
9627
-	 */
9628
-	protected $writer;
9629
-
9630
-	/**
9631
-	 * @var boolean
9632
-	 */
9633
-	protected $isFrozen = FALSE;
9634
-
9635
-	/**
9636
-	 * @var FacadeBeanHelper
9637
-	 */
9638
-	protected $beanhelper = NULL;
9639
-
9640
-	/**
9641
-	 * @var AssociationManager
9642
-	 */
9643
-	protected $assocManager = NULL;
9644
-
9645
-	/**
9646
-	 * @var Repository
9647
-	 */
9648
-	protected $repository = NULL;
9649
-
9650
-	/**
9651
-	 * @var FrozenRepo
9652
-	 */
9653
-	protected $frozenRepository = NULL;
9654
-
9655
-	/**
9656
-	 * @var FluidRepo
9657
-	 */
9658
-	protected $fluidRepository = NULL;
9659
-
9660
-	/**
9661
-	 * @var boolean
9662
-	 */
9663
-	protected static $autoClearHistoryAfterStore = FALSE;
9664
-
9665
-	/**
9666
-	 * If set to TRUE, this method will call clearHistory every time
9667
-	 * the bean gets stored.
9668
-	 *
9669
-	 * @param boolean $autoClear auto clear option
9670
-	 *
9671
-	 * @return void
9672
-	 */
9673
-	public static function autoClearHistoryAfterStore( $autoClear = TRUE )
9674
-	{
9675
-		self::$autoClearHistoryAfterStore = (boolean) $autoClear;
9676
-	}
9677
-
9678
-	/**
9679
-	 * Unboxes a bean from a FUSE model if needed and checks whether the bean is
9680
-	 * an instance of OODBBean.
9681
-	 *
9682
-	 * @param OODBBean $bean bean you wish to unbox
9683
-	 *
9684
-	 * @return OODBBean
9685
-	 */
9686
-	protected function unboxIfNeeded( $bean )
9687
-	{
9688
-		if ( $bean instanceof SimpleModel ) {
9689
-			$bean = $bean->unbox();
9690
-		}
9691
-		if ( !( $bean instanceof OODBBean ) ) {
9692
-			throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) );
9693
-		}
9694
-
9695
-		return $bean;
9696
-	}
9697
-
9698
-	/**
9699
-	 * Constructor, requires a query writer.
9700
-	 * Most of the time, you do not need to use this constructor,
9701
-	 * since the facade takes care of constructing and wiring the
9702
-	 * RedBeanPHP core objects. However if you would like to
9703
-	 * assemble an OODB instance yourself, this is how it works:
9704
-	 *
9705
-	 * Usage:
9706
-	 *
9707
-	 * <code>
9708
-	 * $database = new RPDO( $dsn, $user, $pass );
9709
-	 * $adapter = new DBAdapter( $database );
9710
-	 * $writer = new PostgresWriter( $adapter );
9711
-	 * $oodb = new OODB( $writer, FALSE );
9712
-	 * $bean = $oodb->dispense( 'bean' );
9713
-	 * $bean->name = 'coffeeBean';
9714
-	 * $id = $oodb->store( $bean );
9715
-	 * $bean = $oodb->load( 'bean', $id );
9716
-	 * </code>
9717
-	 *
9718
-	 * The example above creates the 3 RedBeanPHP core objects:
9719
-	 * the Adapter, the Query Writer and the OODB instance and
9720
-	 * wires them together. The example also demonstrates some of
9721
-	 * the methods that can be used with OODB, as you see, they
9722
-	 * closely resemble their facade counterparts.
9723
-	 *
9724
-	 * The wiring process: create an RPDO instance using your database
9725
-	 * connection parameters. Create a database adapter from the RPDO
9726
-	 * object and pass that to the constructor of the writer. Next,
9727
-	 * create an OODB instance from the writer. Now you have an OODB
9728
-	 * object.
9729
-	 *
9730
-	 * @param QueryWriter   $writer writer
9731
-	 * @param array|boolean $frozen mode of operation: TRUE (frozen), FALSE (default, fluid) or ARRAY (chilled)
9732
-	 */
9733
-	public function __construct( QueryWriter $writer, $frozen = FALSE )
9734
-	{
9735
-		if ( $writer instanceof QueryWriter ) {
9736
-			$this->writer = $writer;
9737
-		}
9738
-
9739
-		$this->freeze( $frozen );
9740
-	}
9741
-
9742
-	/**
9743
-	 * Toggles fluid or frozen mode. In fluid mode the database
9744
-	 * structure is adjusted to accomodate your objects. In frozen mode
9745
-	 * this is not the case.
9746
-	 *
9747
-	 * You can also pass an array containing a selection of frozen types.
9748
-	 * Let's call this chilly mode, it's just like fluid mode except that
9749
-	 * certain types (i.e. tables) aren't touched.
9750
-	 *
9751
-	 * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode
9752
-	 *
9753
-	 * @return void
9754
-	 */
9755
-	public function freeze( $toggle )
9756
-	{
9757
-		if ( is_array( $toggle ) ) {
9758
-			$this->chillList = $toggle;
9759
-			$this->isFrozen  = FALSE;
9760
-		} else {
9761
-			$this->isFrozen = (boolean) $toggle;
9762
-		}
9763
-
9764
-		if ( $this->isFrozen ) {
9765
-			if ( !$this->frozenRepository ) {
9766
-				$this->frozenRepository = new FrozenRepo( $this, $this->writer );
9767
-			}
9768
-
9769
-			$this->repository = $this->frozenRepository;
9770
-
9771
-		} else {
9772
-			if ( !$this->fluidRepository ) {
9773
-				$this->fluidRepository = new FluidRepo( $this, $this->writer );
9774
-			}
9775
-
9776
-			$this->repository = $this->fluidRepository;
9777
-		}
9778
-
9779
-		if ( count( self::$sqlFilters ) ) {
9780
-			AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
9781
-		}
9782
-
9783
-	}
9784
-
9785
-	/**
9786
-	 * Returns the current mode of operation of RedBean.
9787
-	 * In fluid mode the database
9788
-	 * structure is adjusted to accomodate your objects.
9789
-	 * In frozen mode
9790
-	 * this is not the case.
9791
-	 *
9792
-	 * @return boolean
9793
-	 */
9794
-	public function isFrozen()
9795
-	{
9796
-		return (bool) $this->isFrozen;
9797
-	}
9798
-
9799
-	/**
9800
-	 * Determines whether a type is in the chill list.
9801
-	 * If a type is 'chilled' it's frozen, so its schema cannot be
9802
-	 * changed anymore. However other bean types may still be modified.
9803
-	 * This method is a convenience method for other objects to check if
9804
-	 * the schema of a certain type is locked for modification.
9805
-	 *
9806
-	 * @param string $type the type you wish to check
9807
-	 *
9808
-	 * @return boolean
9809
-	 */
9810
-	public function isChilled( $type )
9811
-	{
9812
-		return (boolean) ( in_array( $type, $this->chillList ) );
9813
-	}
9814
-
9815
-	/**
9816
-	 * Dispenses a new bean (a OODBBean Bean Object)
9817
-	 * of the specified type. Always
9818
-	 * use this function to get an empty bean object. Never
9819
-	 * instantiate a OODBBean yourself because it needs
9820
-	 * to be configured before you can use it with RedBean. This
9821
-	 * function applies the appropriate initialization /
9822
-	 * configuration for you.
9823
-	 *
9824
-	 * @param string  $type              type of bean you want to dispense
9825
-	 * @param string  $number            number of beans you would like to get
9826
-	 * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
9827
-	 *
9828
-	 * @return OODBBean
9829
-	 */
9830
-	public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
9831
-	{
9832
-		if ( $number < 1 ) {
9833
-			if ( $alwaysReturnArray ) return array();
9834
-			return NULL;
9835
-		}
9836
-
9837
-		return $this->repository->dispense( $type, $number, $alwaysReturnArray );
9838
-	}
9839
-
9840
-	/**
9841
-	 * Sets bean helper to be given to beans.
9842
-	 * Bean helpers assist beans in getting a reference to a toolbox.
9843
-	 *
9844
-	 * @param BeanHelper $beanhelper helper
9845
-	 *
9846
-	 * @return void
9847
-	 */
9848
-	public function setBeanHelper( BeanHelper $beanhelper )
9849
-	{
9850
-		$this->beanhelper = $beanhelper;
9851
-	}
9852
-
9853
-	/**
9854
-	 * Returns the current bean helper.
9855
-	 * Bean helpers assist beans in getting a reference to a toolbox.
9856
-	 *
9857
-	 * @return BeanHelper
9858
-	 */
9859
-	public function getBeanHelper()
9860
-	{
9861
-		return $this->beanhelper;
9862
-	}
9863
-
9864
-	/**
9865
-	 * Checks whether a OODBBean bean is valid.
9866
-	 * If the type is not valid or the ID is not valid it will
9867
-	 * throw an exception: Security.
9868
-	 *
9869
-	 * @param OODBBean $bean the bean that needs to be checked
9870
-	 *
9871
-	 * @return void
9872
-	 */
9873
-	public function check( OODBBean $bean )
9874
-	{
9875
-		$this->repository->check( $bean );
9876
-	}
9877
-
9878
-	/**
9879
-	 * Searches the database for a bean that matches conditions $conditions and sql $addSQL
9880
-	 * and returns an array containing all the beans that have been found.
9881
-	 *
9882
-	 * Conditions need to take form:
9883
-	 *
9884
-	 * <code>
9885
-	 * array(
9886
-	 *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
9887
-	 *    'PROPERTY' => array( POSSIBLE VALUES... )
9888
-	 * );
9889
-	 * </code>
9890
-	 *
9891
-	 * All conditions are glued together using the AND-operator, while all value lists
9892
-	 * are glued using IN-operators thus acting as OR-conditions.
9893
-	 *
9894
-	 * Note that you can use property names; the columns will be extracted using the
9895
-	 * appropriate bean formatter.
9896
-	 *
9897
-	 * @param string $type       type of beans you are looking for
9898
-	 * @param array  $conditions list of conditions
9899
-	 * @param string $sql        SQL to be used in query
9900
-	 * @param array  $bindings   a list of values to bind to query parameters
9901
-	 *
9902
-	 * @return array
9903
-	 */
9904
-	public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
9905
-	{
9906
-		return $this->repository->find( $type, $conditions, $sql, $bindings );
9907
-	}
9908
-
9909
-	/**
9910
-	 * Same as find() but returns a BeanCollection.
9911
-	 *
9912
-	 * @param string $type     type of beans you are looking for
9913
-	 * @param string $sql      SQL to be used in query
9914
-	 * @param array  $bindings a list of values to bind to query parameters
9915
-	 *
9916
-	 * @return BeanCollection
9917
-	 */
9918
-	public function findCollection(  $type, $sql = NULL, $bindings = array() )
9919
-	{
9920
-		return $this->repository->findCollection( $type, $sql, $bindings );
9921
-	}
9922
-
9923
-	/**
9924
-	 * Checks whether the specified table already exists in the database.
9925
-	 * Not part of the Object Database interface!
9926
-	 *
9927
-	 * @deprecated Use AQueryWriter::typeExists() instead.
9928
-	 *
9929
-	 * @param string $table table name
9930
-	 *
9931
-	 * @return boolean
9932
-	 */
9933
-	public function tableExists( $table )
9934
-	{
9935
-		return $this->repository->tableExists( $table );
9936
-	}
9937
-
9938
-	/**
9939
-	 * Stores a bean in the database. This method takes a
9940
-	 * OODBBean Bean Object $bean and stores it
9941
-	 * in the database. If the database schema is not compatible
9942
-	 * with this bean and RedBean runs in fluid mode the schema
9943
-	 * will be altered to store the bean correctly.
9944
-	 * If the database schema is not compatible with this bean and
9945
-	 * RedBean runs in frozen mode it will throw an exception.
9946
-	 * This function returns the primary key ID of the inserted
9947
-	 * bean.
9948
-	 *
9949
-	 * The return value is an integer if possible. If it is not possible to
9950
-	 * represent the value as an integer a string will be returned. We use
9951
-	 * explicit casts instead of functions to preserve performance
9952
-	 * (0.13 vs 0.28 for 10000 iterations on Core i3).
9953
-	 *
9954
-	 * @param OODBBean|SimpleModel $bean bean to store
9955
-	 *
9956
-	 * @return integer|string
9957
-	 */
9958
-	public function store( $bean )
9959
-	{
9960
-		$bean = $this->unboxIfNeeded( $bean );
9961
-		$id = $this->repository->store( $bean );
9962
-		if ( self::$autoClearHistoryAfterStore ) {
9963
-				$bean->clearHistory();
9964
-		}
9965
-		return $id;
9966
-	}
9967
-
9968
-	/**
9969
-	 * Loads a bean from the object database.
9970
-	 * It searches for a OODBBean Bean Object in the
9971
-	 * database. It does not matter how this bean has been stored.
9972
-	 * RedBean uses the primary key ID $id and the string $type
9973
-	 * to find the bean. The $type specifies what kind of bean you
9974
-	 * are looking for; this is the same type as used with the
9975
-	 * dispense() function. If RedBean finds the bean it will return
9976
-	 * the OODB Bean object; if it cannot find the bean
9977
-	 * RedBean will return a new bean of type $type and with
9978
-	 * primary key ID 0. In the latter case it acts basically the
9979
-	 * same as dispense().
9980
-	 *
9981
-	 * Important note:
9982
-	 * If the bean cannot be found in the database a new bean of
9983
-	 * the specified type will be generated and returned.
9984
-	 *
9985
-	 * @param string  $type type of bean you want to load
9986
-	 * @param integer $id   ID of the bean you want to load
9987
-	 *
9988
-	 * @return OODBBean
9989
-	 */
9990
-	public function load( $type, $id )
9991
-	{
9992
-		return $this->repository->load( $type, $id );
9993
-	}
9994
-
9995
-	/**
9996
-	 * Removes a bean from the database.
9997
-	 * This function will remove the specified OODBBean
9998
-	 * Bean Object from the database.
9999
-	 *
10000
-	 * @param OODBBean|SimpleModel $bean bean you want to remove from database
10001
-	 *
10002
-	 * @return void
10003
-	 */
10004
-	public function trash( $bean )
10005
-	{
10006
-		$bean = $this->unboxIfNeeded( $bean );
10007
-		return $this->repository->trash( $bean );
10008
-	}
10009
-
10010
-	/**
10011
-	 * Returns an array of beans. Pass a type and a series of ids and
10012
-	 * this method will bring you the corresponding beans.
10013
-	 *
10014
-	 * important note: Because this method loads beans using the load()
10015
-	 * function (but faster) it will return empty beans with ID 0 for
10016
-	 * every bean that could not be located. The resulting beans will have the
10017
-	 * passed IDs as their keys.
10018
-	 *
10019
-	 * @param string $type type of beans
10020
-	 * @param array  $ids  ids to load
10021
-	 *
10022
-	 * @return array
10023
-	 */
10024
-	public function batch( $type, $ids )
10025
-	{
10026
-		return $this->repository->batch( $type, $ids );
10027
-	}
10028
-
10029
-	/**
10030
-	 * This is a convenience method; it converts database rows
10031
-	 * (arrays) into beans. Given a type and a set of rows this method
10032
-	 * will return an array of beans of the specified type loaded with
10033
-	 * the data fields provided by the result set from the database.
10034
-	 *
10035
-	 * @param string $type type of beans you would like to have
10036
-	 * @param array  $rows rows from the database result
10037
-	 * @param string $mask mask to apply for meta data
10038
-	 *
10039
-	 * @return array
10040
-	 */
10041
-	public function convertToBeans( $type, $rows, $mask = NULL )
10042
-	{
10043
-		return $this->repository->convertToBeans( $type, $rows, $mask );
10044
-	}
10045
-
10046
-	/**
10047
-	 * Counts the number of beans of type $type.
10048
-	 * This method accepts a second argument to modify the count-query.
10049
-	 * A third argument can be used to provide bindings for the SQL snippet.
10050
-	 *
10051
-	 * @param string $type     type of bean we are looking for
10052
-	 * @param string $addSQL   additional SQL snippet
10053
-	 * @param array  $bindings parameters to bind to SQL
10054
-	 *
10055
-	 * @return integer
10056
-	 */
10057
-	public function count( $type, $addSQL = '', $bindings = array() )
10058
-	{
10059
-		return $this->repository->count( $type, $addSQL, $bindings );
10060
-	}
10061
-
10062
-	/**
10063
-	 * Trash all beans of a given type. Wipes an entire type of bean.
10064
-	 *
10065
-	 * @param string $type type of bean you wish to delete all instances of
10066
-	 *
10067
-	 * @return boolean
10068
-	 */
10069
-	public function wipe( $type )
10070
-	{
10071
-		return $this->repository->wipe( $type );
10072
-	}
10073
-
10074
-	/**
10075
-	 * Returns an Association Manager for use with OODB.
10076
-	 * A simple getter function to obtain a reference to the association manager used for
10077
-	 * storage and more.
10078
-	 *
10079
-	 * @return AssociationManager
10080
-	 */
10081
-	public function getAssociationManager()
10082
-	{
10083
-		if ( !isset( $this->assocManager ) ) {
10084
-			throw new RedException( 'No association manager available.' );
10085
-		}
10086
-
10087
-		return $this->assocManager;
10088
-	}
10089
-
10090
-	/**
10091
-	 * Sets the association manager instance to be used by this OODB.
10092
-	 * A simple setter function to set the association manager to be used for storage and
10093
-	 * more.
10094
-	 *
10095
-	 * @param AssociationManager $assocManager sets the association manager to be used
10096
-	 *
10097
-	 * @return void
10098
-	 */
10099
-	public function setAssociationManager( AssociationManager $assocManager )
10100
-	{
10101
-		$this->assocManager = $assocManager;
10102
-	}
10103
-
10104
-	/**
10105
-	 * Returns the currently used repository instance.
10106
-	 * For testing purposes only.
10107
-	 *
10108
-	 * @return Repository
10109
-	 */
10110
-	public function getCurrentRepository()
10111
-	{
10112
-		return $this->repository;
10113
-	}
10114
-
10115
-	/**
10116
-	 * Binds an SQL function to a column.
10117
-	 * This method can be used to setup a decode/encode scheme or
10118
-	 * perform UUID insertion. This method is especially useful for handling
10119
-	 * MySQL spatial columns, because they need to be processed first using
10120
-	 * the asText/GeomFromText functions.
10121
-	 *
10122
-	 * @param string $mode     mode to set function for, i.e. read or write
10123
-	 * @param string $field    field (table.column) to bind SQL function to
10124
-	 * @param string $function SQL function to bind to field
10125
-	 *
10126
-	 * @return void
10127
-	 */
10128
-	public function bindFunc( $mode, $field, $function )
10129
-	{
10130
-		list( $type, $property ) = explode( '.', $field );
10131
-		$mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ;
10132
-
10133
-		if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array();
10134
-		if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array();
10135
-
10136
-		if ( is_null( $function ) ) {
10137
-			unset( self::$sqlFilters[$mode][$type][$property] );
10138
-		} else {
10139
-			if ($mode === QueryWriter::C_SQLFILTER_WRITE) {
10140
-				self::$sqlFilters[$mode][$type][$property] = $function.'(?)';
10141
-			} else {
10142
-				self::$sqlFilters[$mode][$type][$property] = $function."($field)";
10143
-			}
10144
-		}
10145
-
10146
-		AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
10147
-	}
9623
+    protected $nesting = 0;
9624
+
9625
+    /**
9626
+     * @var DBAdapter
9627
+     */
9628
+    protected $writer;
9629
+
9630
+    /**
9631
+     * @var boolean
9632
+     */
9633
+    protected $isFrozen = FALSE;
9634
+
9635
+    /**
9636
+     * @var FacadeBeanHelper
9637
+     */
9638
+    protected $beanhelper = NULL;
9639
+
9640
+    /**
9641
+     * @var AssociationManager
9642
+     */
9643
+    protected $assocManager = NULL;
9644
+
9645
+    /**
9646
+     * @var Repository
9647
+     */
9648
+    protected $repository = NULL;
9649
+
9650
+    /**
9651
+     * @var FrozenRepo
9652
+     */
9653
+    protected $frozenRepository = NULL;
9654
+
9655
+    /**
9656
+     * @var FluidRepo
9657
+     */
9658
+    protected $fluidRepository = NULL;
9659
+
9660
+    /**
9661
+     * @var boolean
9662
+     */
9663
+    protected static $autoClearHistoryAfterStore = FALSE;
9664
+
9665
+    /**
9666
+     * If set to TRUE, this method will call clearHistory every time
9667
+     * the bean gets stored.
9668
+     *
9669
+     * @param boolean $autoClear auto clear option
9670
+     *
9671
+     * @return void
9672
+     */
9673
+    public static function autoClearHistoryAfterStore( $autoClear = TRUE )
9674
+    {
9675
+        self::$autoClearHistoryAfterStore = (boolean) $autoClear;
9676
+    }
9677
+
9678
+    /**
9679
+     * Unboxes a bean from a FUSE model if needed and checks whether the bean is
9680
+     * an instance of OODBBean.
9681
+     *
9682
+     * @param OODBBean $bean bean you wish to unbox
9683
+     *
9684
+     * @return OODBBean
9685
+     */
9686
+    protected function unboxIfNeeded( $bean )
9687
+    {
9688
+        if ( $bean instanceof SimpleModel ) {
9689
+            $bean = $bean->unbox();
9690
+        }
9691
+        if ( !( $bean instanceof OODBBean ) ) {
9692
+            throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) );
9693
+        }
9694
+
9695
+        return $bean;
9696
+    }
9697
+
9698
+    /**
9699
+     * Constructor, requires a query writer.
9700
+     * Most of the time, you do not need to use this constructor,
9701
+     * since the facade takes care of constructing and wiring the
9702
+     * RedBeanPHP core objects. However if you would like to
9703
+     * assemble an OODB instance yourself, this is how it works:
9704
+     *
9705
+     * Usage:
9706
+     *
9707
+     * <code>
9708
+     * $database = new RPDO( $dsn, $user, $pass );
9709
+     * $adapter = new DBAdapter( $database );
9710
+     * $writer = new PostgresWriter( $adapter );
9711
+     * $oodb = new OODB( $writer, FALSE );
9712
+     * $bean = $oodb->dispense( 'bean' );
9713
+     * $bean->name = 'coffeeBean';
9714
+     * $id = $oodb->store( $bean );
9715
+     * $bean = $oodb->load( 'bean', $id );
9716
+     * </code>
9717
+     *
9718
+     * The example above creates the 3 RedBeanPHP core objects:
9719
+     * the Adapter, the Query Writer and the OODB instance and
9720
+     * wires them together. The example also demonstrates some of
9721
+     * the methods that can be used with OODB, as you see, they
9722
+     * closely resemble their facade counterparts.
9723
+     *
9724
+     * The wiring process: create an RPDO instance using your database
9725
+     * connection parameters. Create a database adapter from the RPDO
9726
+     * object and pass that to the constructor of the writer. Next,
9727
+     * create an OODB instance from the writer. Now you have an OODB
9728
+     * object.
9729
+     *
9730
+     * @param QueryWriter   $writer writer
9731
+     * @param array|boolean $frozen mode of operation: TRUE (frozen), FALSE (default, fluid) or ARRAY (chilled)
9732
+     */
9733
+    public function __construct( QueryWriter $writer, $frozen = FALSE )
9734
+    {
9735
+        if ( $writer instanceof QueryWriter ) {
9736
+            $this->writer = $writer;
9737
+        }
9738
+
9739
+        $this->freeze( $frozen );
9740
+    }
9741
+
9742
+    /**
9743
+     * Toggles fluid or frozen mode. In fluid mode the database
9744
+     * structure is adjusted to accomodate your objects. In frozen mode
9745
+     * this is not the case.
9746
+     *
9747
+     * You can also pass an array containing a selection of frozen types.
9748
+     * Let's call this chilly mode, it's just like fluid mode except that
9749
+     * certain types (i.e. tables) aren't touched.
9750
+     *
9751
+     * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode
9752
+     *
9753
+     * @return void
9754
+     */
9755
+    public function freeze( $toggle )
9756
+    {
9757
+        if ( is_array( $toggle ) ) {
9758
+            $this->chillList = $toggle;
9759
+            $this->isFrozen  = FALSE;
9760
+        } else {
9761
+            $this->isFrozen = (boolean) $toggle;
9762
+        }
9763
+
9764
+        if ( $this->isFrozen ) {
9765
+            if ( !$this->frozenRepository ) {
9766
+                $this->frozenRepository = new FrozenRepo( $this, $this->writer );
9767
+            }
9768
+
9769
+            $this->repository = $this->frozenRepository;
9770
+
9771
+        } else {
9772
+            if ( !$this->fluidRepository ) {
9773
+                $this->fluidRepository = new FluidRepo( $this, $this->writer );
9774
+            }
9775
+
9776
+            $this->repository = $this->fluidRepository;
9777
+        }
9778
+
9779
+        if ( count( self::$sqlFilters ) ) {
9780
+            AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
9781
+        }
9782
+
9783
+    }
9784
+
9785
+    /**
9786
+     * Returns the current mode of operation of RedBean.
9787
+     * In fluid mode the database
9788
+     * structure is adjusted to accomodate your objects.
9789
+     * In frozen mode
9790
+     * this is not the case.
9791
+     *
9792
+     * @return boolean
9793
+     */
9794
+    public function isFrozen()
9795
+    {
9796
+        return (bool) $this->isFrozen;
9797
+    }
9798
+
9799
+    /**
9800
+     * Determines whether a type is in the chill list.
9801
+     * If a type is 'chilled' it's frozen, so its schema cannot be
9802
+     * changed anymore. However other bean types may still be modified.
9803
+     * This method is a convenience method for other objects to check if
9804
+     * the schema of a certain type is locked for modification.
9805
+     *
9806
+     * @param string $type the type you wish to check
9807
+     *
9808
+     * @return boolean
9809
+     */
9810
+    public function isChilled( $type )
9811
+    {
9812
+        return (boolean) ( in_array( $type, $this->chillList ) );
9813
+    }
9814
+
9815
+    /**
9816
+     * Dispenses a new bean (a OODBBean Bean Object)
9817
+     * of the specified type. Always
9818
+     * use this function to get an empty bean object. Never
9819
+     * instantiate a OODBBean yourself because it needs
9820
+     * to be configured before you can use it with RedBean. This
9821
+     * function applies the appropriate initialization /
9822
+     * configuration for you.
9823
+     *
9824
+     * @param string  $type              type of bean you want to dispense
9825
+     * @param string  $number            number of beans you would like to get
9826
+     * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
9827
+     *
9828
+     * @return OODBBean
9829
+     */
9830
+    public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
9831
+    {
9832
+        if ( $number < 1 ) {
9833
+            if ( $alwaysReturnArray ) return array();
9834
+            return NULL;
9835
+        }
9836
+
9837
+        return $this->repository->dispense( $type, $number, $alwaysReturnArray );
9838
+    }
9839
+
9840
+    /**
9841
+     * Sets bean helper to be given to beans.
9842
+     * Bean helpers assist beans in getting a reference to a toolbox.
9843
+     *
9844
+     * @param BeanHelper $beanhelper helper
9845
+     *
9846
+     * @return void
9847
+     */
9848
+    public function setBeanHelper( BeanHelper $beanhelper )
9849
+    {
9850
+        $this->beanhelper = $beanhelper;
9851
+    }
9852
+
9853
+    /**
9854
+     * Returns the current bean helper.
9855
+     * Bean helpers assist beans in getting a reference to a toolbox.
9856
+     *
9857
+     * @return BeanHelper
9858
+     */
9859
+    public function getBeanHelper()
9860
+    {
9861
+        return $this->beanhelper;
9862
+    }
9863
+
9864
+    /**
9865
+     * Checks whether a OODBBean bean is valid.
9866
+     * If the type is not valid or the ID is not valid it will
9867
+     * throw an exception: Security.
9868
+     *
9869
+     * @param OODBBean $bean the bean that needs to be checked
9870
+     *
9871
+     * @return void
9872
+     */
9873
+    public function check( OODBBean $bean )
9874
+    {
9875
+        $this->repository->check( $bean );
9876
+    }
9877
+
9878
+    /**
9879
+     * Searches the database for a bean that matches conditions $conditions and sql $addSQL
9880
+     * and returns an array containing all the beans that have been found.
9881
+     *
9882
+     * Conditions need to take form:
9883
+     *
9884
+     * <code>
9885
+     * array(
9886
+     *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
9887
+     *    'PROPERTY' => array( POSSIBLE VALUES... )
9888
+     * );
9889
+     * </code>
9890
+     *
9891
+     * All conditions are glued together using the AND-operator, while all value lists
9892
+     * are glued using IN-operators thus acting as OR-conditions.
9893
+     *
9894
+     * Note that you can use property names; the columns will be extracted using the
9895
+     * appropriate bean formatter.
9896
+     *
9897
+     * @param string $type       type of beans you are looking for
9898
+     * @param array  $conditions list of conditions
9899
+     * @param string $sql        SQL to be used in query
9900
+     * @param array  $bindings   a list of values to bind to query parameters
9901
+     *
9902
+     * @return array
9903
+     */
9904
+    public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
9905
+    {
9906
+        return $this->repository->find( $type, $conditions, $sql, $bindings );
9907
+    }
9908
+
9909
+    /**
9910
+     * Same as find() but returns a BeanCollection.
9911
+     *
9912
+     * @param string $type     type of beans you are looking for
9913
+     * @param string $sql      SQL to be used in query
9914
+     * @param array  $bindings a list of values to bind to query parameters
9915
+     *
9916
+     * @return BeanCollection
9917
+     */
9918
+    public function findCollection(  $type, $sql = NULL, $bindings = array() )
9919
+    {
9920
+        return $this->repository->findCollection( $type, $sql, $bindings );
9921
+    }
9922
+
9923
+    /**
9924
+     * Checks whether the specified table already exists in the database.
9925
+     * Not part of the Object Database interface!
9926
+     *
9927
+     * @deprecated Use AQueryWriter::typeExists() instead.
9928
+     *
9929
+     * @param string $table table name
9930
+     *
9931
+     * @return boolean
9932
+     */
9933
+    public function tableExists( $table )
9934
+    {
9935
+        return $this->repository->tableExists( $table );
9936
+    }
9937
+
9938
+    /**
9939
+     * Stores a bean in the database. This method takes a
9940
+     * OODBBean Bean Object $bean and stores it
9941
+     * in the database. If the database schema is not compatible
9942
+     * with this bean and RedBean runs in fluid mode the schema
9943
+     * will be altered to store the bean correctly.
9944
+     * If the database schema is not compatible with this bean and
9945
+     * RedBean runs in frozen mode it will throw an exception.
9946
+     * This function returns the primary key ID of the inserted
9947
+     * bean.
9948
+     *
9949
+     * The return value is an integer if possible. If it is not possible to
9950
+     * represent the value as an integer a string will be returned. We use
9951
+     * explicit casts instead of functions to preserve performance
9952
+     * (0.13 vs 0.28 for 10000 iterations on Core i3).
9953
+     *
9954
+     * @param OODBBean|SimpleModel $bean bean to store
9955
+     *
9956
+     * @return integer|string
9957
+     */
9958
+    public function store( $bean )
9959
+    {
9960
+        $bean = $this->unboxIfNeeded( $bean );
9961
+        $id = $this->repository->store( $bean );
9962
+        if ( self::$autoClearHistoryAfterStore ) {
9963
+                $bean->clearHistory();
9964
+        }
9965
+        return $id;
9966
+    }
9967
+
9968
+    /**
9969
+     * Loads a bean from the object database.
9970
+     * It searches for a OODBBean Bean Object in the
9971
+     * database. It does not matter how this bean has been stored.
9972
+     * RedBean uses the primary key ID $id and the string $type
9973
+     * to find the bean. The $type specifies what kind of bean you
9974
+     * are looking for; this is the same type as used with the
9975
+     * dispense() function. If RedBean finds the bean it will return
9976
+     * the OODB Bean object; if it cannot find the bean
9977
+     * RedBean will return a new bean of type $type and with
9978
+     * primary key ID 0. In the latter case it acts basically the
9979
+     * same as dispense().
9980
+     *
9981
+     * Important note:
9982
+     * If the bean cannot be found in the database a new bean of
9983
+     * the specified type will be generated and returned.
9984
+     *
9985
+     * @param string  $type type of bean you want to load
9986
+     * @param integer $id   ID of the bean you want to load
9987
+     *
9988
+     * @return OODBBean
9989
+     */
9990
+    public function load( $type, $id )
9991
+    {
9992
+        return $this->repository->load( $type, $id );
9993
+    }
9994
+
9995
+    /**
9996
+     * Removes a bean from the database.
9997
+     * This function will remove the specified OODBBean
9998
+     * Bean Object from the database.
9999
+     *
10000
+     * @param OODBBean|SimpleModel $bean bean you want to remove from database
10001
+     *
10002
+     * @return void
10003
+     */
10004
+    public function trash( $bean )
10005
+    {
10006
+        $bean = $this->unboxIfNeeded( $bean );
10007
+        return $this->repository->trash( $bean );
10008
+    }
10009
+
10010
+    /**
10011
+     * Returns an array of beans. Pass a type and a series of ids and
10012
+     * this method will bring you the corresponding beans.
10013
+     *
10014
+     * important note: Because this method loads beans using the load()
10015
+     * function (but faster) it will return empty beans with ID 0 for
10016
+     * every bean that could not be located. The resulting beans will have the
10017
+     * passed IDs as their keys.
10018
+     *
10019
+     * @param string $type type of beans
10020
+     * @param array  $ids  ids to load
10021
+     *
10022
+     * @return array
10023
+     */
10024
+    public function batch( $type, $ids )
10025
+    {
10026
+        return $this->repository->batch( $type, $ids );
10027
+    }
10028
+
10029
+    /**
10030
+     * This is a convenience method; it converts database rows
10031
+     * (arrays) into beans. Given a type and a set of rows this method
10032
+     * will return an array of beans of the specified type loaded with
10033
+     * the data fields provided by the result set from the database.
10034
+     *
10035
+     * @param string $type type of beans you would like to have
10036
+     * @param array  $rows rows from the database result
10037
+     * @param string $mask mask to apply for meta data
10038
+     *
10039
+     * @return array
10040
+     */
10041
+    public function convertToBeans( $type, $rows, $mask = NULL )
10042
+    {
10043
+        return $this->repository->convertToBeans( $type, $rows, $mask );
10044
+    }
10045
+
10046
+    /**
10047
+     * Counts the number of beans of type $type.
10048
+     * This method accepts a second argument to modify the count-query.
10049
+     * A third argument can be used to provide bindings for the SQL snippet.
10050
+     *
10051
+     * @param string $type     type of bean we are looking for
10052
+     * @param string $addSQL   additional SQL snippet
10053
+     * @param array  $bindings parameters to bind to SQL
10054
+     *
10055
+     * @return integer
10056
+     */
10057
+    public function count( $type, $addSQL = '', $bindings = array() )
10058
+    {
10059
+        return $this->repository->count( $type, $addSQL, $bindings );
10060
+    }
10061
+
10062
+    /**
10063
+     * Trash all beans of a given type. Wipes an entire type of bean.
10064
+     *
10065
+     * @param string $type type of bean you wish to delete all instances of
10066
+     *
10067
+     * @return boolean
10068
+     */
10069
+    public function wipe( $type )
10070
+    {
10071
+        return $this->repository->wipe( $type );
10072
+    }
10073
+
10074
+    /**
10075
+     * Returns an Association Manager for use with OODB.
10076
+     * A simple getter function to obtain a reference to the association manager used for
10077
+     * storage and more.
10078
+     *
10079
+     * @return AssociationManager
10080
+     */
10081
+    public function getAssociationManager()
10082
+    {
10083
+        if ( !isset( $this->assocManager ) ) {
10084
+            throw new RedException( 'No association manager available.' );
10085
+        }
10086
+
10087
+        return $this->assocManager;
10088
+    }
10089
+
10090
+    /**
10091
+     * Sets the association manager instance to be used by this OODB.
10092
+     * A simple setter function to set the association manager to be used for storage and
10093
+     * more.
10094
+     *
10095
+     * @param AssociationManager $assocManager sets the association manager to be used
10096
+     *
10097
+     * @return void
10098
+     */
10099
+    public function setAssociationManager( AssociationManager $assocManager )
10100
+    {
10101
+        $this->assocManager = $assocManager;
10102
+    }
10103
+
10104
+    /**
10105
+     * Returns the currently used repository instance.
10106
+     * For testing purposes only.
10107
+     *
10108
+     * @return Repository
10109
+     */
10110
+    public function getCurrentRepository()
10111
+    {
10112
+        return $this->repository;
10113
+    }
10114
+
10115
+    /**
10116
+     * Binds an SQL function to a column.
10117
+     * This method can be used to setup a decode/encode scheme or
10118
+     * perform UUID insertion. This method is especially useful for handling
10119
+     * MySQL spatial columns, because they need to be processed first using
10120
+     * the asText/GeomFromText functions.
10121
+     *
10122
+     * @param string $mode     mode to set function for, i.e. read or write
10123
+     * @param string $field    field (table.column) to bind SQL function to
10124
+     * @param string $function SQL function to bind to field
10125
+     *
10126
+     * @return void
10127
+     */
10128
+    public function bindFunc( $mode, $field, $function )
10129
+    {
10130
+        list( $type, $property ) = explode( '.', $field );
10131
+        $mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ;
10132
+
10133
+        if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array();
10134
+        if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array();
10135
+
10136
+        if ( is_null( $function ) ) {
10137
+            unset( self::$sqlFilters[$mode][$type][$property] );
10138
+        } else {
10139
+            if ($mode === QueryWriter::C_SQLFILTER_WRITE) {
10140
+                self::$sqlFilters[$mode][$type][$property] = $function.'(?)';
10141
+            } else {
10142
+                self::$sqlFilters[$mode][$type][$property] = $function."($field)";
10143
+            }
10144
+        }
10145
+
10146
+        AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
10147
+    }
10148 10148
 }
10149 10149
 }
10150 10150
 
@@ -10175,143 +10175,143 @@  discard block
 block discarded – undo
10175 10175
  */
10176 10176
 class ToolBox
10177 10177
 {
10178
-	/**
10179
-	 * @var OODB
10180
-	 */
10181
-	protected $oodb;
10182
-
10183
-	/**
10184
-	 * @var QueryWriter
10185
-	 */
10186
-	protected $writer;
10187
-
10188
-	/**
10189
-	 * @var DBAdapter
10190
-	 */
10191
-	protected $adapter;
10192
-
10193
-	/**
10194
-	 * Constructor.
10195
-	 * The toolbox is an integral part of RedBeanPHP providing the basic
10196
-	 * architectural building blocks to manager objects, helpers and additional tools
10197
-	 * like plugins. A toolbox contains the three core components of RedBeanPHP:
10198
-	 * the adapter, the query writer and the core functionality of RedBeanPHP in
10199
-	 * OODB.
10200
-	 *
10201
-	 * Usage:
10202
-	 *
10203
-	 * <code>
10204
-	 * $toolbox = new ToolBox( $oodb, $adapter, $writer );
10205
-	 * $plugin  = new MyPlugin( $toolbox );
10206
-	 * </code>
10207
-	 *
10208
-	 * The example above illustrates how the toolbox is used.
10209
-	 * The core objects are passed to the ToolBox constructor to
10210
-	 * assemble a toolbox instance. The toolbox is then passed to
10211
-	 * the plugin, helper or manager object. Instances of
10212
-	 * TagManager, AssociationManager and so on are examples of
10213
-	 * this, they all require a toolbox. The toolbox can also
10214
-	 * be obtained from the facade using: R::getToolBox();
10215
-	 *
10216
-	 * @param OODB        $oodb    Object Database, OODB
10217
-	 * @param DBAdapter   $adapter Database Adapter
10218
-	 * @param QueryWriter $writer  Query Writer
10219
-	 */
10220
-	public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer )
10221
-	{
10222
-		$this->oodb    = $oodb;
10223
-		$this->adapter = $adapter;
10224
-		$this->writer  = $writer;
10225
-		return $this;
10226
-	}
10227
-
10228
-	/**
10229
-	 * Returns the query writer in this toolbox.
10230
-	 * The Query Writer is responsible for building the queries for a
10231
-	 * specific database and executing them through the adapter.
10232
-	 *
10233
-	 * Usage:
10234
-	 *
10235
-	 * <code>
10236
-	 * $toolbox = R::getToolBox();
10237
-	 * $redbean = $toolbox->getRedBean();
10238
-	 * $adapter = $toolbox->getDatabaseAdapter();
10239
-	 * $writer  = $toolbox->getWriter();
10240
-	 * </code>
10241
-	 *
10242
-	 * The example above illustrates how to obtain the core objects
10243
-	 * from a toolbox instance. If you are working with the R-object
10244
-	 * only, the following shortcuts exist as well:
10245
-	 *
10246
-	 * - R::getRedBean()
10247
-	 * - R::getDatabaseAdapter()
10248
-	 * - R::getWriter()
10249
-	 *
10250
-	 * @return QueryWriter
10251
-	 */
10252
-	public function getWriter()
10253
-	{
10254
-		return $this->writer;
10255
-	}
10256
-
10257
-	/**
10258
-	 * Returns the OODB instance in this toolbox.
10259
-	 * OODB is responsible for creating, storing, retrieving and deleting
10260
-	 * single beans. Other components rely
10261
-	 * on OODB for their basic functionality.
10262
-	 *
10263
-	 * Usage:
10264
-	 *
10265
-	 * <code>
10266
-	 * $toolbox = R::getToolBox();
10267
-	 * $redbean = $toolbox->getRedBean();
10268
-	 * $adapter = $toolbox->getDatabaseAdapter();
10269
-	 * $writer  = $toolbox->getWriter();
10270
-	 * </code>
10271
-	 *
10272
-	 * The example above illustrates how to obtain the core objects
10273
-	 * from a toolbox instance. If you are working with the R-object
10274
-	 * only, the following shortcuts exist as well:
10275
-	 *
10276
-	 * - R::getRedBean()
10277
-	 * - R::getDatabaseAdapter()
10278
-	 * - R::getWriter()
10279
-	 *
10280
-	 * @return OODB
10281
-	 */
10282
-	public function getRedBean()
10283
-	{
10284
-		return $this->oodb;
10285
-	}
10286
-
10287
-	/**
10288
-	 * Returns the database adapter in this toolbox.
10289
-	 * The adapter is responsible for executing the query and binding the values.
10290
-	 * The adapter also takes care of transaction handling.
10291
-	 *
10292
-	 * Usage:
10293
-	 *
10294
-	 * <code>
10295
-	 * $toolbox = R::getToolBox();
10296
-	 * $redbean = $toolbox->getRedBean();
10297
-	 * $adapter = $toolbox->getDatabaseAdapter();
10298
-	 * $writer  = $toolbox->getWriter();
10299
-	 * </code>
10300
-	 *
10301
-	 * The example above illustrates how to obtain the core objects
10302
-	 * from a toolbox instance. If you are working with the R-object
10303
-	 * only, the following shortcuts exist as well:
10304
-	 *
10305
-	 * - R::getRedBean()
10306
-	 * - R::getDatabaseAdapter()
10307
-	 * - R::getWriter()
10308
-	 *
10309
-	 * @return DBAdapter
10310
-	 */
10311
-	public function getDatabaseAdapter()
10312
-	{
10313
-		return $this->adapter;
10314
-	}
10178
+    /**
10179
+     * @var OODB
10180
+     */
10181
+    protected $oodb;
10182
+
10183
+    /**
10184
+     * @var QueryWriter
10185
+     */
10186
+    protected $writer;
10187
+
10188
+    /**
10189
+     * @var DBAdapter
10190
+     */
10191
+    protected $adapter;
10192
+
10193
+    /**
10194
+     * Constructor.
10195
+     * The toolbox is an integral part of RedBeanPHP providing the basic
10196
+     * architectural building blocks to manager objects, helpers and additional tools
10197
+     * like plugins. A toolbox contains the three core components of RedBeanPHP:
10198
+     * the adapter, the query writer and the core functionality of RedBeanPHP in
10199
+     * OODB.
10200
+     *
10201
+     * Usage:
10202
+     *
10203
+     * <code>
10204
+     * $toolbox = new ToolBox( $oodb, $adapter, $writer );
10205
+     * $plugin  = new MyPlugin( $toolbox );
10206
+     * </code>
10207
+     *
10208
+     * The example above illustrates how the toolbox is used.
10209
+     * The core objects are passed to the ToolBox constructor to
10210
+     * assemble a toolbox instance. The toolbox is then passed to
10211
+     * the plugin, helper or manager object. Instances of
10212
+     * TagManager, AssociationManager and so on are examples of
10213
+     * this, they all require a toolbox. The toolbox can also
10214
+     * be obtained from the facade using: R::getToolBox();
10215
+     *
10216
+     * @param OODB        $oodb    Object Database, OODB
10217
+     * @param DBAdapter   $adapter Database Adapter
10218
+     * @param QueryWriter $writer  Query Writer
10219
+     */
10220
+    public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer )
10221
+    {
10222
+        $this->oodb    = $oodb;
10223
+        $this->adapter = $adapter;
10224
+        $this->writer  = $writer;
10225
+        return $this;
10226
+    }
10227
+
10228
+    /**
10229
+     * Returns the query writer in this toolbox.
10230
+     * The Query Writer is responsible for building the queries for a
10231
+     * specific database and executing them through the adapter.
10232
+     *
10233
+     * Usage:
10234
+     *
10235
+     * <code>
10236
+     * $toolbox = R::getToolBox();
10237
+     * $redbean = $toolbox->getRedBean();
10238
+     * $adapter = $toolbox->getDatabaseAdapter();
10239
+     * $writer  = $toolbox->getWriter();
10240
+     * </code>
10241
+     *
10242
+     * The example above illustrates how to obtain the core objects
10243
+     * from a toolbox instance. If you are working with the R-object
10244
+     * only, the following shortcuts exist as well:
10245
+     *
10246
+     * - R::getRedBean()
10247
+     * - R::getDatabaseAdapter()
10248
+     * - R::getWriter()
10249
+     *
10250
+     * @return QueryWriter
10251
+     */
10252
+    public function getWriter()
10253
+    {
10254
+        return $this->writer;
10255
+    }
10256
+
10257
+    /**
10258
+     * Returns the OODB instance in this toolbox.
10259
+     * OODB is responsible for creating, storing, retrieving and deleting
10260
+     * single beans. Other components rely
10261
+     * on OODB for their basic functionality.
10262
+     *
10263
+     * Usage:
10264
+     *
10265
+     * <code>
10266
+     * $toolbox = R::getToolBox();
10267
+     * $redbean = $toolbox->getRedBean();
10268
+     * $adapter = $toolbox->getDatabaseAdapter();
10269
+     * $writer  = $toolbox->getWriter();
10270
+     * </code>
10271
+     *
10272
+     * The example above illustrates how to obtain the core objects
10273
+     * from a toolbox instance. If you are working with the R-object
10274
+     * only, the following shortcuts exist as well:
10275
+     *
10276
+     * - R::getRedBean()
10277
+     * - R::getDatabaseAdapter()
10278
+     * - R::getWriter()
10279
+     *
10280
+     * @return OODB
10281
+     */
10282
+    public function getRedBean()
10283
+    {
10284
+        return $this->oodb;
10285
+    }
10286
+
10287
+    /**
10288
+     * Returns the database adapter in this toolbox.
10289
+     * The adapter is responsible for executing the query and binding the values.
10290
+     * The adapter also takes care of transaction handling.
10291
+     *
10292
+     * Usage:
10293
+     *
10294
+     * <code>
10295
+     * $toolbox = R::getToolBox();
10296
+     * $redbean = $toolbox->getRedBean();
10297
+     * $adapter = $toolbox->getDatabaseAdapter();
10298
+     * $writer  = $toolbox->getWriter();
10299
+     * </code>
10300
+     *
10301
+     * The example above illustrates how to obtain the core objects
10302
+     * from a toolbox instance. If you are working with the R-object
10303
+     * only, the following shortcuts exist as well:
10304
+     *
10305
+     * - R::getRedBean()
10306
+     * - R::getDatabaseAdapter()
10307
+     * - R::getWriter()
10308
+     *
10309
+     * @return DBAdapter
10310
+     */
10311
+    public function getDatabaseAdapter()
10312
+    {
10313
+        return $this->adapter;
10314
+    }
10315 10315
 }
10316 10316
 }
10317 10317
 
@@ -10336,456 +10336,456 @@  discard block
 block discarded – undo
10336 10336
  */
10337 10337
 class Finder
10338 10338
 {
10339
-	/**
10340
-	 * @var ToolBox
10341
-	 */
10342
-	protected $toolbox;
10343
-
10344
-	/**
10345
-	 * @var OODB
10346
-	 */
10347
-	protected $redbean;
10348
-
10349
-	/**
10350
-	 * Constructor.
10351
-	 * The Finder requires a toolbox.
10352
-	 *
10353
-	 * @param ToolBox $toolbox
10354
-	 */
10355
-	public function __construct( ToolBox $toolbox )
10356
-	{
10357
-		$this->toolbox = $toolbox;
10358
-		$this->redbean = $toolbox->getRedBean();
10359
-	}
10360
-
10361
-	/**
10362
-	 * A custom record-to-bean mapping function for findMulti.
10363
-	 *
10364
-	 * Usage:
10365
-	 *
10366
-	 * <code>
10367
-	 * $collection = R::findMulti( 'shop,product,price',
10368
-	 * 'SELECT shop.*, product.*, price.* FROM shop
10369
-	 *	LEFT JOIN product ON product.shop_id = shop.id
10370
-	 *	LEFT JOIN price ON price.product_id = product.id', [], [
10371
-	 *		Finder::map( 'shop', 'product' ),
10372
-	 *		Finder::map( 'product', 'price' ),
10373
-	 *	]);
10374
-	 * </code>
10375
-	 *
10376
-	 * @param string $parentName name of the parent bean
10377
-	 * @param string $childName  name of the child bean
10378
-	 *
10379
-	 * @return array
10380
-	 */
10381
-	public static function map($parentName,$childName) {
10382
-		return array(
10383
-			'a' => $parentName,
10384
-			'b' => $childName,
10385
-			'matcher' => function( $parent, $child ) use ( $parentName, $childName ) {
10386
-				$propertyName = 'own' . ucfirst( $childName );
10387
-				if (!isset($parent[$propertyName])) {
10388
-					$parent->noLoad()->{$propertyName} = array();
10389
-				}
10390
-				$property = "{$parentName}ID";
10391
-				return ( $child->$property == $parent->id );
10392
-			},
10393
-			'do' => function( $parent, $child ) use ( $childName ) {
10394
-				$list = 'own'.ucfirst( $childName ).'List';
10395
-				$parent->noLoad()->{$list}[$child->id] = $child;
10396
-			}
10397
-		);
10398
-	}
10399
-
10400
-	/**
10401
-	* A custom record-to-bean mapping function for findMulti.
10402
-	*
10403
-	* Usage:
10404
-	*
10405
-	* <code>
10406
-	* $collection = R::findMulti( 'book,book_tag,tag',
10407
-	* 'SELECT book.*, book_tag.*, tag.* FROM book
10408
-	*      LEFT JOIN book_tag ON book_tag.book_id = book.id
10409
-	*      LEFT JOIN tag ON book_tag.tag_id = tag.id', [], [
10410
-	*              Finder::nmMap( 'book', 'tag' ),
10411
-	*      ]);
10412
-	* </code>
10413
-	*
10414
-	* @param string $parentName name of the parent bean
10415
-	* @param string $childName  name of the child bean
10416
-	*
10417
-	* @return array
10418
-	*/
10419
-	public static function nmMap( $parentName, $childName )
10420
-	{
10421
-		$types = array($parentName, $childName);
10422
-		sort( $types );
10423
-		$link = implode( '_', $types );
10424
-		return array(
10425
-			'a' => $parentName,
10426
-			'b' => $childName,
10427
-			'matcher' => function( $parent, $child, $beans ) use ( $parentName, $childName, $link ) {
10428
-				$propertyName = 'shared' . ucfirst( $childName );
10429
-				if (!isset($parent[$propertyName])) {
10430
-					$parent->noLoad()->{$propertyName} = array();
10431
-				}
10432
-				foreach( $beans[$link] as $linkBean ) {
10433
-					if ( $linkBean["{$parentName}ID"] == $parent->id && $linkBean["{$childName}ID"] == $child->id ) {
10434
-						return true;
10435
-					}
10436
-				}
10437
-			},
10438
-			'do' => function( $parent, $child ) use ( $childName ) {
10439
-				$list = 'shared'.ucfirst( $childName ).'List';
10440
-				$parent->noLoad()->{$list}[$child->id] = $child;
10441
-			}
10442
-		);
10443
-	}
10444
-
10445
-	/**
10446
-	 * Finds a bean using a type and a where clause (SQL).
10447
-	 * As with most Query tools in RedBean you can provide values to
10448
-	 * be inserted in the SQL statement by populating the value
10449
-	 * array parameter; you can either use the question mark notation
10450
-	 * or the slot-notation (:keyname).
10451
-	 *
10452
-	 * @param string $type     type   the type of bean you are looking for
10453
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10454
-	 * @param array  $bindings values array of values to be bound to parameters in query
10455
-	 *
10456
-	 * @return array
10457
-	 */
10458
-	public function find( $type, $sql = NULL, $bindings = array() )
10459
-	{
10460
-		if ( !is_array( $bindings ) ) {
10461
-			throw new RedException(
10462
-				'Expected array, ' . gettype( $bindings ) . ' given.'
10463
-			);
10464
-		}
10465
-
10466
-		return $this->redbean->find( $type, array(), $sql, $bindings );
10467
-	}
10468
-
10469
-	/**
10470
-	 * Like find() but also exports the beans as an array.
10471
-	 * This method will perform a find-operation. For every bean
10472
-	 * in the result collection this method will call the export() method.
10473
-	 * This method returns an array containing the array representations
10474
-	 * of every bean in the result set.
10475
-	 *
10476
-	 * @see Finder::find
10477
-	 *
10478
-	 * @param string $type     type   the type of bean you are looking for
10479
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10480
-	 * @param array  $bindings values array of values to be bound to parameters in query
10481
-	 *
10482
-	 * @return array
10483
-	 */
10484
-	public function findAndExport( $type, $sql = NULL, $bindings = array() )
10485
-	{
10486
-		$arr = array();
10487
-		foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) {
10488
-			$arr[] = $item->export();
10489
-		}
10490
-
10491
-		return $arr;
10492
-	}
10493
-
10494
-	/**
10495
-	 * Like find() but returns just one bean instead of an array of beans.
10496
-	 * This method will return only the first bean of the array.
10497
-	 * If no beans are found, this method will return NULL.
10498
-	 *
10499
-	 * @see Finder::find
10500
-	 *
10501
-	 * @param string $type     type   the type of bean you are looking for
10502
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10503
-	 * @param array  $bindings values array of values to be bound to parameters in query
10504
-	 *
10505
-	 * @return OODBBean|NULL
10506
-	 */
10507
-	public function findOne( $type, $sql = NULL, $bindings = array() )
10508
-	{
10509
-		$sql = $this->toolbox->getWriter()->glueLimitOne( $sql );
10510
-
10511
-		$items = $this->find( $type, $sql, $bindings );
10512
-
10513
-		if ( empty($items) ) {
10514
-			return NULL;
10515
-		}
10516
-
10517
-		return reset( $items );
10518
-	}
10519
-
10520
-	/**
10521
-	 * Like find() but returns the last bean of the result array.
10522
-	 * Opposite of Finder::findLast().
10523
-	 * If no beans are found, this method will return NULL.
10524
-	 *
10525
-	 * @see Finder::find
10526
-	 *
10527
-	 * @param string $type     the type of bean you are looking for
10528
-	 * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
10529
-	 * @param array  $bindings values array of values to be bound to parameters in query
10530
-	 *
10531
-	 * @return OODBBean|NULL
10532
-	 */
10533
-	public function findLast( $type, $sql = NULL, $bindings = array() )
10534
-	{
10535
-		$items = $this->find( $type, $sql, $bindings );
10536
-
10537
-		if ( empty($items) ) {
10538
-			return NULL;
10539
-		}
10540
-
10541
-		return end( $items );
10542
-	}
10543
-
10544
-	/**
10545
-	 * Tries to find beans of a certain type,
10546
-	 * if no beans are found, it dispenses a bean of that type.
10547
-	 * Note that this function always returns an array.
10548
-	 *
10549
-	 * @see Finder::find
10550
-	 *
10551
-	 * @param  string $type     the type of bean you are looking for
10552
-	 * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
10553
-	 * @param  array  $bindings values array of values to be bound to parameters in query
10554
-	 *
10555
-	 * @return array
10556
-	 */
10557
-	public function findOrDispense( $type, $sql = NULL, $bindings = array() )
10558
-	{
10559
-		$foundBeans = $this->find( $type, $sql, $bindings );
10560
-
10561
-		if ( empty( $foundBeans ) ) {
10562
-			return array( $this->redbean->dispense( $type ) );
10563
-		} else {
10564
-			return $foundBeans;
10565
-		}
10566
-	}
10567
-
10568
-	/**
10569
-	 * Finds a BeanCollection using the repository.
10570
-	 * A bean collection can be used to retrieve one bean at a time using
10571
-	 * cursors - this is useful for processing large datasets. A bean collection
10572
-	 * will not load all beans into memory all at once, just one at a time.
10573
-	 *
10574
-	 * @param  string $type     the type of bean you are looking for
10575
-	 * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
10576
-	 * @param  array  $bindings values array of values to be bound to parameters in query
10577
-	 *
10578
-	 * @return BeanCollection
10579
-	 */
10580
-	public function findCollection( $type, $sql, $bindings = array() )
10581
-	{
10582
-		return $this->redbean->findCollection( $type, $sql, $bindings );
10583
-	}
10584
-
10585
-	/**
10586
-	 * Finds or creates a bean.
10587
-	 * Tries to find a bean with certain properties specified in the second
10588
-	 * parameter ($like). If the bean is found, it will be returned.
10589
-	 * If multiple beans are found, only the first will be returned.
10590
-	 * If no beans match the criteria, a new bean will be dispensed,
10591
-	 * the criteria will be imported as properties and this new bean
10592
-	 * will be stored and returned.
10593
-	 *
10594
-	 * Format of criteria set: property => value
10595
-	 * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
10596
-	 *
10597
-	 * @param string $type type of bean to search for
10598
-	 * @param array  $like criteria set describing bean to search for
10599
-	 *
10600
-	 * @return OODBBean
10601
-	 */
10602
-	public function findOrCreate( $type, $like = array(), $sql = '' )
10603
-	{
10604
-			$sql = $this->toolbox->getWriter()->glueLimitOne( $sql );
10605
-			$beans = $this->findLike( $type, $like, $sql );
10606
-			if ( count( $beans ) ) {
10607
-				$bean = reset( $beans );
10608
-				return $bean;
10609
-			}
10610
-
10611
-			$bean = $this->redbean->dispense( $type );
10612
-			$bean->import( $like );
10613
-			$this->redbean->store( $bean );
10614
-			return $bean;
10615
-	}
10616
-
10617
-	/**
10618
-	 * Finds beans by its type and a certain criteria set.
10619
-	 *
10620
-	 * Format of criteria set: property => value
10621
-	 * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
10622
-	 *
10623
-	 * If the additional SQL is a condition, this condition will be glued to the rest
10624
-	 * of the query using an AND operator. Note that this is as far as this method
10625
-	 * can go, there is no way to glue additional SQL using an OR-condition.
10626
-	 * This method provides access to an underlying mechanism in the RedBeanPHP architecture
10627
-	 * to find beans using criteria sets. However, please do not use this method
10628
-	 * for complex queries, use plain SQL instead ( the regular find method ) as it is
10629
-	 * more suitable for the job. This method is
10630
-	 * meant for basic search-by-example operations.
10631
-	 *
10632
-	 * @param string $type       type of bean to search for
10633
-	 * @param array  $conditions criteria set describing the bean to search for
10634
-	 * @param string $sql        additional SQL (for sorting)
10635
-	 * @param array  $bindings   bindings
10636
-	 *
10637
-	 * @return array
10638
-	 */
10639
-	public function findLike( $type, $conditions = array(), $sql = '', $bindings = array() )
10640
-	{
10641
-		return $this->redbean->find( $type, $conditions, $sql, $bindings );
10642
-	}
10643
-
10644
-	/**
10645
-	 * Returns a hashmap with bean arrays keyed by type using an SQL
10646
-	 * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review'
10647
-	 * this method will return movie and review beans.
10648
-	 *
10649
-	 * Example:
10650
-	 *
10651
-	 * <code>
10652
-	 * $stuff = $finder->findMulti('movie,review', '
10653
-	 *          SELECT movie.*, review.* FROM movie
10654
-	 *          LEFT JOIN review ON review.movie_id = movie.id');
10655
-	 * </code>
10656
-	 *
10657
-	 * After this operation, $stuff will contain an entry 'movie' containing all
10658
-	 * movies and an entry named 'review' containing all reviews (all beans).
10659
-	 * You can also pass bindings.
10660
-	 *
10661
-	 * If you want to re-map your beans, so you can use $movie->ownReviewList without
10662
-	 * having RedBeanPHP executing an SQL query you can use the fourth parameter to
10663
-	 * define a selection of remapping closures.
10664
-	 *
10665
-	 * The remapping argument (optional) should contain an array of arrays.
10666
-	 * Each array in the remapping array should contain the following entries:
10667
-	 *
10668
-	 * <code>
10669
-	 * array(
10670
-	 * 	'a'       => TYPE A
10671
-	 *    'b'       => TYPE B
10672
-	 *    'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS
10673
-	 *    'do'      => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS
10674
-	 * )
10675
-	 * </code>
10676
-	 *
10677
-	 * Using this mechanism you can build your own 'preloader' with tiny function
10678
-	 * snippets (and those can be re-used and shared online of course).
10679
-	 *
10680
-	 * Example:
10681
-	 *
10682
-	 * <code>
10683
-	 * array(
10684
-	 * 	'a'       => 'movie'     //define A as movie
10685
-	 *    'b'       => 'review'    //define B as review
10686
-	 *    'matcher' => function( $a, $b ) {
10687
-	 *       return ( $b->movie_id == $a->id );  //Perform action if review.movie_id equals movie.id
10688
-	 *    }
10689
-	 *    'do'      => function( $a, $b ) {
10690
-	 *       $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie
10691
-	 *       $a->clearHistory();                 //optional, act 'as if these beans have been loaded through ownReviewList'.
10692
-	 *    }
10693
-	 * )
10694
-	 * </code>
10695
-	 *
10696
-	 * The Query Template parameter is optional as well but can be used to
10697
-	 * set a different SQL template (sprintf-style) for processing the original query.
10698
-	 *
10699
-	 * @note the SQL query provided IS NOT THE ONE used internally by this function,
10700
-	 * this function will pre-process the query to get all the data required to find the beans.
10701
-	 *
10702
-	 * @note if you use the 'book.*' notation make SURE you're
10703
-	 * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because
10704
-	 * it's actually an SQL-like template SLOT, not real SQL.
10705
-	 *
10706
-	 * @note instead of an SQL query you can pass a result array as well.
10707
-	 *
10708
-	 * @param string|array $types         a list of types (either array or comma separated string)
10709
-	 * @param string|array $sql           optional, an SQL query or an array of prefetched records
10710
-	 * @param array        $bindings      optional, bindings for SQL query
10711
-	 * @param array        $remappings    optional, an array of remapping arrays
10712
-	 * @param string       $queryTemplate optional, query template
10713
-	 *
10714
-	 * @return array
10715
-	 */
10716
-	public function findMulti( $types, $sql = NULL, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s' )
10717
-	{
10718
-		if ( !is_array( $types ) ) $types = array_map( 'trim', explode( ',', $types ) );
10719
-		if ( is_null( $sql ) ) {
10720
-			$beans = array();
10721
-			foreach( $types as $type ) $beans[$type] = $this->redbean->find( $type );
10722
-		} else {
10723
-			if ( !is_array( $sql ) ) {
10724
-				$writer = $this->toolbox->getWriter();
10725
-				$adapter = $this->toolbox->getDatabaseAdapter();
10726
-
10727
-				//Repair the query, replace book.* with book.id AS book_id etc..
10728
-				foreach( $types as $type ) {
10729
-					$regex = "#( (`?{$type}`?)\.\*)#";
10730
-					if ( preg_match( $regex, $sql, $matches ) ) {
10731
-						$pattern = $matches[1];
10732
-						$table = $matches[2];
10733
-						$newSelectorArray = array();
10734
-						$columns = $writer->getColumns( $type );
10735
-						foreach( $columns as $column => $definition ) {
10736
-							$newSelectorArray[] = sprintf( $queryTemplate, $table, $column, $type, $column );
10737
-						}
10738
-						$newSelector = implode( ',', $newSelectorArray );
10739
-						$sql = str_replace( $pattern, $newSelector, $sql );
10740
-					}
10741
-				}
10742
-
10743
-				$rows = $adapter->get( $sql, $bindings );
10744
-			} else {
10745
-				$rows = $sql;
10746
-			}
10747
-
10748
-			//Gather the bean data from the query results using the prefix
10749
-			$wannaBeans = array();
10750
-			foreach( $types as $type ) {
10751
-				$wannaBeans[$type] = array();
10752
-				$prefix            = "{$type}__";
10753
-				foreach( $rows as $rowkey=>$row ) {
10754
-					$wannaBean = array();
10755
-					foreach( $row as $cell => $value ) {
10756
-						if ( strpos( $cell, $prefix ) === 0 ) {
10757
-							$property = substr( $cell, strlen( $prefix ) );
10758
-							unset( $rows[$rowkey][$cell] );
10759
-							$wannaBean[$property] = $value;
10760
-						}
10761
-					}
10762
-					if ( !isset( $wannaBean['id'] ) ) continue;
10763
-					if ( is_null( $wannaBean['id'] ) ) continue;
10764
-					$wannaBeans[$type][$wannaBean['id']] = $wannaBean;
10765
-				}
10766
-			}
10767
-
10768
-			//Turn the rows into beans
10769
-			$beans = array();
10770
-			foreach( $wannaBeans as $type => $wannabees ) {
10771
-				$beans[$type] = $this->redbean->convertToBeans( $type, $wannabees );
10772
-			}
10773
-		}
10774
-
10775
-		//Apply additional re-mappings
10776
-		foreach($remappings as $remapping) {
10777
-			$a       = $remapping['a'];
10778
-			$b       = $remapping['b'];
10779
-			$matcher = $remapping['matcher'];
10780
-			$do      = $remapping['do'];
10781
-			foreach( $beans[$a] as $bean ) {
10782
-				foreach( $beans[$b] as $putBean ) {
10783
-					if ( $matcher( $bean, $putBean, $beans ) ) $do( $bean, $putBean, $beans, $remapping );
10784
-				}
10785
-			}
10786
-		}
10787
-		return $beans;
10788
-	}
10339
+    /**
10340
+     * @var ToolBox
10341
+     */
10342
+    protected $toolbox;
10343
+
10344
+    /**
10345
+     * @var OODB
10346
+     */
10347
+    protected $redbean;
10348
+
10349
+    /**
10350
+     * Constructor.
10351
+     * The Finder requires a toolbox.
10352
+     *
10353
+     * @param ToolBox $toolbox
10354
+     */
10355
+    public function __construct( ToolBox $toolbox )
10356
+    {
10357
+        $this->toolbox = $toolbox;
10358
+        $this->redbean = $toolbox->getRedBean();
10359
+    }
10360
+
10361
+    /**
10362
+     * A custom record-to-bean mapping function for findMulti.
10363
+     *
10364
+     * Usage:
10365
+     *
10366
+     * <code>
10367
+     * $collection = R::findMulti( 'shop,product,price',
10368
+     * 'SELECT shop.*, product.*, price.* FROM shop
10369
+     *	LEFT JOIN product ON product.shop_id = shop.id
10370
+     *	LEFT JOIN price ON price.product_id = product.id', [], [
10371
+     *		Finder::map( 'shop', 'product' ),
10372
+     *		Finder::map( 'product', 'price' ),
10373
+     *	]);
10374
+     * </code>
10375
+     *
10376
+     * @param string $parentName name of the parent bean
10377
+     * @param string $childName  name of the child bean
10378
+     *
10379
+     * @return array
10380
+     */
10381
+    public static function map($parentName,$childName) {
10382
+        return array(
10383
+            'a' => $parentName,
10384
+            'b' => $childName,
10385
+            'matcher' => function( $parent, $child ) use ( $parentName, $childName ) {
10386
+                $propertyName = 'own' . ucfirst( $childName );
10387
+                if (!isset($parent[$propertyName])) {
10388
+                    $parent->noLoad()->{$propertyName} = array();
10389
+                }
10390
+                $property = "{$parentName}ID";
10391
+                return ( $child->$property == $parent->id );
10392
+            },
10393
+            'do' => function( $parent, $child ) use ( $childName ) {
10394
+                $list = 'own'.ucfirst( $childName ).'List';
10395
+                $parent->noLoad()->{$list}[$child->id] = $child;
10396
+            }
10397
+        );
10398
+    }
10399
+
10400
+    /**
10401
+     * A custom record-to-bean mapping function for findMulti.
10402
+     *
10403
+     * Usage:
10404
+     *
10405
+     * <code>
10406
+     * $collection = R::findMulti( 'book,book_tag,tag',
10407
+     * 'SELECT book.*, book_tag.*, tag.* FROM book
10408
+     *      LEFT JOIN book_tag ON book_tag.book_id = book.id
10409
+     *      LEFT JOIN tag ON book_tag.tag_id = tag.id', [], [
10410
+     *              Finder::nmMap( 'book', 'tag' ),
10411
+     *      ]);
10412
+     * </code>
10413
+     *
10414
+     * @param string $parentName name of the parent bean
10415
+     * @param string $childName  name of the child bean
10416
+     *
10417
+     * @return array
10418
+     */
10419
+    public static function nmMap( $parentName, $childName )
10420
+    {
10421
+        $types = array($parentName, $childName);
10422
+        sort( $types );
10423
+        $link = implode( '_', $types );
10424
+        return array(
10425
+            'a' => $parentName,
10426
+            'b' => $childName,
10427
+            'matcher' => function( $parent, $child, $beans ) use ( $parentName, $childName, $link ) {
10428
+                $propertyName = 'shared' . ucfirst( $childName );
10429
+                if (!isset($parent[$propertyName])) {
10430
+                    $parent->noLoad()->{$propertyName} = array();
10431
+                }
10432
+                foreach( $beans[$link] as $linkBean ) {
10433
+                    if ( $linkBean["{$parentName}ID"] == $parent->id && $linkBean["{$childName}ID"] == $child->id ) {
10434
+                        return true;
10435
+                    }
10436
+                }
10437
+            },
10438
+            'do' => function( $parent, $child ) use ( $childName ) {
10439
+                $list = 'shared'.ucfirst( $childName ).'List';
10440
+                $parent->noLoad()->{$list}[$child->id] = $child;
10441
+            }
10442
+        );
10443
+    }
10444
+
10445
+    /**
10446
+     * Finds a bean using a type and a where clause (SQL).
10447
+     * As with most Query tools in RedBean you can provide values to
10448
+     * be inserted in the SQL statement by populating the value
10449
+     * array parameter; you can either use the question mark notation
10450
+     * or the slot-notation (:keyname).
10451
+     *
10452
+     * @param string $type     type   the type of bean you are looking for
10453
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10454
+     * @param array  $bindings values array of values to be bound to parameters in query
10455
+     *
10456
+     * @return array
10457
+     */
10458
+    public function find( $type, $sql = NULL, $bindings = array() )
10459
+    {
10460
+        if ( !is_array( $bindings ) ) {
10461
+            throw new RedException(
10462
+                'Expected array, ' . gettype( $bindings ) . ' given.'
10463
+            );
10464
+        }
10465
+
10466
+        return $this->redbean->find( $type, array(), $sql, $bindings );
10467
+    }
10468
+
10469
+    /**
10470
+     * Like find() but also exports the beans as an array.
10471
+     * This method will perform a find-operation. For every bean
10472
+     * in the result collection this method will call the export() method.
10473
+     * This method returns an array containing the array representations
10474
+     * of every bean in the result set.
10475
+     *
10476
+     * @see Finder::find
10477
+     *
10478
+     * @param string $type     type   the type of bean you are looking for
10479
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10480
+     * @param array  $bindings values array of values to be bound to parameters in query
10481
+     *
10482
+     * @return array
10483
+     */
10484
+    public function findAndExport( $type, $sql = NULL, $bindings = array() )
10485
+    {
10486
+        $arr = array();
10487
+        foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) {
10488
+            $arr[] = $item->export();
10489
+        }
10490
+
10491
+        return $arr;
10492
+    }
10493
+
10494
+    /**
10495
+     * Like find() but returns just one bean instead of an array of beans.
10496
+     * This method will return only the first bean of the array.
10497
+     * If no beans are found, this method will return NULL.
10498
+     *
10499
+     * @see Finder::find
10500
+     *
10501
+     * @param string $type     type   the type of bean you are looking for
10502
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10503
+     * @param array  $bindings values array of values to be bound to parameters in query
10504
+     *
10505
+     * @return OODBBean|NULL
10506
+     */
10507
+    public function findOne( $type, $sql = NULL, $bindings = array() )
10508
+    {
10509
+        $sql = $this->toolbox->getWriter()->glueLimitOne( $sql );
10510
+
10511
+        $items = $this->find( $type, $sql, $bindings );
10512
+
10513
+        if ( empty($items) ) {
10514
+            return NULL;
10515
+        }
10516
+
10517
+        return reset( $items );
10518
+    }
10519
+
10520
+    /**
10521
+     * Like find() but returns the last bean of the result array.
10522
+     * Opposite of Finder::findLast().
10523
+     * If no beans are found, this method will return NULL.
10524
+     *
10525
+     * @see Finder::find
10526
+     *
10527
+     * @param string $type     the type of bean you are looking for
10528
+     * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
10529
+     * @param array  $bindings values array of values to be bound to parameters in query
10530
+     *
10531
+     * @return OODBBean|NULL
10532
+     */
10533
+    public function findLast( $type, $sql = NULL, $bindings = array() )
10534
+    {
10535
+        $items = $this->find( $type, $sql, $bindings );
10536
+
10537
+        if ( empty($items) ) {
10538
+            return NULL;
10539
+        }
10540
+
10541
+        return end( $items );
10542
+    }
10543
+
10544
+    /**
10545
+     * Tries to find beans of a certain type,
10546
+     * if no beans are found, it dispenses a bean of that type.
10547
+     * Note that this function always returns an array.
10548
+     *
10549
+     * @see Finder::find
10550
+     *
10551
+     * @param  string $type     the type of bean you are looking for
10552
+     * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
10553
+     * @param  array  $bindings values array of values to be bound to parameters in query
10554
+     *
10555
+     * @return array
10556
+     */
10557
+    public function findOrDispense( $type, $sql = NULL, $bindings = array() )
10558
+    {
10559
+        $foundBeans = $this->find( $type, $sql, $bindings );
10560
+
10561
+        if ( empty( $foundBeans ) ) {
10562
+            return array( $this->redbean->dispense( $type ) );
10563
+        } else {
10564
+            return $foundBeans;
10565
+        }
10566
+    }
10567
+
10568
+    /**
10569
+     * Finds a BeanCollection using the repository.
10570
+     * A bean collection can be used to retrieve one bean at a time using
10571
+     * cursors - this is useful for processing large datasets. A bean collection
10572
+     * will not load all beans into memory all at once, just one at a time.
10573
+     *
10574
+     * @param  string $type     the type of bean you are looking for
10575
+     * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
10576
+     * @param  array  $bindings values array of values to be bound to parameters in query
10577
+     *
10578
+     * @return BeanCollection
10579
+     */
10580
+    public function findCollection( $type, $sql, $bindings = array() )
10581
+    {
10582
+        return $this->redbean->findCollection( $type, $sql, $bindings );
10583
+    }
10584
+
10585
+    /**
10586
+     * Finds or creates a bean.
10587
+     * Tries to find a bean with certain properties specified in the second
10588
+     * parameter ($like). If the bean is found, it will be returned.
10589
+     * If multiple beans are found, only the first will be returned.
10590
+     * If no beans match the criteria, a new bean will be dispensed,
10591
+     * the criteria will be imported as properties and this new bean
10592
+     * will be stored and returned.
10593
+     *
10594
+     * Format of criteria set: property => value
10595
+     * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
10596
+     *
10597
+     * @param string $type type of bean to search for
10598
+     * @param array  $like criteria set describing bean to search for
10599
+     *
10600
+     * @return OODBBean
10601
+     */
10602
+    public function findOrCreate( $type, $like = array(), $sql = '' )
10603
+    {
10604
+            $sql = $this->toolbox->getWriter()->glueLimitOne( $sql );
10605
+            $beans = $this->findLike( $type, $like, $sql );
10606
+            if ( count( $beans ) ) {
10607
+                $bean = reset( $beans );
10608
+                return $bean;
10609
+            }
10610
+
10611
+            $bean = $this->redbean->dispense( $type );
10612
+            $bean->import( $like );
10613
+            $this->redbean->store( $bean );
10614
+            return $bean;
10615
+    }
10616
+
10617
+    /**
10618
+     * Finds beans by its type and a certain criteria set.
10619
+     *
10620
+     * Format of criteria set: property => value
10621
+     * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
10622
+     *
10623
+     * If the additional SQL is a condition, this condition will be glued to the rest
10624
+     * of the query using an AND operator. Note that this is as far as this method
10625
+     * can go, there is no way to glue additional SQL using an OR-condition.
10626
+     * This method provides access to an underlying mechanism in the RedBeanPHP architecture
10627
+     * to find beans using criteria sets. However, please do not use this method
10628
+     * for complex queries, use plain SQL instead ( the regular find method ) as it is
10629
+     * more suitable for the job. This method is
10630
+     * meant for basic search-by-example operations.
10631
+     *
10632
+     * @param string $type       type of bean to search for
10633
+     * @param array  $conditions criteria set describing the bean to search for
10634
+     * @param string $sql        additional SQL (for sorting)
10635
+     * @param array  $bindings   bindings
10636
+     *
10637
+     * @return array
10638
+     */
10639
+    public function findLike( $type, $conditions = array(), $sql = '', $bindings = array() )
10640
+    {
10641
+        return $this->redbean->find( $type, $conditions, $sql, $bindings );
10642
+    }
10643
+
10644
+    /**
10645
+     * Returns a hashmap with bean arrays keyed by type using an SQL
10646
+     * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review'
10647
+     * this method will return movie and review beans.
10648
+     *
10649
+     * Example:
10650
+     *
10651
+     * <code>
10652
+     * $stuff = $finder->findMulti('movie,review', '
10653
+     *          SELECT movie.*, review.* FROM movie
10654
+     *          LEFT JOIN review ON review.movie_id = movie.id');
10655
+     * </code>
10656
+     *
10657
+     * After this operation, $stuff will contain an entry 'movie' containing all
10658
+     * movies and an entry named 'review' containing all reviews (all beans).
10659
+     * You can also pass bindings.
10660
+     *
10661
+     * If you want to re-map your beans, so you can use $movie->ownReviewList without
10662
+     * having RedBeanPHP executing an SQL query you can use the fourth parameter to
10663
+     * define a selection of remapping closures.
10664
+     *
10665
+     * The remapping argument (optional) should contain an array of arrays.
10666
+     * Each array in the remapping array should contain the following entries:
10667
+     *
10668
+     * <code>
10669
+     * array(
10670
+     * 	'a'       => TYPE A
10671
+     *    'b'       => TYPE B
10672
+     *    'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS
10673
+     *    'do'      => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS
10674
+     * )
10675
+     * </code>
10676
+     *
10677
+     * Using this mechanism you can build your own 'preloader' with tiny function
10678
+     * snippets (and those can be re-used and shared online of course).
10679
+     *
10680
+     * Example:
10681
+     *
10682
+     * <code>
10683
+     * array(
10684
+     * 	'a'       => 'movie'     //define A as movie
10685
+     *    'b'       => 'review'    //define B as review
10686
+     *    'matcher' => function( $a, $b ) {
10687
+     *       return ( $b->movie_id == $a->id );  //Perform action if review.movie_id equals movie.id
10688
+     *    }
10689
+     *    'do'      => function( $a, $b ) {
10690
+     *       $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie
10691
+     *       $a->clearHistory();                 //optional, act 'as if these beans have been loaded through ownReviewList'.
10692
+     *    }
10693
+     * )
10694
+     * </code>
10695
+     *
10696
+     * The Query Template parameter is optional as well but can be used to
10697
+     * set a different SQL template (sprintf-style) for processing the original query.
10698
+     *
10699
+     * @note the SQL query provided IS NOT THE ONE used internally by this function,
10700
+     * this function will pre-process the query to get all the data required to find the beans.
10701
+     *
10702
+     * @note if you use the 'book.*' notation make SURE you're
10703
+     * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because
10704
+     * it's actually an SQL-like template SLOT, not real SQL.
10705
+     *
10706
+     * @note instead of an SQL query you can pass a result array as well.
10707
+     *
10708
+     * @param string|array $types         a list of types (either array or comma separated string)
10709
+     * @param string|array $sql           optional, an SQL query or an array of prefetched records
10710
+     * @param array        $bindings      optional, bindings for SQL query
10711
+     * @param array        $remappings    optional, an array of remapping arrays
10712
+     * @param string       $queryTemplate optional, query template
10713
+     *
10714
+     * @return array
10715
+     */
10716
+    public function findMulti( $types, $sql = NULL, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s' )
10717
+    {
10718
+        if ( !is_array( $types ) ) $types = array_map( 'trim', explode( ',', $types ) );
10719
+        if ( is_null( $sql ) ) {
10720
+            $beans = array();
10721
+            foreach( $types as $type ) $beans[$type] = $this->redbean->find( $type );
10722
+        } else {
10723
+            if ( !is_array( $sql ) ) {
10724
+                $writer = $this->toolbox->getWriter();
10725
+                $adapter = $this->toolbox->getDatabaseAdapter();
10726
+
10727
+                //Repair the query, replace book.* with book.id AS book_id etc..
10728
+                foreach( $types as $type ) {
10729
+                    $regex = "#( (`?{$type}`?)\.\*)#";
10730
+                    if ( preg_match( $regex, $sql, $matches ) ) {
10731
+                        $pattern = $matches[1];
10732
+                        $table = $matches[2];
10733
+                        $newSelectorArray = array();
10734
+                        $columns = $writer->getColumns( $type );
10735
+                        foreach( $columns as $column => $definition ) {
10736
+                            $newSelectorArray[] = sprintf( $queryTemplate, $table, $column, $type, $column );
10737
+                        }
10738
+                        $newSelector = implode( ',', $newSelectorArray );
10739
+                        $sql = str_replace( $pattern, $newSelector, $sql );
10740
+                    }
10741
+                }
10742
+
10743
+                $rows = $adapter->get( $sql, $bindings );
10744
+            } else {
10745
+                $rows = $sql;
10746
+            }
10747
+
10748
+            //Gather the bean data from the query results using the prefix
10749
+            $wannaBeans = array();
10750
+            foreach( $types as $type ) {
10751
+                $wannaBeans[$type] = array();
10752
+                $prefix            = "{$type}__";
10753
+                foreach( $rows as $rowkey=>$row ) {
10754
+                    $wannaBean = array();
10755
+                    foreach( $row as $cell => $value ) {
10756
+                        if ( strpos( $cell, $prefix ) === 0 ) {
10757
+                            $property = substr( $cell, strlen( $prefix ) );
10758
+                            unset( $rows[$rowkey][$cell] );
10759
+                            $wannaBean[$property] = $value;
10760
+                        }
10761
+                    }
10762
+                    if ( !isset( $wannaBean['id'] ) ) continue;
10763
+                    if ( is_null( $wannaBean['id'] ) ) continue;
10764
+                    $wannaBeans[$type][$wannaBean['id']] = $wannaBean;
10765
+                }
10766
+            }
10767
+
10768
+            //Turn the rows into beans
10769
+            $beans = array();
10770
+            foreach( $wannaBeans as $type => $wannabees ) {
10771
+                $beans[$type] = $this->redbean->convertToBeans( $type, $wannabees );
10772
+            }
10773
+        }
10774
+
10775
+        //Apply additional re-mappings
10776
+        foreach($remappings as $remapping) {
10777
+            $a       = $remapping['a'];
10778
+            $b       = $remapping['b'];
10779
+            $matcher = $remapping['matcher'];
10780
+            $do      = $remapping['do'];
10781
+            foreach( $beans[$a] as $bean ) {
10782
+                foreach( $beans[$b] as $putBean ) {
10783
+                    if ( $matcher( $bean, $putBean, $beans ) ) $do( $bean, $putBean, $beans, $remapping );
10784
+                }
10785
+            }
10786
+        }
10787
+        return $beans;
10788
+    }
10789 10789
 }
10790 10790
 }
10791 10791
 
@@ -10814,339 +10814,339 @@  discard block
 block discarded – undo
10814 10814
  */
10815 10815
 class AssociationManager extends Observable
10816 10816
 {
10817
-	/**
10818
-	 * @var OODB
10819
-	 */
10820
-	protected $oodb;
10821
-
10822
-	/**
10823
-	 * @var DBAdapter
10824
-	 */
10825
-	protected $adapter;
10826
-
10827
-	/**
10828
-	 * @var QueryWriter
10829
-	 */
10830
-	protected $writer;
10831
-
10832
-	/**
10833
-	 * Exception handler.
10834
-	 * Fluid and Frozen mode have different ways of handling
10835
-	 * exceptions. Fluid mode (using the fluid repository) ignores
10836
-	 * exceptions caused by the following:
10837
-	 *
10838
-	 * - missing tables
10839
-	 * - missing column
10840
-	 *
10841
-	 * In these situations, the repository will behave as if
10842
-	 * no beans could be found. This is because in fluid mode
10843
-	 * it might happen to query a table or column that has not been
10844
-	 * created yet. In frozen mode, this is not supposed to happen
10845
-	 * and the corresponding exceptions will be thrown.
10846
-	 *
10847
-	 * @param \Exception $exception exception
10848
-	 *
10849
-	 * @return void
10850
-	 */
10851
-	private function handleException( \Exception $exception )
10852
-	{
10853
-		if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(),
10854
-			array(
10855
-				QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
10856
-				QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ),
10857
-				$exception->getDriverDetails()
10858
-			)
10859
-		) {
10860
-			throw $exception;
10861
-		}
10862
-	}
10863
-
10864
-	/**
10865
-	 * Internal method.
10866
-	 * Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and
10867
-	 * $bindings bindings. If $getLinks is TRUE, link rows are returned instead.
10868
-	 *
10869
-	 * @param OODBBean $bean     reference bean instance
10870
-	 * @param string   $type     target bean type
10871
-	 * @param string   $sql      additional SQL snippet
10872
-	 * @param array    $bindings bindings for query
10873
-	 *
10874
-	 * @return array
10875
-	 */
10876
-	private function relatedRows( $bean, $type, $sql = '', $bindings = array() )
10877
-	{
10878
-		$ids = array( $bean->id );
10879
-		$sourceType = $bean->getMeta( 'type' );
10880
-		try {
10881
-			return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings );
10882
-		} catch ( SQLException $exception ) {
10883
-			$this->handleException( $exception );
10884
-			return array();
10885
-		}
10886
-	}
10887
-
10888
-	/**
10889
-	 * Associates a pair of beans. This method associates two beans, no matter
10890
-	 * what types. Accepts a base bean that contains data for the linking record.
10891
-	 * This method is used by associate. This method also accepts a base bean to be used
10892
-	 * as the template for the link record in the database.
10893
-	 *
10894
-	 * @param OODBBean $bean1 first bean
10895
-	 * @param OODBBean $bean2 second bean
10896
-	 * @param OODBBean $bean  base bean (association record)
10897
-	 *
10898
-	 * @return mixed
10899
-	 */
10900
-	protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean )
10901
-	{
10902
-		$type      = $bean->getMeta( 'type' );
10903
-		$property1 = $bean1->getMeta( 'type' ) . '_id';
10904
-		$property2 = $bean2->getMeta( 'type' ) . '_id';
10905
-
10906
-		if ( $property1 == $property2 ) {
10907
-			$property2 = $bean2->getMeta( 'type' ) . '2_id';
10908
-		}
10909
-
10910
-		$this->oodb->store( $bean1 );
10911
-		$this->oodb->store( $bean2 );
10912
-
10913
-		$bean->setMeta( "cast.$property1", "id" );
10914
-		$bean->setMeta( "cast.$property2", "id" );
10915
-		$bean->setMeta( 'sys.buildcommand.unique', array( $property1, $property2 ) );
10916
-
10917
-		$bean->$property1 = $bean1->id;
10918
-		$bean->$property2 = $bean2->id;
10919
-
10920
-		$results   = array();
10921
-
10922
-		try {
10923
-			$id = $this->oodb->store( $bean );
10924
-			$results[] = $id;
10925
-		} catch ( SQLException $exception ) {
10926
-			if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
10927
-				array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ),
10928
-				$exception->getDriverDetails() )
10929
-			) {
10930
-				throw $exception;
10931
-			}
10932
-		}
10933
-
10934
-		return $results;
10935
-	}
10936
-
10937
-	/**
10938
-	 * Constructor, creates a new instance of the Association Manager.
10939
-	 * The association manager can be used to create and manage
10940
-	 * many-to-many relations (for example sharedLists). In a many-to-many relation,
10941
-	 * one bean can be associated with many other beans, while each of those beans
10942
-	 * can also be related to multiple beans. To create an Association Manager
10943
-	 * instance you'll need to pass a ToolBox object.
10944
-	 *
10945
-	 * @param ToolBox $tools toolbox supplying core RedBeanPHP objects
10946
-	 */
10947
-	public function __construct( ToolBox $tools )
10948
-	{
10949
-		$this->oodb    = $tools->getRedBean();
10950
-		$this->adapter = $tools->getDatabaseAdapter();
10951
-		$this->writer  = $tools->getWriter();
10952
-		$this->toolbox = $tools;
10953
-	}
10954
-
10955
-	/**
10956
-	 * Creates a table name based on a types array.
10957
-	 * Manages the get the correct name for the linking table for the
10958
-	 * types provided.
10959
-	 *
10960
-	 * @param array $types 2 types as strings
10961
-	 *
10962
-	 * @return string
10963
-	 */
10964
-	public function getTable( $types )
10965
-	{
10966
-		return $this->writer->getAssocTable( $types );
10967
-	}
10968
-
10969
-	/**
10970
-	 * Associates two beans in a many-to-many relation.
10971
-	 * This method will associate two beans and store the connection between the
10972
-	 * two in a link table. Instead of two single beans this method also accepts
10973
-	 * two sets of beans. Returns the ID or the IDs of the linking beans.
10974
-	 *
10975
-	 * @param OODBBean|array $beans1 one or more beans to form the association
10976
-	 * @param OODBBean|array $beans2 one or more beans to form the association
10977
-	 *
10978
-	 * @return array
10979
-	 */
10980
-	public function associate( $beans1, $beans2 )
10981
-	{
10982
-		if ( !is_array( $beans1 ) ) {
10983
-			$beans1 = array( $beans1 );
10984
-		}
10985
-
10986
-		if ( !is_array( $beans2 ) ) {
10987
-			$beans2 = array( $beans2 );
10988
-		}
10989
-
10990
-		$results = array();
10991
-		foreach ( $beans1 as $bean1 ) {
10992
-			foreach ( $beans2 as $bean2 ) {
10993
-				$table     = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) );
10994
-				$bean      = $this->oodb->dispense( $table );
10995
-				$results[] = $this->associateBeans( $bean1, $bean2, $bean );
10996
-			}
10997
-		}
10998
-
10999
-		return ( count( $results ) > 1 ) ? $results : reset( $results );
11000
-	}
11001
-
11002
-	/**
11003
-	 * Counts the number of related beans in an N-M relation.
11004
-	 * This method returns the number of beans of type $type associated
11005
-	 * with reference bean(s) $bean. The query can be tuned using an
11006
-	 * SQL snippet for additional filtering.
11007
-	 *
11008
-	 * @param OODBBean|array $bean     a bean object or an array of beans
11009
-	 * @param string         $type     type of bean you're interested in
11010
-	 * @param string         $sql      SQL snippet (optional)
11011
-	 * @param array          $bindings bindings for your SQL string
11012
-	 *
11013
-	 * @return integer
11014
-	 */
11015
-	public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() )
11016
-	{
11017
-		if ( !( $bean instanceof OODBBean ) ) {
11018
-			throw new RedException(
11019
-				'Expected array or OODBBean but got:' . gettype( $bean )
11020
-			);
11021
-		}
11022
-
11023
-		if ( !$bean->id ) {
11024
-			return 0;
11025
-		}
11026
-
11027
-		$beanType = $bean->getMeta( 'type' );
11028
-
11029
-		try {
11030
-			return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings );
11031
-		} catch ( SQLException $exception ) {
11032
-			$this->handleException( $exception );
11033
-
11034
-			return 0;
11035
-		}
11036
-	}
11037
-
11038
-	/**
11039
-	 * Breaks the association between two beans. This method unassociates two beans. If the
11040
-	 * method succeeds the beans will no longer form an association. In the database
11041
-	 * this means that the association record will be removed. This method uses the
11042
-	 * OODB trash() method to remove the association links, thus giving FUSE models the
11043
-	 * opportunity to hook-in additional business logic. If the $fast parameter is
11044
-	 * set to boolean TRUE this method will remove the beans without their consent,
11045
-	 * bypassing FUSE. This can be used to improve performance.
11046
-	 *
11047
-	 * @param OODBBean $beans1 first bean in target association
11048
-	 * @param OODBBean $beans2 second bean in target association
11049
-	 * @param boolean  $fast  if TRUE, removes the entries by query without FUSE
11050
-	 *
11051
-	 * @return void
11052
-	 */
11053
-	public function unassociate( $beans1, $beans2, $fast = NULL )
11054
-	{
11055
-		$beans1 = ( !is_array( $beans1 ) ) ? array( $beans1 ) : $beans1;
11056
-		$beans2 = ( !is_array( $beans2 ) ) ? array( $beans2 ) : $beans2;
11057
-
11058
-		foreach ( $beans1 as $bean1 ) {
11059
-			foreach ( $beans2 as $bean2 ) {
11060
-				try {
11061
-					$this->oodb->store( $bean1 );
11062
-					$this->oodb->store( $bean2 );
11063
-
11064
-					$type1 = $bean1->getMeta( 'type' );
11065
-					$type2 = $bean2->getMeta( 'type' );
11066
-
11067
-					$row      = $this->writer->queryRecordLink( $type1, $type2, $bean1->id, $bean2->id );
11068
-					$linkType = $this->getTable( array( $type1, $type2 ) );
11069
-
11070
-					if ( $fast ) {
11071
-						$this->writer->deleteRecord( $linkType, array( 'id' => $row['id'] ) );
11072
-
11073
-						return;
11074
-					}
11075
-
11076
-					$beans = $this->oodb->convertToBeans( $linkType, array( $row ) );
11077
-
11078
-					if ( count( $beans ) > 0 ) {
11079
-						$bean = reset( $beans );
11080
-						$this->oodb->trash( $bean );
11081
-					}
11082
-				} catch ( SQLException $exception ) {
11083
-					$this->handleException( $exception );
11084
-				}
11085
-			}
11086
-		}
11087
-	}
11088
-
11089
-	/**
11090
-	 * Removes all relations for a bean. This method breaks every connection between
11091
-	 * a certain bean $bean and every other bean of type $type. Warning: this method
11092
-	 * is really fast because it uses a direct SQL query however it does not inform the
11093
-	 * models about this. If you want to notify FUSE models about deletion use a foreach-loop
11094
-	 * with unassociate() instead. (that might be slower though)
11095
-	 *
11096
-	 * @param OODBBean $bean reference bean
11097
-	 * @param string   $type type of beans that need to be unassociated
11098
-	 *
11099
-	 * @return void
11100
-	 */
11101
-	public function clearRelations( OODBBean $bean, $type )
11102
-	{
11103
-		$this->oodb->store( $bean );
11104
-		try {
11105
-			$this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id );
11106
-		} catch ( SQLException $exception ) {
11107
-			$this->handleException( $exception );
11108
-		}
11109
-	}
11110
-
11111
-	/**
11112
-	 * Returns all the beans associated with $bean.
11113
-	 * This method will return an array containing all the beans that have
11114
-	 * been associated once with the associate() function and are still
11115
-	 * associated with the bean specified. The type parameter indicates the
11116
-	 * type of beans you are looking for. You can also pass some extra SQL and
11117
-	 * values for that SQL to filter your results after fetching the
11118
-	 * related beans.
11119
-	 *
11120
-	 * Don't try to make use of subqueries, a subquery using IN() seems to
11121
-	 * be slower than two queries!
11122
-	 *
11123
-	 * Since 3.2, you can now also pass an array of beans instead just one
11124
-	 * bean as the first parameter.
11125
-	 *
11126
-	 * @param OODBBean|array $bean the bean you have
11127
-	 * @param string         $type      the type of beans you want
11128
-	 * @param string         $sql       SQL snippet for extra filtering
11129
-	 * @param array          $bindings  values to be inserted in SQL slots
11130
-	 *
11131
-	 * @return array
11132
-	 */
11133
-	public function related( $bean, $type, $sql = '', $bindings = array() )
11134
-	{
11135
-		$sql   = $this->writer->glueSQLCondition( $sql );
11136
-		$rows  = $this->relatedRows( $bean, $type, $sql, $bindings );
11137
-		$links = array();
11138
-
11139
-		foreach ( $rows as $key => $row ) {
11140
-			if ( !isset( $links[$row['id']] ) ) $links[$row['id']] = array();
11141
-			$links[$row['id']][] = $row['linked_by'];
11142
-			unset( $rows[$key]['linked_by'] );
11143
-		}
11144
-
11145
-		$beans = $this->oodb->convertToBeans( $type, $rows );
11146
-		foreach ( $beans as $bean ) $bean->setMeta( 'sys.belongs-to', $links[$bean->id] );
11147
-
11148
-		return $beans;
11149
-	}
10817
+    /**
10818
+     * @var OODB
10819
+     */
10820
+    protected $oodb;
10821
+
10822
+    /**
10823
+     * @var DBAdapter
10824
+     */
10825
+    protected $adapter;
10826
+
10827
+    /**
10828
+     * @var QueryWriter
10829
+     */
10830
+    protected $writer;
10831
+
10832
+    /**
10833
+     * Exception handler.
10834
+     * Fluid and Frozen mode have different ways of handling
10835
+     * exceptions. Fluid mode (using the fluid repository) ignores
10836
+     * exceptions caused by the following:
10837
+     *
10838
+     * - missing tables
10839
+     * - missing column
10840
+     *
10841
+     * In these situations, the repository will behave as if
10842
+     * no beans could be found. This is because in fluid mode
10843
+     * it might happen to query a table or column that has not been
10844
+     * created yet. In frozen mode, this is not supposed to happen
10845
+     * and the corresponding exceptions will be thrown.
10846
+     *
10847
+     * @param \Exception $exception exception
10848
+     *
10849
+     * @return void
10850
+     */
10851
+    private function handleException( \Exception $exception )
10852
+    {
10853
+        if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(),
10854
+            array(
10855
+                QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
10856
+                QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ),
10857
+                $exception->getDriverDetails()
10858
+            )
10859
+        ) {
10860
+            throw $exception;
10861
+        }
10862
+    }
10863
+
10864
+    /**
10865
+     * Internal method.
10866
+     * Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and
10867
+     * $bindings bindings. If $getLinks is TRUE, link rows are returned instead.
10868
+     *
10869
+     * @param OODBBean $bean     reference bean instance
10870
+     * @param string   $type     target bean type
10871
+     * @param string   $sql      additional SQL snippet
10872
+     * @param array    $bindings bindings for query
10873
+     *
10874
+     * @return array
10875
+     */
10876
+    private function relatedRows( $bean, $type, $sql = '', $bindings = array() )
10877
+    {
10878
+        $ids = array( $bean->id );
10879
+        $sourceType = $bean->getMeta( 'type' );
10880
+        try {
10881
+            return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings );
10882
+        } catch ( SQLException $exception ) {
10883
+            $this->handleException( $exception );
10884
+            return array();
10885
+        }
10886
+    }
10887
+
10888
+    /**
10889
+     * Associates a pair of beans. This method associates two beans, no matter
10890
+     * what types. Accepts a base bean that contains data for the linking record.
10891
+     * This method is used by associate. This method also accepts a base bean to be used
10892
+     * as the template for the link record in the database.
10893
+     *
10894
+     * @param OODBBean $bean1 first bean
10895
+     * @param OODBBean $bean2 second bean
10896
+     * @param OODBBean $bean  base bean (association record)
10897
+     *
10898
+     * @return mixed
10899
+     */
10900
+    protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean )
10901
+    {
10902
+        $type      = $bean->getMeta( 'type' );
10903
+        $property1 = $bean1->getMeta( 'type' ) . '_id';
10904
+        $property2 = $bean2->getMeta( 'type' ) . '_id';
10905
+
10906
+        if ( $property1 == $property2 ) {
10907
+            $property2 = $bean2->getMeta( 'type' ) . '2_id';
10908
+        }
10909
+
10910
+        $this->oodb->store( $bean1 );
10911
+        $this->oodb->store( $bean2 );
10912
+
10913
+        $bean->setMeta( "cast.$property1", "id" );
10914
+        $bean->setMeta( "cast.$property2", "id" );
10915
+        $bean->setMeta( 'sys.buildcommand.unique', array( $property1, $property2 ) );
10916
+
10917
+        $bean->$property1 = $bean1->id;
10918
+        $bean->$property2 = $bean2->id;
10919
+
10920
+        $results   = array();
10921
+
10922
+        try {
10923
+            $id = $this->oodb->store( $bean );
10924
+            $results[] = $id;
10925
+        } catch ( SQLException $exception ) {
10926
+            if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
10927
+                array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ),
10928
+                $exception->getDriverDetails() )
10929
+            ) {
10930
+                throw $exception;
10931
+            }
10932
+        }
10933
+
10934
+        return $results;
10935
+    }
10936
+
10937
+    /**
10938
+     * Constructor, creates a new instance of the Association Manager.
10939
+     * The association manager can be used to create and manage
10940
+     * many-to-many relations (for example sharedLists). In a many-to-many relation,
10941
+     * one bean can be associated with many other beans, while each of those beans
10942
+     * can also be related to multiple beans. To create an Association Manager
10943
+     * instance you'll need to pass a ToolBox object.
10944
+     *
10945
+     * @param ToolBox $tools toolbox supplying core RedBeanPHP objects
10946
+     */
10947
+    public function __construct( ToolBox $tools )
10948
+    {
10949
+        $this->oodb    = $tools->getRedBean();
10950
+        $this->adapter = $tools->getDatabaseAdapter();
10951
+        $this->writer  = $tools->getWriter();
10952
+        $this->toolbox = $tools;
10953
+    }
10954
+
10955
+    /**
10956
+     * Creates a table name based on a types array.
10957
+     * Manages the get the correct name for the linking table for the
10958
+     * types provided.
10959
+     *
10960
+     * @param array $types 2 types as strings
10961
+     *
10962
+     * @return string
10963
+     */
10964
+    public function getTable( $types )
10965
+    {
10966
+        return $this->writer->getAssocTable( $types );
10967
+    }
10968
+
10969
+    /**
10970
+     * Associates two beans in a many-to-many relation.
10971
+     * This method will associate two beans and store the connection between the
10972
+     * two in a link table. Instead of two single beans this method also accepts
10973
+     * two sets of beans. Returns the ID or the IDs of the linking beans.
10974
+     *
10975
+     * @param OODBBean|array $beans1 one or more beans to form the association
10976
+     * @param OODBBean|array $beans2 one or more beans to form the association
10977
+     *
10978
+     * @return array
10979
+     */
10980
+    public function associate( $beans1, $beans2 )
10981
+    {
10982
+        if ( !is_array( $beans1 ) ) {
10983
+            $beans1 = array( $beans1 );
10984
+        }
10985
+
10986
+        if ( !is_array( $beans2 ) ) {
10987
+            $beans2 = array( $beans2 );
10988
+        }
10989
+
10990
+        $results = array();
10991
+        foreach ( $beans1 as $bean1 ) {
10992
+            foreach ( $beans2 as $bean2 ) {
10993
+                $table     = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) );
10994
+                $bean      = $this->oodb->dispense( $table );
10995
+                $results[] = $this->associateBeans( $bean1, $bean2, $bean );
10996
+            }
10997
+        }
10998
+
10999
+        return ( count( $results ) > 1 ) ? $results : reset( $results );
11000
+    }
11001
+
11002
+    /**
11003
+     * Counts the number of related beans in an N-M relation.
11004
+     * This method returns the number of beans of type $type associated
11005
+     * with reference bean(s) $bean. The query can be tuned using an
11006
+     * SQL snippet for additional filtering.
11007
+     *
11008
+     * @param OODBBean|array $bean     a bean object or an array of beans
11009
+     * @param string         $type     type of bean you're interested in
11010
+     * @param string         $sql      SQL snippet (optional)
11011
+     * @param array          $bindings bindings for your SQL string
11012
+     *
11013
+     * @return integer
11014
+     */
11015
+    public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() )
11016
+    {
11017
+        if ( !( $bean instanceof OODBBean ) ) {
11018
+            throw new RedException(
11019
+                'Expected array or OODBBean but got:' . gettype( $bean )
11020
+            );
11021
+        }
11022
+
11023
+        if ( !$bean->id ) {
11024
+            return 0;
11025
+        }
11026
+
11027
+        $beanType = $bean->getMeta( 'type' );
11028
+
11029
+        try {
11030
+            return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings );
11031
+        } catch ( SQLException $exception ) {
11032
+            $this->handleException( $exception );
11033
+
11034
+            return 0;
11035
+        }
11036
+    }
11037
+
11038
+    /**
11039
+     * Breaks the association between two beans. This method unassociates two beans. If the
11040
+     * method succeeds the beans will no longer form an association. In the database
11041
+     * this means that the association record will be removed. This method uses the
11042
+     * OODB trash() method to remove the association links, thus giving FUSE models the
11043
+     * opportunity to hook-in additional business logic. If the $fast parameter is
11044
+     * set to boolean TRUE this method will remove the beans without their consent,
11045
+     * bypassing FUSE. This can be used to improve performance.
11046
+     *
11047
+     * @param OODBBean $beans1 first bean in target association
11048
+     * @param OODBBean $beans2 second bean in target association
11049
+     * @param boolean  $fast  if TRUE, removes the entries by query without FUSE
11050
+     *
11051
+     * @return void
11052
+     */
11053
+    public function unassociate( $beans1, $beans2, $fast = NULL )
11054
+    {
11055
+        $beans1 = ( !is_array( $beans1 ) ) ? array( $beans1 ) : $beans1;
11056
+        $beans2 = ( !is_array( $beans2 ) ) ? array( $beans2 ) : $beans2;
11057
+
11058
+        foreach ( $beans1 as $bean1 ) {
11059
+            foreach ( $beans2 as $bean2 ) {
11060
+                try {
11061
+                    $this->oodb->store( $bean1 );
11062
+                    $this->oodb->store( $bean2 );
11063
+
11064
+                    $type1 = $bean1->getMeta( 'type' );
11065
+                    $type2 = $bean2->getMeta( 'type' );
11066
+
11067
+                    $row      = $this->writer->queryRecordLink( $type1, $type2, $bean1->id, $bean2->id );
11068
+                    $linkType = $this->getTable( array( $type1, $type2 ) );
11069
+
11070
+                    if ( $fast ) {
11071
+                        $this->writer->deleteRecord( $linkType, array( 'id' => $row['id'] ) );
11072
+
11073
+                        return;
11074
+                    }
11075
+
11076
+                    $beans = $this->oodb->convertToBeans( $linkType, array( $row ) );
11077
+
11078
+                    if ( count( $beans ) > 0 ) {
11079
+                        $bean = reset( $beans );
11080
+                        $this->oodb->trash( $bean );
11081
+                    }
11082
+                } catch ( SQLException $exception ) {
11083
+                    $this->handleException( $exception );
11084
+                }
11085
+            }
11086
+        }
11087
+    }
11088
+
11089
+    /**
11090
+     * Removes all relations for a bean. This method breaks every connection between
11091
+     * a certain bean $bean and every other bean of type $type. Warning: this method
11092
+     * is really fast because it uses a direct SQL query however it does not inform the
11093
+     * models about this. If you want to notify FUSE models about deletion use a foreach-loop
11094
+     * with unassociate() instead. (that might be slower though)
11095
+     *
11096
+     * @param OODBBean $bean reference bean
11097
+     * @param string   $type type of beans that need to be unassociated
11098
+     *
11099
+     * @return void
11100
+     */
11101
+    public function clearRelations( OODBBean $bean, $type )
11102
+    {
11103
+        $this->oodb->store( $bean );
11104
+        try {
11105
+            $this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id );
11106
+        } catch ( SQLException $exception ) {
11107
+            $this->handleException( $exception );
11108
+        }
11109
+    }
11110
+
11111
+    /**
11112
+     * Returns all the beans associated with $bean.
11113
+     * This method will return an array containing all the beans that have
11114
+     * been associated once with the associate() function and are still
11115
+     * associated with the bean specified. The type parameter indicates the
11116
+     * type of beans you are looking for. You can also pass some extra SQL and
11117
+     * values for that SQL to filter your results after fetching the
11118
+     * related beans.
11119
+     *
11120
+     * Don't try to make use of subqueries, a subquery using IN() seems to
11121
+     * be slower than two queries!
11122
+     *
11123
+     * Since 3.2, you can now also pass an array of beans instead just one
11124
+     * bean as the first parameter.
11125
+     *
11126
+     * @param OODBBean|array $bean the bean you have
11127
+     * @param string         $type      the type of beans you want
11128
+     * @param string         $sql       SQL snippet for extra filtering
11129
+     * @param array          $bindings  values to be inserted in SQL slots
11130
+     *
11131
+     * @return array
11132
+     */
11133
+    public function related( $bean, $type, $sql = '', $bindings = array() )
11134
+    {
11135
+        $sql   = $this->writer->glueSQLCondition( $sql );
11136
+        $rows  = $this->relatedRows( $bean, $type, $sql, $bindings );
11137
+        $links = array();
11138
+
11139
+        foreach ( $rows as $key => $row ) {
11140
+            if ( !isset( $links[$row['id']] ) ) $links[$row['id']] = array();
11141
+            $links[$row['id']][] = $row['linked_by'];
11142
+            unset( $rows[$key]['linked_by'] );
11143
+        }
11144
+
11145
+        $beans = $this->oodb->convertToBeans( $type, $rows );
11146
+        foreach ( $beans as $bean ) $bean->setMeta( 'sys.belongs-to', $links[$bean->id] );
11147
+
11148
+        return $beans;
11149
+    }
11150 11150
 }
11151 11151
 }
11152 11152
 
@@ -11178,35 +11178,35 @@  discard block
 block discarded – undo
11178 11178
  */
11179 11179
 interface BeanHelper
11180 11180
 {
11181
-	/**
11182
-	 * Returns a toolbox to empower the bean.
11183
-	 * This allows beans to perform OODB operations by themselves,
11184
-	 * as such the bean is a proxy for OODB. This allows beans to implement
11185
-	 * their magic getters and setters and return lists.
11186
-	 *
11187
-	 * @return ToolBox
11188
-	 */
11189
-	public function getToolbox();
11190
-
11191
-	/**
11192
-	 * Does approximately the same as getToolbox but also extracts the
11193
-	 * toolbox for you.
11194
-	 * This method returns a list with all toolbox items in Toolbox Constructor order:
11195
-	 * OODB, adapter, writer and finally the toolbox itself!.
11196
-	 *
11197
-	 * @return array
11198
-	 */
11199
-	public function getExtractedToolbox();
11200
-
11201
-	/**
11202
-	 * Given a certain bean this method will
11203
-	 * return the corresponding model.
11204
-	 *
11205
-	 * @param OODBBean $bean bean to obtain the corresponding model of
11206
-	 *
11207
-	 * @return SimpleModel|CustomModel|NULL
11208
-	 */
11209
-	public function getModelForBean( OODBBean $bean );
11181
+    /**
11182
+     * Returns a toolbox to empower the bean.
11183
+     * This allows beans to perform OODB operations by themselves,
11184
+     * as such the bean is a proxy for OODB. This allows beans to implement
11185
+     * their magic getters and setters and return lists.
11186
+     *
11187
+     * @return ToolBox
11188
+     */
11189
+    public function getToolbox();
11190
+
11191
+    /**
11192
+     * Does approximately the same as getToolbox but also extracts the
11193
+     * toolbox for you.
11194
+     * This method returns a list with all toolbox items in Toolbox Constructor order:
11195
+     * OODB, adapter, writer and finally the toolbox itself!.
11196
+     *
11197
+     * @return array
11198
+     */
11199
+    public function getExtractedToolbox();
11200
+
11201
+    /**
11202
+     * Given a certain bean this method will
11203
+     * return the corresponding model.
11204
+     *
11205
+     * @param OODBBean $bean bean to obtain the corresponding model of
11206
+     *
11207
+     * @return SimpleModel|CustomModel|NULL
11208
+     */
11209
+    public function getModelForBean( OODBBean $bean );
11210 11210
 }
11211 11211
 }
11212 11212
 
@@ -11235,87 +11235,87 @@  discard block
 block discarded – undo
11235 11235
  */
11236 11236
 class SimpleFacadeBeanHelper implements BeanHelper
11237 11237
 {
11238
-	/**
11239
-	 * Factory function to create instance of Simple Model, if any.
11240
-	 *
11241
-	 * @var \Closure
11242
-	 */
11243
-	private static $factory = null;
11244
-
11245
-	/**
11246
-	 * Factory method using a customizable factory function to create
11247
-	 * the instance of the Simple Model.
11248
-	 *
11249
-	 * @param string $modelClassName name of the class
11250
-	 *
11251
-	 * @return SimpleModel
11252
-	 */
11253
-	public static function factory( $modelClassName )
11254
-	{
11255
-		$factory = self::$factory;
11256
-		return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName();
11257
-	}
11258
-
11259
-	/**
11260
-	 * Sets the factory function to create the model when using FUSE
11261
-	 * to connect a bean to a model.
11262
-	 *
11263
-	 * @param \Closure $factory factory function
11264
-	 *
11265
-	 * @return void
11266
-	 */
11267
-	public static function setFactoryFunction( $factory )
11268
-	{
11269
-		self::$factory = $factory;
11270
-	}
11271
-
11272
-	/**
11273
-	 * @see BeanHelper::getToolbox
11274
-	 */
11275
-	public function getToolbox()
11276
-	{
11277
-		return Facade::getToolBox();
11278
-	}
11279
-
11280
-	/**
11281
-	 * @see BeanHelper::getModelForBean
11282
-	 */
11283
-	public function getModelForBean( OODBBean $bean )
11284
-	{
11285
-		$model     = $bean->getMeta( 'type' );
11286
-		$prefix    = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_';
11287
-
11288
-		if ( strpos( $model, '_' ) !== FALSE ) {
11289
-			$modelParts = explode( '_', $model );
11290
-			$modelName = '';
11291
-			foreach( $modelParts as $part ) {
11292
-				$modelName .= ucfirst( $part );
11293
-			}
11294
-			$modelName = $prefix . $modelName;
11295
-			if ( !class_exists( $modelName ) ) {
11296
-				$modelName = $prefix . ucfirst( $model );
11297
-				if ( !class_exists( $modelName ) ) {
11298
-					return NULL;
11299
-				}
11300
-			}
11301
-		} else {
11302
-			$modelName = $prefix . ucfirst( $model );
11303
-			if ( !class_exists( $modelName ) ) {
11304
-				return NULL;
11305
-			}
11306
-		}
11307
-		$obj = self::factory( $modelName );
11308
-		$obj->loadBean( $bean );
11309
-		return $obj;
11310
-	}
11311
-
11312
-	/**
11313
-	 * @see BeanHelper::getExtractedToolbox
11314
-	 */
11315
-	public function getExtractedToolbox()
11316
-	{
11317
-		return Facade::getExtractedToolbox();
11318
-	}
11238
+    /**
11239
+     * Factory function to create instance of Simple Model, if any.
11240
+     *
11241
+     * @var \Closure
11242
+     */
11243
+    private static $factory = null;
11244
+
11245
+    /**
11246
+     * Factory method using a customizable factory function to create
11247
+     * the instance of the Simple Model.
11248
+     *
11249
+     * @param string $modelClassName name of the class
11250
+     *
11251
+     * @return SimpleModel
11252
+     */
11253
+    public static function factory( $modelClassName )
11254
+    {
11255
+        $factory = self::$factory;
11256
+        return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName();
11257
+    }
11258
+
11259
+    /**
11260
+     * Sets the factory function to create the model when using FUSE
11261
+     * to connect a bean to a model.
11262
+     *
11263
+     * @param \Closure $factory factory function
11264
+     *
11265
+     * @return void
11266
+     */
11267
+    public static function setFactoryFunction( $factory )
11268
+    {
11269
+        self::$factory = $factory;
11270
+    }
11271
+
11272
+    /**
11273
+     * @see BeanHelper::getToolbox
11274
+     */
11275
+    public function getToolbox()
11276
+    {
11277
+        return Facade::getToolBox();
11278
+    }
11279
+
11280
+    /**
11281
+     * @see BeanHelper::getModelForBean
11282
+     */
11283
+    public function getModelForBean( OODBBean $bean )
11284
+    {
11285
+        $model     = $bean->getMeta( 'type' );
11286
+        $prefix    = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_';
11287
+
11288
+        if ( strpos( $model, '_' ) !== FALSE ) {
11289
+            $modelParts = explode( '_', $model );
11290
+            $modelName = '';
11291
+            foreach( $modelParts as $part ) {
11292
+                $modelName .= ucfirst( $part );
11293
+            }
11294
+            $modelName = $prefix . $modelName;
11295
+            if ( !class_exists( $modelName ) ) {
11296
+                $modelName = $prefix . ucfirst( $model );
11297
+                if ( !class_exists( $modelName ) ) {
11298
+                    return NULL;
11299
+                }
11300
+            }
11301
+        } else {
11302
+            $modelName = $prefix . ucfirst( $model );
11303
+            if ( !class_exists( $modelName ) ) {
11304
+                return NULL;
11305
+            }
11306
+        }
11307
+        $obj = self::factory( $modelName );
11308
+        $obj->loadBean( $bean );
11309
+        return $obj;
11310
+    }
11311
+
11312
+    /**
11313
+     * @see BeanHelper::getExtractedToolbox
11314
+     */
11315
+    public function getExtractedToolbox()
11316
+    {
11317
+        return Facade::getExtractedToolbox();
11318
+    }
11319 11319
 }
11320 11320
 }
11321 11321
 
@@ -11342,107 +11342,107 @@  discard block
 block discarded – undo
11342 11342
  */
11343 11343
 class SimpleModel
11344 11344
 {
11345
-	/**
11346
-	 * @var OODBBean
11347
-	 */
11348
-	protected $bean;
11349
-
11350
-	/**
11351
-	 * Used by FUSE: the ModelHelper class to connect a bean to a model.
11352
-	 * This method loads a bean in the model.
11353
-	 *
11354
-	 * @param OODBBean $bean bean to load
11355
-	 *
11356
-	 * @return void
11357
-	 */
11358
-	public function loadBean( OODBBean $bean )
11359
-	{
11360
-		$this->bean = $bean;
11361
-	}
11362
-
11363
-	/**
11364
-	 * Magic Getter to make the bean properties available from
11365
-	 * the $this-scope.
11366
-	 *
11367
-	 * @note this method returns a value, not a reference!
11368
-	 *       To obtain a reference unbox the bean first!
11369
-	 *
11370
-	 * @param string $prop property to get
11371
-	 *
11372
-	 * @return mixed
11373
-	 */
11374
-	public function __get( $prop )
11375
-	{
11376
-		return $this->bean->$prop;
11377
-	}
11378
-
11379
-	/**
11380
-	 * Magic Setter.
11381
-	 * Sets the value directly as a bean property.
11382
-	 *
11383
-	 * @param string $prop  property to set value of
11384
-	 * @param mixed  $value value to set
11385
-	 *
11386
-	 * @return void
11387
-	 */
11388
-	public function __set( $prop, $value )
11389
-	{
11390
-		$this->bean->$prop = $value;
11391
-	}
11392
-
11393
-	/**
11394
-	 * Isset implementation.
11395
-	 * Implements the isset function for array-like access.
11396
-	 *
11397
-	 * @param  string $key key to check
11398
-	 *
11399
-	 * @return boolean
11400
-	 */
11401
-	public function __isset( $key )
11402
-	{
11403
-		return isset( $this->bean->$key );
11404
-	}
11405
-
11406
-	/**
11407
-	 * Box the bean using the current model.
11408
-	 * This method wraps the current bean in this model.
11409
-	 * This method can be reached using FUSE through a simple
11410
-	 * OODBBean. The method returns a RedBeanPHP Simple Model.
11411
-	 * This is useful if you would like to rely on PHP type hinting.
11412
-	 * You can box your beans before passing them to functions or methods
11413
-	 * with typed parameters.
11414
-	 *
11415
-	 * Note about beans vs models:
11416
-	 * Use unbox to obtain the bean powering the model. If you want to use bean functionality,
11417
-	 * you should -always- unbox first. While some functionality (like magic get/set) is
11418
-	 * available in the model, this is just read-only. To use a model as a typical RedBean
11419
-	 * OODBBean you should always unbox the model to a bean. Models are meant to
11420
-	 * expose only domain logic added by the developer (business logic, no ORM logic).
11421
-	 *
11422
-	 * @return SimpleModel
11423
-	 */
11424
-	public function box()
11425
-	{
11426
-		return $this;
11427
-	}
11428
-
11429
-	/**
11430
-	 * Unbox the bean from the model.
11431
-	 * This method returns the bean inside the model.
11432
-	 *
11433
-	 * Note about beans vs models:
11434
-	 * Use unbox to obtain the bean powering the model. If you want to use bean functionality,
11435
-	 * you should -always- unbox first. While some functionality (like magic get/set) is
11436
-	 * available in the model, this is just read-only. To use a model as a typical RedBean
11437
-	 * OODBBean you should always unbox the model to a bean. Models are meant to
11438
-	 * expose only domain logic added by the developer (business logic, no ORM logic).
11439
-	 *
11440
-	 * @return OODBBean
11441
-	 */
11442
-	public function unbox()
11443
-	{
11444
-		return $this->bean;
11445
-	}
11345
+    /**
11346
+     * @var OODBBean
11347
+     */
11348
+    protected $bean;
11349
+
11350
+    /**
11351
+     * Used by FUSE: the ModelHelper class to connect a bean to a model.
11352
+     * This method loads a bean in the model.
11353
+     *
11354
+     * @param OODBBean $bean bean to load
11355
+     *
11356
+     * @return void
11357
+     */
11358
+    public function loadBean( OODBBean $bean )
11359
+    {
11360
+        $this->bean = $bean;
11361
+    }
11362
+
11363
+    /**
11364
+     * Magic Getter to make the bean properties available from
11365
+     * the $this-scope.
11366
+     *
11367
+     * @note this method returns a value, not a reference!
11368
+     *       To obtain a reference unbox the bean first!
11369
+     *
11370
+     * @param string $prop property to get
11371
+     *
11372
+     * @return mixed
11373
+     */
11374
+    public function __get( $prop )
11375
+    {
11376
+        return $this->bean->$prop;
11377
+    }
11378
+
11379
+    /**
11380
+     * Magic Setter.
11381
+     * Sets the value directly as a bean property.
11382
+     *
11383
+     * @param string $prop  property to set value of
11384
+     * @param mixed  $value value to set
11385
+     *
11386
+     * @return void
11387
+     */
11388
+    public function __set( $prop, $value )
11389
+    {
11390
+        $this->bean->$prop = $value;
11391
+    }
11392
+
11393
+    /**
11394
+     * Isset implementation.
11395
+     * Implements the isset function for array-like access.
11396
+     *
11397
+     * @param  string $key key to check
11398
+     *
11399
+     * @return boolean
11400
+     */
11401
+    public function __isset( $key )
11402
+    {
11403
+        return isset( $this->bean->$key );
11404
+    }
11405
+
11406
+    /**
11407
+     * Box the bean using the current model.
11408
+     * This method wraps the current bean in this model.
11409
+     * This method can be reached using FUSE through a simple
11410
+     * OODBBean. The method returns a RedBeanPHP Simple Model.
11411
+     * This is useful if you would like to rely on PHP type hinting.
11412
+     * You can box your beans before passing them to functions or methods
11413
+     * with typed parameters.
11414
+     *
11415
+     * Note about beans vs models:
11416
+     * Use unbox to obtain the bean powering the model. If you want to use bean functionality,
11417
+     * you should -always- unbox first. While some functionality (like magic get/set) is
11418
+     * available in the model, this is just read-only. To use a model as a typical RedBean
11419
+     * OODBBean you should always unbox the model to a bean. Models are meant to
11420
+     * expose only domain logic added by the developer (business logic, no ORM logic).
11421
+     *
11422
+     * @return SimpleModel
11423
+     */
11424
+    public function box()
11425
+    {
11426
+        return $this;
11427
+    }
11428
+
11429
+    /**
11430
+     * Unbox the bean from the model.
11431
+     * This method returns the bean inside the model.
11432
+     *
11433
+     * Note about beans vs models:
11434
+     * Use unbox to obtain the bean powering the model. If you want to use bean functionality,
11435
+     * you should -always- unbox first. While some functionality (like magic get/set) is
11436
+     * available in the model, this is just read-only. To use a model as a typical RedBean
11437
+     * OODBBean you should always unbox the model to a bean. Models are meant to
11438
+     * expose only domain logic added by the developer (business logic, no ORM logic).
11439
+     *
11440
+     * @return OODBBean
11441
+     */
11442
+    public function unbox()
11443
+    {
11444
+        return $this->bean;
11445
+    }
11446 11446
 }
11447 11447
 }
11448 11448
 
@@ -11469,51 +11469,51 @@  discard block
 block discarded – undo
11469 11469
  */
11470 11470
 class SimpleModelHelper implements Observer
11471 11471
 {
11472
-	/**
11473
-	 * Gets notified by an observable.
11474
-	 * This method decouples the FUSE system from the actual beans.
11475
-	 * If a FUSE event happens 'update', this method will attempt to
11476
-	 * invoke the corresponding method on the bean.
11477
-	 *
11478
-	 * @param string  $eventName i.e. 'delete', 'after_delete'
11479
-	 * @param OODBean $bean      affected bean
11480
-	 *
11481
-	 * @return void
11482
-	 */
11483
-	public function onEvent( $eventName, $bean )
11484
-	{
11485
-		$bean->$eventName();
11486
-	}
11487
-
11488
-	/**
11489
-	 * Attaches the FUSE event listeners. Now the Model Helper will listen for
11490
-	 * CRUD events. If a CRUD event occurs it will send a signal to the model
11491
-	 * that belongs to the CRUD bean and this model will take over control from
11492
-	 * there. This method will attach the following event listeners to the observable:
11493
-	 *
11494
-	 * - 'update'       (gets called by R::store, before the records gets inserted / updated)
11495
-	 * - 'after_update' (gets called by R::store, after the records have been inserted / updated)
11496
-	 * - 'open'         (gets called by R::load, after the record has been retrieved)
11497
-	 * - 'delete'       (gets called by R::trash, before deletion of record)
11498
-	 * - 'after_delete' (gets called by R::trash, after deletion)
11499
-	 * - 'dispense'     (gets called by R::dispense)
11500
-	 *
11501
-	 * For every event type, this method will register this helper as a listener.
11502
-	 * The observable will notify the listener (this object) with the event ID and the
11503
-	 * affected bean. This helper will then process the event (onEvent) by invoking
11504
-	 * the event on the bean. If a bean offers a method with the same name as the
11505
-	 * event ID, this method will be invoked.
11506
-	 *
11507
-	 * @param Observable $observable object to observe
11508
-	 *
11509
-	 * @return void
11510
-	 */
11511
-	public function attachEventListeners( Observable $observable )
11512
-	{
11513
-		foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $eventID ) {
11514
-			$observable->addEventListener( $eventID, $this );
11515
-		}
11516
-	}
11472
+    /**
11473
+     * Gets notified by an observable.
11474
+     * This method decouples the FUSE system from the actual beans.
11475
+     * If a FUSE event happens 'update', this method will attempt to
11476
+     * invoke the corresponding method on the bean.
11477
+     *
11478
+     * @param string  $eventName i.e. 'delete', 'after_delete'
11479
+     * @param OODBean $bean      affected bean
11480
+     *
11481
+     * @return void
11482
+     */
11483
+    public function onEvent( $eventName, $bean )
11484
+    {
11485
+        $bean->$eventName();
11486
+    }
11487
+
11488
+    /**
11489
+     * Attaches the FUSE event listeners. Now the Model Helper will listen for
11490
+     * CRUD events. If a CRUD event occurs it will send a signal to the model
11491
+     * that belongs to the CRUD bean and this model will take over control from
11492
+     * there. This method will attach the following event listeners to the observable:
11493
+     *
11494
+     * - 'update'       (gets called by R::store, before the records gets inserted / updated)
11495
+     * - 'after_update' (gets called by R::store, after the records have been inserted / updated)
11496
+     * - 'open'         (gets called by R::load, after the record has been retrieved)
11497
+     * - 'delete'       (gets called by R::trash, before deletion of record)
11498
+     * - 'after_delete' (gets called by R::trash, after deletion)
11499
+     * - 'dispense'     (gets called by R::dispense)
11500
+     *
11501
+     * For every event type, this method will register this helper as a listener.
11502
+     * The observable will notify the listener (this object) with the event ID and the
11503
+     * affected bean. This helper will then process the event (onEvent) by invoking
11504
+     * the event on the bean. If a bean offers a method with the same name as the
11505
+     * event ID, this method will be invoked.
11506
+     *
11507
+     * @param Observable $observable object to observe
11508
+     *
11509
+     * @return void
11510
+     */
11511
+    public function attachEventListeners( Observable $observable )
11512
+    {
11513
+        foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $eventID ) {
11514
+            $observable->addEventListener( $eventID, $this );
11515
+        }
11516
+    }
11517 11517
 }
11518 11518
 }
11519 11519
 
@@ -11528,362 +11528,362 @@  discard block
 block discarded – undo
11528 11528
  *
11529 11529
  * The tag manager offers an easy way to quickly implement basic tagging
11530 11530
  * functionality.
11531
- *
11532
- * Provides methods to tag beans and perform tag-based searches in the
11533
- * bean database.
11534
- *
11535
- * @file       RedBeanPHP/TagManager.php
11536
- * @author     Gabor de Mooij and the RedBeanPHP community
11537
- * @license    BSD/GPLv2
11538
- *
11539
- * @copyright
11540
- * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
11541
- * This source file is subject to the BSD/GPLv2 License that is bundled
11542
- * with this source code in the file license.txt.
11543
- */
11544
-class TagManager
11545
-{
11546
-	/**
11547
-	 * @var ToolBox
11548
-	 */
11549
-	protected $toolbox;
11550
-
11551
-	/**
11552
-	 * @var AssociationManager
11553
-	 */
11554
-	protected $associationManager;
11555
-
11556
-	/**
11557
-	 * @var OODBBean
11558
-	 */
11559
-	protected $redbean;
11560
-
11561
-	/**
11562
-	 * Checks if the argument is a comma separated string, in this case
11563
-	 * it will split the string into words and return an array instead.
11564
-	 * In case of an array the argument will be returned 'as is'.
11565
-	 *
11566
-	 * @param array|string $tagList list of tags
11567
-	 *
11568
-	 * @return array
11569
-	 */
11570
-	private function extractTagsIfNeeded( $tagList )
11571
-	{
11572
-		if ( $tagList !== FALSE && !is_array( $tagList ) ) {
11573
-			$tags = explode( ',', (string) $tagList );
11574
-		} else {
11575
-			$tags = $tagList;
11576
-		}
11577
-
11578
-		return $tags;
11579
-	}
11580
-
11581
-	/**
11582
-	 * Finds a tag bean by it's title.
11583
-	 * Internal method.
11584
-	 *
11585
-	 * @param string $title title to search for
11586
-	 *
11587
-	 * @return OODBBean
11588
-	 */
11589
-	protected function findTagByTitle( $title )
11590
-	{
11591
-		$beans = $this->redbean->find( 'tag', array( 'title' => array( $title ) ) );
11592
-
11593
-		if ( $beans ) {
11594
-			$bean = reset( $beans );
11595
-
11596
-			return $bean;
11597
-		}
11598
-
11599
-		return NULL;
11600
-	}
11601
-
11602
-	/**
11603
-	 * Constructor.
11604
-	 * The tag manager offers an easy way to quickly implement basic tagging
11605
-	 * functionality.
11606
-	 *
11607
-	 * @param ToolBox $toolbox toolbox object
11608
-	 */
11609
-	public function __construct( ToolBox $toolbox )
11610
-	{
11611
-		$this->toolbox = $toolbox;
11612
-		$this->redbean = $toolbox->getRedBean();
11613
-
11614
-		$this->associationManager = $this->redbean->getAssociationManager();
11615
-	}
11616
-
11617
-	/**
11618
-	 * Tests whether a bean has been associated with one ore more
11619
-	 * of the listed tags. If the third parameter is TRUE this method
11620
-	 * will return TRUE only if all tags that have been specified are indeed
11621
-	 * associated with the given bean, otherwise FALSE.
11622
-	 * If the third parameter is FALSE this
11623
-	 * method will return TRUE if one of the tags matches, FALSE if none
11624
-	 * match.
11625
-	 *
11626
-	 * Tag list can be either an array with tag names or a comma separated list
11627
-	 * of tag names.
11628
-	 *
11629
-	 * Usage:
11630
-	 *
11631
-	 * <code>
11632
-	 * R::hasTag( $blog, 'horror,movie', TRUE );
11633
-	 * </code>
11634
-	 *
11635
-	 * The example above returns TRUE if the $blog bean has been tagged
11636
-	 * as BOTH horror and movie. If the post has only been tagged as 'movie'
11637
-	 * or 'horror' this operation will return FALSE because the third parameter
11638
-	 * has been set to TRUE.
11639
-	 *
11640
-	 * @param  OODBBean     $bean bean to check for tags
11641
-	 * @param  array|string $tags list of tags
11642
-	 * @param  boolean      $all  whether they must all match or just some
11643
-	 *
11644
-	 * @return boolean
11645
-	 */
11646
-	public function hasTag( $bean, $tags, $all = FALSE )
11647
-	{
11648
-		$foundtags = $this->tag( $bean );
11649
-
11650
-		$tags = $this->extractTagsIfNeeded( $tags );
11651
-		$same = array_intersect( $tags, $foundtags );
11652
-
11653
-		if ( $all ) {
11654
-			return ( implode( ',', $same ) === implode( ',', $tags ) );
11655
-		}
11656
-
11657
-		return (bool) ( count( $same ) > 0 );
11658
-	}
11659
-
11660
-	/**
11661
-	 * Removes all specified tags from the bean. The tags specified in
11662
-	 * the second parameter will no longer be associated with the bean.
11663
-	 *
11664
-	 * Tag list can be either an array with tag names or a comma separated list
11665
-	 * of tag names.
11666
-	 *
11667
-	 * Usage:
11668
-	 *
11669
-	 * <code>
11670
-	 * R::untag( $blog, 'smart,interesting' );
11671
-	 * </code>
11672
-	 *
11673
-	 * In the example above, the $blog bean will no longer
11674
-	 * be associated with the tags 'smart' and 'interesting'.
11675
-	 *
11676
-	 * @param  OODBBean $bean    tagged bean
11677
-	 * @param  array    $tagList list of tags (names)
11678
-	 *
11679
-	 * @return void
11680
-	 */
11681
-	public function untag( $bean, $tagList )
11682
-	{
11683
-		$tags = $this->extractTagsIfNeeded( $tagList );
11684
-
11685
-		foreach ( $tags as $tag ) {
11686
-			if ( $t = $this->findTagByTitle( $tag ) ) {
11687
-				$this->associationManager->unassociate( $bean, $t );
11688
-			}
11689
-		}
11690
-	}
11691
-
11692
-	/**
11693
-	 * Part of RedBeanPHP Tagging API.
11694
-	 * Tags a bean or returns tags associated with a bean.
11695
-	 * If $tagList is NULL or omitted this method will return a
11696
-	 * comma separated list of tags associated with the bean provided.
11697
-	 * If $tagList is a comma separated list (string) of tags all tags will
11698
-	 * be associated with the bean.
11699
-	 * You may also pass an array instead of a string.
11700
-	 *
11701
-	 * Usage:
11702
-	 *
11703
-	 * <code>
11704
-	 * R::tag( $meal, "TexMex,Mexican" );
11705
-	 * $tags = R::tag( $meal );
11706
-	 * </code>
11707
-	 *
11708
-	 * The first line in the example above will tag the $meal
11709
-	 * as 'TexMex' and 'Mexican Cuisine'. The second line will
11710
-	 * retrieve all tags attached to the meal object.
11711
-	 *
11712
-	 * @param OODBBean $bean    bean to tag
11713
-	 * @param mixed    $tagList tags to attach to the specified bean
11714
-	 *
11715
-	 * @return string
11716
-	 */
11717
-	public function tag( OODBBean $bean, $tagList = NULL )
11718
-	{
11719
-		if ( is_null( $tagList ) ) {
11720
-
11721
-			$tags = $bean->sharedTag;
11722
-			$foundTags = array();
11723
-
11724
-			foreach ( $tags as $tag ) {
11725
-				$foundTags[] = $tag->title;
11726
-			}
11727
-
11728
-			return $foundTags;
11729
-		}
11730
-
11731
-		$this->associationManager->clearRelations( $bean, 'tag' );
11732
-		$this->addTags( $bean, $tagList );
11733
-
11734
-		return $tagList;
11735
-	}
11736
-
11737
-	/**
11738
-	 * Part of RedBeanPHP Tagging API.
11739
-	 * Adds tags to a bean.
11740
-	 * If $tagList is a comma separated list of tags all tags will
11741
-	 * be associated with the bean.
11742
-	 * You may also pass an array instead of a string.
11743
-	 *
11744
-	 * Usage:
11745
-	 *
11746
-	 * <code>
11747
-	 * R::addTags( $blog, ["halloween"] );
11748
-	 * </code>
11749
-	 *
11750
-	 * The example adds the tag 'halloween' to the $blog
11751
-	 * bean.
11752
-	 *
11753
-	 * @param OODBBean $bean    bean to tag
11754
-	 * @param array    $tagList list of tags to add to bean
11755
-	 *
11756
-	 * @return void
11757
-	 */
11758
-	public function addTags( OODBBean $bean, $tagList )
11759
-	{
11760
-		$tags = $this->extractTagsIfNeeded( $tagList );
11761
-
11762
-		if ( $tagList === FALSE ) {
11763
-			return;
11764
-		}
11765
-
11766
-		foreach ( $tags as $tag ) {
11767
-			if ( !$t = $this->findTagByTitle( $tag ) ) {
11768
-				$t        = $this->redbean->dispense( 'tag' );
11769
-				$t->title = $tag;
11770
-
11771
-				$this->redbean->store( $t );
11772
-			}
11773
-
11774
-			$this->associationManager->associate( $bean, $t );
11775
-		}
11776
-	}
11777
-
11778
-	/**
11779
-	 * Returns all beans that have been tagged with one or more
11780
-	 * of the specified tags.
11781
-	 *
11782
-	 * Tag list can be either an array with tag names or a comma separated list
11783
-	 * of tag names.
11784
-	 *
11785
-	 * Usage:
11786
-	 *
11787
-	 * <code>
11788
-	 * $watchList = R::tagged(
11789
-	 *   'movie',
11790
-	 *   'horror,gothic',
11791
-	 *   ' ORDER BY movie.title DESC LIMIT ?',
11792
-	 *   [ 10 ]
11793
-	 * );
11794
-	 * </code>
11795
-	 *
11796
-	 * The example uses R::tagged() to find all movies that have been
11797
-	 * tagged as 'horror' or 'gothic', order them by title and limit
11798
-	 * the number of movies to be returned to 10.
11799
-	 *
11800
-	 * @param string       $beanType type of bean you are looking for
11801
-	 * @param array|string $tagList  list of tags to match
11802
-	 * @param string       $sql      additional SQL (use only for pagination)
11803
-	 * @param array        $bindings bindings
11804
-	 *
11805
-	 * @return array
11806
-	 */
11807
-	public function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
11808
-	{
11809
-		$tags       = $this->extractTagsIfNeeded( $tagList );
11810
-		$records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings );
11811
-
11812
-		return $this->redbean->convertToBeans( $beanType, $records );
11813
-	}
11814
-
11815
-	/**
11816
-	 * Returns all beans that have been tagged with ALL of the tags given.
11817
-	 * This method works the same as R::tagged() except that this method only returns
11818
-	 * beans that have been tagged with all the specified labels.
11819
-	 *
11820
-	 * Tag list can be either an array with tag names or a comma separated list
11821
-	 * of tag names.
11822
-	 *
11823
-	 * Usage:
11824
-	 *
11825
-	 * <code>
11826
-	 * $watchList = R::taggedAll(
11827
-	 *    'movie',
11828
-	 *    [ 'gothic', 'short' ],
11829
-	 *    ' ORDER BY movie.id DESC LIMIT ? ',
11830
-	 *    [ 4 ]
11831
-	 * );
11832
-	 * </code>
11833
-	 *
11834
-	 * The example above returns at most 4 movies (due to the LIMIT clause in the SQL
11835
-	 * Query Snippet) that have been tagged as BOTH 'short' AND 'gothic'.
11836
-	 *
11837
-	 * @param string       $beanType type of bean you are looking for
11838
-	 * @param array|string $tagList  list of tags to match
11839
-	 * @param string       $sql      additional sql snippet
11840
-	 * @param array        $bindings bindings
11841
-	 *
11842
-	 * @return array
11843
-	 */
11844
-	public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
11845
-	{
11846
-		$tags  = $this->extractTagsIfNeeded( $tagList );
11847
-		$records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings );
11848
-
11849
-		return $this->redbean->convertToBeans( $beanType, $records );
11850
-	}
11851
-
11852
-	/**
11853
-	 * Like taggedAll() but only counts.
11854
-	 *
11855
-	 * @see taggedAll
11856
-	 *
11857
-	 * @param string       $beanType type of bean you are looking for
11858
-	 * @param array|string $tagList  list of tags to match
11859
-	 * @param string       $sql      additional sql snippet
11860
-	 * @param array        $bindings bindings
11861
-	 *
11862
-	 * @return integer
11863
-	 */
11864
-	public function countTaggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
11865
-	{
11866
-		$tags  = $this->extractTagsIfNeeded( $tagList );
11867
-		return $this->toolbox->getWriter()->queryCountTagged( $beanType, $tags, TRUE, $sql, $bindings );
11868
-	}
11869
-
11870
-	/**
11871
-	 * Like tagged() but only counts.
11872
-	 *
11873
-	 * @see tagged
11874
-	 *
11875
-	 * @param string       $beanType type of bean you are looking for
11876
-	 * @param array|string $tagList  list of tags to match
11877
-	 * @param string       $sql      additional sql snippet
11878
-	 * @param array        $bindings bindings
11879
-	 *
11880
-	 * @return integer
11881
-	 */
11882
-	public function countTagged( $beanType, $tagList, $sql = '', $bindings = array() )
11883
-	{
11884
-		$tags  = $this->extractTagsIfNeeded( $tagList );
11885
-		return $this->toolbox->getWriter()->queryCountTagged( $beanType, $tags, FALSE, $sql, $bindings );
11886
-	}
11531
+ *
11532
+ * Provides methods to tag beans and perform tag-based searches in the
11533
+ * bean database.
11534
+ *
11535
+ * @file       RedBeanPHP/TagManager.php
11536
+ * @author     Gabor de Mooij and the RedBeanPHP community
11537
+ * @license    BSD/GPLv2
11538
+ *
11539
+ * @copyright
11540
+ * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
11541
+ * This source file is subject to the BSD/GPLv2 License that is bundled
11542
+ * with this source code in the file license.txt.
11543
+ */
11544
+class TagManager
11545
+{
11546
+    /**
11547
+     * @var ToolBox
11548
+     */
11549
+    protected $toolbox;
11550
+
11551
+    /**
11552
+     * @var AssociationManager
11553
+     */
11554
+    protected $associationManager;
11555
+
11556
+    /**
11557
+     * @var OODBBean
11558
+     */
11559
+    protected $redbean;
11560
+
11561
+    /**
11562
+     * Checks if the argument is a comma separated string, in this case
11563
+     * it will split the string into words and return an array instead.
11564
+     * In case of an array the argument will be returned 'as is'.
11565
+     *
11566
+     * @param array|string $tagList list of tags
11567
+     *
11568
+     * @return array
11569
+     */
11570
+    private function extractTagsIfNeeded( $tagList )
11571
+    {
11572
+        if ( $tagList !== FALSE && !is_array( $tagList ) ) {
11573
+            $tags = explode( ',', (string) $tagList );
11574
+        } else {
11575
+            $tags = $tagList;
11576
+        }
11577
+
11578
+        return $tags;
11579
+    }
11580
+
11581
+    /**
11582
+     * Finds a tag bean by it's title.
11583
+     * Internal method.
11584
+     *
11585
+     * @param string $title title to search for
11586
+     *
11587
+     * @return OODBBean
11588
+     */
11589
+    protected function findTagByTitle( $title )
11590
+    {
11591
+        $beans = $this->redbean->find( 'tag', array( 'title' => array( $title ) ) );
11592
+
11593
+        if ( $beans ) {
11594
+            $bean = reset( $beans );
11595
+
11596
+            return $bean;
11597
+        }
11598
+
11599
+        return NULL;
11600
+    }
11601
+
11602
+    /**
11603
+     * Constructor.
11604
+     * The tag manager offers an easy way to quickly implement basic tagging
11605
+     * functionality.
11606
+     *
11607
+     * @param ToolBox $toolbox toolbox object
11608
+     */
11609
+    public function __construct( ToolBox $toolbox )
11610
+    {
11611
+        $this->toolbox = $toolbox;
11612
+        $this->redbean = $toolbox->getRedBean();
11613
+
11614
+        $this->associationManager = $this->redbean->getAssociationManager();
11615
+    }
11616
+
11617
+    /**
11618
+     * Tests whether a bean has been associated with one ore more
11619
+     * of the listed tags. If the third parameter is TRUE this method
11620
+     * will return TRUE only if all tags that have been specified are indeed
11621
+     * associated with the given bean, otherwise FALSE.
11622
+     * If the third parameter is FALSE this
11623
+     * method will return TRUE if one of the tags matches, FALSE if none
11624
+     * match.
11625
+     *
11626
+     * Tag list can be either an array with tag names or a comma separated list
11627
+     * of tag names.
11628
+     *
11629
+     * Usage:
11630
+     *
11631
+     * <code>
11632
+     * R::hasTag( $blog, 'horror,movie', TRUE );
11633
+     * </code>
11634
+     *
11635
+     * The example above returns TRUE if the $blog bean has been tagged
11636
+     * as BOTH horror and movie. If the post has only been tagged as 'movie'
11637
+     * or 'horror' this operation will return FALSE because the third parameter
11638
+     * has been set to TRUE.
11639
+     *
11640
+     * @param  OODBBean     $bean bean to check for tags
11641
+     * @param  array|string $tags list of tags
11642
+     * @param  boolean      $all  whether they must all match or just some
11643
+     *
11644
+     * @return boolean
11645
+     */
11646
+    public function hasTag( $bean, $tags, $all = FALSE )
11647
+    {
11648
+        $foundtags = $this->tag( $bean );
11649
+
11650
+        $tags = $this->extractTagsIfNeeded( $tags );
11651
+        $same = array_intersect( $tags, $foundtags );
11652
+
11653
+        if ( $all ) {
11654
+            return ( implode( ',', $same ) === implode( ',', $tags ) );
11655
+        }
11656
+
11657
+        return (bool) ( count( $same ) > 0 );
11658
+    }
11659
+
11660
+    /**
11661
+     * Removes all specified tags from the bean. The tags specified in
11662
+     * the second parameter will no longer be associated with the bean.
11663
+     *
11664
+     * Tag list can be either an array with tag names or a comma separated list
11665
+     * of tag names.
11666
+     *
11667
+     * Usage:
11668
+     *
11669
+     * <code>
11670
+     * R::untag( $blog, 'smart,interesting' );
11671
+     * </code>
11672
+     *
11673
+     * In the example above, the $blog bean will no longer
11674
+     * be associated with the tags 'smart' and 'interesting'.
11675
+     *
11676
+     * @param  OODBBean $bean    tagged bean
11677
+     * @param  array    $tagList list of tags (names)
11678
+     *
11679
+     * @return void
11680
+     */
11681
+    public function untag( $bean, $tagList )
11682
+    {
11683
+        $tags = $this->extractTagsIfNeeded( $tagList );
11684
+
11685
+        foreach ( $tags as $tag ) {
11686
+            if ( $t = $this->findTagByTitle( $tag ) ) {
11687
+                $this->associationManager->unassociate( $bean, $t );
11688
+            }
11689
+        }
11690
+    }
11691
+
11692
+    /**
11693
+     * Part of RedBeanPHP Tagging API.
11694
+     * Tags a bean or returns tags associated with a bean.
11695
+     * If $tagList is NULL or omitted this method will return a
11696
+     * comma separated list of tags associated with the bean provided.
11697
+     * If $tagList is a comma separated list (string) of tags all tags will
11698
+     * be associated with the bean.
11699
+     * You may also pass an array instead of a string.
11700
+     *
11701
+     * Usage:
11702
+     *
11703
+     * <code>
11704
+     * R::tag( $meal, "TexMex,Mexican" );
11705
+     * $tags = R::tag( $meal );
11706
+     * </code>
11707
+     *
11708
+     * The first line in the example above will tag the $meal
11709
+     * as 'TexMex' and 'Mexican Cuisine'. The second line will
11710
+     * retrieve all tags attached to the meal object.
11711
+     *
11712
+     * @param OODBBean $bean    bean to tag
11713
+     * @param mixed    $tagList tags to attach to the specified bean
11714
+     *
11715
+     * @return string
11716
+     */
11717
+    public function tag( OODBBean $bean, $tagList = NULL )
11718
+    {
11719
+        if ( is_null( $tagList ) ) {
11720
+
11721
+            $tags = $bean->sharedTag;
11722
+            $foundTags = array();
11723
+
11724
+            foreach ( $tags as $tag ) {
11725
+                $foundTags[] = $tag->title;
11726
+            }
11727
+
11728
+            return $foundTags;
11729
+        }
11730
+
11731
+        $this->associationManager->clearRelations( $bean, 'tag' );
11732
+        $this->addTags( $bean, $tagList );
11733
+
11734
+        return $tagList;
11735
+    }
11736
+
11737
+    /**
11738
+     * Part of RedBeanPHP Tagging API.
11739
+     * Adds tags to a bean.
11740
+     * If $tagList is a comma separated list of tags all tags will
11741
+     * be associated with the bean.
11742
+     * You may also pass an array instead of a string.
11743
+     *
11744
+     * Usage:
11745
+     *
11746
+     * <code>
11747
+     * R::addTags( $blog, ["halloween"] );
11748
+     * </code>
11749
+     *
11750
+     * The example adds the tag 'halloween' to the $blog
11751
+     * bean.
11752
+     *
11753
+     * @param OODBBean $bean    bean to tag
11754
+     * @param array    $tagList list of tags to add to bean
11755
+     *
11756
+     * @return void
11757
+     */
11758
+    public function addTags( OODBBean $bean, $tagList )
11759
+    {
11760
+        $tags = $this->extractTagsIfNeeded( $tagList );
11761
+
11762
+        if ( $tagList === FALSE ) {
11763
+            return;
11764
+        }
11765
+
11766
+        foreach ( $tags as $tag ) {
11767
+            if ( !$t = $this->findTagByTitle( $tag ) ) {
11768
+                $t        = $this->redbean->dispense( 'tag' );
11769
+                $t->title = $tag;
11770
+
11771
+                $this->redbean->store( $t );
11772
+            }
11773
+
11774
+            $this->associationManager->associate( $bean, $t );
11775
+        }
11776
+    }
11777
+
11778
+    /**
11779
+     * Returns all beans that have been tagged with one or more
11780
+     * of the specified tags.
11781
+     *
11782
+     * Tag list can be either an array with tag names or a comma separated list
11783
+     * of tag names.
11784
+     *
11785
+     * Usage:
11786
+     *
11787
+     * <code>
11788
+     * $watchList = R::tagged(
11789
+     *   'movie',
11790
+     *   'horror,gothic',
11791
+     *   ' ORDER BY movie.title DESC LIMIT ?',
11792
+     *   [ 10 ]
11793
+     * );
11794
+     * </code>
11795
+     *
11796
+     * The example uses R::tagged() to find all movies that have been
11797
+     * tagged as 'horror' or 'gothic', order them by title and limit
11798
+     * the number of movies to be returned to 10.
11799
+     *
11800
+     * @param string       $beanType type of bean you are looking for
11801
+     * @param array|string $tagList  list of tags to match
11802
+     * @param string       $sql      additional SQL (use only for pagination)
11803
+     * @param array        $bindings bindings
11804
+     *
11805
+     * @return array
11806
+     */
11807
+    public function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
11808
+    {
11809
+        $tags       = $this->extractTagsIfNeeded( $tagList );
11810
+        $records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings );
11811
+
11812
+        return $this->redbean->convertToBeans( $beanType, $records );
11813
+    }
11814
+
11815
+    /**
11816
+     * Returns all beans that have been tagged with ALL of the tags given.
11817
+     * This method works the same as R::tagged() except that this method only returns
11818
+     * beans that have been tagged with all the specified labels.
11819
+     *
11820
+     * Tag list can be either an array with tag names or a comma separated list
11821
+     * of tag names.
11822
+     *
11823
+     * Usage:
11824
+     *
11825
+     * <code>
11826
+     * $watchList = R::taggedAll(
11827
+     *    'movie',
11828
+     *    [ 'gothic', 'short' ],
11829
+     *    ' ORDER BY movie.id DESC LIMIT ? ',
11830
+     *    [ 4 ]
11831
+     * );
11832
+     * </code>
11833
+     *
11834
+     * The example above returns at most 4 movies (due to the LIMIT clause in the SQL
11835
+     * Query Snippet) that have been tagged as BOTH 'short' AND 'gothic'.
11836
+     *
11837
+     * @param string       $beanType type of bean you are looking for
11838
+     * @param array|string $tagList  list of tags to match
11839
+     * @param string       $sql      additional sql snippet
11840
+     * @param array        $bindings bindings
11841
+     *
11842
+     * @return array
11843
+     */
11844
+    public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
11845
+    {
11846
+        $tags  = $this->extractTagsIfNeeded( $tagList );
11847
+        $records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings );
11848
+
11849
+        return $this->redbean->convertToBeans( $beanType, $records );
11850
+    }
11851
+
11852
+    /**
11853
+     * Like taggedAll() but only counts.
11854
+     *
11855
+     * @see taggedAll
11856
+     *
11857
+     * @param string       $beanType type of bean you are looking for
11858
+     * @param array|string $tagList  list of tags to match
11859
+     * @param string       $sql      additional sql snippet
11860
+     * @param array        $bindings bindings
11861
+     *
11862
+     * @return integer
11863
+     */
11864
+    public function countTaggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
11865
+    {
11866
+        $tags  = $this->extractTagsIfNeeded( $tagList );
11867
+        return $this->toolbox->getWriter()->queryCountTagged( $beanType, $tags, TRUE, $sql, $bindings );
11868
+    }
11869
+
11870
+    /**
11871
+     * Like tagged() but only counts.
11872
+     *
11873
+     * @see tagged
11874
+     *
11875
+     * @param string       $beanType type of bean you are looking for
11876
+     * @param array|string $tagList  list of tags to match
11877
+     * @param string       $sql      additional sql snippet
11878
+     * @param array        $bindings bindings
11879
+     *
11880
+     * @return integer
11881
+     */
11882
+    public function countTagged( $beanType, $tagList, $sql = '', $bindings = array() )
11883
+    {
11884
+        $tags  = $this->extractTagsIfNeeded( $tagList );
11885
+        return $this->toolbox->getWriter()->queryCountTagged( $beanType, $tags, FALSE, $sql, $bindings );
11886
+    }
11887 11887
 }
11888 11888
 }
11889 11889
 
@@ -11911,161 +11911,161 @@  discard block
 block discarded – undo
11911 11911
  */
11912 11912
 class LabelMaker
11913 11913
 {
11914
-	/**
11915
-	 * @var ToolBox
11916
-	 */
11917
-	protected $toolbox;
11918
-
11919
-	/**
11920
-	 * Constructor.
11921
-	 *
11922
-	 * @param ToolBox $toolbox
11923
-	 */
11924
-	public function __construct( ToolBox $toolbox )
11925
-	{
11926
-		$this->toolbox = $toolbox;
11927
-	}
11928
-
11929
-	/**
11930
-	 * A label is a bean with only an id, type and name property.
11931
-	 * This function will dispense beans for all entries in the array. The
11932
-	 * values of the array will be assigned to the name property of each
11933
-	 * individual bean.
11934
-	 *
11935
-	 * <code>
11936
-	 * $people = R::dispenseLabels( 'person', [ 'Santa', 'Claus' ] );
11937
-	 * </code>
11938
-	 *
11939
-	 * @param string $type   type of beans you would like to have
11940
-	 * @param array  $labels list of labels, names for each bean
11941
-	 *
11942
-	 * @return array
11943
-	 */
11944
-	public function dispenseLabels( $type, $labels )
11945
-	{
11946
-		$labelBeans = array();
11947
-		foreach ( $labels as $label ) {
11948
-			$labelBean       = $this->toolbox->getRedBean()->dispense( $type );
11949
-			$labelBean->name = $label;
11950
-			$labelBeans[]    = $labelBean;
11951
-		}
11952
-
11953
-		return $labelBeans;
11954
-	}
11955
-
11956
-	/**
11957
-	 * Gathers labels from beans. This function loops through the beans,
11958
-	 * collects the value of the name property for each individual bean
11959
-	 * and stores the names in a new array. The array then gets sorted using the
11960
-	 * default sort function of PHP (sort).
11961
-	 *
11962
-	 * Usage:
11963
-	 *
11964
-	 * <code>
11965
-	 * $o1->name = 'hamburger';
11966
-	 * $o2->name = 'pizza';
11967
-	 * implode( ',', R::gatherLabels( [ $o1, $o2 ] ) ); //hamburger,pizza
11968
-	 * </code>
11969
-	 *
11970
-	 * Note that the return value is an array of strings, not beans.
11971
-	 *
11972
-	 * @param array $beans list of beans to loop through
11973
-	 *
11974
-	 * @return array
11975
-	 */
11976
-	public function gatherLabels( $beans )
11977
-	{
11978
-		$labels = array();
11979
-
11980
-		foreach ( $beans as $bean ) {
11981
-			$labels[] = $bean->name;
11982
-		}
11983
-
11984
-		sort( $labels );
11985
-
11986
-		return $labels;
11987
-	}
11988
-
11989
-	/**
11990
-	 * Fetches an ENUM from the database and creates it if necessary.
11991
-	 * An ENUM has the following format:
11992
-	 *
11993
-	 * <code>
11994
-	 * ENUM:VALUE
11995
-	 * </code>
11996
-	 *
11997
-	 * If you pass 'ENUM' only, this method will return an array of its
11998
-	 * values:
11999
-	 *
12000
-	 * <code>
12001
-	 * implode( ',', R::gatherLabels( R::enum( 'flavour' ) ) ) //'BANANA,MOCCA'
12002
-	 * </code>
12003
-	 *
12004
-	 * If you pass 'ENUM:VALUE' this method will return the specified enum bean
12005
-	 * and create it in the database if it does not exist yet:
12006
-	 *
12007
-	 * <code>
12008
-	 * $bananaFlavour = R::enum( 'flavour:banana' );
12009
-	 * $bananaFlavour->name;
12010
-	 * </code>
12011
-	 *
12012
-	 * So you can use this method to set an ENUM value in a bean:
12013
-	 *
12014
-	 * <code>
12015
-	 * $shake->flavour = R::enum( 'flavour:banana' );
12016
-	 * </code>
12017
-	 *
12018
-	 * the property flavour now contains the enum bean, a parent bean.
12019
-	 * In the database, flavour_id will point to the flavour record with name 'banana'.
12020
-	 *
12021
-	 * @param string $enum ENUM specification for label
12022
-	 *
12023
-	 * @return array|OODBBean
12024
-	 */
12025
-	public function enum( $enum )
12026
-	{
12027
-		$oodb = $this->toolbox->getRedBean();
12028
-
12029
-		if ( strpos( $enum, ':' ) === FALSE ) {
12030
-			$type  = $enum;
12031
-			$value = FALSE;
12032
-		} else {
12033
-			list( $type, $value ) = explode( ':', $enum );
12034
-			$value                = preg_replace( '/\W+/', '_', strtoupper( trim( $value ) ) );
12035
-		}
12036
-
12037
-		/**
12038
-		 * We use simply find here, we could use inspect() in fluid mode etc,
12039
-		 * but this would be useless. At first sight it looks clean, you could even
12040
-		 * bake this into find(), however, find not only has to deal with the primary
12041
-		 * search type, people can also include references in the SQL part, so avoiding
12042
-		 * find failures does not matter, this is still the quickest way making use
12043
-		 * of existing functionality.
12044
-		 *
12045
-		 * @note There seems to be a bug in XDebug v2.3.2 causing suppressed
12046
-		 * exceptions like these to surface anyway, to prevent this use:
12047
-		 *
12048
-		 * "xdebug.default_enable = 0"
12049
-		 *
12050
-		 *  Also see Github Issue #464
12051
-		 */
12052
-		$values = $oodb->find( $type );
12053
-
12054
-		if ( $value === FALSE ) {
12055
-			return $values;
12056
-		}
12057
-
12058
-		foreach( $values as $enumItem ) {
12059
-				if ( $enumItem->name === $value ) return $enumItem;
12060
-		}
12061
-
12062
-		$newEnumItems = $this->dispenseLabels( $type, array( $value ) );
12063
-		$newEnumItem  = reset( $newEnumItems );
12064
-
12065
-		$oodb->store( $newEnumItem );
12066
-
12067
-		return $newEnumItem;
12068
-	}
11914
+    /**
11915
+     * @var ToolBox
11916
+     */
11917
+    protected $toolbox;
11918
+
11919
+    /**
11920
+     * Constructor.
11921
+     *
11922
+     * @param ToolBox $toolbox
11923
+     */
11924
+    public function __construct( ToolBox $toolbox )
11925
+    {
11926
+        $this->toolbox = $toolbox;
11927
+    }
11928
+
11929
+    /**
11930
+     * A label is a bean with only an id, type and name property.
11931
+     * This function will dispense beans for all entries in the array. The
11932
+     * values of the array will be assigned to the name property of each
11933
+     * individual bean.
11934
+     *
11935
+     * <code>
11936
+     * $people = R::dispenseLabels( 'person', [ 'Santa', 'Claus' ] );
11937
+     * </code>
11938
+     *
11939
+     * @param string $type   type of beans you would like to have
11940
+     * @param array  $labels list of labels, names for each bean
11941
+     *
11942
+     * @return array
11943
+     */
11944
+    public function dispenseLabels( $type, $labels )
11945
+    {
11946
+        $labelBeans = array();
11947
+        foreach ( $labels as $label ) {
11948
+            $labelBean       = $this->toolbox->getRedBean()->dispense( $type );
11949
+            $labelBean->name = $label;
11950
+            $labelBeans[]    = $labelBean;
11951
+        }
11952
+
11953
+        return $labelBeans;
11954
+    }
11955
+
11956
+    /**
11957
+     * Gathers labels from beans. This function loops through the beans,
11958
+     * collects the value of the name property for each individual bean
11959
+     * and stores the names in a new array. The array then gets sorted using the
11960
+     * default sort function of PHP (sort).
11961
+     *
11962
+     * Usage:
11963
+     *
11964
+     * <code>
11965
+     * $o1->name = 'hamburger';
11966
+     * $o2->name = 'pizza';
11967
+     * implode( ',', R::gatherLabels( [ $o1, $o2 ] ) ); //hamburger,pizza
11968
+     * </code>
11969
+     *
11970
+     * Note that the return value is an array of strings, not beans.
11971
+     *
11972
+     * @param array $beans list of beans to loop through
11973
+     *
11974
+     * @return array
11975
+     */
11976
+    public function gatherLabels( $beans )
11977
+    {
11978
+        $labels = array();
11979
+
11980
+        foreach ( $beans as $bean ) {
11981
+            $labels[] = $bean->name;
11982
+        }
11983
+
11984
+        sort( $labels );
11985
+
11986
+        return $labels;
11987
+    }
11988
+
11989
+    /**
11990
+     * Fetches an ENUM from the database and creates it if necessary.
11991
+     * An ENUM has the following format:
11992
+     *
11993
+     * <code>
11994
+     * ENUM:VALUE
11995
+     * </code>
11996
+     *
11997
+     * If you pass 'ENUM' only, this method will return an array of its
11998
+     * values:
11999
+     *
12000
+     * <code>
12001
+     * implode( ',', R::gatherLabels( R::enum( 'flavour' ) ) ) //'BANANA,MOCCA'
12002
+     * </code>
12003
+     *
12004
+     * If you pass 'ENUM:VALUE' this method will return the specified enum bean
12005
+     * and create it in the database if it does not exist yet:
12006
+     *
12007
+     * <code>
12008
+     * $bananaFlavour = R::enum( 'flavour:banana' );
12009
+     * $bananaFlavour->name;
12010
+     * </code>
12011
+     *
12012
+     * So you can use this method to set an ENUM value in a bean:
12013
+     *
12014
+     * <code>
12015
+     * $shake->flavour = R::enum( 'flavour:banana' );
12016
+     * </code>
12017
+     *
12018
+     * the property flavour now contains the enum bean, a parent bean.
12019
+     * In the database, flavour_id will point to the flavour record with name 'banana'.
12020
+     *
12021
+     * @param string $enum ENUM specification for label
12022
+     *
12023
+     * @return array|OODBBean
12024
+     */
12025
+    public function enum( $enum )
12026
+    {
12027
+        $oodb = $this->toolbox->getRedBean();
12028
+
12029
+        if ( strpos( $enum, ':' ) === FALSE ) {
12030
+            $type  = $enum;
12031
+            $value = FALSE;
12032
+        } else {
12033
+            list( $type, $value ) = explode( ':', $enum );
12034
+            $value                = preg_replace( '/\W+/', '_', strtoupper( trim( $value ) ) );
12035
+        }
12036
+
12037
+        /**
12038
+         * We use simply find here, we could use inspect() in fluid mode etc,
12039
+         * but this would be useless. At first sight it looks clean, you could even
12040
+         * bake this into find(), however, find not only has to deal with the primary
12041
+         * search type, people can also include references in the SQL part, so avoiding
12042
+         * find failures does not matter, this is still the quickest way making use
12043
+         * of existing functionality.
12044
+         *
12045
+         * @note There seems to be a bug in XDebug v2.3.2 causing suppressed
12046
+         * exceptions like these to surface anyway, to prevent this use:
12047
+         *
12048
+         * "xdebug.default_enable = 0"
12049
+         *
12050
+         *  Also see Github Issue #464
12051
+         */
12052
+        $values = $oodb->find( $type );
12053
+
12054
+        if ( $value === FALSE ) {
12055
+            return $values;
12056
+        }
12057
+
12058
+        foreach( $values as $enumItem ) {
12059
+                if ( $enumItem->name === $value ) return $enumItem;
12060
+        }
12061
+
12062
+        $newEnumItems = $this->dispenseLabels( $type, array( $value ) );
12063
+        $newEnumItem  = reset( $newEnumItems );
12064
+
12065
+        $oodb->store( $newEnumItem );
12066
+
12067
+        return $newEnumItem;
12068
+    }
12069 12069
 }
12070 12070
 }
12071 12071
 
@@ -12115,2985 +12115,2985 @@  discard block
 block discarded – undo
12115 12115
  */
12116 12116
 class Facade
12117 12117
 {
12118
-	/**
12119
-	 * RedBeanPHP version constant.
12120
-	 */
12121
-	const C_REDBEANPHP_VERSION = '5.3';
12122
-
12123
-	/**
12124
-	 * @var ToolBox
12125
-	 */
12126
-	public static $toolbox;
12127
-
12128
-	/**
12129
-	 * @var OODB
12130
-	 */
12131
-	private static $redbean;
12132
-
12133
-	/**
12134
-	 * @var QueryWriter
12135
-	 */
12136
-	private static $writer;
12137
-
12138
-	/**
12139
-	 * @var DBAdapter
12140
-	 */
12141
-	private static $adapter;
12142
-
12143
-	/**
12144
-	 * @var AssociationManager
12145
-	 */
12146
-	private static $associationManager;
12147
-
12148
-	/**
12149
-	 * @var TagManager
12150
-	 */
12151
-	private static $tagManager;
12152
-
12153
-	/**
12154
-	 * @var DuplicationManager
12155
-	 */
12156
-	private static $duplicationManager;
12157
-
12158
-	/**
12159
-	 * @var LabelMaker
12160
-	 */
12161
-	private static $labelMaker;
12162
-
12163
-	/**
12164
-	 * @var Finder
12165
-	 */
12166
-	private static $finder;
12167
-
12168
-	/**
12169
-	 * @var Tree
12170
-	 */
12171
-	private static $tree;
12172
-
12173
-	/**
12174
-	 * @var Logger
12175
-	 */
12176
-	private static $logger;
12177
-
12178
-	/**
12179
-	 * @var array
12180
-	 */
12181
-	private static $plugins = array();
12182
-
12183
-	/**
12184
-	 * @var string
12185
-	 */
12186
-	private static $exportCaseStyle = 'default';
12187
-
12188
-	/**
12189
-	 * @var flag allows transactions through facade in fluid mode
12190
-	 */
12191
-	private static $allowFluidTransactions = FALSE;
12192
-
12193
-	/**
12194
-	 * Not in use (backward compatibility SQLHelper)
12195
-	 */
12196
-	public static $f;
12197
-
12198
-	/**
12199
-	 * @var string
12200
-	 */
12201
-	public static $currentDB = '';
12202
-
12203
-	/**
12204
-	 * @var array
12205
-	 */
12206
-	public static $toolboxes = array();
12207
-
12208
-	/**
12209
-	 * Internal Query function, executes the desired query. Used by
12210
-	 * all facade query functions. This keeps things DRY.
12211
-	 *
12212
-	 * @param string $method   desired query method (i.e. 'cell', 'col', 'exec' etc..)
12213
-	 * @param string $sql      the sql you want to execute
12214
-	 * @param array  $bindings array of values to be bound to query statement
12215
-	 *
12216
-	 * @return array
12217
-	 */
12218
-	private static function query( $method, $sql, $bindings )
12219
-	{
12220
-		if ( !self::$redbean->isFrozen() ) {
12221
-			try {
12222
-				$rs = Facade::$adapter->$method( $sql, $bindings );
12223
-			} catch ( SQLException $exception ) {
12224
-				if ( self::$writer->sqlStateIn( $exception->getSQLState(),
12225
-					array(
12226
-						QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
12227
-						QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
12228
-					,$exception->getDriverDetails()
12229
-					)
12230
-				) {
12231
-					return ( $method === 'getCell' ) ? NULL : array();
12232
-				} else {
12233
-					throw $exception;
12234
-				}
12235
-			}
12236
-
12237
-			return $rs;
12238
-		} else {
12239
-			return Facade::$adapter->$method( $sql, $bindings );
12240
-		}
12241
-	}
12242
-
12243
-	/**
12244
-	 * Returns the RedBeanPHP version string.
12245
-	 * The RedBeanPHP version string always has the same format "X.Y"
12246
-	 * where X is the major version number and Y is the minor version number.
12247
-	 * Point releases are not mentioned in the version string.
12248
-	 *
12249
-	 * @return string
12250
-	 */
12251
-	public static function getVersion()
12252
-	{
12253
-		return self::C_REDBEANPHP_VERSION;
12254
-	}
12255
-
12256
-	/**
12257
-	 * Tests the database connection.
12258
-	 * Returns TRUE if connection has been established and
12259
-	 * FALSE otherwise. Suppresses any warnings that may
12260
-	 * occur during the testing process and catches all
12261
-	 * exceptions that might be thrown during the test.
12262
-	 *
12263
-	 * @return boolean
12264
-	 */
12265
-	public static function testConnection()
12266
-	{
12267
-		if ( !isset( self::$adapter ) ) return FALSE;
12268
-
12269
-		$database = self::$adapter->getDatabase();
12270
-		try {
12271
-			@$database->connect();
12272
-		} catch ( \Exception $e ) {}
12273
-		return $database->isConnected();
12274
-	}
12275
-
12276
-	/**
12277
-	 * Kickstarts redbean for you. This method should be called before you start using
12278
-	 * RedBeanPHP. The Setup() method can be called without any arguments, in this case it will
12279
-	 * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems).
12280
-	 *
12281
-	 * Usage:
12282
-	 *
12283
-	 * <code>
12284
-	 * R::setup( 'mysql:host=localhost;dbname=mydatabase', 'dba', 'dbapassword' );
12285
-	 * </code>
12286
-	 *
12287
-	 * You can replace 'mysql:' with the name of the database you want to use.
12288
-	 * Possible values are:
12289
-	 *
12290
-	 * - pgsql  (PostgreSQL database)
12291
-	 * - sqlite (SQLite database)
12292
-	 * - mysql  (MySQL database)
12293
-	 * - mysql  (also for Maria database)
12294
-	 * - sqlsrv (MS SQL Server - community supported experimental driver)
12295
-	 * - CUBRID (CUBRID driver - basic support provided by Plugin)
12296
-	 *
12297
-	 * Note that setup() will not immediately establish a connection to the database.
12298
-	 * Instead, it will prepare the connection and connect 'lazily', i.e. the moment
12299
-	 * a connection is really required, for instance when attempting to load
12300
-	 * a bean.
12301
-	 *
12302
-	 * @param string  $dsn      Database connection string
12303
-	 * @param string  $username Username for database
12304
-	 * @param string  $password Password for database
12305
-	 * @param boolean $frozen   TRUE if you want to setup in frozen mode
12306
-	 *
12307
-	 * @return ToolBox
12308
-	 */
12309
-	public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE, $partialBeans = FALSE )
12310
-	{
12311
-		if ( is_null( $dsn ) ) {
12312
-			$dsn = 'sqlite:/' . sys_get_temp_dir() . '/red.db';
12313
-		}
12314
-
12315
-		self::addDatabase( 'default', $dsn, $username, $password, $frozen, $partialBeans );
12316
-		self::selectDatabase( 'default' );
12317
-
12318
-		return self::$toolbox;
12319
-	}
12320
-
12321
-	/**
12322
-	 * Toggles 'Narrow Field Mode'.
12323
-	 * In Narrow Field mode the queryRecord method will
12324
-	 * narrow its selection field to
12325
-	 *
12326
-	 * <code>
12327
-	 * SELECT {table}.*
12328
-	 * </code>
12329
-	 *
12330
-	 * instead of
12331
-	 *
12332
-	 * <code>
12333
-	 * SELECT *
12334
-	 * </code>
12335
-	 *
12336
-	 * This is a better way of querying because it allows
12337
-	 * more flexibility (for instance joins). However if you need
12338
-	 * the wide selector for backward compatibility; use this method
12339
-	 * to turn OFF Narrow Field Mode by passing FALSE.
12340
-	 * Default is TRUE.
12341
-	 *
12342
-	 * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field
12343
-	 *
12344
-	 * @return void
12345
-	 */
12346
-	public static function setNarrowFieldMode( $mode )
12347
-	{
12348
-		AQueryWriter::setNarrowFieldMode( $mode );
12349
-	}
12350
-
12351
-	/**
12352
-	 * Toggles fluid transactions. By default fluid transactions
12353
-	 * are not active. Starting, committing or rolling back a transaction
12354
-	 * through the facade in fluid mode will have no effect. If you wish
12355
-	 * to replace this standard portable behavor with behavior depending
12356
-	 * on how the used database platform handles fluid (DDL) transactions
12357
-	 * set this flag to TRUE.
12358
-	 *
12359
-	 * @param boolean $mode allow fluid transaction mode
12360
-	 *
12361
-	 * @return void
12362
-	 */
12363
-	public static function setAllowFluidTransactions( $mode )
12364
-	{
12365
-		self::$allowFluidTransactions = $mode;
12366
-	}
12367
-
12368
-	/**
12369
-	 * Wraps a transaction around a closure or string callback.
12370
-	 * If an Exception is thrown inside, the operation is automatically rolled back.
12371
-	 * If no Exception happens, it commits automatically.
12372
-	 * It also supports (simulated) nested transactions (that is useful when
12373
-	 * you have many methods that needs transactions but are unaware of
12374
-	 * each other).
12375
-	 *
12376
-	 * Example:
12377
-	 *
12378
-	 * <code>
12379
-	 * $from = 1;
12380
-	 * $to = 2;
12381
-	 * $amount = 300;
12382
-	 *
12383
-	 * R::transaction(function() use($from, $to, $amount)
12384
-	 * {
12385
-	 *   $accountFrom = R::load('account', $from);
12386
-	 *   $accountTo = R::load('account', $to);
12387
-	 *   $accountFrom->money -= $amount;
12388
-	 *   $accountTo->money += $amount;
12389
-	 *   R::store($accountFrom);
12390
-	 *   R::store($accountTo);
12391
-	 * });
12392
-	 * </code>
12393
-	 *
12394
-	 * @param callable $callback Closure (or other callable) with the transaction logic
12395
-	 *
12396
-	 * @return mixed
12397
-	 */
12398
-	public static function transaction( $callback )
12399
-	{
12400
-		return Transaction::transaction( self::$adapter, $callback );
12401
-	}
12402
-
12403
-	/**
12404
-	 * Adds a database to the facade, afterwards you can select the database using
12405
-	 * selectDatabase($key), where $key is the name you assigned to this database.
12406
-	 *
12407
-	 * Usage:
12408
-	 *
12409
-	 * <code>
12410
-	 * R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' );
12411
-	 * R::selectDatabase( 'database-1' ); //to select database again
12412
-	 * </code>
12413
-	 *
12414
-	 * This method allows you to dynamically add (and select) new databases
12415
-	 * to the facade. Adding a database with the same key will cause an exception.
12416
-	 *
12417
-	 * @param string      $key    ID for the database
12418
-	 * @param string      $dsn    DSN for the database
12419
-	 * @param string      $user   user for connection
12420
-	 * @param NULL|string $pass   password for connection
12421
-	 * @param bool        $frozen whether this database is frozen or not
12422
-	 *
12423
-	 * @return void
12424
-	 */
12425
-	public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE, $partialBeans = FALSE )
12426
-	{
12427
-		if ( isset( self::$toolboxes[$key] ) ) {
12428
-			throw new RedException( 'A database has already been specified for this key.' );
12429
-		}
12430
-
12431
-		if ( is_object($dsn) ) {
12432
-			$db  = new RPDO( $dsn );
12433
-			$dbType = $db->getDatabaseType();
12434
-		} else {
12435
-			$db = new RPDO( $dsn, $user, $pass, TRUE );
12436
-			$dbType = substr( $dsn, 0, strpos( $dsn, ':' ) );
12437
-		}
12438
-
12439
-		$adapter = new DBAdapter( $db );
12440
-
12441
-		$writers = array(
12442
-			'pgsql'  => 'PostgreSQL',
12443
-			'sqlite' => 'SQLiteT',
12444
-			'cubrid' => 'CUBRID',
12445
-			'mysql'  => 'MySQL',
12446
-			'sqlsrv' => 'SQLServer',
12447
-		);
12448
-
12449
-		$wkey = trim( strtolower( $dbType ) );
12450
-		if ( !isset( $writers[$wkey] ) ) {
12451
-			$wkey = preg_replace( '/\W/', '' , $wkey );
12452
-			throw new RedException( 'Unsupported database ('.$wkey.').' );
12453
-		}
12454
-		$writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey];
12455
-		$writer      = new $writerClass( $adapter );
12456
-		$redbean     = new OODB( $writer, $frozen );
12118
+    /**
12119
+     * RedBeanPHP version constant.
12120
+     */
12121
+    const C_REDBEANPHP_VERSION = '5.3';
12122
+
12123
+    /**
12124
+     * @var ToolBox
12125
+     */
12126
+    public static $toolbox;
12127
+
12128
+    /**
12129
+     * @var OODB
12130
+     */
12131
+    private static $redbean;
12132
+
12133
+    /**
12134
+     * @var QueryWriter
12135
+     */
12136
+    private static $writer;
12137
+
12138
+    /**
12139
+     * @var DBAdapter
12140
+     */
12141
+    private static $adapter;
12142
+
12143
+    /**
12144
+     * @var AssociationManager
12145
+     */
12146
+    private static $associationManager;
12147
+
12148
+    /**
12149
+     * @var TagManager
12150
+     */
12151
+    private static $tagManager;
12152
+
12153
+    /**
12154
+     * @var DuplicationManager
12155
+     */
12156
+    private static $duplicationManager;
12157
+
12158
+    /**
12159
+     * @var LabelMaker
12160
+     */
12161
+    private static $labelMaker;
12162
+
12163
+    /**
12164
+     * @var Finder
12165
+     */
12166
+    private static $finder;
12167
+
12168
+    /**
12169
+     * @var Tree
12170
+     */
12171
+    private static $tree;
12172
+
12173
+    /**
12174
+     * @var Logger
12175
+     */
12176
+    private static $logger;
12177
+
12178
+    /**
12179
+     * @var array
12180
+     */
12181
+    private static $plugins = array();
12182
+
12183
+    /**
12184
+     * @var string
12185
+     */
12186
+    private static $exportCaseStyle = 'default';
12187
+
12188
+    /**
12189
+     * @var flag allows transactions through facade in fluid mode
12190
+     */
12191
+    private static $allowFluidTransactions = FALSE;
12192
+
12193
+    /**
12194
+     * Not in use (backward compatibility SQLHelper)
12195
+     */
12196
+    public static $f;
12197
+
12198
+    /**
12199
+     * @var string
12200
+     */
12201
+    public static $currentDB = '';
12202
+
12203
+    /**
12204
+     * @var array
12205
+     */
12206
+    public static $toolboxes = array();
12207
+
12208
+    /**
12209
+     * Internal Query function, executes the desired query. Used by
12210
+     * all facade query functions. This keeps things DRY.
12211
+     *
12212
+     * @param string $method   desired query method (i.e. 'cell', 'col', 'exec' etc..)
12213
+     * @param string $sql      the sql you want to execute
12214
+     * @param array  $bindings array of values to be bound to query statement
12215
+     *
12216
+     * @return array
12217
+     */
12218
+    private static function query( $method, $sql, $bindings )
12219
+    {
12220
+        if ( !self::$redbean->isFrozen() ) {
12221
+            try {
12222
+                $rs = Facade::$adapter->$method( $sql, $bindings );
12223
+            } catch ( SQLException $exception ) {
12224
+                if ( self::$writer->sqlStateIn( $exception->getSQLState(),
12225
+                    array(
12226
+                        QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
12227
+                        QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
12228
+                    ,$exception->getDriverDetails()
12229
+                    )
12230
+                ) {
12231
+                    return ( $method === 'getCell' ) ? NULL : array();
12232
+                } else {
12233
+                    throw $exception;
12234
+                }
12235
+            }
12236
+
12237
+            return $rs;
12238
+        } else {
12239
+            return Facade::$adapter->$method( $sql, $bindings );
12240
+        }
12241
+    }
12242
+
12243
+    /**
12244
+     * Returns the RedBeanPHP version string.
12245
+     * The RedBeanPHP version string always has the same format "X.Y"
12246
+     * where X is the major version number and Y is the minor version number.
12247
+     * Point releases are not mentioned in the version string.
12248
+     *
12249
+     * @return string
12250
+     */
12251
+    public static function getVersion()
12252
+    {
12253
+        return self::C_REDBEANPHP_VERSION;
12254
+    }
12255
+
12256
+    /**
12257
+     * Tests the database connection.
12258
+     * Returns TRUE if connection has been established and
12259
+     * FALSE otherwise. Suppresses any warnings that may
12260
+     * occur during the testing process and catches all
12261
+     * exceptions that might be thrown during the test.
12262
+     *
12263
+     * @return boolean
12264
+     */
12265
+    public static function testConnection()
12266
+    {
12267
+        if ( !isset( self::$adapter ) ) return FALSE;
12268
+
12269
+        $database = self::$adapter->getDatabase();
12270
+        try {
12271
+            @$database->connect();
12272
+        } catch ( \Exception $e ) {}
12273
+        return $database->isConnected();
12274
+    }
12275
+
12276
+    /**
12277
+     * Kickstarts redbean for you. This method should be called before you start using
12278
+     * RedBeanPHP. The Setup() method can be called without any arguments, in this case it will
12279
+     * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems).
12280
+     *
12281
+     * Usage:
12282
+     *
12283
+     * <code>
12284
+     * R::setup( 'mysql:host=localhost;dbname=mydatabase', 'dba', 'dbapassword' );
12285
+     * </code>
12286
+     *
12287
+     * You can replace 'mysql:' with the name of the database you want to use.
12288
+     * Possible values are:
12289
+     *
12290
+     * - pgsql  (PostgreSQL database)
12291
+     * - sqlite (SQLite database)
12292
+     * - mysql  (MySQL database)
12293
+     * - mysql  (also for Maria database)
12294
+     * - sqlsrv (MS SQL Server - community supported experimental driver)
12295
+     * - CUBRID (CUBRID driver - basic support provided by Plugin)
12296
+     *
12297
+     * Note that setup() will not immediately establish a connection to the database.
12298
+     * Instead, it will prepare the connection and connect 'lazily', i.e. the moment
12299
+     * a connection is really required, for instance when attempting to load
12300
+     * a bean.
12301
+     *
12302
+     * @param string  $dsn      Database connection string
12303
+     * @param string  $username Username for database
12304
+     * @param string  $password Password for database
12305
+     * @param boolean $frozen   TRUE if you want to setup in frozen mode
12306
+     *
12307
+     * @return ToolBox
12308
+     */
12309
+    public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE, $partialBeans = FALSE )
12310
+    {
12311
+        if ( is_null( $dsn ) ) {
12312
+            $dsn = 'sqlite:/' . sys_get_temp_dir() . '/red.db';
12313
+        }
12314
+
12315
+        self::addDatabase( 'default', $dsn, $username, $password, $frozen, $partialBeans );
12316
+        self::selectDatabase( 'default' );
12317
+
12318
+        return self::$toolbox;
12319
+    }
12320
+
12321
+    /**
12322
+     * Toggles 'Narrow Field Mode'.
12323
+     * In Narrow Field mode the queryRecord method will
12324
+     * narrow its selection field to
12325
+     *
12326
+     * <code>
12327
+     * SELECT {table}.*
12328
+     * </code>
12329
+     *
12330
+     * instead of
12331
+     *
12332
+     * <code>
12333
+     * SELECT *
12334
+     * </code>
12335
+     *
12336
+     * This is a better way of querying because it allows
12337
+     * more flexibility (for instance joins). However if you need
12338
+     * the wide selector for backward compatibility; use this method
12339
+     * to turn OFF Narrow Field Mode by passing FALSE.
12340
+     * Default is TRUE.
12341
+     *
12342
+     * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field
12343
+     *
12344
+     * @return void
12345
+     */
12346
+    public static function setNarrowFieldMode( $mode )
12347
+    {
12348
+        AQueryWriter::setNarrowFieldMode( $mode );
12349
+    }
12350
+
12351
+    /**
12352
+     * Toggles fluid transactions. By default fluid transactions
12353
+     * are not active. Starting, committing or rolling back a transaction
12354
+     * through the facade in fluid mode will have no effect. If you wish
12355
+     * to replace this standard portable behavor with behavior depending
12356
+     * on how the used database platform handles fluid (DDL) transactions
12357
+     * set this flag to TRUE.
12358
+     *
12359
+     * @param boolean $mode allow fluid transaction mode
12360
+     *
12361
+     * @return void
12362
+     */
12363
+    public static function setAllowFluidTransactions( $mode )
12364
+    {
12365
+        self::$allowFluidTransactions = $mode;
12366
+    }
12367
+
12368
+    /**
12369
+     * Wraps a transaction around a closure or string callback.
12370
+     * If an Exception is thrown inside, the operation is automatically rolled back.
12371
+     * If no Exception happens, it commits automatically.
12372
+     * It also supports (simulated) nested transactions (that is useful when
12373
+     * you have many methods that needs transactions but are unaware of
12374
+     * each other).
12375
+     *
12376
+     * Example:
12377
+     *
12378
+     * <code>
12379
+     * $from = 1;
12380
+     * $to = 2;
12381
+     * $amount = 300;
12382
+     *
12383
+     * R::transaction(function() use($from, $to, $amount)
12384
+     * {
12385
+     *   $accountFrom = R::load('account', $from);
12386
+     *   $accountTo = R::load('account', $to);
12387
+     *   $accountFrom->money -= $amount;
12388
+     *   $accountTo->money += $amount;
12389
+     *   R::store($accountFrom);
12390
+     *   R::store($accountTo);
12391
+     * });
12392
+     * </code>
12393
+     *
12394
+     * @param callable $callback Closure (or other callable) with the transaction logic
12395
+     *
12396
+     * @return mixed
12397
+     */
12398
+    public static function transaction( $callback )
12399
+    {
12400
+        return Transaction::transaction( self::$adapter, $callback );
12401
+    }
12402
+
12403
+    /**
12404
+     * Adds a database to the facade, afterwards you can select the database using
12405
+     * selectDatabase($key), where $key is the name you assigned to this database.
12406
+     *
12407
+     * Usage:
12408
+     *
12409
+     * <code>
12410
+     * R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' );
12411
+     * R::selectDatabase( 'database-1' ); //to select database again
12412
+     * </code>
12413
+     *
12414
+     * This method allows you to dynamically add (and select) new databases
12415
+     * to the facade. Adding a database with the same key will cause an exception.
12416
+     *
12417
+     * @param string      $key    ID for the database
12418
+     * @param string      $dsn    DSN for the database
12419
+     * @param string      $user   user for connection
12420
+     * @param NULL|string $pass   password for connection
12421
+     * @param bool        $frozen whether this database is frozen or not
12422
+     *
12423
+     * @return void
12424
+     */
12425
+    public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE, $partialBeans = FALSE )
12426
+    {
12427
+        if ( isset( self::$toolboxes[$key] ) ) {
12428
+            throw new RedException( 'A database has already been specified for this key.' );
12429
+        }
12430
+
12431
+        if ( is_object($dsn) ) {
12432
+            $db  = new RPDO( $dsn );
12433
+            $dbType = $db->getDatabaseType();
12434
+        } else {
12435
+            $db = new RPDO( $dsn, $user, $pass, TRUE );
12436
+            $dbType = substr( $dsn, 0, strpos( $dsn, ':' ) );
12437
+        }
12438
+
12439
+        $adapter = new DBAdapter( $db );
12440
+
12441
+        $writers = array(
12442
+            'pgsql'  => 'PostgreSQL',
12443
+            'sqlite' => 'SQLiteT',
12444
+            'cubrid' => 'CUBRID',
12445
+            'mysql'  => 'MySQL',
12446
+            'sqlsrv' => 'SQLServer',
12447
+        );
12448
+
12449
+        $wkey = trim( strtolower( $dbType ) );
12450
+        if ( !isset( $writers[$wkey] ) ) {
12451
+            $wkey = preg_replace( '/\W/', '' , $wkey );
12452
+            throw new RedException( 'Unsupported database ('.$wkey.').' );
12453
+        }
12454
+        $writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey];
12455
+        $writer      = new $writerClass( $adapter );
12456
+        $redbean     = new OODB( $writer, $frozen );
12457 12457
 		
12458
-		if ( $partialBeans ) {
12459
-			$redbean->getCurrentRepository()->usePartialBeans( $partialBeans );
12460
-		}
12461
-
12462
-		self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer );
12463
-	}
12464
-
12465
-	/**
12466
-	 * Sets PDO attributes for MySQL SSL connection.
12467
-	 *
12468
-	 * @param string $key  path client key i.e. '/etc/mysql/ssl/client-key.pem'
12469
-	 * @param string $cert path client cert i.e. '/etc/mysql/ssl/client-cert.pem'
12470
-	 * @param string $ca   path certifying agent certificate '/etc/mysql/ssl/ca-cert.pem'
12471
-	 * @param string $id   apply to toolbox (default = 'default')
12472
-	 */
12473
-	public static function useMysqlSSL( $key, $cert, $ca, $id = 'default' ) {
12474
-		$pdo = self::$toolboxes[$id]->getDatabaseAdapter()->getDatabase()->getPDO();
12475
-		$pdo->setAttribute( \PDO::MYSQL_ATTR_SSL_KEY,  $key);
12476
-		$pdo->setAttribute( \PDO::MYSQL_ATTR_SSL_CERT,  $cert);
12477
-		$pdo->setAttribute( \PDO::MYSQL_ATTR_SSL_CA,  $ca);
12478
-	}
12479
-
12480
-	/**
12481
-	 * Determines whether a database identified with the specified key has
12482
-	 * already been added to the facade. This function will return TRUE
12483
-	 * if the database indicated by the key is available and FALSE otherwise.
12484
-	 *
12485
-	 * @param string $key the key/name of the database to check for
12486
-	 *
12487
-	 * @return boolean
12488
-	 */
12489
-	public static function hasDatabase( $key )
12490
-	{
12491
-		return ( isset( self::$toolboxes[$key] ) );
12492
-	}
12493
-
12494
-	/**
12495
-	 * Selects a different database for the Facade to work with.
12496
-	 * If you use the R::setup() you don't need this method. This method is meant
12497
-	 * for multiple database setups. This method selects the database identified by the
12498
-	 * database ID ($key). Use addDatabase() to add a new database, which in turn
12499
-	 * can be selected using selectDatabase(). If you use R::setup(), the resulting
12500
-	 * database will be stored under key 'default', to switch (back) to this database
12501
-	 * use R::selectDatabase( 'default' ). This method returns TRUE if the database has been
12502
-	 * switched and FALSE otherwise (for instance if you already using the specified database).
12503
-	 *
12504
-	 * @param  string $key Key of the database to select
12505
-	 *
12506
-	 * @return boolean
12507
-	 */
12508
-	public static function selectDatabase( $key, $force = FALSE )
12509
-	{
12510
-		if ( self::$currentDB === $key && !$force ) {
12511
-			return FALSE;
12512
-		}
12513
-
12514
-		if ( !isset( self::$toolboxes[$key] ) ) {
12515
-			throw new RedException( 'Database not found in registry. Add database using R::addDatabase().' );
12516
-		}
12517
-
12518
-		self::configureFacadeWithToolbox( self::$toolboxes[$key] );
12519
-		self::$currentDB = $key;
12520
-
12521
-		return TRUE;
12522
-	}
12523
-
12524
-	/**
12525
-	 * Toggles DEBUG mode.
12526
-	 * In Debug mode all SQL that happens under the hood will
12527
-	 * be printed to the screen and/or logged.
12528
-	 * If no database connection has been configured using R::setup() or
12529
-	 * R::selectDatabase() this method will throw an exception.
12530
-	 *
12531
-	 * There are 2 debug styles:
12532
-	 *
12533
-	 * Classic: separate parameter bindings, explicit and complete but less readable
12534
-	 * Fancy:   interpersed bindings, truncates large strings, highlighted schema changes
12535
-	 *
12536
-	 * Fancy style is more readable but sometimes incomplete.
12537
-	 *
12538
-	 * The first parameter turns debugging ON or OFF.
12539
-	 * The second parameter indicates the mode of operation:
12540
-	 *
12541
-	 * 0 Log and write to STDOUT classic style (default)
12542
-	 * 1 Log only, class style
12543
-	 * 2 Log and write to STDOUT fancy style
12544
-	 * 3 Log only, fancy style
12545
-	 *
12546
-	 * This function always returns the logger instance created to generate the
12547
-	 * debug messages.
12548
-	 *
12549
-	 * @param boolean $tf   debug mode (TRUE or FALSE)
12550
-	 * @param integer $mode mode of operation
12551
-	 *
12552
-	 * @return RDefault
12553
-	 * @throws RedException
12554
-	 */
12555
-	public static function debug( $tf = TRUE, $mode = 0 )
12556
-	{
12557
-		if ($mode > 1) {
12558
-			$mode -= 2;
12559
-			$logger = new Debug;
12560
-		} else {
12561
-			$logger = new RDefault;
12562
-		}
12563
-
12564
-		if ( !isset( self::$adapter ) ) {
12565
-			throw new RedException( 'Use R::setup() first.' );
12566
-		}
12567
-		$logger->setMode($mode);
12568
-		self::$adapter->getDatabase()->setDebugMode( $tf, $logger );
12569
-
12570
-		return $logger;
12571
-	}
12572
-
12573
-	/**
12574
-	 * Turns on the fancy debugger.
12575
-	 * In 'fancy' mode the debugger will output queries with bound
12576
-	 * parameters inside the SQL itself. This method has been added to
12577
-	 * offer a convenient way to activate the fancy debugger system
12578
-	 * in one call.
12579
-	 *
12580
-	 * @param boolean $toggle TRUE to activate debugger and select 'fancy' mode
12581
-	 *
12582
-	 * @return void
12583
-	 */
12584
-	public static function fancyDebug( $toggle = TRUE )
12585
-	{
12586
-		self::debug( $toggle, 2 );
12587
-	}
12588
-
12589
-	/**
12590
-	* Inspects the database schema. If you pass the type of a bean this
12591
-	* method will return the fields of its table in the database.
12592
-	* The keys of this array will be the field names and the values will be
12593
-	* the column types used to store their values.
12594
-	* If no type is passed, this method returns a list of all tables in the database.
12595
-	*
12596
-	* @param string $type Type of bean (i.e. table) you want to inspect
12597
-	*
12598
-	* @return array
12599
-	*/
12600
-	public static function inspect( $type = NULL )
12601
-	{
12602
-		return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type );
12603
-	}
12604
-
12605
-	/**
12606
-	 * Stores a bean in the database. This method takes a
12607
-	 * OODBBean Bean Object $bean and stores it
12608
-	 * in the database. If the database schema is not compatible
12609
-	 * with this bean and RedBean runs in fluid mode the schema
12610
-	 * will be altered to store the bean correctly.
12611
-	 * If the database schema is not compatible with this bean and
12612
-	 * RedBean runs in frozen mode it will throw an exception.
12613
-	 * This function returns the primary key ID of the inserted
12614
-	 * bean.
12615
-	 *
12616
-	 * The return value is an integer if possible. If it is not possible to
12617
-	 * represent the value as an integer a string will be returned.
12618
-	 *
12619
-	 * Usage:
12620
-	 *
12621
-	 * <code>
12622
-	 * $post = R::dispense('post');
12623
-	 * $post->title = 'my post';
12624
-	 * $id = R::store( $post );
12625
-	 * $post = R::load( 'post', $id );
12626
-	 * R::trash( $post );
12627
-	 * </code>
12628
-	 *
12629
-	 * In the example above, we create a new bean of type 'post'.
12630
-	 * We then set the title of the bean to 'my post' and we
12631
-	 * store the bean. The store() method will return the primary
12632
-	 * key ID $id assigned by the database. We can now use this
12633
-	 * ID to load the bean from the database again and delete it.
12634
-	 *
12635
-	 * @param OODBBean|SimpleModel $bean bean to store
12636
-	 *
12637
-	 * @return integer|string
12638
-	 */
12639
-	public static function store( $bean )
12640
-	{
12641
-		return self::$redbean->store( $bean );
12642
-	}
12643
-
12644
-	/**
12645
-	 * Toggles fluid or frozen mode. In fluid mode the database
12646
-	 * structure is adjusted to accomodate your objects. In frozen mode
12647
-	 * this is not the case.
12648
-	 *
12649
-	 * You can also pass an array containing a selection of frozen types.
12650
-	 * Let's call this chilly mode, it's just like fluid mode except that
12651
-	 * certain types (i.e. tables) aren't touched.
12652
-	 *
12653
-	 * @param boolean|array $tf mode of operation (TRUE means frozen)
12654
-	 */
12655
-	public static function freeze( $tf = TRUE )
12656
-	{
12657
-		self::$redbean->freeze( $tf );
12658
-	}
12659
-
12660
-	/**
12661
-	 * Loads multiple types of beans with the same ID.
12662
-	 * This might look like a strange method, however it can be useful
12663
-	 * for loading a one-to-one relation. In a typical 1-1 relation,
12664
-	 * you have two records sharing the same primary key.
12665
-	 * RedBeanPHP has only limited support for 1-1 relations.
12666
-	 * In general it is recommended to use 1-N for this.
12667
-	 *
12668
-	 * Usage:
12669
-	 *
12670
-	 * <code>
12671
-	 * list( $author, $bio ) = R::loadMulti( 'author, bio', $id );
12672
-	 * </code>
12673
-	 *
12674
-	 * @param string|array $types the set of types to load at once
12675
-	 * @param mixed        $id    the common ID
12676
-	 *
12677
-	 * @return OODBBean
12678
-	 */
12679
-	public static function loadMulti( $types, $id )
12680
-	{
12681
-		return MultiLoader::load( self::$redbean, $types, $id );
12682
-	}
12683
-
12684
-	/**
12685
-	 * Loads a bean from the object database.
12686
-	 * It searches for a OODBBean Bean Object in the
12687
-	 * database. It does not matter how this bean has been stored.
12688
-	 * RedBean uses the primary key ID $id and the string $type
12689
-	 * to find the bean. The $type specifies what kind of bean you
12690
-	 * are looking for; this is the same type as used with the
12691
-	 * dispense() function. If RedBean finds the bean it will return
12692
-	 * the OODB Bean object; if it cannot find the bean
12693
-	 * RedBean will return a new bean of type $type and with
12694
-	 * primary key ID 0. In the latter case it acts basically the
12695
-	 * same as dispense().
12696
-	 *
12697
-	 * Important note:
12698
-	 * If the bean cannot be found in the database a new bean of
12699
-	 * the specified type will be generated and returned.
12700
-	 *
12701
-	 * Usage:
12702
-	 *
12703
-	 * <code>
12704
-	 * $post = R::dispense('post');
12705
-	 * $post->title = 'my post';
12706
-	 * $id = R::store( $post );
12707
-	 * $post = R::load( 'post', $id );
12708
-	 * R::trash( $post );
12709
-	 * </code>
12710
-	 *
12711
-	 * In the example above, we create a new bean of type 'post'.
12712
-	 * We then set the title of the bean to 'my post' and we
12713
-	 * store the bean. The store() method will return the primary
12714
-	 * key ID $id assigned by the database. We can now use this
12715
-	 * ID to load the bean from the database again and delete it.
12716
-	 *
12717
-	 * @param string  $type    type of bean you want to load
12718
-	 * @param integer $id      ID of the bean you want to load
12719
-	 * @param string  $snippet string to use after select  (optional)
12720
-	 *
12721
-	 * @return OODBBean
12722
-	 */
12723
-	public static function load( $type, $id, $snippet = NULL )
12724
-	{
12725
-		if ( $snippet !== NULL ) self::$writer->setSQLSelectSnippet( $snippet );
12726
-		$bean = self::$redbean->load( $type, $id );
12727
-		return $bean;
12728
-	}
12729
-
12730
-	/**
12731
-	 * Same as load, but selects the bean for update, thus locking the bean.
12732
-	 * This equals an SQL query like 'SELECT ... FROM ... FOR UPDATE'.
12733
-	 * Use this method if you want to load a bean you intend to UPDATE.
12734
-	 * This method should be used to 'LOCK a bean'.
12735
-	 *
12736
-	 * Usage:
12737
-	 *
12738
-	 * <code>
12739
-	 * $bean = R::loadForUpdate( 'bean', $id );
12740
-	 * ...update...
12741
-	 * R::store( $bean );
12742
-	 * </code>
12743
-	 *
12744
-	 * @param string  $type    type of bean you want to load
12745
-	 * @param integer $id      ID of the bean you want to load
12746
-	 *
12747
-	 * @return OODBBean
12748
-	 */
12749
-	public static function loadForUpdate( $type, $id )
12750
-	{
12751
-		return self::load( $type, $id, AQueryWriter::C_SELECT_SNIPPET_FOR_UPDATE );
12752
-	}
12753
-
12754
-	/**
12755
-	 * Removes a bean from the database.
12756
-	 * This function will remove the specified OODBBean
12757
-	 * Bean Object from the database.
12758
-	 *
12759
-	 * This facade method also accepts a type-id combination,
12760
-	 * in the latter case this method will attempt to load the specified bean
12761
-	 * and THEN trash it.
12762
-	 *
12763
-	 * Usage:
12764
-	 *
12765
-	 * <code>
12766
-	 * $post = R::dispense('post');
12767
-	 * $post->title = 'my post';
12768
-	 * $id = R::store( $post );
12769
-	 * $post = R::load( 'post', $id );
12770
-	 * R::trash( $post );
12771
-	 * </code>
12772
-	 *
12773
-	 * In the example above, we create a new bean of type 'post'.
12774
-	 * We then set the title of the bean to 'my post' and we
12775
-	 * store the bean. The store() method will return the primary
12776
-	 * key ID $id assigned by the database. We can now use this
12777
-	 * ID to load the bean from the database again and delete it.
12778
-	 *
12779
-	 * @param string|OODBBean|SimpleModel $beanOrType bean you want to remove from database
12780
-	 * @param integer                     $id         ID if the bean to trash (optional, type-id variant only)
12781
-	 *
12782
-	 * @return void
12783
-	 */
12784
-	public static function trash( $beanOrType, $id = NULL )
12785
-	{
12786
-		if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) );
12787
-		return self::$redbean->trash( $beanOrType );
12788
-	}
12789
-
12790
-	/**
12791
-	 * Dispenses a new RedBean OODB Bean for use with
12792
-	 * the rest of the methods. RedBeanPHP thinks in beans, the bean is the
12793
-	 * primary way to interact with RedBeanPHP and the database managed by
12794
-	 * RedBeanPHP. To load, store and delete data from the database using RedBeanPHP
12795
-	 * you exchange these RedBeanPHP OODB Beans. The only exception to this rule
12796
-	 * are the raw query methods like R::getCell() or R::exec() and so on.
12797
-	 * The dispense method is the 'preferred way' to create a new bean.
12798
-	 *
12799
-	 * Usage:
12800
-	 *
12801
-	 * <code>
12802
-	 * $book = R::dispense( 'book' );
12803
-	 * $book->title = 'My Book';
12804
-	 * R::store( $book );
12805
-	 * </code>
12806
-	 *
12807
-	 * This method can also be used to create an entire bean graph at once.
12808
-	 * Given an array with keys specifying the property names of the beans
12809
-	 * and a special _type key to indicate the type of bean, one can
12810
-	 * make the Dispense Helper generate an entire hierarchy of beans, including
12811
-	 * lists. To make dispense() generate a list, simply add a key like:
12812
-	 * ownXList or sharedXList where X is the type of beans it contains and
12813
-	 * a set its value to an array filled with arrays representing the beans.
12814
-	 * Note that, although the type may have been hinted at in the list name,
12815
-	 * you still have to specify a _type key for every bean array in the list.
12816
-	 * Note that, if you specify an array to generate a bean graph, the number
12817
-	 * parameter will be ignored.
12818
-	 *
12819
-	 * Usage:
12820
-	 *
12821
-	 * <code>
12822
-	 *  $book = R::dispense( [
12458
+        if ( $partialBeans ) {
12459
+            $redbean->getCurrentRepository()->usePartialBeans( $partialBeans );
12460
+        }
12461
+
12462
+        self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer );
12463
+    }
12464
+
12465
+    /**
12466
+     * Sets PDO attributes for MySQL SSL connection.
12467
+     *
12468
+     * @param string $key  path client key i.e. '/etc/mysql/ssl/client-key.pem'
12469
+     * @param string $cert path client cert i.e. '/etc/mysql/ssl/client-cert.pem'
12470
+     * @param string $ca   path certifying agent certificate '/etc/mysql/ssl/ca-cert.pem'
12471
+     * @param string $id   apply to toolbox (default = 'default')
12472
+     */
12473
+    public static function useMysqlSSL( $key, $cert, $ca, $id = 'default' ) {
12474
+        $pdo = self::$toolboxes[$id]->getDatabaseAdapter()->getDatabase()->getPDO();
12475
+        $pdo->setAttribute( \PDO::MYSQL_ATTR_SSL_KEY,  $key);
12476
+        $pdo->setAttribute( \PDO::MYSQL_ATTR_SSL_CERT,  $cert);
12477
+        $pdo->setAttribute( \PDO::MYSQL_ATTR_SSL_CA,  $ca);
12478
+    }
12479
+
12480
+    /**
12481
+     * Determines whether a database identified with the specified key has
12482
+     * already been added to the facade. This function will return TRUE
12483
+     * if the database indicated by the key is available and FALSE otherwise.
12484
+     *
12485
+     * @param string $key the key/name of the database to check for
12486
+     *
12487
+     * @return boolean
12488
+     */
12489
+    public static function hasDatabase( $key )
12490
+    {
12491
+        return ( isset( self::$toolboxes[$key] ) );
12492
+    }
12493
+
12494
+    /**
12495
+     * Selects a different database for the Facade to work with.
12496
+     * If you use the R::setup() you don't need this method. This method is meant
12497
+     * for multiple database setups. This method selects the database identified by the
12498
+     * database ID ($key). Use addDatabase() to add a new database, which in turn
12499
+     * can be selected using selectDatabase(). If you use R::setup(), the resulting
12500
+     * database will be stored under key 'default', to switch (back) to this database
12501
+     * use R::selectDatabase( 'default' ). This method returns TRUE if the database has been
12502
+     * switched and FALSE otherwise (for instance if you already using the specified database).
12503
+     *
12504
+     * @param  string $key Key of the database to select
12505
+     *
12506
+     * @return boolean
12507
+     */
12508
+    public static function selectDatabase( $key, $force = FALSE )
12509
+    {
12510
+        if ( self::$currentDB === $key && !$force ) {
12511
+            return FALSE;
12512
+        }
12513
+
12514
+        if ( !isset( self::$toolboxes[$key] ) ) {
12515
+            throw new RedException( 'Database not found in registry. Add database using R::addDatabase().' );
12516
+        }
12517
+
12518
+        self::configureFacadeWithToolbox( self::$toolboxes[$key] );
12519
+        self::$currentDB = $key;
12520
+
12521
+        return TRUE;
12522
+    }
12523
+
12524
+    /**
12525
+     * Toggles DEBUG mode.
12526
+     * In Debug mode all SQL that happens under the hood will
12527
+     * be printed to the screen and/or logged.
12528
+     * If no database connection has been configured using R::setup() or
12529
+     * R::selectDatabase() this method will throw an exception.
12530
+     *
12531
+     * There are 2 debug styles:
12532
+     *
12533
+     * Classic: separate parameter bindings, explicit and complete but less readable
12534
+     * Fancy:   interpersed bindings, truncates large strings, highlighted schema changes
12535
+     *
12536
+     * Fancy style is more readable but sometimes incomplete.
12537
+     *
12538
+     * The first parameter turns debugging ON or OFF.
12539
+     * The second parameter indicates the mode of operation:
12540
+     *
12541
+     * 0 Log and write to STDOUT classic style (default)
12542
+     * 1 Log only, class style
12543
+     * 2 Log and write to STDOUT fancy style
12544
+     * 3 Log only, fancy style
12545
+     *
12546
+     * This function always returns the logger instance created to generate the
12547
+     * debug messages.
12548
+     *
12549
+     * @param boolean $tf   debug mode (TRUE or FALSE)
12550
+     * @param integer $mode mode of operation
12551
+     *
12552
+     * @return RDefault
12553
+     * @throws RedException
12554
+     */
12555
+    public static function debug( $tf = TRUE, $mode = 0 )
12556
+    {
12557
+        if ($mode > 1) {
12558
+            $mode -= 2;
12559
+            $logger = new Debug;
12560
+        } else {
12561
+            $logger = new RDefault;
12562
+        }
12563
+
12564
+        if ( !isset( self::$adapter ) ) {
12565
+            throw new RedException( 'Use R::setup() first.' );
12566
+        }
12567
+        $logger->setMode($mode);
12568
+        self::$adapter->getDatabase()->setDebugMode( $tf, $logger );
12569
+
12570
+        return $logger;
12571
+    }
12572
+
12573
+    /**
12574
+     * Turns on the fancy debugger.
12575
+     * In 'fancy' mode the debugger will output queries with bound
12576
+     * parameters inside the SQL itself. This method has been added to
12577
+     * offer a convenient way to activate the fancy debugger system
12578
+     * in one call.
12579
+     *
12580
+     * @param boolean $toggle TRUE to activate debugger and select 'fancy' mode
12581
+     *
12582
+     * @return void
12583
+     */
12584
+    public static function fancyDebug( $toggle = TRUE )
12585
+    {
12586
+        self::debug( $toggle, 2 );
12587
+    }
12588
+
12589
+    /**
12590
+     * Inspects the database schema. If you pass the type of a bean this
12591
+     * method will return the fields of its table in the database.
12592
+     * The keys of this array will be the field names and the values will be
12593
+     * the column types used to store their values.
12594
+     * If no type is passed, this method returns a list of all tables in the database.
12595
+     *
12596
+     * @param string $type Type of bean (i.e. table) you want to inspect
12597
+     *
12598
+     * @return array
12599
+     */
12600
+    public static function inspect( $type = NULL )
12601
+    {
12602
+        return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type );
12603
+    }
12604
+
12605
+    /**
12606
+     * Stores a bean in the database. This method takes a
12607
+     * OODBBean Bean Object $bean and stores it
12608
+     * in the database. If the database schema is not compatible
12609
+     * with this bean and RedBean runs in fluid mode the schema
12610
+     * will be altered to store the bean correctly.
12611
+     * If the database schema is not compatible with this bean and
12612
+     * RedBean runs in frozen mode it will throw an exception.
12613
+     * This function returns the primary key ID of the inserted
12614
+     * bean.
12615
+     *
12616
+     * The return value is an integer if possible. If it is not possible to
12617
+     * represent the value as an integer a string will be returned.
12618
+     *
12619
+     * Usage:
12620
+     *
12621
+     * <code>
12622
+     * $post = R::dispense('post');
12623
+     * $post->title = 'my post';
12624
+     * $id = R::store( $post );
12625
+     * $post = R::load( 'post', $id );
12626
+     * R::trash( $post );
12627
+     * </code>
12628
+     *
12629
+     * In the example above, we create a new bean of type 'post'.
12630
+     * We then set the title of the bean to 'my post' and we
12631
+     * store the bean. The store() method will return the primary
12632
+     * key ID $id assigned by the database. We can now use this
12633
+     * ID to load the bean from the database again and delete it.
12634
+     *
12635
+     * @param OODBBean|SimpleModel $bean bean to store
12636
+     *
12637
+     * @return integer|string
12638
+     */
12639
+    public static function store( $bean )
12640
+    {
12641
+        return self::$redbean->store( $bean );
12642
+    }
12643
+
12644
+    /**
12645
+     * Toggles fluid or frozen mode. In fluid mode the database
12646
+     * structure is adjusted to accomodate your objects. In frozen mode
12647
+     * this is not the case.
12648
+     *
12649
+     * You can also pass an array containing a selection of frozen types.
12650
+     * Let's call this chilly mode, it's just like fluid mode except that
12651
+     * certain types (i.e. tables) aren't touched.
12652
+     *
12653
+     * @param boolean|array $tf mode of operation (TRUE means frozen)
12654
+     */
12655
+    public static function freeze( $tf = TRUE )
12656
+    {
12657
+        self::$redbean->freeze( $tf );
12658
+    }
12659
+
12660
+    /**
12661
+     * Loads multiple types of beans with the same ID.
12662
+     * This might look like a strange method, however it can be useful
12663
+     * for loading a one-to-one relation. In a typical 1-1 relation,
12664
+     * you have two records sharing the same primary key.
12665
+     * RedBeanPHP has only limited support for 1-1 relations.
12666
+     * In general it is recommended to use 1-N for this.
12667
+     *
12668
+     * Usage:
12669
+     *
12670
+     * <code>
12671
+     * list( $author, $bio ) = R::loadMulti( 'author, bio', $id );
12672
+     * </code>
12673
+     *
12674
+     * @param string|array $types the set of types to load at once
12675
+     * @param mixed        $id    the common ID
12676
+     *
12677
+     * @return OODBBean
12678
+     */
12679
+    public static function loadMulti( $types, $id )
12680
+    {
12681
+        return MultiLoader::load( self::$redbean, $types, $id );
12682
+    }
12683
+
12684
+    /**
12685
+     * Loads a bean from the object database.
12686
+     * It searches for a OODBBean Bean Object in the
12687
+     * database. It does not matter how this bean has been stored.
12688
+     * RedBean uses the primary key ID $id and the string $type
12689
+     * to find the bean. The $type specifies what kind of bean you
12690
+     * are looking for; this is the same type as used with the
12691
+     * dispense() function. If RedBean finds the bean it will return
12692
+     * the OODB Bean object; if it cannot find the bean
12693
+     * RedBean will return a new bean of type $type and with
12694
+     * primary key ID 0. In the latter case it acts basically the
12695
+     * same as dispense().
12696
+     *
12697
+     * Important note:
12698
+     * If the bean cannot be found in the database a new bean of
12699
+     * the specified type will be generated and returned.
12700
+     *
12701
+     * Usage:
12702
+     *
12703
+     * <code>
12704
+     * $post = R::dispense('post');
12705
+     * $post->title = 'my post';
12706
+     * $id = R::store( $post );
12707
+     * $post = R::load( 'post', $id );
12708
+     * R::trash( $post );
12709
+     * </code>
12710
+     *
12711
+     * In the example above, we create a new bean of type 'post'.
12712
+     * We then set the title of the bean to 'my post' and we
12713
+     * store the bean. The store() method will return the primary
12714
+     * key ID $id assigned by the database. We can now use this
12715
+     * ID to load the bean from the database again and delete it.
12716
+     *
12717
+     * @param string  $type    type of bean you want to load
12718
+     * @param integer $id      ID of the bean you want to load
12719
+     * @param string  $snippet string to use after select  (optional)
12720
+     *
12721
+     * @return OODBBean
12722
+     */
12723
+    public static function load( $type, $id, $snippet = NULL )
12724
+    {
12725
+        if ( $snippet !== NULL ) self::$writer->setSQLSelectSnippet( $snippet );
12726
+        $bean = self::$redbean->load( $type, $id );
12727
+        return $bean;
12728
+    }
12729
+
12730
+    /**
12731
+     * Same as load, but selects the bean for update, thus locking the bean.
12732
+     * This equals an SQL query like 'SELECT ... FROM ... FOR UPDATE'.
12733
+     * Use this method if you want to load a bean you intend to UPDATE.
12734
+     * This method should be used to 'LOCK a bean'.
12735
+     *
12736
+     * Usage:
12737
+     *
12738
+     * <code>
12739
+     * $bean = R::loadForUpdate( 'bean', $id );
12740
+     * ...update...
12741
+     * R::store( $bean );
12742
+     * </code>
12743
+     *
12744
+     * @param string  $type    type of bean you want to load
12745
+     * @param integer $id      ID of the bean you want to load
12746
+     *
12747
+     * @return OODBBean
12748
+     */
12749
+    public static function loadForUpdate( $type, $id )
12750
+    {
12751
+        return self::load( $type, $id, AQueryWriter::C_SELECT_SNIPPET_FOR_UPDATE );
12752
+    }
12753
+
12754
+    /**
12755
+     * Removes a bean from the database.
12756
+     * This function will remove the specified OODBBean
12757
+     * Bean Object from the database.
12758
+     *
12759
+     * This facade method also accepts a type-id combination,
12760
+     * in the latter case this method will attempt to load the specified bean
12761
+     * and THEN trash it.
12762
+     *
12763
+     * Usage:
12764
+     *
12765
+     * <code>
12766
+     * $post = R::dispense('post');
12767
+     * $post->title = 'my post';
12768
+     * $id = R::store( $post );
12769
+     * $post = R::load( 'post', $id );
12770
+     * R::trash( $post );
12771
+     * </code>
12772
+     *
12773
+     * In the example above, we create a new bean of type 'post'.
12774
+     * We then set the title of the bean to 'my post' and we
12775
+     * store the bean. The store() method will return the primary
12776
+     * key ID $id assigned by the database. We can now use this
12777
+     * ID to load the bean from the database again and delete it.
12778
+     *
12779
+     * @param string|OODBBean|SimpleModel $beanOrType bean you want to remove from database
12780
+     * @param integer                     $id         ID if the bean to trash (optional, type-id variant only)
12781
+     *
12782
+     * @return void
12783
+     */
12784
+    public static function trash( $beanOrType, $id = NULL )
12785
+    {
12786
+        if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) );
12787
+        return self::$redbean->trash( $beanOrType );
12788
+    }
12789
+
12790
+    /**
12791
+     * Dispenses a new RedBean OODB Bean for use with
12792
+     * the rest of the methods. RedBeanPHP thinks in beans, the bean is the
12793
+     * primary way to interact with RedBeanPHP and the database managed by
12794
+     * RedBeanPHP. To load, store and delete data from the database using RedBeanPHP
12795
+     * you exchange these RedBeanPHP OODB Beans. The only exception to this rule
12796
+     * are the raw query methods like R::getCell() or R::exec() and so on.
12797
+     * The dispense method is the 'preferred way' to create a new bean.
12798
+     *
12799
+     * Usage:
12800
+     *
12801
+     * <code>
12802
+     * $book = R::dispense( 'book' );
12803
+     * $book->title = 'My Book';
12804
+     * R::store( $book );
12805
+     * </code>
12806
+     *
12807
+     * This method can also be used to create an entire bean graph at once.
12808
+     * Given an array with keys specifying the property names of the beans
12809
+     * and a special _type key to indicate the type of bean, one can
12810
+     * make the Dispense Helper generate an entire hierarchy of beans, including
12811
+     * lists. To make dispense() generate a list, simply add a key like:
12812
+     * ownXList or sharedXList where X is the type of beans it contains and
12813
+     * a set its value to an array filled with arrays representing the beans.
12814
+     * Note that, although the type may have been hinted at in the list name,
12815
+     * you still have to specify a _type key for every bean array in the list.
12816
+     * Note that, if you specify an array to generate a bean graph, the number
12817
+     * parameter will be ignored.
12818
+     *
12819
+     * Usage:
12820
+     *
12821
+     * <code>
12822
+     *  $book = R::dispense( [
12823 12823
      *   '_type' => 'book',
12824 12824
      *   'title'  => 'Gifted Programmers',
12825 12825
      *   'author' => [ '_type' => 'author', 'name' => 'Xavier' ],
12826 12826
      *   'ownPageList' => [ ['_type'=>'page', 'text' => '...'] ]
12827 12827
      * ] );
12828
-	 * </code>
12829
-	 *
12830
-	 * @param string|array $typeOrBeanArray   type or bean array to import
12831
-	 * @param integer      $num               number of beans to dispense
12832
-	 * @param boolean      $alwaysReturnArray if TRUE always returns the result as an array
12833
-	 *
12834
-	 * @return array|OODBBean
12835
-	 */
12836
-	public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE )
12837
-	{
12838
-		return DispenseHelper::dispense( self::$redbean, $typeOrBeanArray, $num, $alwaysReturnArray );
12839
-	}
12840
-
12841
-	/**
12842
-	 * Takes a comma separated list of bean types
12843
-	 * and dispenses these beans. For each type in the list
12844
-	 * you can specify the number of beans to be dispensed.
12845
-	 *
12846
-	 * Usage:
12847
-	 *
12848
-	 * <code>
12849
-	 * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' );
12850
-	 * </code>
12851
-	 *
12852
-	 * This will dispense a book, a page and a text. This way you can
12853
-	 * quickly dispense beans of various types in just one line of code.
12854
-	 *
12855
-	 * Usage:
12856
-	 *
12857
-	 * <code>
12858
-	 * list($book, $pages) = R::dispenseAll('book,page*100');
12859
-	 * </code>
12860
-	 *
12861
-	 * This returns an array with a book bean and then another array
12862
-	 * containing 100 page beans.
12863
-	 *
12864
-	 * @param string  $order      a description of the desired dispense order using the syntax above
12865
-	 * @param boolean $onlyArrays return only arrays even if amount < 2
12866
-	 *
12867
-	 * @return array
12868
-	 */
12869
-	public static function dispenseAll( $order, $onlyArrays = FALSE )
12870
-	{
12871
-		return DispenseHelper::dispenseAll( self::$redbean, $order, $onlyArrays );
12872
-	}
12873
-
12874
-	/**
12875
-	 * Convience method. Tries to find beans of a certain type,
12876
-	 * if no beans are found, it dispenses a bean of that type.
12877
-	 * Note that this function always returns an array.
12878
-	 *
12879
-	 * @param  string $type     type of bean you are looking for
12880
-	 * @param  string $sql      SQL code for finding the bean
12881
-	 * @param  array  $bindings parameters to bind to SQL
12882
-	 *
12883
-	 * @return array
12884
-	 */
12885
-	public static function findOrDispense( $type, $sql = NULL, $bindings = array() )
12886
-	{
12887
-		DispenseHelper::checkType( $type );
12888
-		return self::$finder->findOrDispense( $type, $sql, $bindings );
12889
-	}
12890
-
12891
-	/**
12892
-	 * Same as findOrDispense but returns just one element.
12893
-	 *
12894
-	 * @param  string $type     type of bean you are looking for
12895
-	 * @param  string $sql      SQL code for finding the bean
12896
-	 * @param  array  $bindings parameters to bind to SQL
12897
-	 *
12898
-	 * @return OODBBean
12899
-	 */
12900
-	public static function findOneOrDispense( $type, $sql = NULL, $bindings = array() )
12901
-	{
12902
-		DispenseHelper::checkType( $type );
12903
-		$arrayOfBeans = self::findOrDispense( $type, $sql, $bindings );
12904
-		return reset($arrayOfBeans);
12905
-	}
12906
-
12907
-	/**
12908
-	 * Finds beans using a type and optional SQL statement.
12909
-	 * As with most Query tools in RedBean you can provide values to
12910
-	 * be inserted in the SQL statement by populating the value
12911
-	 * array parameter; you can either use the question mark notation
12912
-	 * or the slot-notation (:keyname).
12913
-	 *
12914
-	 * Your SQL does not have to start with a WHERE-clause condition.
12915
-	 *
12916
-	 * @param string $type     the type of bean you are looking for
12917
-	 * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
12918
-	 * @param array  $bindings array of values to be bound to parameters in query
12919
-	 *
12920
-	 * @return array
12921
-	 */
12922
-	public static function find( $type, $sql = NULL, $bindings = array() )
12923
-	{
12924
-		return self::$finder->find( $type, $sql, $bindings );
12925
-	}
12926
-
12927
-	/**
12928
-	 * Alias for find().
12929
-	 *
12930
-	 * @param string $type     the type of bean you are looking for
12931
-	 * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
12932
-	 * @param array  $bindings array of values to be bound to parameters in query
12933
-	 *
12934
-	 * @return array
12935
-	 */
12936
-	public static function findAll( $type, $sql = NULL, $bindings = array() )
12937
-	{
12938
-		return self::$finder->find( $type, $sql, $bindings );
12939
-	}
12940
-
12941
-	/**
12942
-	 * Like find() but also exports the beans as an array.
12943
-	 * This method will perform a find-operation. For every bean
12944
-	 * in the result collection this method will call the export() method.
12945
-	 * This method returns an array containing the array representations
12946
-	 * of every bean in the result set.
12947
-	 *
12948
-	 * @see Finder::find
12949
-	 *
12950
-	 * @param string $type     type   the type of bean you are looking for
12951
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
12952
-	 * @param array  $bindings values array of values to be bound to parameters in query
12953
-	 *
12954
-	 * @return array
12955
-	 */
12956
-	public static function findAndExport( $type, $sql = NULL, $bindings = array() )
12957
-	{
12958
-		return self::$finder->findAndExport( $type, $sql, $bindings );
12959
-	}
12960
-
12961
-	/**
12962
-	 * Like R::find() but returns the first bean only.
12963
-	 *
12964
-	 * @param string $type     the type of bean you are looking for
12965
-	 * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
12966
-	 * @param array  $bindings array of values to be bound to parameters in query
12967
-	 *
12968
-	 * @return OODBBean|NULL
12969
-	 */
12970
-	public static function findOne( $type, $sql = NULL, $bindings = array() )
12971
-	{
12972
-		return self::$finder->findOne( $type, $sql, $bindings );
12973
-	}
12974
-
12975
-	/**
12976
-	 * @deprecated
12977
-	 *
12978
-	 * Like find() but returns the last bean of the result array.
12979
-	 * Opposite of Finder::findLast().
12980
-	 * If no beans are found, this method will return NULL.
12981
-	 *
12982
-	 * Please do not use this function, it is horribly ineffective.
12983
-	 * Instead use a reversed ORDER BY clause and a LIMIT 1 with R::findOne().
12984
-	 * This function should never be used and only remains for
12985
-	 * the sake of backward compatibility.
12986
-	 *
12987
-	 * @see Finder::find
12988
-	 *
12989
-	 * @param string $type     the type of bean you are looking for
12990
-	 * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
12991
-	 * @param array  $bindings values array of values to be bound to parameters in query
12992
-	 *
12993
-	 * @return OODBBean|NULL
12994
-	 */
12995
-	public static function findLast( $type, $sql = NULL, $bindings = array() )
12996
-	{
12997
-		return self::$finder->findLast( $type, $sql, $bindings );
12998
-	}
12999
-
13000
-	/**
13001
-	 * Finds a BeanCollection using the repository.
13002
-	 * A bean collection can be used to retrieve one bean at a time using
13003
-	 * cursors - this is useful for processing large datasets. A bean collection
13004
-	 * will not load all beans into memory all at once, just one at a time.
13005
-	 *
13006
-	 * @param  string $type     the type of bean you are looking for
13007
-	 * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
13008
-	 * @param  array  $bindings values array of values to be bound to parameters in query
13009
-	 *
13010
-	 * @return BeanCollection
13011
-	 */
13012
-	public static function findCollection( $type, $sql = NULL, $bindings = array() )
13013
-	{
13014
-		return self::$finder->findCollection( $type, $sql, $bindings );
13015
-	}
13016
-
13017
-	/**
13018
-	 * Returns a hashmap with bean arrays keyed by type using an SQL
13019
-	 * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review'
13020
-	 * this method will return movie and review beans.
13021
-	 *
13022
-	 * Example:
13023
-	 *
13024
-	 * <code>
13025
-	 * $stuff = $finder->findMulti('movie,review', '
13026
-	 *          SELECT movie.*, review.* FROM movie
13027
-	 *          LEFT JOIN review ON review.movie_id = movie.id');
13028
-	 * </code>
13029
-	 *
13030
-	 * After this operation, $stuff will contain an entry 'movie' containing all
13031
-	 * movies and an entry named 'review' containing all reviews (all beans).
13032
-	 * You can also pass bindings.
13033
-	 *
13034
-	 * If you want to re-map your beans, so you can use $movie->ownReviewList without
13035
-	 * having RedBeanPHP executing an SQL query you can use the fourth parameter to
13036
-	 * define a selection of remapping closures.
13037
-	 *
13038
-	 * The remapping argument (optional) should contain an array of arrays.
13039
-	 * Each array in the remapping array should contain the following entries:
13040
-	 *
13041
-	 * <code>
13042
-	 * array(
13043
-	 * 	'a'       => TYPE A
13044
-	 *    'b'       => TYPE B
13045
-	 *    'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS
13046
-	 *    'do'      => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS
13047
-	 * )
13048
-	 * </code>
13049
-	 *
13050
-	 * Using this mechanism you can build your own 'preloader' with tiny function
13051
-	 * snippets (and those can be re-used and shared online of course).
13052
-	 *
13053
-	 * Example:
13054
-	 *
13055
-	 * <code>
13056
-	 * array(
13057
-	 * 	'a'       => 'movie'     //define A as movie
13058
-	 *    'b'       => 'review'    //define B as review
13059
-	 *    'matcher' => function( $a, $b ) {
13060
-	 *       return ( $b->movie_id == $a->id );  //Perform action if review.movie_id equals movie.id
13061
-	 *    }
13062
-	 *    'do'      => function( $a, $b ) {
13063
-	 *       $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie
13064
-	 *       $a->clearHistory();                 //optional, act 'as if these beans have been loaded through ownReviewList'.
13065
-	 *    }
13066
-	 * )
13067
-	 * </code>
13068
-	 *
13069
-	 * @note the SQL query provided IS NOT THE ONE used internally by this function,
13070
-	 * this function will pre-process the query to get all the data required to find the beans.
13071
-	 *
13072
-	 * @note if you use the 'book.*' notation make SURE you're
13073
-	 * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because
13074
-	 * it's actually an SQL-like template SLOT, not real SQL.
13075
-	 *
13076
-	 * @note instead of an SQL query you can pass a result array as well.
13077
-	 *
13078
-	 * @param string|array $types         a list of types (either array or comma separated string)
13079
-	 * @param string|array $sql           an SQL query or an array of prefetched records
13080
-	 * @param array        $bindings      optional, bindings for SQL query
13081
-	 * @param array        $remappings    optional, an array of remapping arrays
13082
-	 *
13083
-	 * @return array
13084
-	 */
13085
-	public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() )
13086
-	{
13087
-		return self::$finder->findMulti( $types, $sql, $bindings, $remappings );
13088
-	}
13089
-
13090
-	/**
13091
-	 * Returns an array of beans. Pass a type and a series of ids and
13092
-	 * this method will bring you the corresponding beans.
13093
-	 *
13094
-	 * important note: Because this method loads beans using the load()
13095
-	 * function (but faster) it will return empty beans with ID 0 for
13096
-	 * every bean that could not be located. The resulting beans will have the
13097
-	 * passed IDs as their keys.
13098
-	 *
13099
-	 * @param string $type type of beans
13100
-	 * @param array  $ids  ids to load
13101
-	 *
13102
-	 * @return array
13103
-	 */
13104
-	public static function batch( $type, $ids )
13105
-	{
13106
-		return self::$redbean->batch( $type, $ids );
13107
-	}
13108
-
13109
-	/**
13110
-	 * Alias for batch(). Batch method is older but since we added so-called *All
13111
-	 * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to
13112
-	 * improve the consistency of the Facade API and also add an alias for batch() called
13113
-	 * loadAll.
13114
-	 *
13115
-	 * @param string $type type of beans
13116
-	 * @param array  $ids  ids to load
13117
-	 *
13118
-	 * @return array
13119
-	 */
13120
-	public static function loadAll( $type, $ids )
13121
-	{
13122
-		return self::$redbean->batch( $type, $ids );
13123
-	}
13124
-
13125
-	/**
13126
-	 * Convenience function to execute Queries directly.
13127
-	 * Executes SQL.
13128
-	 *
13129
-	 * @param string $sql       SQL query to execute
13130
-	 * @param array  $bindings  a list of values to be bound to query parameters
13131
-	 *
13132
-	 * @return integer
13133
-	 */
13134
-	public static function exec( $sql, $bindings = array() )
13135
-	{
13136
-		return self::query( 'exec', $sql, $bindings );
13137
-	}
13138
-
13139
-	/**
13140
-	 * Convenience function to fire an SQL query using the RedBeanPHP
13141
-	 * database adapter. This method allows you to directly query the
13142
-	 * database without having to obtain an database adapter instance first.
13143
-	 * Executes the specified SQL query together with the specified
13144
-	 * parameter bindings and returns all rows
13145
-	 * and all columns.
13146
-	 *
13147
-	 * @param string $sql      SQL query to execute
13148
-	 * @param array  $bindings a list of values to be bound to query parameters
13149
-	 *
13150
-	 * @return array
13151
-	 */
13152
-	public static function getAll( $sql, $bindings = array() )
13153
-	{
13154
-		return self::query( 'get', $sql, $bindings );
13155
-	}
13156
-
13157
-	/**
13158
-	 * Convenience function to fire an SQL query using the RedBeanPHP
13159
-	 * database adapter. This method allows you to directly query the
13160
-	 * database without having to obtain an database adapter instance first.
13161
-	 * Executes the specified SQL query together with the specified
13162
-	 * parameter bindings and returns a single cell.
13163
-	 *
13164
-	 * @param string $sql      SQL query to execute
13165
-	 * @param array  $bindings a list of values to be bound to query parameters
13166
-	 *
13167
-	 * @return string
13168
-	 */
13169
-	public static function getCell( $sql, $bindings = array() )
13170
-	{
13171
-		return self::query( 'getCell', $sql, $bindings );
13172
-	}
13173
-
13174
-	/**
13175
-	 * Convenience function to fire an SQL query using the RedBeanPHP
13176
-	 * database adapter. This method allows you to directly query the
13177
-	 * database without having to obtain an database adapter instance first.
13178
-	 * Executes the specified SQL query together with the specified
13179
-	 * parameter bindings and returns a PDOCursor instance.
13180
-	 *
13181
-	 * @param string $sql      SQL query to execute
13182
-	 * @param array  $bindings a list of values to be bound to query parameters
13183
-	 *
13184
-	 * @return RedBeanPHP\Cursor\PDOCursor
13185
-	 */
13186
-	public static function getCursor( $sql, $bindings = array() )
13187
-	{
13188
-		return self::query( 'getCursor', $sql, $bindings );
13189
-	}
13190
-
13191
-	/**
13192
-	 * Convenience function to fire an SQL query using the RedBeanPHP
13193
-	 * database adapter. This method allows you to directly query the
13194
-	 * database without having to obtain an database adapter instance first.
13195
-	 * Executes the specified SQL query together with the specified
13196
-	 * parameter bindings and returns a single row.
13197
-	 *
13198
-	 * @param string $sql      SQL query to execute
13199
-	 * @param array  $bindings a list of values to be bound to query parameters
13200
-	 *
13201
-	 * @return array
13202
-	 */
13203
-	public static function getRow( $sql, $bindings = array() )
13204
-	{
13205
-		return self::query( 'getRow', $sql, $bindings );
13206
-	}
13207
-
13208
-	/**
13209
-	 * Convenience function to fire an SQL query using the RedBeanPHP
13210
-	 * database adapter. This method allows you to directly query the
13211
-	 * database without having to obtain an database adapter instance first.
13212
-	 * Executes the specified SQL query together with the specified
13213
-	 * parameter bindings and returns a single column.
13214
-	 *
13215
-	 * @param string $sql      SQL query to execute
13216
-	 * @param array  $bindings a list of values to be bound to query parameters
13217
-	 *
13218
-	 * @return array
13219
-	 */
13220
-	public static function getCol( $sql, $bindings = array() )
13221
-	{
13222
-		return self::query( 'getCol', $sql, $bindings );
13223
-	}
13224
-
13225
-	/**
13226
-	 * Convenience function to execute Queries directly.
13227
-	 * Executes SQL.
13228
-	 * Results will be returned as an associative array. The first
13229
-	 * column in the select clause will be used for the keys in this array and
13230
-	 * the second column will be used for the values. If only one column is
13231
-	 * selected in the query, both key and value of the array will have the
13232
-	 * value of this field for each row.
13233
-	 *
13234
-	 * @param string $sql      SQL query to execute
13235
-	 * @param array  $bindings a list of values to be bound to query parameters
13236
-	 *
13237
-	 * @return array
13238
-	 */
13239
-	public static function getAssoc( $sql, $bindings = array() )
13240
-	{
13241
-		return self::query( 'getAssoc', $sql, $bindings );
13242
-	}
13243
-
13244
-	/**
13245
-	 *Convenience function to fire an SQL query using the RedBeanPHP
13246
-	 * database adapter. This method allows you to directly query the
13247
-	 * database without having to obtain an database adapter instance first.
13248
-	 * Executes the specified SQL query together with the specified
13249
-	 * parameter bindings and returns an associative array.
13250
-	 * Results will be returned as an associative array indexed by the first
13251
-	 * column in the select.
13252
-	 *
13253
-	 * @param string $sql      SQL query to execute
13254
-	 * @param array  $bindings a list of values to be bound to query parameters
13255
-	 *
13256
-	 * @return array
13257
-	 */
13258
-	public static function getAssocRow( $sql, $bindings = array() )
13259
-	{
13260
-		return self::query( 'getAssocRow', $sql, $bindings );
13261
-	}
13262
-
13263
-	/**
13264
-	 * Returns the insert ID for databases that support/require this
13265
-	 * functionality. Alias for R::getAdapter()->getInsertID().
13266
-	 *
13267
-	 * @return mixed
13268
-	 */
13269
-	public static function getInsertID()
13270
-	{
13271
-		return self::$adapter->getInsertID();
13272
-	}
13273
-
13274
-	/**
13275
-	 * Makes a copy of a bean. This method makes a deep copy
13276
-	 * of the bean.The copy will have the following features.
13277
-	 * - All beans in own-lists will be duplicated as well
13278
-	 * - All references to shared beans will be copied but not the shared beans themselves
13279
-	 * - All references to parent objects (_id fields) will be copied but not the parents themselves
13280
-	 * In most cases this is the desired scenario for copying beans.
13281
-	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
13282
-	 * (i.e. one that already has been processed) the ID of the bean will be returned.
13283
-	 * This should not happen though.
13284
-	 *
13285
-	 * Note:
13286
-	 * This function does a reflectional database query so it may be slow.
13287
-	 *
13288
-	 * @deprecated
13289
-	 * This function is deprecated in favour of R::duplicate().
13290
-	 * This function has a confusing method signature, the R::duplicate() function
13291
-	 * only accepts two arguments: bean and filters.
13292
-	 *
13293
-	 * @param OODBBean $bean    bean to be copied
13294
-	 * @param array    $trail   for internal usage, pass array()
13295
-	 * @param boolean  $pid     for internal usage
13296
-	 * @param array    $filters white list filter with bean types to duplicate
13297
-	 *
13298
-	 * @return array
13299
-	 */
13300
-	public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() )
13301
-	{
13302
-		self::$duplicationManager->setFilters( $filters );
13303
-		return self::$duplicationManager->dup( $bean, $trail, $pid );
13304
-	}
13305
-
13306
-	/**
13307
-	 * Makes a deep copy of a bean. This method makes a deep copy
13308
-	 * of the bean.The copy will have the following:
13309
-	 *
13310
-	 * * All beans in own-lists will be duplicated as well
13311
-	 * * All references to shared beans will be copied but not the shared beans themselves
13312
-	 * * All references to parent objects (_id fields) will be copied but not the parents themselves
13313
-	 *
13314
-	 * In most cases this is the desired scenario for copying beans.
13315
-	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
13316
-	 * (i.e. one that already has been processed) the ID of the bean will be returned.
13317
-	 * This should not happen though.
13318
-	 *
13319
-	 * Note:
13320
-	 * This function does a reflectional database query so it may be slow.
13321
-	 *
13322
-	 * Note:
13323
-	 * This is a simplified version of the deprecated R::dup() function.
13324
-	 *
13325
-	 * @param OODBBean $bean  bean to be copied
13326
-	 * @param array    $white white list filter with bean types to duplicate
13327
-	 *
13328
-	 * @return array
13329
-	 */
13330
-	public static function duplicate( $bean, $filters = array() )
13331
-	{
13332
-		return self::dup( $bean, array(), FALSE, $filters );
13333
-	}
13334
-
13335
-	/**
13336
-	 * Exports a collection of beans. Handy for XML/JSON exports with a
13337
-	 * Javascript framework like Dojo or ExtJS.
13338
-	 * What will be exported:
13339
-	 *
13340
-	 * * contents of the bean
13341
-	 * * all own bean lists (recursively)
13342
-	 * * all shared beans (not THEIR own lists)
13343
-	 *
13344
-	 * @param    array|OODBBean $beans   beans to be exported
13345
-	 * @param    boolean        $parents whether you want parent beans to be exported
13346
-	 * @param    array          $filters whitelist of types
13347
-	 *
13348
-	 * @return array
13349
-	 */
13350
-	public static function exportAll( $beans, $parents = FALSE, $filters = array())
13351
-	{
13352
-		return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle );
13353
-	}
13354
-
13355
-	/**
13356
-	 * Selects case style for export.
13357
-	 * This will determine the case style for the keys of exported beans (see exportAll).
13358
-	 * The following options are accepted:
13359
-	 *
13360
-	 * * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid )
13361
-	 * * 'camel'   Camel Case   (i.e. bookId isValid   )
13362
-	 * * 'dolphin' Dolphin Case (i.e. bookID isValid   ) Like CamelCase but ID is written all uppercase
13363
-	 *
13364
-	 * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different
13365
-	 * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id.
13366
-	 * Due to information loss this cannot be corrected. However if you might try
13367
-	 * DolphinCase for IDs it takes into account the exception concerning IDs.
13368
-	 *
13369
-	 * @param string $caseStyle case style identifier
13370
-	 *
13371
-	 * @return void
13372
-	 */
13373
-	public static function useExportCase( $caseStyle = 'default' )
13374
-	{
13375
-		if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' );
13376
-		self::$exportCaseStyle = $caseStyle;
13377
-	}
13378
-
13379
-	/**
13380
-	 * Converts a series of rows to beans.
13381
-	 * This method converts a series of rows to beans.
13382
-	 * The type of the desired output beans can be specified in the
13383
-	 * first parameter. The second parameter is meant for the database
13384
-	 * result rows.
13385
-	 *
13386
-	 * Usage:
13387
-	 *
13388
-	 * <code>
13389
-	 * $rows = R::getAll( 'SELECT * FROM ...' )
13390
-	 * $beans = R::convertToBeans( $rows );
13391
-	 * </code>
13392
-	 *
13393
-	 * As of version 4.3.2 you can specify a meta-mask.
13394
-	 * Data from columns with names starting with the value specified in the mask
13395
-	 * will be transferred to the meta section of a bean (under data.bundle).
13396
-	 *
13397
-	 * <code>
13398
-	 * $rows = R::getAll( 'SELECT FROM... COUNT(*) AS extra_count ...' );
13399
-	 * $beans = R::convertToBeans( $rows );
13400
-	 * $bean = reset( $beans );
13401
-	 * $data = $bean->getMeta( 'data.bundle' );
13402
-	 * $extra_count = $data['extra_count'];
13403
-	 * </code>
13404
-	 *
13405
-	 * New in 4.3.2: meta mask. The meta mask is a special mask to send
13406
-	 * data from raw result rows to the meta store of the bean. This is
13407
-	 * useful for bundling additional information with custom queries.
13408
-	 * Values of every column whos name starts with $mask will be
13409
-	 * transferred to the meta section of the bean under key 'data.bundle'.
13410
-	 *
13411
-	 * @param string $type     type of beans to produce
13412
-	 * @param array  $rows     must contain an array of array
13413
-	 * @param string $metamask meta mask to apply (optional)
13414
-	 *
13415
-	 * @return array
13416
-	 */
13417
-	public static function convertToBeans( $type, $rows, $metamask = NULL )
13418
-	{
13419
-		return self::$redbean->convertToBeans( $type, $rows, $metamask );
13420
-	}
13421
-
13422
-	/**
13423
-	 * Just like converToBeans, but for one bean.
13424
-	 *
13425
-	 * @param string $type      type of bean to produce
13426
-	 * @param array  $row       one row from the database
13427
-	 * @param string $metamask  metamask (see convertToBeans)
13428
-	 *
13429
-	 * @return OODBBean
13430
-	 */
13431
-	public static function convertToBean( $type, $row, $metamask = NULL )
13432
-	{
13433
-		$beans = self::$redbean->convertToBeans( $type, array( $row ), $metamask );
13434
-		$bean  = reset( $beans );
13435
-		return $bean;
13436
-	}
13437
-
13438
-	/**
13439
-	 * Tests whether a bean has been associated with one ore more
13440
-	 * of the listed tags. If the third parameter is TRUE this method
13441
-	 * will return TRUE only if all tags that have been specified are indeed
13442
-	 * associated with the given bean, otherwise FALSE.
13443
-	 * If the third parameter is FALSE this
13444
-	 * method will return TRUE if one of the tags matches, FALSE if none
13445
-	 * match.
13446
-	 *
13447
-	 * Tag list can be either an array with tag names or a comma separated list
13448
-	 * of tag names.
13449
-	 *
13450
-	 * Usage:
13451
-	 *
13452
-	 * <code>
13453
-	 * R::hasTag( $blog, 'horror,movie', TRUE );
13454
-	 * </code>
13455
-	 *
13456
-	 * The example above returns TRUE if the $blog bean has been tagged
13457
-	 * as BOTH horror and movie. If the post has only been tagged as 'movie'
13458
-	 * or 'horror' this operation will return FALSE because the third parameter
13459
-	 * has been set to TRUE.
13460
-	 *
13461
-	 * @param  OODBBean     $bean bean to check for tags
13462
-	 * @param  array|string $tags list of tags
13463
-	 * @param  boolean      $all  whether they must all match or just some
13464
-	 *
13465
-	 * @return boolean
13466
-	 */
13467
-	public static function hasTag( $bean, $tags, $all = FALSE )
13468
-	{
13469
-		return self::$tagManager->hasTag( $bean, $tags, $all );
13470
-	}
13471
-
13472
-	/**
13473
-	 * Removes all specified tags from the bean. The tags specified in
13474
-	 * the second parameter will no longer be associated with the bean.
13475
-	 *
13476
-	 * Tag list can be either an array with tag names or a comma separated list
13477
-	 * of tag names.
13478
-	 *
13479
-	 * Usage:
13480
-	 *
13481
-	 * <code>
13482
-	 * R::untag( $blog, 'smart,interesting' );
13483
-	 * </code>
13484
-	 *
13485
-	 * In the example above, the $blog bean will no longer
13486
-	 * be associated with the tags 'smart' and 'interesting'.
13487
-	 *
13488
-	 * @param  OODBBean $bean    tagged bean
13489
-	 * @param  array    $tagList list of tags (names)
13490
-	 *
13491
-	 * @return void
13492
-	 */
13493
-	public static function untag( $bean, $tagList )
13494
-	{
13495
-		self::$tagManager->untag( $bean, $tagList );
13496
-	}
13497
-
13498
-	/**
13499
-	 * Tags a bean or returns tags associated with a bean.
13500
-	 * If $tagList is NULL or omitted this method will return a
13501
-	 * comma separated list of tags associated with the bean provided.
13502
-	 * If $tagList is a comma separated list (string) of tags all tags will
13503
-	 * be associated with the bean.
13504
-	 * You may also pass an array instead of a string.
13505
-	 *
13506
-	 * Usage:
13507
-	 *
13508
-	 * <code>
13509
-	 * R::tag( $meal, "TexMex,Mexican" );
13510
-	 * $tags = R::tag( $meal );
13511
-	 * </code>
13512
-	 *
13513
-	 * The first line in the example above will tag the $meal
13514
-	 * as 'TexMex' and 'Mexican Cuisine'. The second line will
13515
-	 * retrieve all tags attached to the meal object.
13516
-	 *
13517
-	 * @param OODBBean $bean    bean to tag
13518
-	 * @param mixed    $tagList tags to attach to the specified bean
13519
-	 *
13520
-	 * @return string
13521
-	 */
13522
-	public static function tag( OODBBean $bean, $tagList = NULL )
13523
-	{
13524
-		return self::$tagManager->tag( $bean, $tagList );
13525
-	}
13526
-
13527
-	/**
13528
-	 * Adds tags to a bean.
13529
-	 * If $tagList is a comma separated list of tags all tags will
13530
-	 * be associated with the bean.
13531
-	 * You may also pass an array instead of a string.
13532
-	 *
13533
-	 * Usage:
13534
-	 *
13535
-	 * <code>
13536
-	 * R::addTags( $blog, ["halloween"] );
13537
-	 * </code>
13538
-	 *
13539
-	 * The example adds the tag 'halloween' to the $blog
13540
-	 * bean.
13541
-	 *
13542
-	 * @param OODBBean $bean    bean to tag
13543
-	 * @param array    $tagList list of tags to add to bean
13544
-	 *
13545
-	 * @return void
13546
-	 */
13547
-	public static function addTags( OODBBean $bean, $tagList )
13548
-	{
13549
-		self::$tagManager->addTags( $bean, $tagList );
13550
-	}
13551
-
13552
-	/**
13553
-	 * Returns all beans that have been tagged with one or more
13554
-	 * of the specified tags.
13555
-	 *
13556
-	 * Tag list can be either an array with tag names or a comma separated list
13557
-	 * of tag names.
13558
-	 *
13559
-	 * Usage:
13560
-	 *
13561
-	 * <code>
13562
-	 * $watchList = R::tagged(
13563
-	 *   'movie',
13564
-	 *   'horror,gothic',
13565
-	 *   ' ORDER BY movie.title DESC LIMIT ?',
13566
-	 *   [ 10 ]
13567
-	 * );
13568
-	 * </code>
13569
-	 *
13570
-	 * The example uses R::tagged() to find all movies that have been
13571
-	 * tagged as 'horror' or 'gothic', order them by title and limit
13572
-	 * the number of movies to be returned to 10.
13573
-	 *
13574
-	 * @param string       $beanType type of bean you are looking for
13575
-	 * @param array|string $tagList  list of tags to match
13576
-	 * @param string       $sql      additional SQL (use only for pagination)
13577
-	 * @param array        $bindings bindings
13578
-	 *
13579
-	 * @return array
13580
-	 */
13581
-	public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
13582
-	{
13583
-		return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings );
13584
-	}
13585
-
13586
-	/**
13587
-	 * Returns all beans that have been tagged with ALL of the tags given.
13588
-	 * This method works the same as R::tagged() except that this method only returns
13589
-	 * beans that have been tagged with all the specified labels.
13590
-	 *
13591
-	 * Tag list can be either an array with tag names or a comma separated list
13592
-	 * of tag names.
13593
-	 *
13594
-	 * Usage:
13595
-	 *
13596
-	 * <code>
13597
-	 * $watchList = R::taggedAll(
13598
-	 *    'movie',
13599
-	 *    [ 'gothic', 'short' ],
13600
-	 *    ' ORDER BY movie.id DESC LIMIT ? ',
13601
-	 *    [ 4 ]
13602
-	 * );
13603
-	 * </code>
13604
-	 *
13605
-	 * The example above returns at most 4 movies (due to the LIMIT clause in the SQL
13606
-	 * Query Snippet) that have been tagged as BOTH 'short' AND 'gothic'.
13607
-	 *
13608
-	 * @param string       $beanType type of bean you are looking for
13609
-	 * @param array|string $tagList  list of tags to match
13610
-	 * @param string       $sql      additional sql snippet
13611
-	 * @param array        $bindings bindings
13612
-	 *
13613
-	 * @return array
13614
-	 */
13615
-	public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
13616
-	{
13617
-		return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings );
13618
-	}
13619
-
13620
-	/**
13621
-	 * Same as taggedAll() but counts beans only (does not return beans).
13622
-	 *
13623
-	 * @see R::taggedAll
13624
-	 *
13625
-	 * @param string       $beanType type of bean you are looking for
13626
-	 * @param array|string $tagList  list of tags to match
13627
-	 * @param string       $sql      additional sql snippet
13628
-	 * @param array        $bindings bindings
13629
-	 *
13630
-	 * @return integer
13631
-	 */
13632
-	public static function countTaggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
13633
-	{
13634
-		return self::$tagManager->countTaggedAll( $beanType, $tagList, $sql, $bindings );
13635
-	}
13636
-
13637
-	/**
13638
-	 * Same as tagged() but counts beans only (does not return beans).
13639
-	 *
13640
-	 * @see R::tagged
13641
-	 *
13642
-	 * @param string       $beanType type of bean you are looking for
13643
-	 * @param array|string $tagList  list of tags to match
13644
-	 * @param string       $sql      additional sql snippet
13645
-	 * @param array        $bindings bindings
13646
-	 *
13647
-	 * @return integer
13648
-	 */
13649
-	public static function countTagged( $beanType, $tagList, $sql = '', $bindings = array() )
13650
-	{
13651
-		return self::$tagManager->countTagged( $beanType, $tagList, $sql, $bindings );
13652
-	}
13653
-
13654
-	/**
13655
-	 * Wipes all beans of type $beanType.
13656
-	 *
13657
-	 * @param string $beanType type of bean you want to destroy entirely
13658
-	 *
13659
-	 * @return boolean
13660
-	 */
13661
-	public static function wipe( $beanType )
13662
-	{
13663
-		return Facade::$redbean->wipe( $beanType );
13664
-	}
13665
-
13666
-	/**
13667
-	 * Counts the number of beans of type $type.
13668
-	 * This method accepts a second argument to modify the count-query.
13669
-	 * A third argument can be used to provide bindings for the SQL snippet.
13670
-	 *
13671
-	 * @param string $type     type of bean we are looking for
13672
-	 * @param string $addSQL   additional SQL snippet
13673
-	 * @param array  $bindings parameters to bind to SQL
13674
-	 *
13675
-	 * @return integer
13676
-	 */
13677
-	public static function count( $type, $addSQL = '', $bindings = array() )
13678
-	{
13679
-		return Facade::$redbean->count( $type, $addSQL, $bindings );
13680
-	}
13681
-
13682
-	/**
13683
-	 * Configures the facade, want to have a new Writer? A new Object Database or a new
13684
-	 * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new
13685
-	 * toolbox.
13686
-	 *
13687
-	 * @param ToolBox $tb toolbox to configure facade with
13688
-	 *
13689
-	 * @return ToolBox
13690
-	 */
13691
-	public static function configureFacadeWithToolbox( ToolBox $tb )
13692
-	{
13693
-		$oldTools                 = self::$toolbox;
13694
-		self::$toolbox            = $tb;
13695
-		self::$writer             = self::$toolbox->getWriter();
13696
-		self::$adapter            = self::$toolbox->getDatabaseAdapter();
13697
-		self::$redbean            = self::$toolbox->getRedBean();
13698
-		self::$finder             = new Finder( self::$toolbox );
13699
-		self::$associationManager = new AssociationManager( self::$toolbox );
13700
-		self::$tree               = new Tree( self::$toolbox );
13701
-		self::$redbean->setAssociationManager( self::$associationManager );
13702
-		self::$labelMaker         = new LabelMaker( self::$toolbox );
13703
-		$helper                   = new SimpleModelHelper();
13704
-		$helper->attachEventListeners( self::$redbean );
13705
-		if (self::$redbean->getBeanHelper() == NULL) {
13706
-			self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper );
13707
-		}
13708
-		self::$duplicationManager = new DuplicationManager( self::$toolbox );
13709
-		self::$tagManager         = new TagManager( self::$toolbox );
13710
-		return $oldTools;
13711
-	}
13712
-
13713
-	/**
13714
-	 * Facade Convience method for adapter transaction system.
13715
-	 * Begins a transaction.
13716
-	 *
13717
-	 * Usage:
13718
-	 *
13719
-	 * <code>
13720
-	 * R::begin();
13721
-	 * try {
13722
-	 *  $bean1 = R::dispense( 'bean' );
13723
-	 *  R::store( $bean1 );
13724
-	 *  $bean2 = R::dispense( 'bean' );
13725
-	 *  R::store( $bean2 );
13726
-	 *  R::commit();
13727
-	 * } catch( \Exception $e ) {
13728
-	 *  R::rollback();
13729
-	 * }
13730
-	 * </code>
13731
-	 *
13732
-	 * The example above illustrates how transactions in RedBeanPHP are used.
13733
-	 * In this example 2 beans are stored or nothing is stored at all.
13734
-	 * It's not possible for this piece of code to store only half of the beans.
13735
-	 * If an exception occurs, the transaction gets rolled back and the database
13736
-	 * will be left 'untouched'.
13737
-	 *
13738
-	 * In fluid mode transactions will be ignored and all queries will
13739
-	 * be executed as-is because database schema changes will automatically
13740
-	 * trigger the transaction system to commit everything in some database
13741
-	 * systems. If you use a database that can handle DDL changes you might wish
13742
-	 * to use setAllowFluidTransactions(TRUE). If you do this, the behavior of
13743
-	 * this function in fluid mode will depend on the database platform used.
13744
-	 *
13745
-	 * @return bool
13746
-	 */
13747
-	public static function begin()
13748
-	{
13749
-		if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
13750
-		self::$adapter->startTransaction();
13751
-		return TRUE;
13752
-	}
13753
-
13754
-	/**
13755
-	 * Facade Convience method for adapter transaction system.
13756
-	 * Commits a transaction.
13757
-	 *
13758
-	 * Usage:
13759
-	 *
13760
-	 * <code>
13761
-	 * R::begin();
13762
-	 * try {
13763
-	 *  $bean1 = R::dispense( 'bean' );
13764
-	 *  R::store( $bean1 );
13765
-	 *  $bean2 = R::dispense( 'bean' );
13766
-	 *  R::store( $bean2 );
13767
-	 *  R::commit();
13768
-	 * } catch( \Exception $e ) {
13769
-	 *  R::rollback();
13770
-	 * }
13771
-	 * </code>
13772
-	 *
13773
-	 * The example above illustrates how transactions in RedBeanPHP are used.
13774
-	 * In this example 2 beans are stored or nothing is stored at all.
13775
-	 * It's not possible for this piece of code to store only half of the beans.
13776
-	 * If an exception occurs, the transaction gets rolled back and the database
13777
-	 * will be left 'untouched'.
13778
-	 *
13779
-	 * In fluid mode transactions will be ignored and all queries will
13780
-	 * be executed as-is because database schema changes will automatically
13781
-	 * trigger the transaction system to commit everything in some database
13782
-	 * systems. If you use a database that can handle DDL changes you might wish
13783
-	 * to use setAllowFluidTransactions(TRUE). If you do this, the behavior of
13784
-	 * this function in fluid mode will depend on the database platform used.
13785
-	 *
13786
-	 * @return bool
13787
-	 */
13788
-	public static function commit()
13789
-	{
13790
-		if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
13791
-		self::$adapter->commit();
13792
-		return TRUE;
13793
-	}
13794
-
13795
-	/**
13796
-	 * Facade Convience method for adapter transaction system.
13797
-	 * Rolls back a transaction.
13798
-	 *
13799
-	 * Usage:
13800
-	 *
13801
-	 * <code>
13802
-	 * R::begin();
13803
-	 * try {
13804
-	 *  $bean1 = R::dispense( 'bean' );
13805
-	 *  R::store( $bean1 );
13806
-	 *  $bean2 = R::dispense( 'bean' );
13807
-	 *  R::store( $bean2 );
13808
-	 *  R::commit();
13809
-	 * } catch( \Exception $e ) {
13810
-	 *  R::rollback();
13811
-	 * }
13812
-	 * </code>
13813
-	 *
13814
-	 * The example above illustrates how transactions in RedBeanPHP are used.
13815
-	 * In this example 2 beans are stored or nothing is stored at all.
13816
-	 * It's not possible for this piece of code to store only half of the beans.
13817
-	 * If an exception occurs, the transaction gets rolled back and the database
13818
-	 * will be left 'untouched'.
13819
-	 *
13820
-	 * In fluid mode transactions will be ignored and all queries will
13821
-	 * be executed as-is because database schema changes will automatically
13822
-	 * trigger the transaction system to commit everything in some database
13823
-	 * systems. If you use a database that can handle DDL changes you might wish
13824
-	 * to use setAllowFluidTransactions(TRUE). If you do this, the behavior of
13825
-	 * this function in fluid mode will depend on the database platform used.
13826
-	 *
13827
-	 * @return bool
13828
-	 */
13829
-	public static function rollback()
13830
-	{
13831
-		if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
13832
-		self::$adapter->rollback();
13833
-		return TRUE;
13834
-	}
13835
-
13836
-	/**
13837
-	 * Returns a list of columns. Format of this array:
13838
-	 * array( fieldname => type )
13839
-	 * Note that this method only works in fluid mode because it might be
13840
-	 * quite heavy on production servers!
13841
-	 *
13842
-	 * @param  string $table name of the table (not type) you want to get columns of
13843
-	 *
13844
-	 * @return array
13845
-	 */
13846
-	public static function getColumns( $table )
13847
-	{
13848
-		return self::$writer->getColumns( $table );
13849
-	}
13850
-
13851
-	/**
13852
-	 * Generates question mark slots for an array of values.
13853
-	 * Given an array and an optional template string this method
13854
-	 * will produce string containing parameter slots for use in
13855
-	 * an SQL query string.
13856
-	 *
13857
-	 * Usage:
13858
-	 *
13859
-	 * <code>
13860
-	 * R::genSlots( array( 'a', 'b' ) );
13861
-	 * </code>
13862
-	 *
13863
-	 * The statement in the example will produce the string:
13864
-	 * '?,?'.
13865
-	 *
13866
-	 * Another example, using a template string:
13867
-	 *
13868
-	 * <code>
13869
-	 * R::genSlots( array('a', 'b'), ' IN( %s ) ' );
13870
-	 * </code>
13871
-	 *
13872
-	 * The statement in the example will produce the string:
13873
-	 * ' IN( ?,? ) '.
13874
-	 *
13875
-	 * @param array  $array    array to generate question mark slots for
13876
-	 * @param string $template template to use
13877
-	 *
13878
-	 * @return string
13879
-	 */
13880
-	public static function genSlots( $array, $template = NULL )
13881
-	{
13882
-		return ArrayTool::genSlots( $array, $template );
13883
-	}
13884
-
13885
-	/**
13886
-	 * Flattens a multi dimensional bindings array for use with genSlots().
13887
-	 *
13888
-	 * Usage:
13889
-	 *
13890
-	 * <code>
13891
-	 * R::flat( array( 'a', array( 'b' ), 'c' ) );
13892
-	 * </code>
13893
-	 *
13894
-	 * produces an array like: [ 'a', 'b', 'c' ]
13895
-	 *
13896
-	 * @param array $array  array to flatten
13897
-	 * @param array $result result array parameter (for recursion)
13898
-	 *
13899
-	 * @return array
13900
-	 */
13901
-	public static function flat( $array, $result = array() )
13902
-	{
13903
-		return ArrayTool::flat( $array, $result );
13904
-	}
13905
-
13906
-	/**
13907
-	 * Nukes the entire database.
13908
-	 * This will remove all schema structures from the database.
13909
-	 * Only works in fluid mode. Be careful with this method.
13910
-	 *
13911
-	 * @warning dangerous method, will remove all tables, columns etc.
13912
-	 *
13913
-	 * @return void
13914
-	 */
13915
-	public static function nuke()
13916
-	{
13917
-		if ( !self::$redbean->isFrozen() ) {
13918
-			self::$writer->wipeAll();
13919
-		}
13920
-	}
13921
-
13922
-	/**
13923
-	 * Short hand function to store a set of beans at once, IDs will be
13924
-	 * returned as an array. For information please consult the R::store()
13925
-	 * function.
13926
-	 * A loop saver.
13927
-	 *
13928
-	 * @param array $beans list of beans to be stored
13929
-	 *
13930
-	 * @return array
13931
-	 */
13932
-	public static function storeAll( $beans )
13933
-	{
13934
-		$ids = array();
13935
-		foreach ( $beans as $bean ) {
13936
-			$ids[] = self::store( $bean );
13937
-		}
13938
-		return $ids;
13939
-	}
13940
-
13941
-	/**
13942
-	 * Short hand function to trash a set of beans at once.
13943
-	 * For information please consult the R::trash() function.
13944
-	 * A loop saver.
13945
-	 *
13946
-	 * @param array $beans list of beans to be trashed
13947
-	 *
13948
-	 * @return void
13949
-	 */
13950
-	public static function trashAll( $beans )
13951
-	{
13952
-		foreach ( $beans as $bean ) {
13953
-			self::trash( $bean );
13954
-		}
13955
-	}
13956
-
13957
-	/**
13958
-	 * Short hand function to trash a series of beans using
13959
-	 * only IDs. This function combines trashAll and batch loading
13960
-	 * in one call. Note that while this function accepts just
13961
-	 * bean IDs, the beans will still be loaded first. This is because
13962
-	 * the function still respects all the FUSE hooks that may have beeb
13963
-	 * associated with the domain logic associated with these beans.
13964
-	 * If you really want to delete just records from the database use
13965
-	 * a simple DELETE-FROM SQL query instead.
13966
-	 *
13967
-	 * @param string type  $type the bean type you wish to trash
13968
-	 * @param string array $ids  list of bean IDs
13969
-	 *
13970
-	 * @return void
13971
-	 */
13972
-	public static function trashBatch( $type, $ids )
13973
-	{
13974
-		self::trashAll( self::batch( $type, $ids ) );
13975
-	}
13976
-
13977
-	/**
13978
-	 * Short hand function to find and trash beans.
13979
-	 * This function combines trashAll and find.
13980
-	 * Given a bean type, a query snippet and optionally some parameter
13981
-	 * bindings, this function will search for the beans described in the
13982
-	 * query and its parameters and then feed them to the trashAll function
13983
-	 * to be trashed.
13984
-	 *
13985
-	 * Note that while this function accepts just
13986
-	 * a bean type and query snippet, the beans will still be loaded first. This is because
13987
-	 * the function still respects all the FUSE hooks that may have been
13988
-	 * associated with the domain logic associated with these beans.
13989
-	 * If you really want to delete just records from the database use
13990
-	 * a simple DELETE-FROM SQL query instead.
13991
-	 *
13992
-	 * Returns the number of beans deleted.
13993
-	 *
13994
-	 * @param string $type       bean type to look for in database
13995
-	 * @param string $sqlSnippet an SQL query snippet
13996
-	 * @param array  $bindings   SQL parameter bindings
13997
-	 *
13998
-	 * @return int
13999
-	 */
14000
-	public static function hunt( $type, $sqlSnippet = NULL, $bindings = array() )
14001
-	{
14002
-		$numberOfTrashedBeans = 0;
14003
-		$beans = self::findCollection( $type, $sqlSnippet, $bindings );
14004
-		while( $bean = $beans->next() ) {
14005
-			self::trash( $bean );
14006
-			$numberOfTrashedBeans++;
14007
-		}
14008
-		return $numberOfTrashedBeans;
14009
-	}
14010
-
14011
-	/**
14012
-	 * Toggles Writer Cache.
14013
-	 * Turns the Writer Cache on or off. The Writer Cache is a simple
14014
-	 * query based caching system that may improve performance without the need
14015
-	 * for cache management. This caching system will cache non-modifying queries
14016
-	 * that are marked with special SQL comments. As soon as a non-marked query
14017
-	 * gets executed the cache will be flushed. Only non-modifying select queries
14018
-	 * have been marked therefore this mechanism is a rather safe way of caching, requiring
14019
-	 * no explicit flushes or reloads. Of course this does not apply if you intend to test
14020
-	 * or simulate concurrent querying.
14021
-	 *
14022
-	 * @param boolean $yesNo TRUE to enable cache, FALSE to disable cache
14023
-	 *
14024
-	 * @return void
14025
-	 */
14026
-	public static function useWriterCache( $yesNo )
14027
-	{
14028
-		self::getWriter()->setUseCache( $yesNo );
14029
-	}
14030
-
14031
-	/**
14032
-	 * A label is a bean with only an id, type and name property.
14033
-	 * This function will dispense beans for all entries in the array. The
14034
-	 * values of the array will be assigned to the name property of each
14035
-	 * individual bean.
14036
-	 *
14037
-	 * @param string $type   type of beans you would like to have
14038
-	 * @param array  $labels list of labels, names for each bean
14039
-	 *
14040
-	 * @return array
14041
-	 */
14042
-	public static function dispenseLabels( $type, $labels )
14043
-	{
14044
-		return self::$labelMaker->dispenseLabels( $type, $labels );
14045
-	}
14046
-
14047
-	/**
14048
-	 * Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs.
14049
-	 * Either returns a (newly created) bean respresenting the desired ENUM
14050
-	 * value or returns a list of all enums for the type.
14051
-	 *
14052
-	 * To obtain (and add if necessary) an ENUM value:
14053
-	 *
14054
-	 * <code>
14055
-	 * $tea->flavour = R::enum( 'flavour:apple' );
14056
-	 * </code>
14057
-	 *
14058
-	 * Returns a bean of type 'flavour' with  name = apple.
14059
-	 * This will add a bean with property name (set to APPLE) to the database
14060
-	 * if it does not exist yet.
14061
-	 *
14062
-	 * To obtain all flavours:
14063
-	 *
14064
-	 * <code>
14065
-	 * R::enum('flavour');
14066
-	 * </code>
14067
-	 *
14068
-	 * To get a list of all flavour names:
14069
-	 *
14070
-	 * <code>
14071
-	 * R::gatherLabels( R::enum( 'flavour' ) );
14072
-	 * </code>
14073
-	 *
14074
-	 * @param string $enum either type or type-value
14075
-	 *
14076
-	 * @return array|OODBBean
14077
-	 */
14078
-	public static function enum( $enum )
14079
-	{
14080
-		return self::$labelMaker->enum( $enum );
14081
-	}
14082
-
14083
-	/**
14084
-	 * Gathers labels from beans. This function loops through the beans,
14085
-	 * collects the values of the name properties of each individual bean
14086
-	 * and stores the names in a new array. The array then gets sorted using the
14087
-	 * default sort function of PHP (sort).
14088
-	 *
14089
-	 * @param array $beans list of beans to loop
14090
-	 *
14091
-	 * @return array
14092
-	 */
14093
-	public static function gatherLabels( $beans )
14094
-	{
14095
-		return self::$labelMaker->gatherLabels( $beans );
14096
-	}
14097
-
14098
-	/**
14099
-	 * Closes the database connection.
14100
-	 * While database connections are closed automatically at the end of the PHP script,
14101
-	 * closing database connections is generally recommended to improve performance.
14102
-	 * Closing a database connection will immediately return the resources to PHP.
14103
-	 *
14104
-	 * Usage:
14105
-	 *
14106
-	 * <code>
14107
-	 * R::setup( ... );
14108
-	 * ... do stuff ...
14109
-	 * R::close();
14110
-	 * </code>
14111
-	 *
14112
-	 * @return void
14113
-	 */
14114
-	public static function close()
14115
-	{
14116
-		if ( isset( self::$adapter ) ) {
14117
-			self::$adapter->close();
14118
-		}
14119
-	}
14120
-
14121
-	/**
14122
-	 * Simple convenience function, returns ISO date formatted representation
14123
-	 * of $time.
14124
-	 *
14125
-	 * @param mixed $time UNIX timestamp
14126
-	 *
14127
-	 * @return string
14128
-	 */
14129
-	public static function isoDate( $time = NULL )
14130
-	{
14131
-		if ( !$time ) {
14132
-			$time = time();
14133
-		}
14134
-
14135
-		return @date( 'Y-m-d', $time );
14136
-	}
14137
-
14138
-	/**
14139
-	 * Simple convenience function, returns ISO date time
14140
-	 * formatted representation
14141
-	 * of $time.
14142
-	 *
14143
-	 * @param mixed $time UNIX timestamp
14144
-	 *
14145
-	 * @return string
14146
-	 */
14147
-	public static function isoDateTime( $time = NULL )
14148
-	{
14149
-		if ( !$time ) $time = time();
14150
-		return @date( 'Y-m-d H:i:s', $time );
14151
-	}
14152
-
14153
-	/**
14154
-	 * Sets the database adapter you want to use.
14155
-	 * The database adapter manages the connection to the database
14156
-	 * and abstracts away database driver specific interfaces.
14157
-	 *
14158
-	 * @param Adapter $adapter Database Adapter for facade to use
14159
-	 *
14160
-	 * @return void
14161
-	 */
14162
-	public static function setDatabaseAdapter( Adapter $adapter )
14163
-	{
14164
-		self::$adapter = $adapter;
14165
-	}
14166
-
14167
-	/**
14168
-	 * Sets the Query Writer you want to use.
14169
-	 * The Query Writer writes and executes database queries using
14170
-	 * the database adapter. It turns RedBeanPHP 'commands' into
14171
-	 * database 'statements'.
14172
-	 *
14173
-	 * @param QueryWriter $writer Query Writer instance for facade to use
14174
-	 *
14175
-	 * @return void
14176
-	 */
14177
-	public static function setWriter( QueryWriter $writer )
14178
-	{
14179
-		self::$writer = $writer;
14180
-	}
14181
-
14182
-	/**
14183
-	 * Sets the OODB you want to use.
14184
-	 * The RedBeanPHP Object oriented database is the main RedBeanPHP
14185
-	 * interface that allows you to store and retrieve RedBeanPHP
14186
-	 * objects (i.e. beans).
14187
-	 *
14188
-	 * @param OODB $redbean Object Database for facade to use
14189
-	 */
14190
-	public static function setRedBean( OODB $redbean )
14191
-	{
14192
-		self::$redbean = $redbean;
14193
-	}
14194
-
14195
-	/**
14196
-	 * Optional accessor for neat code.
14197
-	 * Sets the database adapter you want to use.
14198
-	 *
14199
-	 * @return DBAdapter
14200
-	 */
14201
-	public static function getDatabaseAdapter()
14202
-	{
14203
-		return self::$adapter;
14204
-	}
14205
-
14206
-	/**
14207
-	 * In case you use PDO (which is recommended and the default but not mandatory, hence
14208
-	 * the database adapter), you can use this method to obtain the PDO object directly.
14209
-	 * This is a convenience method, it will do the same as:
14210
-	 *
14211
-	 * <code>
14212
-	 * R::getDatabaseAdapter()->getDatabase()->getPDO();
14213
-	 * </code>
14214
-	 *
14215
-	 * If the PDO object could not be found, for whatever reason, this method
14216
-	 * will return NULL instead.
14217
-	 *
14218
-	 * @return NULL|PDO
14219
-	 */
14220
-	public static function getPDO()
14221
-	{
14222
-		$databaseAdapter = self::getDatabaseAdapter();
14223
-		if ( is_null( $databaseAdapter ) ) return NULL;
14224
-		$database = $databaseAdapter->getDatabase();
14225
-		if ( is_null( $database ) ) return NULL;
14226
-		if ( !method_exists( $database, 'getPDO' ) ) return NULL;
14227
-		return $database->getPDO();
14228
-	}
14229
-
14230
-	/**
14231
-	 * Returns the current duplication manager instance.
14232
-	 *
14233
-	 * @return DuplicationManager
14234
-	 */
14235
-	public static function getDuplicationManager()
14236
-	{
14237
-		return self::$duplicationManager;
14238
-	}
14239
-
14240
-	/**
14241
-	 * Optional accessor for neat code.
14242
-	 * Sets the database adapter you want to use.
14243
-	 *
14244
-	 * @return QueryWriter
14245
-	 */
14246
-	public static function getWriter()
14247
-	{
14248
-		return self::$writer;
14249
-	}
14250
-
14251
-	/**
14252
-	 * Optional accessor for neat code.
14253
-	 * Sets the database adapter you want to use.
14254
-	 *
14255
-	 * @return OODB
14256
-	 */
14257
-	public static function getRedBean()
14258
-	{
14259
-		return self::$redbean;
14260
-	}
14261
-
14262
-	/**
14263
-	 * Returns the toolbox currently used by the facade.
14264
-	 * To set the toolbox use R::setup() or R::configureFacadeWithToolbox().
14265
-	 * To create a toolbox use Setup::kickstart(). Or create a manual
14266
-	 * toolbox using the ToolBox class.
14267
-	 *
14268
-	 * @return ToolBox
14269
-	 */
14270
-	public static function getToolBox()
14271
-	{
14272
-		return self::$toolbox;
14273
-	}
14274
-
14275
-	/**
14276
-	 * Mostly for internal use, but might be handy
14277
-	 * for some users.
14278
-	 * This returns all the components of the currently
14279
-	 * selected toolbox.
14280
-	 *
14281
-	 * Returns the components in the following order:
14282
-	 *
14283
-	 * # OODB instance (getRedBean())
14284
-	 * # Database Adapter
14285
-	 * # Query Writer
14286
-	 * # Toolbox itself
14287
-	 *
14288
-	 * @return array
14289
-	 */
14290
-	public static function getExtractedToolbox()
14291
-	{
14292
-		return array( self::$redbean, self::$adapter, self::$writer, self::$toolbox );
14293
-	}
14294
-
14295
-	/**
14296
-	 * Facade method for AQueryWriter::renameAssociation()
14297
-	 *
14298
-	 * @param string|array $from
14299
-	 * @param string       $to
14300
-	 *
14301
-	 * @return void
14302
-	 */
14303
-	public static function renameAssociation( $from, $to = NULL )
14304
-	{
14305
-		AQueryWriter::renameAssociation( $from, $to );
14306
-	}
14307
-
14308
-	/**
14309
-	 * Little helper method for Resty Bean Can server and others.
14310
-	 * Takes an array of beans and exports each bean.
14311
-	 * Unlike exportAll this method does not recurse into own lists
14312
-	 * and shared lists, the beans are exported as-is, only loaded lists
14313
-	 * are exported.
14314
-	 *
14315
-	 * @param array $beans beans
14316
-	 *
14317
-	 * @return array
14318
-	 */
14319
-	public static function beansToArray( $beans )
14320
-	{
14321
-		$list = array();
14322
-		foreach( $beans as $bean ) $list[] = $bean->export();
14323
-		return $list;
14324
-	}
14325
-
14326
-	/**
14327
-	 * Sets the error mode for FUSE.
14328
-	 * What to do if a FUSE model method does not exist?
14329
-	 * You can set the following options:
14330
-	 *
14331
-	 * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
14332
-	 * * OODBBean::C_ERR_LOG, logs the incident using error_log
14333
-	 * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
14334
-	 * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
14335
-	 * * OODBBean::C_ERR_EXCEPTION, throws an exception
14336
-	 * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
14337
-	 * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
14338
-	 *
14339
-	 * <code>
14340
-	 * Custom handler method signature: handler( array (
14341
-	 * 	'message' => string
14342
-	 * 	'bean' => OODBBean
14343
-	 * 	'method' => string
14344
-	 * ) )
14345
-	 * </code>
14346
-	 *
14347
-	 * This method returns the old mode and handler as an array.
14348
-	 *
14349
-	 * @param integer       $mode mode, determines how to handle errors
14350
-	 * @param callable|NULL $func custom handler (if applicable)
14351
-	 *
14352
-	 * @return array
14353
-	 */
14354
-	public static function setErrorHandlingFUSE( $mode, $func = NULL )
14355
-	{
14356
-		return OODBBean::setErrorHandlingFUSE( $mode, $func );
14357
-	}
14358
-
14359
-	/**
14360
-	 * Dumps bean data to array.
14361
-	 * Given a one or more beans this method will
14362
-	 * return an array containing first part of the string
14363
-	 * representation of each item in the array.
14364
-	 *
14365
-	 * Usage:
14366
-	 *
14367
-	 * <code>
14368
-	 * echo R::dump( $bean );
14369
-	 * </code>
14370
-	 *
14371
-	 * The example shows how to echo the result of a simple
14372
-	 * dump. This will print the string representation of the
14373
-	 * specified bean to the screen, limiting the output per bean
14374
-	 * to 35 characters to improve readability. Nested beans will
14375
-	 * also be dumped.
14376
-	 *
14377
-	 * @param OODBBean|array $data either a bean or an array of beans
14378
-	 *
14379
-	 * @return array
14380
-	 */
14381
-	public static function dump( $data )
14382
-	{
14383
-		return Dump::dump( $data );
14384
-	}
14385
-
14386
-	/**
14387
-	 * Binds an SQL function to a column.
14388
-	 * This method can be used to setup a decode/encode scheme or
14389
-	 * perform UUID insertion. This method is especially useful for handling
14390
-	 * MySQL spatial columns, because they need to be processed first using
14391
-	 * the asText/GeomFromText functions.
14392
-	 *
14393
-	 * Example:
14394
-	 *
14395
-	 * <code>
14396
-	 * R::bindFunc( 'read', 'location.point', 'asText' );
14397
-	 * R::bindFunc( 'write', 'location.point', 'GeomFromText' );
14398
-	 * </code>
14399
-	 *
14400
-	 * Passing NULL as the function will reset (clear) the function
14401
-	 * for this column/mode.
14402
-	 *
14403
-	 * @param string $mode     mode for function: i.e. read or write
14404
-	 * @param string $field    field (table.column) to bind function to
14405
-	 * @param string $function SQL function to bind to specified column
14406
-	 *
14407
-	 * @return void
14408
-	 */
14409
-	public static function bindFunc( $mode, $field, $function )
14410
-	{
14411
-		self::$redbean->bindFunc( $mode, $field, $function );
14412
-	}
14413
-
14414
-	/**
14415
-	 * Sets global aliases.
14416
-	 * Registers a batch of aliases in one go. This works the same as
14417
-	 * fetchAs and setAutoResolve but explicitly. For instance if you register
14418
-	 * the alias 'cover' for 'page' a property containing a reference to a
14419
-	 * page bean called 'cover' will correctly return the page bean and not
14420
-	 * a (non-existant) cover bean.
14421
-	 *
14422
-	 * <code>
14423
-	 * R::aliases( array( 'cover' => 'page' ) );
14424
-	 * $book = R::dispense( 'book' );
14425
-	 * $page = R::dispense( 'page' );
14426
-	 * $book->cover = $page;
14427
-	 * R::store( $book );
14428
-	 * $book = $book->fresh();
14429
-	 * $cover = $book->cover;
14430
-	 * echo $cover->getMeta( 'type' ); //page
14431
-	 * </code>
14432
-	 *
14433
-	 * The format of the aliases registration array is:
14434
-	 *
14435
-	 * {alias} => {actual type}
14436
-	 *
14437
-	 * In the example above we use:
14438
-	 *
14439
-	 * cover => page
14440
-	 *
14441
-	 * From that point on, every bean reference to a cover
14442
-	 * will return a 'page' bean. Note that with autoResolve this
14443
-	 * feature along with fetchAs() is no longer very important, although
14444
-	 * relying on explicit aliases can be a bit faster.
14445
-	 *
14446
-	 * @param array $list list of global aliases to use
14447
-	 *
14448
-	 * @return void
14449
-	 */
14450
-	public static function aliases( $list )
14451
-	{
14452
-		OODBBean::aliases( $list );
14453
-	}
14454
-
14455
-	/**
14456
-	 * Tries to find a bean matching a certain type and
14457
-	 * criteria set. If no beans are found a new bean
14458
-	 * will be created, the criteria will be imported into this
14459
-	 * bean and the bean will be stored and returned.
14460
-	 * If multiple beans match the criteria only the first one
14461
-	 * will be returned.
14462
-	 *
14463
-	 * @param string $type type of bean to search for
14464
-	 * @param array  $like criteria set describing the bean to search for
14465
-	 *
14466
-	 * @return OODBBean
14467
-	 */
14468
-	public static function findOrCreate( $type, $like = array(), $sql = '' )
14469
-	{
14470
-		return self::$finder->findOrCreate( $type, $like, $sql = '' );
14471
-	}
14472
-
14473
-	/**
14474
-	 * Tries to find beans matching the specified type and
14475
-	 * criteria set.
14476
-	 *
14477
-	 * If the optional additional SQL snippet is a condition, it will
14478
-	 * be glued to the rest of the query using the AND operator.
14479
-	 *
14480
-	 * @param string $type type of bean to search for
14481
-	 * @param array  $like optional criteria set describing the bean to search for
14482
-	 * @param string $sql  optional additional SQL for sorting
14483
-	 * @param array  $bindings bindings
14484
-	 *
14485
-	 * @return array
14486
-	 */
14487
-	public static function findLike( $type, $like = array(), $sql = '', $bindings = array() )
14488
-	{
14489
-		return self::$finder->findLike( $type, $like, $sql, $bindings );
14490
-	}
14491
-
14492
-	/**
14493
-	 * Starts logging queries.
14494
-	 * Use this method to start logging SQL queries being
14495
-	 * executed by the adapter. Logging queries will not
14496
-	 * print them on the screen. Use R::getLogs() to
14497
-	 * retrieve the logs.
14498
-	 *
14499
-	 * Usage:
14500
-	 *
14501
-	 * <code>
14502
-	 * R::startLogging();
14503
-	 * R::store( R::dispense( 'book' ) );
14504
-	 * R::find('book', 'id > ?',[0]);
14505
-	 * $logs = R::getLogs();
14506
-	 * $count = count( $logs );
14507
-	 * print_r( $logs );
14508
-	 * R::stopLogging();
14509
-	 * </code>
14510
-	 *
14511
-	 * In the example above we start a logging session during
14512
-	 * which we store an empty bean of type book. To inspect the
14513
-	 * logs we invoke R::getLogs() after stopping the logging.
14514
-	 *
14515
-	 * @note you cannot use R::debug and R::startLogging
14516
-	 * at the same time because R::debug is essentially a
14517
-	 * special kind of logging.
14518
-	 *
14519
-	 * @return void
14520
-	 */
14521
-	public static function startLogging()
14522
-	{
14523
-		self::debug( TRUE, RDefault::C_LOGGER_ARRAY );
14524
-	}
14525
-
14526
-	/**
14527
-	 * Stops logging and flushes the logs,
14528
-	 * convient method to stop logging of queries.
14529
-	 * Use this method to stop logging SQL queries being
14530
-	 * executed by the adapter. Logging queries will not
14531
-	 * print them on the screen. Use R::getLogs() to
14532
-	 * retrieve the logs.
14533
-	 *
14534
-	 * <code>
14535
-	 * R::startLogging();
14536
-	 * R::store( R::dispense( 'book' ) );
14537
-	 * R::find('book', 'id > ?',[0]);
14538
-	 * $logs = R::getLogs();
14539
-	 * $count = count( $logs );
14540
-	 * print_r( $logs );
14541
-	 * R::stopLogging();
14542
-	 * </code>
14543
-	 *
14544
-	 * In the example above we start a logging session during
14545
-	 * which we store an empty bean of type book. To inspect the
14546
-	 * logs we invoke R::getLogs() after stopping the logging.
14547
-	 *
14548
-	 * @note you cannot use R::debug and R::startLogging
14549
-	 * at the same time because R::debug is essentially a
14550
-	 * special kind of logging.
14551
-	 *
14552
-	 * @note by stopping the logging you also flush the logs.
14553
-	 * Therefore, only stop logging AFTER you have obtained the
14554
-	 * query logs using R::getLogs()
14555
-	 *
14556
-	 * @return void
14557
-	 */
14558
-	public static function stopLogging()
14559
-	{
14560
-		self::debug( FALSE );
14561
-	}
14562
-
14563
-	/**
14564
-	 * Returns the log entries written after the startLogging.
14565
-	 *
14566
-	 * Use this method to obtain the query logs gathered
14567
-	 * by the logging mechanisms.
14568
-	 * Logging queries will not
14569
-	 * print them on the screen. Use R::getLogs() to
14570
-	 * retrieve the logs.
14571
-	 *
14572
-	 * <code>
14573
-	 * R::startLogging();
14574
-	 * R::store( R::dispense( 'book' ) );
14575
-	 * R::find('book', 'id > ?',[0]);
14576
-	 * $logs = R::getLogs();
14577
-	 * $count = count( $logs );
14578
-	 * print_r( $logs );
14579
-	 * R::stopLogging();
14580
-	 * </code>
14581
-	 *
14582
-	 * In the example above we start a logging session during
14583
-	 * which we store an empty bean of type book. To inspect the
14584
-	 * logs we invoke R::getLogs() after stopping the logging.
14585
-	 *
14586
-	 * The logs may look like:
14587
-	 *
14588
-	 * [1] => SELECT `book`.*  FROM `book`  WHERE id > ?  -- keep-cache
14589
-	 * [2] => array ( 0 => 0, )
14590
-	 * [3] => resultset: 1 rows
14591
-	 *
14592
-	 * Basically, element in the array is a log entry.
14593
-	 * Parameter bindings are  represented as nested arrays (see 2).
14594
-	 *
14595
-	 * @note you cannot use R::debug and R::startLogging
14596
-	 * at the same time because R::debug is essentially a
14597
-	 * special kind of logging.
14598
-	 *
14599
-	 * @note by stopping the logging you also flush the logs.
14600
-	 * Therefore, only stop logging AFTER you have obtained the
14601
-	 * query logs using R::getLogs()
14602
-	 *
14603
-	 * @return array
14604
-	 */
14605
-	public static function getLogs()
14606
-	{
14607
-		return self::getLogger()->getLogs();
14608
-	}
14609
-
14610
-	/**
14611
-	 * Resets the query counter.
14612
-	 * The query counter can be used to monitor the number
14613
-	 * of database queries that have
14614
-	 * been processed according to the database driver. You can use this
14615
-	 * to monitor the number of queries required to render a page.
14616
-	 *
14617
-	 * Usage:
14618
-	 *
14619
-	 * <code>
14620
-	 * R::resetQueryCount();
14621
-	 * echo R::getQueryCount() . ' queries processed.';
14622
-	 * </code>
14623
-	 *
14624
-	 * @return void
14625
-	 */
14626
-	public static function resetQueryCount()
14627
-	{
14628
-		self::$adapter->getDatabase()->resetCounter();
14629
-	}
14630
-
14631
-	/**
14632
-	 * Returns the number of SQL queries processed.
14633
-	 * This method returns the number of database queries that have
14634
-	 * been processed according to the database driver. You can use this
14635
-	 * to monitor the number of queries required to render a page.
14636
-	 *
14637
-	 * Usage:
14638
-	 *
14639
-	 * <code>
14640
-	 * echo R::getQueryCount() . ' queries processed.';
14641
-	 * </code>
14642
-	 *
14643
-	 * @return integer
14644
-	 */
14645
-	public static function getQueryCount()
14646
-	{
14647
-		return self::$adapter->getDatabase()->getQueryCount();
14648
-	}
14649
-
14650
-	/**
14651
-	 * Returns the current logger instance being used by the
14652
-	 * database object.
14653
-	 *
14654
-	 * @return Logger
14655
-	 */
14656
-	public static function getLogger()
14657
-	{
14658
-		return self::$adapter->getDatabase()->getLogger();
14659
-	}
14660
-
14661
-	/**
14662
-	 * Alias for setAutoResolve() method on OODBBean.
14663
-	 * Enables or disables auto-resolving fetch types.
14664
-	 * Auto-resolving aliased parent beans is convenient but can
14665
-	 * be slower and can create infinite recursion if you
14666
-	 * used aliases to break cyclic relations in your domain.
14667
-	 *
14668
-	 * @param boolean $automatic TRUE to enable automatic resolving aliased parents
14669
-	 *
14670
-	 * @return void
14671
-	 */
14672
-	public static function setAutoResolve( $automatic = TRUE )
14673
-	{
14674
-		OODBBean::setAutoResolve( (boolean) $automatic );
14675
-	}
14676
-
14677
-	/**
14678
-	 * Toggles 'partial bean mode'. If this mode has been
14679
-	 * selected the repository will only update the fields of a bean that
14680
-	 * have been changed rather than the entire bean.
14681
-	 * Pass the value TRUE to select 'partial mode' for all beans.
14682
-	 * Pass the value FALSE to disable 'partial mode'.
14683
-	 * Pass an array of bean types if you wish to use partial mode only
14684
-	 * for some types.
14685
-	 * This method will return the previous value.
14686
-	 *
14687
-	 * @param boolean|array $yesNoBeans List of type names or 'all'
14688
-	 *
14689
-	 * @return mixed
14690
-	 */
14691
-	public static function usePartialBeans( $yesNoBeans )
14692
-	{
14693
-		return self::$redbean->getCurrentRepository()->usePartialBeans( $yesNoBeans );
14694
-	}
14695
-
14696
-	/**
14697
-	 * Exposes the result of the specified SQL query as a CSV file.
14698
-	 * Usage:
14699
-	 *
14700
-	 * R::csv( 'SELECT
14701
-	 *                 `name`,
14702
-	 *                  population
14703
-	 *          FROM city
14704
-	 *          WHERE region = :region ',
14705
-	 *          array( ':region' => 'Denmark' ),
14706
-	 *          array( 'city', 'population' ),
14707
-	 *          '/tmp/cities.csv'
14708
-	 * );
14709
-	 *
14710
-	 * The command above will select all cities in Denmark
14711
-	 * and create a CSV with columns 'city' and 'population' and
14712
-	 * populate the cells under these column headers with the
14713
-	 * names of the cities and the population numbers respectively.
14714
-	 *
14715
-	 * @param string  $sql      SQL query to expose result of
14716
-	 * @param array   $bindings parameter bindings
14717
-	 * @param array   $columns  column headers for CSV file
14718
-	 * @param string  $path     path to save CSV file to
14719
-	 * @param boolean $output   TRUE to output CSV directly using readfile
14720
-	 * @param array   $options  delimiter, quote and escape character respectively
14721
-	 *
14722
-	 * @return void
14723
-	 */
14724
-	public static function csv( $sql = '', $bindings = array(), $columns = NULL, $path = '/tmp/redexport_%s.csv', $output = TRUE )
14725
-	{
14726
-		$quickExport = new QuickExport( self::$toolbox );
14727
-		$quickExport->csv( $sql, $bindings, $columns, $path, $output );
14728
-	}
14729
-
14730
-	/**
14731
-	 * MatchUp is a powerful productivity boosting method that can replace simple control
14732
-	 * scripts with a single RedBeanPHP command. Typically, matchUp() is used to
14733
-	 * replace login scripts, token generation scripts and password reset scripts.
14734
-	 * The MatchUp method takes a bean type, an SQL query snippet (starting at the WHERE clause),
14735
-	 * SQL bindings, a pair of task arrays and a bean reference.
14736
-	 *
14737
-	 * If the first 3 parameters match a bean, the first task list will be considered,
14738
-	 * otherwise the second one will be considered. On consideration, each task list,
14739
-	 * an array of keys and values will be executed. Every key in the task list should
14740
-	 * correspond to a bean property while every value can either be an expression to
14741
-	 * be evaluated or a closure (PHP 5.3+). After applying the task list to the bean
14742
-	 * it will be stored. If no bean has been found, a new bean will be dispensed.
14743
-	 *
14744
-	 * This method will return TRUE if the bean was found and FALSE if not AND
14745
-	 * there was a NOT-FOUND task list. If no bean was found AND there was also
14746
-	 * no second task list, NULL will be returned.
14747
-	 *
14748
-	 * To obtain the bean, pass a variable as the sixth parameter.
14749
-	 * The function will put the matching bean in the specified variable.
14750
-	 *
14751
-	 * @param string   $type         type of bean you're looking for
14752
-	 * @param string   $sql          SQL snippet (starting at the WHERE clause, omit WHERE-keyword)
14753
-	 * @param array    $bindings     array of parameter bindings for SQL snippet
14754
-	 * @param array    $onFoundDo    task list to be considered on finding the bean
14755
-	 * @param array    $onNotFoundDo task list to be considered on NOT finding the bean
14756
-	 * @param OODBBean &$bean        reference to obtain the found bean
14757
-	 *
14758
-	 * @return mixed
14759
-	 */
14760
-	public static function matchUp( $type, $sql, $bindings = array(), $onFoundDo = NULL, $onNotFoundDo = NULL, &$bean = NULL 	) {
14761
-		$matchUp = new MatchUp( self::$toolbox );
14762
-		return $matchUp->matchUp( $type, $sql, $bindings, $onFoundDo, $onNotFoundDo, $bean );
14763
-	}
14764
-
14765
-	/**
14766
-	 * @deprecated
14767
-	 *
14768
-	 * Returns an instance of the Look Helper class.
14769
-	 * The instance will be configured with the current toolbox.
14770
-	 *
14771
-	 * In previous versions of RedBeanPHP you had to use:
14772
-	 * R::getLook()->look() instead of R::look(). However to improve useability of the
14773
-	 * library the look() function can now directly be invoked from the facade.
14774
-	 *
14775
-	 * For more details regarding the Look functionality, please consult R::look().
14776
-	 * @see Facade::look
14777
-	 * @see Look::look
14778
-	 *
14779
-	 * @return Look
14780
-	 */
14781
-	public static function getLook()
14782
-	{
14783
-		return new Look( self::$toolbox );
14784
-	}
14785
-
14786
-	/**
14787
-	 * Takes an full SQL query with optional bindings, a series of keys, a template
14788
-	 * and optionally a filter function and glue and assembles a view from all this.
14789
-	 * This is the fastest way from SQL to view. Typically this function is used to
14790
-	 * generate pulldown (select tag) menus with options queried from the database.
14791
-	 *
14792
-	 * Usage:
14793
-	 *
14794
-	 * <code>
14795
-	 * $htmlPulldown = R::look(
14796
-	 *   'SELECT * FROM color WHERE value != ? ORDER BY value ASC',
14797
-	 *   [ 'g' ],
14798
-	 *   [ 'value', 'name' ],
14799
-	 *   '<option value="%s">%s</option>',
14800
-	 *   'strtoupper',
14801
-	 *   "\n"
14802
-	 * );
14803
-	 *</code>
14804
-	 *
14805
-	 * The example above creates an HTML fragment like this:
14806
-	 *
14807
-	 * <option value="B">BLUE</option>
14808
-	 * <option value="R">RED</option>
14809
-	 *
14810
-	 * to pick a color from a palette. The HTML fragment gets constructed by
14811
-	 * an SQL query that selects all colors that do not have value 'g' - this
14812
-	 * excludes green. Next, the bean properties 'value' and 'name' are mapped to the
14813
-	 * HTML template string, note that the order here is important. The mapping and
14814
-	 * the HTML template string follow vsprintf-rules. All property values are then
14815
-	 * passed through the specified filter function 'strtoupper' which in this case
14816
-	 * is a native PHP function to convert strings to uppercase characters only.
14817
-	 * Finally the resulting HTML fragment strings are glued together using a
14818
-	 * newline character specified in the last parameter for readability.
14819
-	 *
14820
-	 * In previous versions of RedBeanPHP you had to use:
14821
-	 * R::getLook()->look() instead of R::look(). However to improve useability of the
14822
-	 * library the look() function can now directly be invoked from the facade.
14823
-	 *
14824
-	 * @param string   $sql      query to execute
14825
-	 * @param array    $bindings parameters to bind to slots mentioned in query or an empty array
14826
-	 * @param array    $keys     names in result collection to map to template
14827
-	 * @param string   $template HTML template to fill with values associated with keys, use printf notation (i.e. %s)
14828
-	 * @param callable $filter   function to pass values through (for translation for instance)
14829
-	 * @param string   $glue     optional glue to use when joining resulting strings
14830
-	 *
14831
-	 * @return string
14832
-	 */
14833
-	public static function look( $sql, $bindings = array(), $keys = array( 'selected', 'id', 'name' ), $template = '<option %s value="%s">%s</option>', $filter = 'trim', $glue = '' )
14834
-	{
14835
-		return self::getLook()->look( $sql, $bindings, $keys, $template, $filter, $glue );
14836
-	}
14837
-
14838
-	/**
14839
-	 * Calculates a diff between two beans (or arrays of beans).
14840
-	 * The result of this method is an array describing the differences of the second bean compared to
14841
-	 * the first, where the first bean is taken as reference. The array is keyed by type/property, id and property name, where
14842
-	 * type/property is either the type (in case of the root bean) or the property of the parent bean where the type resides.
14843
-	 * The diffs are mainly intended for logging, you cannot apply these diffs as patches to other beans.
14844
-	 * However this functionality might be added in the future.
14845
-	 *
14846
-	 * The keys of the array can be formatted using the $format parameter.
14847
-	 * A key will be composed of a path (1st), id (2nd) and property (3rd).
14848
-	 * Using printf-style notation you can determine the exact format of the key.
14849
-	 * The default format will look like:
14850
-	 *
14851
-	 * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
14852
-	 *
14853
-	 * If you only want a simple diff of one bean and you don't care about ids,
14854
-	 * you might pass a format like: '%1$s.%3$s' which gives:
14855
-	 *
14856
-	 * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
14857
-	 *
14858
-	 * The filter parameter can be used to set filters, it should be an array
14859
-	 * of property names that have to be skipped. By default this array is filled with
14860
-	 * two strings: 'created' and 'modified'.
14861
-	 *
14862
-	 * @param OODBBean|array $bean    reference beans
14863
-	 * @param OODBBean|array $other   beans to compare
14864
-	 * @param array          $filters names of properties of all beans to skip
14865
-	 * @param string         $format  the format of the key, defaults to '%s.%s.%s'
14866
-	 * @param string         $type    type/property of bean to use for key generation
14867
-	 *
14868
-	 * @return array
14869
-	 */
14870
-	public static function diff( $bean, $other, $filters = array( 'created', 'modified' ), $pattern = '%s.%s.%s' )
14871
-	{
14872
-		$diff = new Diff( self::$toolbox );
14873
-		return $diff->diff( $bean, $other, $filters, $pattern );
14874
-	}
14875
-
14876
-	/**
14877
-	 * The gentleman's way to register a RedBeanPHP ToolBox instance
14878
-	 * with the facade. Stores the toolbox in the static toolbox
14879
-	 * registry of the facade class. This allows for a neat and
14880
-	 * explicit way to register a toolbox.
14881
-	 *
14882
-	 * @param string  $key     key to store toolbox instance under
14883
-	 * @param ToolBox $toolbox toolbox to register
14884
-	 *
14885
-	 * @return void
14886
-	 */
14887
-	public static function addToolBoxWithKey( $key, ToolBox $toolbox )
14888
-	{
14889
-		self::$toolboxes[$key] = $toolbox;
14890
-	}
14891
-
14892
-	/**
14893
-	 * The gentleman's way to remove a RedBeanPHP ToolBox instance
14894
-	 * from the facade. Removes the toolbox identified by
14895
-	 * the specified key in the static toolbox
14896
-	 * registry of the facade class. This allows for a neat and
14897
-	 * explicit way to remove a toolbox.
14898
-	 * Returns TRUE if the specified toolbox was found and removed.
14899
-	 * Returns FALSE otherwise.
14900
-	 *
14901
-	 * @param string  $key     identifier of the toolbox to remove
14902
-	 *
14903
-	 * @return boolean
14904
-	 */
14905
-	public static function removeToolBoxByKey( $key )
14906
-	{
14907
-		if ( !array_key_exists( $key, self::$toolboxes ) ) {
14908
-			return FALSE;
14909
-		}
14910
-		return TRUE;
14911
-	}
14912
-
14913
-	/**
14914
-	 * Returns the toolbox associated with the specified key.
14915
-	 *
14916
-	 * @param string  $key     key to store toolbox instance under
14917
-	 * @param ToolBox $toolbox toolbox to register
14918
-	 *
14919
-	 * @return ToolBox|NULL
14920
-	 */
14921
-	public static function getToolBoxByKey( $key )
14922
-	{
14923
-		if ( !array_key_exists( $key, self::$toolboxes ) ) {
14924
-			return NULL;
14925
-		}
14926
-		return self::$toolboxes[$key];
14927
-	}
14928
-
14929
-	/**
14930
-	 * Toggles JSON column features.
14931
-	 * Invoking this method with boolean TRUE causes 2 JSON features to be enabled.
14932
-	 * Beans will automatically JSONify any array that's not in a list property and
14933
-	 * the Query Writer (if capable) will attempt to create a JSON column for strings that
14934
-	 * appear to contain JSON.
14935
-	 *
14936
-	 * Feature #1:
14937
-	 * AQueryWriter::useJSONColumns
14938
-	 *
14939
-	 * Toggles support for automatic generation of JSON columns.
14940
-	 * Using JSON columns means that strings containing JSON will
14941
-	 * cause the column to be created (not modified) as a JSON column.
14942
-	 * However it might also trigger exceptions if this means the DB attempts to
14943
-	 * convert a non-json column to a JSON column.
14944
-	 *
14945
-	 * Feature #2:
14946
-	 * OODBBean::convertArraysToJSON
14947
-	 *
14948
-	 * Toggles array to JSON conversion. If set to TRUE any array
14949
-	 * set to a bean property that's not a list will be turned into
14950
-	 * a JSON string. Used together with AQueryWriter::useJSONColumns this
14951
-	 * extends the data type support for JSON columns.
14952
-	 *
14953
-	 * So invoking this method is the same as:
14954
-	 *
14955
-	 * AQueryWriter::useJSONColumns( $flag );
14956
-	 * OODBBean::convertArraysToJSON( $flag );
14957
-	 *
14958
-	 * Unlike the methods above, that return the previous state, this
14959
-	 * method does not return anything (void).
14960
-	 *
14961
-	 * @param boolean $flag feature flag (either TRUE or FALSE)
14962
-	 *
14963
-	 * @return void
14964
-	 */
14965
-	public static function useJSONFeatures( $flag )
14966
-	{
14967
-		AQueryWriter::useJSONColumns( $flag );
14968
-		OODBBean::convertArraysToJSON( $flag );
14969
-	}
14970
-
14971
-	/**
14972
-	 * @experimental
14973
-	 *
14974
-	 * Given a bean and an optional SQL snippet,
14975
-	 * this method will return all child beans in a hierarchically structured
14976
-	 * bean table.
14977
-	 *
14978
-	 * @note that not all database support this functionality. You'll need
14979
-	 * at least MariaDB 10.2.2 or Postgres. This method does not include
14980
-	 * a warning mechanism in case your database does not support this
14981
-	 * functionality.
14982
-	 *
14983
-	 * @note that this functionality is considered 'experimental'.
14984
-	 * It may still contain bugs.
14985
-	 *
14986
-	 * @param OODBBean $bean     bean to find children of
14987
-	 * @param string   $sql      optional SQL snippet
14988
-	 * @param array    $bindings SQL snippet parameter bindings
14989
-	 */
14990
-	public static function children( OODBBean $bean, $sql = NULL, $bindings = array() )
14991
-	{
14992
-		return self::$tree->children( $bean, $sql, $bindings );
14993
-	}
14994
-
14995
-	/**
14996
-	 * @experimental
14997
-	 *
14998
-	 * Given a bean and an optional SQL snippet,
14999
-	 * this method will return all parent beans in a hierarchically structured
15000
-	 * bean table.
15001
-	 *
15002
-	 * @note that not all database support this functionality. You'll need
15003
-	 * at least MariaDB 10.2.2 or Postgres. This method does not include
15004
-	 * a warning mechanism in case your database does not support this
15005
-	 * functionality.
15006
-	 *
15007
-	 * @note that this functionality is considered 'experimental'.
15008
-	 * It may still contain bugs.
15009
-	 *
15010
-	 * @param OODBBean $bean     bean to find parents of
15011
-	 * @param string   $sql      optional SQL snippet
15012
-	 * @param array    $bindings SQL snippet parameter bindings
15013
-	 */
15014
-	public static function parents( OODBBean $bean, $sql = NULL, $bindings = array() )
15015
-	{
15016
-		return self::$tree->parents( $bean, $sql, $bindings );
15017
-	}
15018
-
15019
-	/**
15020
-	 * Toggles support for nuke().
15021
-	 * Can be used to turn off the nuke() feature for security reasons.
15022
-	 * Returns the old flag value.
15023
-	 *
15024
-	 * @param boolean $flag TRUE or FALSE
15025
-	 *
15026
-	 * @return boolean
15027
-	 */
15028
-	public static function noNuke( $yesNo ) {
15029
-		return AQueryWriter::forbidNuke( $yesNo );
15030
-	}
15031
-
15032
-	/**
15033
-	 * Selects the feature set you want as specified by
15034
-	 * the label.
15035
-	 *
15036
-	 * Usage:
15037
-	 * R::useFeatureSet( 'novice/latest' );
15038
-	 *
15039
-	 * @param string $label label
15040
-	 *
15041
-	 * @return void
15042
-	 */
15043
-	public static function useFeatureSet( $label ) {
15044
-		return Feature::feature($label);
15045
-	}
15046
-
15047
-	/**
15048
-	 * Dynamically extends the facade with a plugin.
15049
-	 * Using this method you can register your plugin with the facade and then
15050
-	 * use the plugin by invoking the name specified plugin name as a method on
15051
-	 * the facade.
15052
-	 *
15053
-	 * Usage:
15054
-	 *
15055
-	 * <code>
15056
-	 * R::ext( 'makeTea', function() { ... }  );
15057
-	 * </code>
15058
-	 *
15059
-	 * Now you can use your makeTea plugin like this:
15060
-	 *
15061
-	 * <code>
15062
-	 * R::makeTea();
15063
-	 * </code>
15064
-	 *
15065
-	 * @param string   $pluginName name of the method to call the plugin
15066
-	 * @param callable $callable   a PHP callable
15067
-	 *
15068
-	 * @return void
15069
-	 */
15070
-	public static function ext( $pluginName, $callable )
15071
-	{
15072
-		if ( !ctype_alnum( $pluginName ) ) {
15073
-			throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
15074
-		}
15075
-		self::$plugins[$pluginName] = $callable;
15076
-	}
15077
-
15078
-	/**
15079
-	 * Call static for use with dynamic plugins. This magic method will
15080
-	 * intercept static calls and route them to the specified plugin.
15081
-	 *
15082
-	 * @param string $pluginName name of the plugin
15083
-	 * @param array  $params     list of arguments to pass to plugin method
15084
-	 *
15085
-	 * @return mixed
15086
-	 */
15087
-	public static function __callStatic( $pluginName, $params )
15088
-	{
15089
-		if ( !ctype_alnum( $pluginName) ) {
15090
-			throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
15091
-		}
15092
-		if ( !isset( self::$plugins[$pluginName] ) ) {
15093
-			throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' );
15094
-		}
15095
-		return call_user_func_array( self::$plugins[$pluginName], $params );
15096
-	}
12828
+     * </code>
12829
+     *
12830
+     * @param string|array $typeOrBeanArray   type or bean array to import
12831
+     * @param integer      $num               number of beans to dispense
12832
+     * @param boolean      $alwaysReturnArray if TRUE always returns the result as an array
12833
+     *
12834
+     * @return array|OODBBean
12835
+     */
12836
+    public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE )
12837
+    {
12838
+        return DispenseHelper::dispense( self::$redbean, $typeOrBeanArray, $num, $alwaysReturnArray );
12839
+    }
12840
+
12841
+    /**
12842
+     * Takes a comma separated list of bean types
12843
+     * and dispenses these beans. For each type in the list
12844
+     * you can specify the number of beans to be dispensed.
12845
+     *
12846
+     * Usage:
12847
+     *
12848
+     * <code>
12849
+     * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' );
12850
+     * </code>
12851
+     *
12852
+     * This will dispense a book, a page and a text. This way you can
12853
+     * quickly dispense beans of various types in just one line of code.
12854
+     *
12855
+     * Usage:
12856
+     *
12857
+     * <code>
12858
+     * list($book, $pages) = R::dispenseAll('book,page*100');
12859
+     * </code>
12860
+     *
12861
+     * This returns an array with a book bean and then another array
12862
+     * containing 100 page beans.
12863
+     *
12864
+     * @param string  $order      a description of the desired dispense order using the syntax above
12865
+     * @param boolean $onlyArrays return only arrays even if amount < 2
12866
+     *
12867
+     * @return array
12868
+     */
12869
+    public static function dispenseAll( $order, $onlyArrays = FALSE )
12870
+    {
12871
+        return DispenseHelper::dispenseAll( self::$redbean, $order, $onlyArrays );
12872
+    }
12873
+
12874
+    /**
12875
+     * Convience method. Tries to find beans of a certain type,
12876
+     * if no beans are found, it dispenses a bean of that type.
12877
+     * Note that this function always returns an array.
12878
+     *
12879
+     * @param  string $type     type of bean you are looking for
12880
+     * @param  string $sql      SQL code for finding the bean
12881
+     * @param  array  $bindings parameters to bind to SQL
12882
+     *
12883
+     * @return array
12884
+     */
12885
+    public static function findOrDispense( $type, $sql = NULL, $bindings = array() )
12886
+    {
12887
+        DispenseHelper::checkType( $type );
12888
+        return self::$finder->findOrDispense( $type, $sql, $bindings );
12889
+    }
12890
+
12891
+    /**
12892
+     * Same as findOrDispense but returns just one element.
12893
+     *
12894
+     * @param  string $type     type of bean you are looking for
12895
+     * @param  string $sql      SQL code for finding the bean
12896
+     * @param  array  $bindings parameters to bind to SQL
12897
+     *
12898
+     * @return OODBBean
12899
+     */
12900
+    public static function findOneOrDispense( $type, $sql = NULL, $bindings = array() )
12901
+    {
12902
+        DispenseHelper::checkType( $type );
12903
+        $arrayOfBeans = self::findOrDispense( $type, $sql, $bindings );
12904
+        return reset($arrayOfBeans);
12905
+    }
12906
+
12907
+    /**
12908
+     * Finds beans using a type and optional SQL statement.
12909
+     * As with most Query tools in RedBean you can provide values to
12910
+     * be inserted in the SQL statement by populating the value
12911
+     * array parameter; you can either use the question mark notation
12912
+     * or the slot-notation (:keyname).
12913
+     *
12914
+     * Your SQL does not have to start with a WHERE-clause condition.
12915
+     *
12916
+     * @param string $type     the type of bean you are looking for
12917
+     * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
12918
+     * @param array  $bindings array of values to be bound to parameters in query
12919
+     *
12920
+     * @return array
12921
+     */
12922
+    public static function find( $type, $sql = NULL, $bindings = array() )
12923
+    {
12924
+        return self::$finder->find( $type, $sql, $bindings );
12925
+    }
12926
+
12927
+    /**
12928
+     * Alias for find().
12929
+     *
12930
+     * @param string $type     the type of bean you are looking for
12931
+     * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
12932
+     * @param array  $bindings array of values to be bound to parameters in query
12933
+     *
12934
+     * @return array
12935
+     */
12936
+    public static function findAll( $type, $sql = NULL, $bindings = array() )
12937
+    {
12938
+        return self::$finder->find( $type, $sql, $bindings );
12939
+    }
12940
+
12941
+    /**
12942
+     * Like find() but also exports the beans as an array.
12943
+     * This method will perform a find-operation. For every bean
12944
+     * in the result collection this method will call the export() method.
12945
+     * This method returns an array containing the array representations
12946
+     * of every bean in the result set.
12947
+     *
12948
+     * @see Finder::find
12949
+     *
12950
+     * @param string $type     type   the type of bean you are looking for
12951
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
12952
+     * @param array  $bindings values array of values to be bound to parameters in query
12953
+     *
12954
+     * @return array
12955
+     */
12956
+    public static function findAndExport( $type, $sql = NULL, $bindings = array() )
12957
+    {
12958
+        return self::$finder->findAndExport( $type, $sql, $bindings );
12959
+    }
12960
+
12961
+    /**
12962
+     * Like R::find() but returns the first bean only.
12963
+     *
12964
+     * @param string $type     the type of bean you are looking for
12965
+     * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
12966
+     * @param array  $bindings array of values to be bound to parameters in query
12967
+     *
12968
+     * @return OODBBean|NULL
12969
+     */
12970
+    public static function findOne( $type, $sql = NULL, $bindings = array() )
12971
+    {
12972
+        return self::$finder->findOne( $type, $sql, $bindings );
12973
+    }
12974
+
12975
+    /**
12976
+     * @deprecated
12977
+     *
12978
+     * Like find() but returns the last bean of the result array.
12979
+     * Opposite of Finder::findLast().
12980
+     * If no beans are found, this method will return NULL.
12981
+     *
12982
+     * Please do not use this function, it is horribly ineffective.
12983
+     * Instead use a reversed ORDER BY clause and a LIMIT 1 with R::findOne().
12984
+     * This function should never be used and only remains for
12985
+     * the sake of backward compatibility.
12986
+     *
12987
+     * @see Finder::find
12988
+     *
12989
+     * @param string $type     the type of bean you are looking for
12990
+     * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
12991
+     * @param array  $bindings values array of values to be bound to parameters in query
12992
+     *
12993
+     * @return OODBBean|NULL
12994
+     */
12995
+    public static function findLast( $type, $sql = NULL, $bindings = array() )
12996
+    {
12997
+        return self::$finder->findLast( $type, $sql, $bindings );
12998
+    }
12999
+
13000
+    /**
13001
+     * Finds a BeanCollection using the repository.
13002
+     * A bean collection can be used to retrieve one bean at a time using
13003
+     * cursors - this is useful for processing large datasets. A bean collection
13004
+     * will not load all beans into memory all at once, just one at a time.
13005
+     *
13006
+     * @param  string $type     the type of bean you are looking for
13007
+     * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
13008
+     * @param  array  $bindings values array of values to be bound to parameters in query
13009
+     *
13010
+     * @return BeanCollection
13011
+     */
13012
+    public static function findCollection( $type, $sql = NULL, $bindings = array() )
13013
+    {
13014
+        return self::$finder->findCollection( $type, $sql, $bindings );
13015
+    }
13016
+
13017
+    /**
13018
+     * Returns a hashmap with bean arrays keyed by type using an SQL
13019
+     * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review'
13020
+     * this method will return movie and review beans.
13021
+     *
13022
+     * Example:
13023
+     *
13024
+     * <code>
13025
+     * $stuff = $finder->findMulti('movie,review', '
13026
+     *          SELECT movie.*, review.* FROM movie
13027
+     *          LEFT JOIN review ON review.movie_id = movie.id');
13028
+     * </code>
13029
+     *
13030
+     * After this operation, $stuff will contain an entry 'movie' containing all
13031
+     * movies and an entry named 'review' containing all reviews (all beans).
13032
+     * You can also pass bindings.
13033
+     *
13034
+     * If you want to re-map your beans, so you can use $movie->ownReviewList without
13035
+     * having RedBeanPHP executing an SQL query you can use the fourth parameter to
13036
+     * define a selection of remapping closures.
13037
+     *
13038
+     * The remapping argument (optional) should contain an array of arrays.
13039
+     * Each array in the remapping array should contain the following entries:
13040
+     *
13041
+     * <code>
13042
+     * array(
13043
+     * 	'a'       => TYPE A
13044
+     *    'b'       => TYPE B
13045
+     *    'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS
13046
+     *    'do'      => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS
13047
+     * )
13048
+     * </code>
13049
+     *
13050
+     * Using this mechanism you can build your own 'preloader' with tiny function
13051
+     * snippets (and those can be re-used and shared online of course).
13052
+     *
13053
+     * Example:
13054
+     *
13055
+     * <code>
13056
+     * array(
13057
+     * 	'a'       => 'movie'     //define A as movie
13058
+     *    'b'       => 'review'    //define B as review
13059
+     *    'matcher' => function( $a, $b ) {
13060
+     *       return ( $b->movie_id == $a->id );  //Perform action if review.movie_id equals movie.id
13061
+     *    }
13062
+     *    'do'      => function( $a, $b ) {
13063
+     *       $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie
13064
+     *       $a->clearHistory();                 //optional, act 'as if these beans have been loaded through ownReviewList'.
13065
+     *    }
13066
+     * )
13067
+     * </code>
13068
+     *
13069
+     * @note the SQL query provided IS NOT THE ONE used internally by this function,
13070
+     * this function will pre-process the query to get all the data required to find the beans.
13071
+     *
13072
+     * @note if you use the 'book.*' notation make SURE you're
13073
+     * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because
13074
+     * it's actually an SQL-like template SLOT, not real SQL.
13075
+     *
13076
+     * @note instead of an SQL query you can pass a result array as well.
13077
+     *
13078
+     * @param string|array $types         a list of types (either array or comma separated string)
13079
+     * @param string|array $sql           an SQL query or an array of prefetched records
13080
+     * @param array        $bindings      optional, bindings for SQL query
13081
+     * @param array        $remappings    optional, an array of remapping arrays
13082
+     *
13083
+     * @return array
13084
+     */
13085
+    public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() )
13086
+    {
13087
+        return self::$finder->findMulti( $types, $sql, $bindings, $remappings );
13088
+    }
13089
+
13090
+    /**
13091
+     * Returns an array of beans. Pass a type and a series of ids and
13092
+     * this method will bring you the corresponding beans.
13093
+     *
13094
+     * important note: Because this method loads beans using the load()
13095
+     * function (but faster) it will return empty beans with ID 0 for
13096
+     * every bean that could not be located. The resulting beans will have the
13097
+     * passed IDs as their keys.
13098
+     *
13099
+     * @param string $type type of beans
13100
+     * @param array  $ids  ids to load
13101
+     *
13102
+     * @return array
13103
+     */
13104
+    public static function batch( $type, $ids )
13105
+    {
13106
+        return self::$redbean->batch( $type, $ids );
13107
+    }
13108
+
13109
+    /**
13110
+     * Alias for batch(). Batch method is older but since we added so-called *All
13111
+     * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to
13112
+     * improve the consistency of the Facade API and also add an alias for batch() called
13113
+     * loadAll.
13114
+     *
13115
+     * @param string $type type of beans
13116
+     * @param array  $ids  ids to load
13117
+     *
13118
+     * @return array
13119
+     */
13120
+    public static function loadAll( $type, $ids )
13121
+    {
13122
+        return self::$redbean->batch( $type, $ids );
13123
+    }
13124
+
13125
+    /**
13126
+     * Convenience function to execute Queries directly.
13127
+     * Executes SQL.
13128
+     *
13129
+     * @param string $sql       SQL query to execute
13130
+     * @param array  $bindings  a list of values to be bound to query parameters
13131
+     *
13132
+     * @return integer
13133
+     */
13134
+    public static function exec( $sql, $bindings = array() )
13135
+    {
13136
+        return self::query( 'exec', $sql, $bindings );
13137
+    }
13138
+
13139
+    /**
13140
+     * Convenience function to fire an SQL query using the RedBeanPHP
13141
+     * database adapter. This method allows you to directly query the
13142
+     * database without having to obtain an database adapter instance first.
13143
+     * Executes the specified SQL query together with the specified
13144
+     * parameter bindings and returns all rows
13145
+     * and all columns.
13146
+     *
13147
+     * @param string $sql      SQL query to execute
13148
+     * @param array  $bindings a list of values to be bound to query parameters
13149
+     *
13150
+     * @return array
13151
+     */
13152
+    public static function getAll( $sql, $bindings = array() )
13153
+    {
13154
+        return self::query( 'get', $sql, $bindings );
13155
+    }
13156
+
13157
+    /**
13158
+     * Convenience function to fire an SQL query using the RedBeanPHP
13159
+     * database adapter. This method allows you to directly query the
13160
+     * database without having to obtain an database adapter instance first.
13161
+     * Executes the specified SQL query together with the specified
13162
+     * parameter bindings and returns a single cell.
13163
+     *
13164
+     * @param string $sql      SQL query to execute
13165
+     * @param array  $bindings a list of values to be bound to query parameters
13166
+     *
13167
+     * @return string
13168
+     */
13169
+    public static function getCell( $sql, $bindings = array() )
13170
+    {
13171
+        return self::query( 'getCell', $sql, $bindings );
13172
+    }
13173
+
13174
+    /**
13175
+     * Convenience function to fire an SQL query using the RedBeanPHP
13176
+     * database adapter. This method allows you to directly query the
13177
+     * database without having to obtain an database adapter instance first.
13178
+     * Executes the specified SQL query together with the specified
13179
+     * parameter bindings and returns a PDOCursor instance.
13180
+     *
13181
+     * @param string $sql      SQL query to execute
13182
+     * @param array  $bindings a list of values to be bound to query parameters
13183
+     *
13184
+     * @return RedBeanPHP\Cursor\PDOCursor
13185
+     */
13186
+    public static function getCursor( $sql, $bindings = array() )
13187
+    {
13188
+        return self::query( 'getCursor', $sql, $bindings );
13189
+    }
13190
+
13191
+    /**
13192
+     * Convenience function to fire an SQL query using the RedBeanPHP
13193
+     * database adapter. This method allows you to directly query the
13194
+     * database without having to obtain an database adapter instance first.
13195
+     * Executes the specified SQL query together with the specified
13196
+     * parameter bindings and returns a single row.
13197
+     *
13198
+     * @param string $sql      SQL query to execute
13199
+     * @param array  $bindings a list of values to be bound to query parameters
13200
+     *
13201
+     * @return array
13202
+     */
13203
+    public static function getRow( $sql, $bindings = array() )
13204
+    {
13205
+        return self::query( 'getRow', $sql, $bindings );
13206
+    }
13207
+
13208
+    /**
13209
+     * Convenience function to fire an SQL query using the RedBeanPHP
13210
+     * database adapter. This method allows you to directly query the
13211
+     * database without having to obtain an database adapter instance first.
13212
+     * Executes the specified SQL query together with the specified
13213
+     * parameter bindings and returns a single column.
13214
+     *
13215
+     * @param string $sql      SQL query to execute
13216
+     * @param array  $bindings a list of values to be bound to query parameters
13217
+     *
13218
+     * @return array
13219
+     */
13220
+    public static function getCol( $sql, $bindings = array() )
13221
+    {
13222
+        return self::query( 'getCol', $sql, $bindings );
13223
+    }
13224
+
13225
+    /**
13226
+     * Convenience function to execute Queries directly.
13227
+     * Executes SQL.
13228
+     * Results will be returned as an associative array. The first
13229
+     * column in the select clause will be used for the keys in this array and
13230
+     * the second column will be used for the values. If only one column is
13231
+     * selected in the query, both key and value of the array will have the
13232
+     * value of this field for each row.
13233
+     *
13234
+     * @param string $sql      SQL query to execute
13235
+     * @param array  $bindings a list of values to be bound to query parameters
13236
+     *
13237
+     * @return array
13238
+     */
13239
+    public static function getAssoc( $sql, $bindings = array() )
13240
+    {
13241
+        return self::query( 'getAssoc', $sql, $bindings );
13242
+    }
13243
+
13244
+    /**
13245
+     *Convenience function to fire an SQL query using the RedBeanPHP
13246
+     * database adapter. This method allows you to directly query the
13247
+     * database without having to obtain an database adapter instance first.
13248
+     * Executes the specified SQL query together with the specified
13249
+     * parameter bindings and returns an associative array.
13250
+     * Results will be returned as an associative array indexed by the first
13251
+     * column in the select.
13252
+     *
13253
+     * @param string $sql      SQL query to execute
13254
+     * @param array  $bindings a list of values to be bound to query parameters
13255
+     *
13256
+     * @return array
13257
+     */
13258
+    public static function getAssocRow( $sql, $bindings = array() )
13259
+    {
13260
+        return self::query( 'getAssocRow', $sql, $bindings );
13261
+    }
13262
+
13263
+    /**
13264
+     * Returns the insert ID for databases that support/require this
13265
+     * functionality. Alias for R::getAdapter()->getInsertID().
13266
+     *
13267
+     * @return mixed
13268
+     */
13269
+    public static function getInsertID()
13270
+    {
13271
+        return self::$adapter->getInsertID();
13272
+    }
13273
+
13274
+    /**
13275
+     * Makes a copy of a bean. This method makes a deep copy
13276
+     * of the bean.The copy will have the following features.
13277
+     * - All beans in own-lists will be duplicated as well
13278
+     * - All references to shared beans will be copied but not the shared beans themselves
13279
+     * - All references to parent objects (_id fields) will be copied but not the parents themselves
13280
+     * In most cases this is the desired scenario for copying beans.
13281
+     * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
13282
+     * (i.e. one that already has been processed) the ID of the bean will be returned.
13283
+     * This should not happen though.
13284
+     *
13285
+     * Note:
13286
+     * This function does a reflectional database query so it may be slow.
13287
+     *
13288
+     * @deprecated
13289
+     * This function is deprecated in favour of R::duplicate().
13290
+     * This function has a confusing method signature, the R::duplicate() function
13291
+     * only accepts two arguments: bean and filters.
13292
+     *
13293
+     * @param OODBBean $bean    bean to be copied
13294
+     * @param array    $trail   for internal usage, pass array()
13295
+     * @param boolean  $pid     for internal usage
13296
+     * @param array    $filters white list filter with bean types to duplicate
13297
+     *
13298
+     * @return array
13299
+     */
13300
+    public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() )
13301
+    {
13302
+        self::$duplicationManager->setFilters( $filters );
13303
+        return self::$duplicationManager->dup( $bean, $trail, $pid );
13304
+    }
13305
+
13306
+    /**
13307
+     * Makes a deep copy of a bean. This method makes a deep copy
13308
+     * of the bean.The copy will have the following:
13309
+     *
13310
+     * * All beans in own-lists will be duplicated as well
13311
+     * * All references to shared beans will be copied but not the shared beans themselves
13312
+     * * All references to parent objects (_id fields) will be copied but not the parents themselves
13313
+     *
13314
+     * In most cases this is the desired scenario for copying beans.
13315
+     * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
13316
+     * (i.e. one that already has been processed) the ID of the bean will be returned.
13317
+     * This should not happen though.
13318
+     *
13319
+     * Note:
13320
+     * This function does a reflectional database query so it may be slow.
13321
+     *
13322
+     * Note:
13323
+     * This is a simplified version of the deprecated R::dup() function.
13324
+     *
13325
+     * @param OODBBean $bean  bean to be copied
13326
+     * @param array    $white white list filter with bean types to duplicate
13327
+     *
13328
+     * @return array
13329
+     */
13330
+    public static function duplicate( $bean, $filters = array() )
13331
+    {
13332
+        return self::dup( $bean, array(), FALSE, $filters );
13333
+    }
13334
+
13335
+    /**
13336
+     * Exports a collection of beans. Handy for XML/JSON exports with a
13337
+     * Javascript framework like Dojo or ExtJS.
13338
+     * What will be exported:
13339
+     *
13340
+     * * contents of the bean
13341
+     * * all own bean lists (recursively)
13342
+     * * all shared beans (not THEIR own lists)
13343
+     *
13344
+     * @param    array|OODBBean $beans   beans to be exported
13345
+     * @param    boolean        $parents whether you want parent beans to be exported
13346
+     * @param    array          $filters whitelist of types
13347
+     *
13348
+     * @return array
13349
+     */
13350
+    public static function exportAll( $beans, $parents = FALSE, $filters = array())
13351
+    {
13352
+        return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle );
13353
+    }
13354
+
13355
+    /**
13356
+     * Selects case style for export.
13357
+     * This will determine the case style for the keys of exported beans (see exportAll).
13358
+     * The following options are accepted:
13359
+     *
13360
+     * * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid )
13361
+     * * 'camel'   Camel Case   (i.e. bookId isValid   )
13362
+     * * 'dolphin' Dolphin Case (i.e. bookID isValid   ) Like CamelCase but ID is written all uppercase
13363
+     *
13364
+     * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different
13365
+     * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id.
13366
+     * Due to information loss this cannot be corrected. However if you might try
13367
+     * DolphinCase for IDs it takes into account the exception concerning IDs.
13368
+     *
13369
+     * @param string $caseStyle case style identifier
13370
+     *
13371
+     * @return void
13372
+     */
13373
+    public static function useExportCase( $caseStyle = 'default' )
13374
+    {
13375
+        if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' );
13376
+        self::$exportCaseStyle = $caseStyle;
13377
+    }
13378
+
13379
+    /**
13380
+     * Converts a series of rows to beans.
13381
+     * This method converts a series of rows to beans.
13382
+     * The type of the desired output beans can be specified in the
13383
+     * first parameter. The second parameter is meant for the database
13384
+     * result rows.
13385
+     *
13386
+     * Usage:
13387
+     *
13388
+     * <code>
13389
+     * $rows = R::getAll( 'SELECT * FROM ...' )
13390
+     * $beans = R::convertToBeans( $rows );
13391
+     * </code>
13392
+     *
13393
+     * As of version 4.3.2 you can specify a meta-mask.
13394
+     * Data from columns with names starting with the value specified in the mask
13395
+     * will be transferred to the meta section of a bean (under data.bundle).
13396
+     *
13397
+     * <code>
13398
+     * $rows = R::getAll( 'SELECT FROM... COUNT(*) AS extra_count ...' );
13399
+     * $beans = R::convertToBeans( $rows );
13400
+     * $bean = reset( $beans );
13401
+     * $data = $bean->getMeta( 'data.bundle' );
13402
+     * $extra_count = $data['extra_count'];
13403
+     * </code>
13404
+     *
13405
+     * New in 4.3.2: meta mask. The meta mask is a special mask to send
13406
+     * data from raw result rows to the meta store of the bean. This is
13407
+     * useful for bundling additional information with custom queries.
13408
+     * Values of every column whos name starts with $mask will be
13409
+     * transferred to the meta section of the bean under key 'data.bundle'.
13410
+     *
13411
+     * @param string $type     type of beans to produce
13412
+     * @param array  $rows     must contain an array of array
13413
+     * @param string $metamask meta mask to apply (optional)
13414
+     *
13415
+     * @return array
13416
+     */
13417
+    public static function convertToBeans( $type, $rows, $metamask = NULL )
13418
+    {
13419
+        return self::$redbean->convertToBeans( $type, $rows, $metamask );
13420
+    }
13421
+
13422
+    /**
13423
+     * Just like converToBeans, but for one bean.
13424
+     *
13425
+     * @param string $type      type of bean to produce
13426
+     * @param array  $row       one row from the database
13427
+     * @param string $metamask  metamask (see convertToBeans)
13428
+     *
13429
+     * @return OODBBean
13430
+     */
13431
+    public static function convertToBean( $type, $row, $metamask = NULL )
13432
+    {
13433
+        $beans = self::$redbean->convertToBeans( $type, array( $row ), $metamask );
13434
+        $bean  = reset( $beans );
13435
+        return $bean;
13436
+    }
13437
+
13438
+    /**
13439
+     * Tests whether a bean has been associated with one ore more
13440
+     * of the listed tags. If the third parameter is TRUE this method
13441
+     * will return TRUE only if all tags that have been specified are indeed
13442
+     * associated with the given bean, otherwise FALSE.
13443
+     * If the third parameter is FALSE this
13444
+     * method will return TRUE if one of the tags matches, FALSE if none
13445
+     * match.
13446
+     *
13447
+     * Tag list can be either an array with tag names or a comma separated list
13448
+     * of tag names.
13449
+     *
13450
+     * Usage:
13451
+     *
13452
+     * <code>
13453
+     * R::hasTag( $blog, 'horror,movie', TRUE );
13454
+     * </code>
13455
+     *
13456
+     * The example above returns TRUE if the $blog bean has been tagged
13457
+     * as BOTH horror and movie. If the post has only been tagged as 'movie'
13458
+     * or 'horror' this operation will return FALSE because the third parameter
13459
+     * has been set to TRUE.
13460
+     *
13461
+     * @param  OODBBean     $bean bean to check for tags
13462
+     * @param  array|string $tags list of tags
13463
+     * @param  boolean      $all  whether they must all match or just some
13464
+     *
13465
+     * @return boolean
13466
+     */
13467
+    public static function hasTag( $bean, $tags, $all = FALSE )
13468
+    {
13469
+        return self::$tagManager->hasTag( $bean, $tags, $all );
13470
+    }
13471
+
13472
+    /**
13473
+     * Removes all specified tags from the bean. The tags specified in
13474
+     * the second parameter will no longer be associated with the bean.
13475
+     *
13476
+     * Tag list can be either an array with tag names or a comma separated list
13477
+     * of tag names.
13478
+     *
13479
+     * Usage:
13480
+     *
13481
+     * <code>
13482
+     * R::untag( $blog, 'smart,interesting' );
13483
+     * </code>
13484
+     *
13485
+     * In the example above, the $blog bean will no longer
13486
+     * be associated with the tags 'smart' and 'interesting'.
13487
+     *
13488
+     * @param  OODBBean $bean    tagged bean
13489
+     * @param  array    $tagList list of tags (names)
13490
+     *
13491
+     * @return void
13492
+     */
13493
+    public static function untag( $bean, $tagList )
13494
+    {
13495
+        self::$tagManager->untag( $bean, $tagList );
13496
+    }
13497
+
13498
+    /**
13499
+     * Tags a bean or returns tags associated with a bean.
13500
+     * If $tagList is NULL or omitted this method will return a
13501
+     * comma separated list of tags associated with the bean provided.
13502
+     * If $tagList is a comma separated list (string) of tags all tags will
13503
+     * be associated with the bean.
13504
+     * You may also pass an array instead of a string.
13505
+     *
13506
+     * Usage:
13507
+     *
13508
+     * <code>
13509
+     * R::tag( $meal, "TexMex,Mexican" );
13510
+     * $tags = R::tag( $meal );
13511
+     * </code>
13512
+     *
13513
+     * The first line in the example above will tag the $meal
13514
+     * as 'TexMex' and 'Mexican Cuisine'. The second line will
13515
+     * retrieve all tags attached to the meal object.
13516
+     *
13517
+     * @param OODBBean $bean    bean to tag
13518
+     * @param mixed    $tagList tags to attach to the specified bean
13519
+     *
13520
+     * @return string
13521
+     */
13522
+    public static function tag( OODBBean $bean, $tagList = NULL )
13523
+    {
13524
+        return self::$tagManager->tag( $bean, $tagList );
13525
+    }
13526
+
13527
+    /**
13528
+     * Adds tags to a bean.
13529
+     * If $tagList is a comma separated list of tags all tags will
13530
+     * be associated with the bean.
13531
+     * You may also pass an array instead of a string.
13532
+     *
13533
+     * Usage:
13534
+     *
13535
+     * <code>
13536
+     * R::addTags( $blog, ["halloween"] );
13537
+     * </code>
13538
+     *
13539
+     * The example adds the tag 'halloween' to the $blog
13540
+     * bean.
13541
+     *
13542
+     * @param OODBBean $bean    bean to tag
13543
+     * @param array    $tagList list of tags to add to bean
13544
+     *
13545
+     * @return void
13546
+     */
13547
+    public static function addTags( OODBBean $bean, $tagList )
13548
+    {
13549
+        self::$tagManager->addTags( $bean, $tagList );
13550
+    }
13551
+
13552
+    /**
13553
+     * Returns all beans that have been tagged with one or more
13554
+     * of the specified tags.
13555
+     *
13556
+     * Tag list can be either an array with tag names or a comma separated list
13557
+     * of tag names.
13558
+     *
13559
+     * Usage:
13560
+     *
13561
+     * <code>
13562
+     * $watchList = R::tagged(
13563
+     *   'movie',
13564
+     *   'horror,gothic',
13565
+     *   ' ORDER BY movie.title DESC LIMIT ?',
13566
+     *   [ 10 ]
13567
+     * );
13568
+     * </code>
13569
+     *
13570
+     * The example uses R::tagged() to find all movies that have been
13571
+     * tagged as 'horror' or 'gothic', order them by title and limit
13572
+     * the number of movies to be returned to 10.
13573
+     *
13574
+     * @param string       $beanType type of bean you are looking for
13575
+     * @param array|string $tagList  list of tags to match
13576
+     * @param string       $sql      additional SQL (use only for pagination)
13577
+     * @param array        $bindings bindings
13578
+     *
13579
+     * @return array
13580
+     */
13581
+    public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
13582
+    {
13583
+        return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings );
13584
+    }
13585
+
13586
+    /**
13587
+     * Returns all beans that have been tagged with ALL of the tags given.
13588
+     * This method works the same as R::tagged() except that this method only returns
13589
+     * beans that have been tagged with all the specified labels.
13590
+     *
13591
+     * Tag list can be either an array with tag names or a comma separated list
13592
+     * of tag names.
13593
+     *
13594
+     * Usage:
13595
+     *
13596
+     * <code>
13597
+     * $watchList = R::taggedAll(
13598
+     *    'movie',
13599
+     *    [ 'gothic', 'short' ],
13600
+     *    ' ORDER BY movie.id DESC LIMIT ? ',
13601
+     *    [ 4 ]
13602
+     * );
13603
+     * </code>
13604
+     *
13605
+     * The example above returns at most 4 movies (due to the LIMIT clause in the SQL
13606
+     * Query Snippet) that have been tagged as BOTH 'short' AND 'gothic'.
13607
+     *
13608
+     * @param string       $beanType type of bean you are looking for
13609
+     * @param array|string $tagList  list of tags to match
13610
+     * @param string       $sql      additional sql snippet
13611
+     * @param array        $bindings bindings
13612
+     *
13613
+     * @return array
13614
+     */
13615
+    public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
13616
+    {
13617
+        return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings );
13618
+    }
13619
+
13620
+    /**
13621
+     * Same as taggedAll() but counts beans only (does not return beans).
13622
+     *
13623
+     * @see R::taggedAll
13624
+     *
13625
+     * @param string       $beanType type of bean you are looking for
13626
+     * @param array|string $tagList  list of tags to match
13627
+     * @param string       $sql      additional sql snippet
13628
+     * @param array        $bindings bindings
13629
+     *
13630
+     * @return integer
13631
+     */
13632
+    public static function countTaggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
13633
+    {
13634
+        return self::$tagManager->countTaggedAll( $beanType, $tagList, $sql, $bindings );
13635
+    }
13636
+
13637
+    /**
13638
+     * Same as tagged() but counts beans only (does not return beans).
13639
+     *
13640
+     * @see R::tagged
13641
+     *
13642
+     * @param string       $beanType type of bean you are looking for
13643
+     * @param array|string $tagList  list of tags to match
13644
+     * @param string       $sql      additional sql snippet
13645
+     * @param array        $bindings bindings
13646
+     *
13647
+     * @return integer
13648
+     */
13649
+    public static function countTagged( $beanType, $tagList, $sql = '', $bindings = array() )
13650
+    {
13651
+        return self::$tagManager->countTagged( $beanType, $tagList, $sql, $bindings );
13652
+    }
13653
+
13654
+    /**
13655
+     * Wipes all beans of type $beanType.
13656
+     *
13657
+     * @param string $beanType type of bean you want to destroy entirely
13658
+     *
13659
+     * @return boolean
13660
+     */
13661
+    public static function wipe( $beanType )
13662
+    {
13663
+        return Facade::$redbean->wipe( $beanType );
13664
+    }
13665
+
13666
+    /**
13667
+     * Counts the number of beans of type $type.
13668
+     * This method accepts a second argument to modify the count-query.
13669
+     * A third argument can be used to provide bindings for the SQL snippet.
13670
+     *
13671
+     * @param string $type     type of bean we are looking for
13672
+     * @param string $addSQL   additional SQL snippet
13673
+     * @param array  $bindings parameters to bind to SQL
13674
+     *
13675
+     * @return integer
13676
+     */
13677
+    public static function count( $type, $addSQL = '', $bindings = array() )
13678
+    {
13679
+        return Facade::$redbean->count( $type, $addSQL, $bindings );
13680
+    }
13681
+
13682
+    /**
13683
+     * Configures the facade, want to have a new Writer? A new Object Database or a new
13684
+     * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new
13685
+     * toolbox.
13686
+     *
13687
+     * @param ToolBox $tb toolbox to configure facade with
13688
+     *
13689
+     * @return ToolBox
13690
+     */
13691
+    public static function configureFacadeWithToolbox( ToolBox $tb )
13692
+    {
13693
+        $oldTools                 = self::$toolbox;
13694
+        self::$toolbox            = $tb;
13695
+        self::$writer             = self::$toolbox->getWriter();
13696
+        self::$adapter            = self::$toolbox->getDatabaseAdapter();
13697
+        self::$redbean            = self::$toolbox->getRedBean();
13698
+        self::$finder             = new Finder( self::$toolbox );
13699
+        self::$associationManager = new AssociationManager( self::$toolbox );
13700
+        self::$tree               = new Tree( self::$toolbox );
13701
+        self::$redbean->setAssociationManager( self::$associationManager );
13702
+        self::$labelMaker         = new LabelMaker( self::$toolbox );
13703
+        $helper                   = new SimpleModelHelper();
13704
+        $helper->attachEventListeners( self::$redbean );
13705
+        if (self::$redbean->getBeanHelper() == NULL) {
13706
+            self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper );
13707
+        }
13708
+        self::$duplicationManager = new DuplicationManager( self::$toolbox );
13709
+        self::$tagManager         = new TagManager( self::$toolbox );
13710
+        return $oldTools;
13711
+    }
13712
+
13713
+    /**
13714
+     * Facade Convience method for adapter transaction system.
13715
+     * Begins a transaction.
13716
+     *
13717
+     * Usage:
13718
+     *
13719
+     * <code>
13720
+     * R::begin();
13721
+     * try {
13722
+     *  $bean1 = R::dispense( 'bean' );
13723
+     *  R::store( $bean1 );
13724
+     *  $bean2 = R::dispense( 'bean' );
13725
+     *  R::store( $bean2 );
13726
+     *  R::commit();
13727
+     * } catch( \Exception $e ) {
13728
+     *  R::rollback();
13729
+     * }
13730
+     * </code>
13731
+     *
13732
+     * The example above illustrates how transactions in RedBeanPHP are used.
13733
+     * In this example 2 beans are stored or nothing is stored at all.
13734
+     * It's not possible for this piece of code to store only half of the beans.
13735
+     * If an exception occurs, the transaction gets rolled back and the database
13736
+     * will be left 'untouched'.
13737
+     *
13738
+     * In fluid mode transactions will be ignored and all queries will
13739
+     * be executed as-is because database schema changes will automatically
13740
+     * trigger the transaction system to commit everything in some database
13741
+     * systems. If you use a database that can handle DDL changes you might wish
13742
+     * to use setAllowFluidTransactions(TRUE). If you do this, the behavior of
13743
+     * this function in fluid mode will depend on the database platform used.
13744
+     *
13745
+     * @return bool
13746
+     */
13747
+    public static function begin()
13748
+    {
13749
+        if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
13750
+        self::$adapter->startTransaction();
13751
+        return TRUE;
13752
+    }
13753
+
13754
+    /**
13755
+     * Facade Convience method for adapter transaction system.
13756
+     * Commits a transaction.
13757
+     *
13758
+     * Usage:
13759
+     *
13760
+     * <code>
13761
+     * R::begin();
13762
+     * try {
13763
+     *  $bean1 = R::dispense( 'bean' );
13764
+     *  R::store( $bean1 );
13765
+     *  $bean2 = R::dispense( 'bean' );
13766
+     *  R::store( $bean2 );
13767
+     *  R::commit();
13768
+     * } catch( \Exception $e ) {
13769
+     *  R::rollback();
13770
+     * }
13771
+     * </code>
13772
+     *
13773
+     * The example above illustrates how transactions in RedBeanPHP are used.
13774
+     * In this example 2 beans are stored or nothing is stored at all.
13775
+     * It's not possible for this piece of code to store only half of the beans.
13776
+     * If an exception occurs, the transaction gets rolled back and the database
13777
+     * will be left 'untouched'.
13778
+     *
13779
+     * In fluid mode transactions will be ignored and all queries will
13780
+     * be executed as-is because database schema changes will automatically
13781
+     * trigger the transaction system to commit everything in some database
13782
+     * systems. If you use a database that can handle DDL changes you might wish
13783
+     * to use setAllowFluidTransactions(TRUE). If you do this, the behavior of
13784
+     * this function in fluid mode will depend on the database platform used.
13785
+     *
13786
+     * @return bool
13787
+     */
13788
+    public static function commit()
13789
+    {
13790
+        if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
13791
+        self::$adapter->commit();
13792
+        return TRUE;
13793
+    }
13794
+
13795
+    /**
13796
+     * Facade Convience method for adapter transaction system.
13797
+     * Rolls back a transaction.
13798
+     *
13799
+     * Usage:
13800
+     *
13801
+     * <code>
13802
+     * R::begin();
13803
+     * try {
13804
+     *  $bean1 = R::dispense( 'bean' );
13805
+     *  R::store( $bean1 );
13806
+     *  $bean2 = R::dispense( 'bean' );
13807
+     *  R::store( $bean2 );
13808
+     *  R::commit();
13809
+     * } catch( \Exception $e ) {
13810
+     *  R::rollback();
13811
+     * }
13812
+     * </code>
13813
+     *
13814
+     * The example above illustrates how transactions in RedBeanPHP are used.
13815
+     * In this example 2 beans are stored or nothing is stored at all.
13816
+     * It's not possible for this piece of code to store only half of the beans.
13817
+     * If an exception occurs, the transaction gets rolled back and the database
13818
+     * will be left 'untouched'.
13819
+     *
13820
+     * In fluid mode transactions will be ignored and all queries will
13821
+     * be executed as-is because database schema changes will automatically
13822
+     * trigger the transaction system to commit everything in some database
13823
+     * systems. If you use a database that can handle DDL changes you might wish
13824
+     * to use setAllowFluidTransactions(TRUE). If you do this, the behavior of
13825
+     * this function in fluid mode will depend on the database platform used.
13826
+     *
13827
+     * @return bool
13828
+     */
13829
+    public static function rollback()
13830
+    {
13831
+        if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
13832
+        self::$adapter->rollback();
13833
+        return TRUE;
13834
+    }
13835
+
13836
+    /**
13837
+     * Returns a list of columns. Format of this array:
13838
+     * array( fieldname => type )
13839
+     * Note that this method only works in fluid mode because it might be
13840
+     * quite heavy on production servers!
13841
+     *
13842
+     * @param  string $table name of the table (not type) you want to get columns of
13843
+     *
13844
+     * @return array
13845
+     */
13846
+    public static function getColumns( $table )
13847
+    {
13848
+        return self::$writer->getColumns( $table );
13849
+    }
13850
+
13851
+    /**
13852
+     * Generates question mark slots for an array of values.
13853
+     * Given an array and an optional template string this method
13854
+     * will produce string containing parameter slots for use in
13855
+     * an SQL query string.
13856
+     *
13857
+     * Usage:
13858
+     *
13859
+     * <code>
13860
+     * R::genSlots( array( 'a', 'b' ) );
13861
+     * </code>
13862
+     *
13863
+     * The statement in the example will produce the string:
13864
+     * '?,?'.
13865
+     *
13866
+     * Another example, using a template string:
13867
+     *
13868
+     * <code>
13869
+     * R::genSlots( array('a', 'b'), ' IN( %s ) ' );
13870
+     * </code>
13871
+     *
13872
+     * The statement in the example will produce the string:
13873
+     * ' IN( ?,? ) '.
13874
+     *
13875
+     * @param array  $array    array to generate question mark slots for
13876
+     * @param string $template template to use
13877
+     *
13878
+     * @return string
13879
+     */
13880
+    public static function genSlots( $array, $template = NULL )
13881
+    {
13882
+        return ArrayTool::genSlots( $array, $template );
13883
+    }
13884
+
13885
+    /**
13886
+     * Flattens a multi dimensional bindings array for use with genSlots().
13887
+     *
13888
+     * Usage:
13889
+     *
13890
+     * <code>
13891
+     * R::flat( array( 'a', array( 'b' ), 'c' ) );
13892
+     * </code>
13893
+     *
13894
+     * produces an array like: [ 'a', 'b', 'c' ]
13895
+     *
13896
+     * @param array $array  array to flatten
13897
+     * @param array $result result array parameter (for recursion)
13898
+     *
13899
+     * @return array
13900
+     */
13901
+    public static function flat( $array, $result = array() )
13902
+    {
13903
+        return ArrayTool::flat( $array, $result );
13904
+    }
13905
+
13906
+    /**
13907
+     * Nukes the entire database.
13908
+     * This will remove all schema structures from the database.
13909
+     * Only works in fluid mode. Be careful with this method.
13910
+     *
13911
+     * @warning dangerous method, will remove all tables, columns etc.
13912
+     *
13913
+     * @return void
13914
+     */
13915
+    public static function nuke()
13916
+    {
13917
+        if ( !self::$redbean->isFrozen() ) {
13918
+            self::$writer->wipeAll();
13919
+        }
13920
+    }
13921
+
13922
+    /**
13923
+     * Short hand function to store a set of beans at once, IDs will be
13924
+     * returned as an array. For information please consult the R::store()
13925
+     * function.
13926
+     * A loop saver.
13927
+     *
13928
+     * @param array $beans list of beans to be stored
13929
+     *
13930
+     * @return array
13931
+     */
13932
+    public static function storeAll( $beans )
13933
+    {
13934
+        $ids = array();
13935
+        foreach ( $beans as $bean ) {
13936
+            $ids[] = self::store( $bean );
13937
+        }
13938
+        return $ids;
13939
+    }
13940
+
13941
+    /**
13942
+     * Short hand function to trash a set of beans at once.
13943
+     * For information please consult the R::trash() function.
13944
+     * A loop saver.
13945
+     *
13946
+     * @param array $beans list of beans to be trashed
13947
+     *
13948
+     * @return void
13949
+     */
13950
+    public static function trashAll( $beans )
13951
+    {
13952
+        foreach ( $beans as $bean ) {
13953
+            self::trash( $bean );
13954
+        }
13955
+    }
13956
+
13957
+    /**
13958
+     * Short hand function to trash a series of beans using
13959
+     * only IDs. This function combines trashAll and batch loading
13960
+     * in one call. Note that while this function accepts just
13961
+     * bean IDs, the beans will still be loaded first. This is because
13962
+     * the function still respects all the FUSE hooks that may have beeb
13963
+     * associated with the domain logic associated with these beans.
13964
+     * If you really want to delete just records from the database use
13965
+     * a simple DELETE-FROM SQL query instead.
13966
+     *
13967
+     * @param string type  $type the bean type you wish to trash
13968
+     * @param string array $ids  list of bean IDs
13969
+     *
13970
+     * @return void
13971
+     */
13972
+    public static function trashBatch( $type, $ids )
13973
+    {
13974
+        self::trashAll( self::batch( $type, $ids ) );
13975
+    }
13976
+
13977
+    /**
13978
+     * Short hand function to find and trash beans.
13979
+     * This function combines trashAll and find.
13980
+     * Given a bean type, a query snippet and optionally some parameter
13981
+     * bindings, this function will search for the beans described in the
13982
+     * query and its parameters and then feed them to the trashAll function
13983
+     * to be trashed.
13984
+     *
13985
+     * Note that while this function accepts just
13986
+     * a bean type and query snippet, the beans will still be loaded first. This is because
13987
+     * the function still respects all the FUSE hooks that may have been
13988
+     * associated with the domain logic associated with these beans.
13989
+     * If you really want to delete just records from the database use
13990
+     * a simple DELETE-FROM SQL query instead.
13991
+     *
13992
+     * Returns the number of beans deleted.
13993
+     *
13994
+     * @param string $type       bean type to look for in database
13995
+     * @param string $sqlSnippet an SQL query snippet
13996
+     * @param array  $bindings   SQL parameter bindings
13997
+     *
13998
+     * @return int
13999
+     */
14000
+    public static function hunt( $type, $sqlSnippet = NULL, $bindings = array() )
14001
+    {
14002
+        $numberOfTrashedBeans = 0;
14003
+        $beans = self::findCollection( $type, $sqlSnippet, $bindings );
14004
+        while( $bean = $beans->next() ) {
14005
+            self::trash( $bean );
14006
+            $numberOfTrashedBeans++;
14007
+        }
14008
+        return $numberOfTrashedBeans;
14009
+    }
14010
+
14011
+    /**
14012
+     * Toggles Writer Cache.
14013
+     * Turns the Writer Cache on or off. The Writer Cache is a simple
14014
+     * query based caching system that may improve performance without the need
14015
+     * for cache management. This caching system will cache non-modifying queries
14016
+     * that are marked with special SQL comments. As soon as a non-marked query
14017
+     * gets executed the cache will be flushed. Only non-modifying select queries
14018
+     * have been marked therefore this mechanism is a rather safe way of caching, requiring
14019
+     * no explicit flushes or reloads. Of course this does not apply if you intend to test
14020
+     * or simulate concurrent querying.
14021
+     *
14022
+     * @param boolean $yesNo TRUE to enable cache, FALSE to disable cache
14023
+     *
14024
+     * @return void
14025
+     */
14026
+    public static function useWriterCache( $yesNo )
14027
+    {
14028
+        self::getWriter()->setUseCache( $yesNo );
14029
+    }
14030
+
14031
+    /**
14032
+     * A label is a bean with only an id, type and name property.
14033
+     * This function will dispense beans for all entries in the array. The
14034
+     * values of the array will be assigned to the name property of each
14035
+     * individual bean.
14036
+     *
14037
+     * @param string $type   type of beans you would like to have
14038
+     * @param array  $labels list of labels, names for each bean
14039
+     *
14040
+     * @return array
14041
+     */
14042
+    public static function dispenseLabels( $type, $labels )
14043
+    {
14044
+        return self::$labelMaker->dispenseLabels( $type, $labels );
14045
+    }
14046
+
14047
+    /**
14048
+     * Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs.
14049
+     * Either returns a (newly created) bean respresenting the desired ENUM
14050
+     * value or returns a list of all enums for the type.
14051
+     *
14052
+     * To obtain (and add if necessary) an ENUM value:
14053
+     *
14054
+     * <code>
14055
+     * $tea->flavour = R::enum( 'flavour:apple' );
14056
+     * </code>
14057
+     *
14058
+     * Returns a bean of type 'flavour' with  name = apple.
14059
+     * This will add a bean with property name (set to APPLE) to the database
14060
+     * if it does not exist yet.
14061
+     *
14062
+     * To obtain all flavours:
14063
+     *
14064
+     * <code>
14065
+     * R::enum('flavour');
14066
+     * </code>
14067
+     *
14068
+     * To get a list of all flavour names:
14069
+     *
14070
+     * <code>
14071
+     * R::gatherLabels( R::enum( 'flavour' ) );
14072
+     * </code>
14073
+     *
14074
+     * @param string $enum either type or type-value
14075
+     *
14076
+     * @return array|OODBBean
14077
+     */
14078
+    public static function enum( $enum )
14079
+    {
14080
+        return self::$labelMaker->enum( $enum );
14081
+    }
14082
+
14083
+    /**
14084
+     * Gathers labels from beans. This function loops through the beans,
14085
+     * collects the values of the name properties of each individual bean
14086
+     * and stores the names in a new array. The array then gets sorted using the
14087
+     * default sort function of PHP (sort).
14088
+     *
14089
+     * @param array $beans list of beans to loop
14090
+     *
14091
+     * @return array
14092
+     */
14093
+    public static function gatherLabels( $beans )
14094
+    {
14095
+        return self::$labelMaker->gatherLabels( $beans );
14096
+    }
14097
+
14098
+    /**
14099
+     * Closes the database connection.
14100
+     * While database connections are closed automatically at the end of the PHP script,
14101
+     * closing database connections is generally recommended to improve performance.
14102
+     * Closing a database connection will immediately return the resources to PHP.
14103
+     *
14104
+     * Usage:
14105
+     *
14106
+     * <code>
14107
+     * R::setup( ... );
14108
+     * ... do stuff ...
14109
+     * R::close();
14110
+     * </code>
14111
+     *
14112
+     * @return void
14113
+     */
14114
+    public static function close()
14115
+    {
14116
+        if ( isset( self::$adapter ) ) {
14117
+            self::$adapter->close();
14118
+        }
14119
+    }
14120
+
14121
+    /**
14122
+     * Simple convenience function, returns ISO date formatted representation
14123
+     * of $time.
14124
+     *
14125
+     * @param mixed $time UNIX timestamp
14126
+     *
14127
+     * @return string
14128
+     */
14129
+    public static function isoDate( $time = NULL )
14130
+    {
14131
+        if ( !$time ) {
14132
+            $time = time();
14133
+        }
14134
+
14135
+        return @date( 'Y-m-d', $time );
14136
+    }
14137
+
14138
+    /**
14139
+     * Simple convenience function, returns ISO date time
14140
+     * formatted representation
14141
+     * of $time.
14142
+     *
14143
+     * @param mixed $time UNIX timestamp
14144
+     *
14145
+     * @return string
14146
+     */
14147
+    public static function isoDateTime( $time = NULL )
14148
+    {
14149
+        if ( !$time ) $time = time();
14150
+        return @date( 'Y-m-d H:i:s', $time );
14151
+    }
14152
+
14153
+    /**
14154
+     * Sets the database adapter you want to use.
14155
+     * The database adapter manages the connection to the database
14156
+     * and abstracts away database driver specific interfaces.
14157
+     *
14158
+     * @param Adapter $adapter Database Adapter for facade to use
14159
+     *
14160
+     * @return void
14161
+     */
14162
+    public static function setDatabaseAdapter( Adapter $adapter )
14163
+    {
14164
+        self::$adapter = $adapter;
14165
+    }
14166
+
14167
+    /**
14168
+     * Sets the Query Writer you want to use.
14169
+     * The Query Writer writes and executes database queries using
14170
+     * the database adapter. It turns RedBeanPHP 'commands' into
14171
+     * database 'statements'.
14172
+     *
14173
+     * @param QueryWriter $writer Query Writer instance for facade to use
14174
+     *
14175
+     * @return void
14176
+     */
14177
+    public static function setWriter( QueryWriter $writer )
14178
+    {
14179
+        self::$writer = $writer;
14180
+    }
14181
+
14182
+    /**
14183
+     * Sets the OODB you want to use.
14184
+     * The RedBeanPHP Object oriented database is the main RedBeanPHP
14185
+     * interface that allows you to store and retrieve RedBeanPHP
14186
+     * objects (i.e. beans).
14187
+     *
14188
+     * @param OODB $redbean Object Database for facade to use
14189
+     */
14190
+    public static function setRedBean( OODB $redbean )
14191
+    {
14192
+        self::$redbean = $redbean;
14193
+    }
14194
+
14195
+    /**
14196
+     * Optional accessor for neat code.
14197
+     * Sets the database adapter you want to use.
14198
+     *
14199
+     * @return DBAdapter
14200
+     */
14201
+    public static function getDatabaseAdapter()
14202
+    {
14203
+        return self::$adapter;
14204
+    }
14205
+
14206
+    /**
14207
+     * In case you use PDO (which is recommended and the default but not mandatory, hence
14208
+     * the database adapter), you can use this method to obtain the PDO object directly.
14209
+     * This is a convenience method, it will do the same as:
14210
+     *
14211
+     * <code>
14212
+     * R::getDatabaseAdapter()->getDatabase()->getPDO();
14213
+     * </code>
14214
+     *
14215
+     * If the PDO object could not be found, for whatever reason, this method
14216
+     * will return NULL instead.
14217
+     *
14218
+     * @return NULL|PDO
14219
+     */
14220
+    public static function getPDO()
14221
+    {
14222
+        $databaseAdapter = self::getDatabaseAdapter();
14223
+        if ( is_null( $databaseAdapter ) ) return NULL;
14224
+        $database = $databaseAdapter->getDatabase();
14225
+        if ( is_null( $database ) ) return NULL;
14226
+        if ( !method_exists( $database, 'getPDO' ) ) return NULL;
14227
+        return $database->getPDO();
14228
+    }
14229
+
14230
+    /**
14231
+     * Returns the current duplication manager instance.
14232
+     *
14233
+     * @return DuplicationManager
14234
+     */
14235
+    public static function getDuplicationManager()
14236
+    {
14237
+        return self::$duplicationManager;
14238
+    }
14239
+
14240
+    /**
14241
+     * Optional accessor for neat code.
14242
+     * Sets the database adapter you want to use.
14243
+     *
14244
+     * @return QueryWriter
14245
+     */
14246
+    public static function getWriter()
14247
+    {
14248
+        return self::$writer;
14249
+    }
14250
+
14251
+    /**
14252
+     * Optional accessor for neat code.
14253
+     * Sets the database adapter you want to use.
14254
+     *
14255
+     * @return OODB
14256
+     */
14257
+    public static function getRedBean()
14258
+    {
14259
+        return self::$redbean;
14260
+    }
14261
+
14262
+    /**
14263
+     * Returns the toolbox currently used by the facade.
14264
+     * To set the toolbox use R::setup() or R::configureFacadeWithToolbox().
14265
+     * To create a toolbox use Setup::kickstart(). Or create a manual
14266
+     * toolbox using the ToolBox class.
14267
+     *
14268
+     * @return ToolBox
14269
+     */
14270
+    public static function getToolBox()
14271
+    {
14272
+        return self::$toolbox;
14273
+    }
14274
+
14275
+    /**
14276
+     * Mostly for internal use, but might be handy
14277
+     * for some users.
14278
+     * This returns all the components of the currently
14279
+     * selected toolbox.
14280
+     *
14281
+     * Returns the components in the following order:
14282
+     *
14283
+     * # OODB instance (getRedBean())
14284
+     * # Database Adapter
14285
+     * # Query Writer
14286
+     * # Toolbox itself
14287
+     *
14288
+     * @return array
14289
+     */
14290
+    public static function getExtractedToolbox()
14291
+    {
14292
+        return array( self::$redbean, self::$adapter, self::$writer, self::$toolbox );
14293
+    }
14294
+
14295
+    /**
14296
+     * Facade method for AQueryWriter::renameAssociation()
14297
+     *
14298
+     * @param string|array $from
14299
+     * @param string       $to
14300
+     *
14301
+     * @return void
14302
+     */
14303
+    public static function renameAssociation( $from, $to = NULL )
14304
+    {
14305
+        AQueryWriter::renameAssociation( $from, $to );
14306
+    }
14307
+
14308
+    /**
14309
+     * Little helper method for Resty Bean Can server and others.
14310
+     * Takes an array of beans and exports each bean.
14311
+     * Unlike exportAll this method does not recurse into own lists
14312
+     * and shared lists, the beans are exported as-is, only loaded lists
14313
+     * are exported.
14314
+     *
14315
+     * @param array $beans beans
14316
+     *
14317
+     * @return array
14318
+     */
14319
+    public static function beansToArray( $beans )
14320
+    {
14321
+        $list = array();
14322
+        foreach( $beans as $bean ) $list[] = $bean->export();
14323
+        return $list;
14324
+    }
14325
+
14326
+    /**
14327
+     * Sets the error mode for FUSE.
14328
+     * What to do if a FUSE model method does not exist?
14329
+     * You can set the following options:
14330
+     *
14331
+     * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
14332
+     * * OODBBean::C_ERR_LOG, logs the incident using error_log
14333
+     * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
14334
+     * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
14335
+     * * OODBBean::C_ERR_EXCEPTION, throws an exception
14336
+     * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
14337
+     * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
14338
+     *
14339
+     * <code>
14340
+     * Custom handler method signature: handler( array (
14341
+     * 	'message' => string
14342
+     * 	'bean' => OODBBean
14343
+     * 	'method' => string
14344
+     * ) )
14345
+     * </code>
14346
+     *
14347
+     * This method returns the old mode and handler as an array.
14348
+     *
14349
+     * @param integer       $mode mode, determines how to handle errors
14350
+     * @param callable|NULL $func custom handler (if applicable)
14351
+     *
14352
+     * @return array
14353
+     */
14354
+    public static function setErrorHandlingFUSE( $mode, $func = NULL )
14355
+    {
14356
+        return OODBBean::setErrorHandlingFUSE( $mode, $func );
14357
+    }
14358
+
14359
+    /**
14360
+     * Dumps bean data to array.
14361
+     * Given a one or more beans this method will
14362
+     * return an array containing first part of the string
14363
+     * representation of each item in the array.
14364
+     *
14365
+     * Usage:
14366
+     *
14367
+     * <code>
14368
+     * echo R::dump( $bean );
14369
+     * </code>
14370
+     *
14371
+     * The example shows how to echo the result of a simple
14372
+     * dump. This will print the string representation of the
14373
+     * specified bean to the screen, limiting the output per bean
14374
+     * to 35 characters to improve readability. Nested beans will
14375
+     * also be dumped.
14376
+     *
14377
+     * @param OODBBean|array $data either a bean or an array of beans
14378
+     *
14379
+     * @return array
14380
+     */
14381
+    public static function dump( $data )
14382
+    {
14383
+        return Dump::dump( $data );
14384
+    }
14385
+
14386
+    /**
14387
+     * Binds an SQL function to a column.
14388
+     * This method can be used to setup a decode/encode scheme or
14389
+     * perform UUID insertion. This method is especially useful for handling
14390
+     * MySQL spatial columns, because they need to be processed first using
14391
+     * the asText/GeomFromText functions.
14392
+     *
14393
+     * Example:
14394
+     *
14395
+     * <code>
14396
+     * R::bindFunc( 'read', 'location.point', 'asText' );
14397
+     * R::bindFunc( 'write', 'location.point', 'GeomFromText' );
14398
+     * </code>
14399
+     *
14400
+     * Passing NULL as the function will reset (clear) the function
14401
+     * for this column/mode.
14402
+     *
14403
+     * @param string $mode     mode for function: i.e. read or write
14404
+     * @param string $field    field (table.column) to bind function to
14405
+     * @param string $function SQL function to bind to specified column
14406
+     *
14407
+     * @return void
14408
+     */
14409
+    public static function bindFunc( $mode, $field, $function )
14410
+    {
14411
+        self::$redbean->bindFunc( $mode, $field, $function );
14412
+    }
14413
+
14414
+    /**
14415
+     * Sets global aliases.
14416
+     * Registers a batch of aliases in one go. This works the same as
14417
+     * fetchAs and setAutoResolve but explicitly. For instance if you register
14418
+     * the alias 'cover' for 'page' a property containing a reference to a
14419
+     * page bean called 'cover' will correctly return the page bean and not
14420
+     * a (non-existant) cover bean.
14421
+     *
14422
+     * <code>
14423
+     * R::aliases( array( 'cover' => 'page' ) );
14424
+     * $book = R::dispense( 'book' );
14425
+     * $page = R::dispense( 'page' );
14426
+     * $book->cover = $page;
14427
+     * R::store( $book );
14428
+     * $book = $book->fresh();
14429
+     * $cover = $book->cover;
14430
+     * echo $cover->getMeta( 'type' ); //page
14431
+     * </code>
14432
+     *
14433
+     * The format of the aliases registration array is:
14434
+     *
14435
+     * {alias} => {actual type}
14436
+     *
14437
+     * In the example above we use:
14438
+     *
14439
+     * cover => page
14440
+     *
14441
+     * From that point on, every bean reference to a cover
14442
+     * will return a 'page' bean. Note that with autoResolve this
14443
+     * feature along with fetchAs() is no longer very important, although
14444
+     * relying on explicit aliases can be a bit faster.
14445
+     *
14446
+     * @param array $list list of global aliases to use
14447
+     *
14448
+     * @return void
14449
+     */
14450
+    public static function aliases( $list )
14451
+    {
14452
+        OODBBean::aliases( $list );
14453
+    }
14454
+
14455
+    /**
14456
+     * Tries to find a bean matching a certain type and
14457
+     * criteria set. If no beans are found a new bean
14458
+     * will be created, the criteria will be imported into this
14459
+     * bean and the bean will be stored and returned.
14460
+     * If multiple beans match the criteria only the first one
14461
+     * will be returned.
14462
+     *
14463
+     * @param string $type type of bean to search for
14464
+     * @param array  $like criteria set describing the bean to search for
14465
+     *
14466
+     * @return OODBBean
14467
+     */
14468
+    public static function findOrCreate( $type, $like = array(), $sql = '' )
14469
+    {
14470
+        return self::$finder->findOrCreate( $type, $like, $sql = '' );
14471
+    }
14472
+
14473
+    /**
14474
+     * Tries to find beans matching the specified type and
14475
+     * criteria set.
14476
+     *
14477
+     * If the optional additional SQL snippet is a condition, it will
14478
+     * be glued to the rest of the query using the AND operator.
14479
+     *
14480
+     * @param string $type type of bean to search for
14481
+     * @param array  $like optional criteria set describing the bean to search for
14482
+     * @param string $sql  optional additional SQL for sorting
14483
+     * @param array  $bindings bindings
14484
+     *
14485
+     * @return array
14486
+     */
14487
+    public static function findLike( $type, $like = array(), $sql = '', $bindings = array() )
14488
+    {
14489
+        return self::$finder->findLike( $type, $like, $sql, $bindings );
14490
+    }
14491
+
14492
+    /**
14493
+     * Starts logging queries.
14494
+     * Use this method to start logging SQL queries being
14495
+     * executed by the adapter. Logging queries will not
14496
+     * print them on the screen. Use R::getLogs() to
14497
+     * retrieve the logs.
14498
+     *
14499
+     * Usage:
14500
+     *
14501
+     * <code>
14502
+     * R::startLogging();
14503
+     * R::store( R::dispense( 'book' ) );
14504
+     * R::find('book', 'id > ?',[0]);
14505
+     * $logs = R::getLogs();
14506
+     * $count = count( $logs );
14507
+     * print_r( $logs );
14508
+     * R::stopLogging();
14509
+     * </code>
14510
+     *
14511
+     * In the example above we start a logging session during
14512
+     * which we store an empty bean of type book. To inspect the
14513
+     * logs we invoke R::getLogs() after stopping the logging.
14514
+     *
14515
+     * @note you cannot use R::debug and R::startLogging
14516
+     * at the same time because R::debug is essentially a
14517
+     * special kind of logging.
14518
+     *
14519
+     * @return void
14520
+     */
14521
+    public static function startLogging()
14522
+    {
14523
+        self::debug( TRUE, RDefault::C_LOGGER_ARRAY );
14524
+    }
14525
+
14526
+    /**
14527
+     * Stops logging and flushes the logs,
14528
+     * convient method to stop logging of queries.
14529
+     * Use this method to stop logging SQL queries being
14530
+     * executed by the adapter. Logging queries will not
14531
+     * print them on the screen. Use R::getLogs() to
14532
+     * retrieve the logs.
14533
+     *
14534
+     * <code>
14535
+     * R::startLogging();
14536
+     * R::store( R::dispense( 'book' ) );
14537
+     * R::find('book', 'id > ?',[0]);
14538
+     * $logs = R::getLogs();
14539
+     * $count = count( $logs );
14540
+     * print_r( $logs );
14541
+     * R::stopLogging();
14542
+     * </code>
14543
+     *
14544
+     * In the example above we start a logging session during
14545
+     * which we store an empty bean of type book. To inspect the
14546
+     * logs we invoke R::getLogs() after stopping the logging.
14547
+     *
14548
+     * @note you cannot use R::debug and R::startLogging
14549
+     * at the same time because R::debug is essentially a
14550
+     * special kind of logging.
14551
+     *
14552
+     * @note by stopping the logging you also flush the logs.
14553
+     * Therefore, only stop logging AFTER you have obtained the
14554
+     * query logs using R::getLogs()
14555
+     *
14556
+     * @return void
14557
+     */
14558
+    public static function stopLogging()
14559
+    {
14560
+        self::debug( FALSE );
14561
+    }
14562
+
14563
+    /**
14564
+     * Returns the log entries written after the startLogging.
14565
+     *
14566
+     * Use this method to obtain the query logs gathered
14567
+     * by the logging mechanisms.
14568
+     * Logging queries will not
14569
+     * print them on the screen. Use R::getLogs() to
14570
+     * retrieve the logs.
14571
+     *
14572
+     * <code>
14573
+     * R::startLogging();
14574
+     * R::store( R::dispense( 'book' ) );
14575
+     * R::find('book', 'id > ?',[0]);
14576
+     * $logs = R::getLogs();
14577
+     * $count = count( $logs );
14578
+     * print_r( $logs );
14579
+     * R::stopLogging();
14580
+     * </code>
14581
+     *
14582
+     * In the example above we start a logging session during
14583
+     * which we store an empty bean of type book. To inspect the
14584
+     * logs we invoke R::getLogs() after stopping the logging.
14585
+     *
14586
+     * The logs may look like:
14587
+     *
14588
+     * [1] => SELECT `book`.*  FROM `book`  WHERE id > ?  -- keep-cache
14589
+     * [2] => array ( 0 => 0, )
14590
+     * [3] => resultset: 1 rows
14591
+     *
14592
+     * Basically, element in the array is a log entry.
14593
+     * Parameter bindings are  represented as nested arrays (see 2).
14594
+     *
14595
+     * @note you cannot use R::debug and R::startLogging
14596
+     * at the same time because R::debug is essentially a
14597
+     * special kind of logging.
14598
+     *
14599
+     * @note by stopping the logging you also flush the logs.
14600
+     * Therefore, only stop logging AFTER you have obtained the
14601
+     * query logs using R::getLogs()
14602
+     *
14603
+     * @return array
14604
+     */
14605
+    public static function getLogs()
14606
+    {
14607
+        return self::getLogger()->getLogs();
14608
+    }
14609
+
14610
+    /**
14611
+     * Resets the query counter.
14612
+     * The query counter can be used to monitor the number
14613
+     * of database queries that have
14614
+     * been processed according to the database driver. You can use this
14615
+     * to monitor the number of queries required to render a page.
14616
+     *
14617
+     * Usage:
14618
+     *
14619
+     * <code>
14620
+     * R::resetQueryCount();
14621
+     * echo R::getQueryCount() . ' queries processed.';
14622
+     * </code>
14623
+     *
14624
+     * @return void
14625
+     */
14626
+    public static function resetQueryCount()
14627
+    {
14628
+        self::$adapter->getDatabase()->resetCounter();
14629
+    }
14630
+
14631
+    /**
14632
+     * Returns the number of SQL queries processed.
14633
+     * This method returns the number of database queries that have
14634
+     * been processed according to the database driver. You can use this
14635
+     * to monitor the number of queries required to render a page.
14636
+     *
14637
+     * Usage:
14638
+     *
14639
+     * <code>
14640
+     * echo R::getQueryCount() . ' queries processed.';
14641
+     * </code>
14642
+     *
14643
+     * @return integer
14644
+     */
14645
+    public static function getQueryCount()
14646
+    {
14647
+        return self::$adapter->getDatabase()->getQueryCount();
14648
+    }
14649
+
14650
+    /**
14651
+     * Returns the current logger instance being used by the
14652
+     * database object.
14653
+     *
14654
+     * @return Logger
14655
+     */
14656
+    public static function getLogger()
14657
+    {
14658
+        return self::$adapter->getDatabase()->getLogger();
14659
+    }
14660
+
14661
+    /**
14662
+     * Alias for setAutoResolve() method on OODBBean.
14663
+     * Enables or disables auto-resolving fetch types.
14664
+     * Auto-resolving aliased parent beans is convenient but can
14665
+     * be slower and can create infinite recursion if you
14666
+     * used aliases to break cyclic relations in your domain.
14667
+     *
14668
+     * @param boolean $automatic TRUE to enable automatic resolving aliased parents
14669
+     *
14670
+     * @return void
14671
+     */
14672
+    public static function setAutoResolve( $automatic = TRUE )
14673
+    {
14674
+        OODBBean::setAutoResolve( (boolean) $automatic );
14675
+    }
14676
+
14677
+    /**
14678
+     * Toggles 'partial bean mode'. If this mode has been
14679
+     * selected the repository will only update the fields of a bean that
14680
+     * have been changed rather than the entire bean.
14681
+     * Pass the value TRUE to select 'partial mode' for all beans.
14682
+     * Pass the value FALSE to disable 'partial mode'.
14683
+     * Pass an array of bean types if you wish to use partial mode only
14684
+     * for some types.
14685
+     * This method will return the previous value.
14686
+     *
14687
+     * @param boolean|array $yesNoBeans List of type names or 'all'
14688
+     *
14689
+     * @return mixed
14690
+     */
14691
+    public static function usePartialBeans( $yesNoBeans )
14692
+    {
14693
+        return self::$redbean->getCurrentRepository()->usePartialBeans( $yesNoBeans );
14694
+    }
14695
+
14696
+    /**
14697
+     * Exposes the result of the specified SQL query as a CSV file.
14698
+     * Usage:
14699
+     *
14700
+     * R::csv( 'SELECT
14701
+     *                 `name`,
14702
+     *                  population
14703
+     *          FROM city
14704
+     *          WHERE region = :region ',
14705
+     *          array( ':region' => 'Denmark' ),
14706
+     *          array( 'city', 'population' ),
14707
+     *          '/tmp/cities.csv'
14708
+     * );
14709
+     *
14710
+     * The command above will select all cities in Denmark
14711
+     * and create a CSV with columns 'city' and 'population' and
14712
+     * populate the cells under these column headers with the
14713
+     * names of the cities and the population numbers respectively.
14714
+     *
14715
+     * @param string  $sql      SQL query to expose result of
14716
+     * @param array   $bindings parameter bindings
14717
+     * @param array   $columns  column headers for CSV file
14718
+     * @param string  $path     path to save CSV file to
14719
+     * @param boolean $output   TRUE to output CSV directly using readfile
14720
+     * @param array   $options  delimiter, quote and escape character respectively
14721
+     *
14722
+     * @return void
14723
+     */
14724
+    public static function csv( $sql = '', $bindings = array(), $columns = NULL, $path = '/tmp/redexport_%s.csv', $output = TRUE )
14725
+    {
14726
+        $quickExport = new QuickExport( self::$toolbox );
14727
+        $quickExport->csv( $sql, $bindings, $columns, $path, $output );
14728
+    }
14729
+
14730
+    /**
14731
+     * MatchUp is a powerful productivity boosting method that can replace simple control
14732
+     * scripts with a single RedBeanPHP command. Typically, matchUp() is used to
14733
+     * replace login scripts, token generation scripts and password reset scripts.
14734
+     * The MatchUp method takes a bean type, an SQL query snippet (starting at the WHERE clause),
14735
+     * SQL bindings, a pair of task arrays and a bean reference.
14736
+     *
14737
+     * If the first 3 parameters match a bean, the first task list will be considered,
14738
+     * otherwise the second one will be considered. On consideration, each task list,
14739
+     * an array of keys and values will be executed. Every key in the task list should
14740
+     * correspond to a bean property while every value can either be an expression to
14741
+     * be evaluated or a closure (PHP 5.3+). After applying the task list to the bean
14742
+     * it will be stored. If no bean has been found, a new bean will be dispensed.
14743
+     *
14744
+     * This method will return TRUE if the bean was found and FALSE if not AND
14745
+     * there was a NOT-FOUND task list. If no bean was found AND there was also
14746
+     * no second task list, NULL will be returned.
14747
+     *
14748
+     * To obtain the bean, pass a variable as the sixth parameter.
14749
+     * The function will put the matching bean in the specified variable.
14750
+     *
14751
+     * @param string   $type         type of bean you're looking for
14752
+     * @param string   $sql          SQL snippet (starting at the WHERE clause, omit WHERE-keyword)
14753
+     * @param array    $bindings     array of parameter bindings for SQL snippet
14754
+     * @param array    $onFoundDo    task list to be considered on finding the bean
14755
+     * @param array    $onNotFoundDo task list to be considered on NOT finding the bean
14756
+     * @param OODBBean &$bean        reference to obtain the found bean
14757
+     *
14758
+     * @return mixed
14759
+     */
14760
+    public static function matchUp( $type, $sql, $bindings = array(), $onFoundDo = NULL, $onNotFoundDo = NULL, &$bean = NULL 	) {
14761
+        $matchUp = new MatchUp( self::$toolbox );
14762
+        return $matchUp->matchUp( $type, $sql, $bindings, $onFoundDo, $onNotFoundDo, $bean );
14763
+    }
14764
+
14765
+    /**
14766
+     * @deprecated
14767
+     *
14768
+     * Returns an instance of the Look Helper class.
14769
+     * The instance will be configured with the current toolbox.
14770
+     *
14771
+     * In previous versions of RedBeanPHP you had to use:
14772
+     * R::getLook()->look() instead of R::look(). However to improve useability of the
14773
+     * library the look() function can now directly be invoked from the facade.
14774
+     *
14775
+     * For more details regarding the Look functionality, please consult R::look().
14776
+     * @see Facade::look
14777
+     * @see Look::look
14778
+     *
14779
+     * @return Look
14780
+     */
14781
+    public static function getLook()
14782
+    {
14783
+        return new Look( self::$toolbox );
14784
+    }
14785
+
14786
+    /**
14787
+     * Takes an full SQL query with optional bindings, a series of keys, a template
14788
+     * and optionally a filter function and glue and assembles a view from all this.
14789
+     * This is the fastest way from SQL to view. Typically this function is used to
14790
+     * generate pulldown (select tag) menus with options queried from the database.
14791
+     *
14792
+     * Usage:
14793
+     *
14794
+     * <code>
14795
+     * $htmlPulldown = R::look(
14796
+     *   'SELECT * FROM color WHERE value != ? ORDER BY value ASC',
14797
+     *   [ 'g' ],
14798
+     *   [ 'value', 'name' ],
14799
+     *   '<option value="%s">%s</option>',
14800
+     *   'strtoupper',
14801
+     *   "\n"
14802
+     * );
14803
+     *</code>
14804
+     *
14805
+     * The example above creates an HTML fragment like this:
14806
+     *
14807
+     * <option value="B">BLUE</option>
14808
+     * <option value="R">RED</option>
14809
+     *
14810
+     * to pick a color from a palette. The HTML fragment gets constructed by
14811
+     * an SQL query that selects all colors that do not have value 'g' - this
14812
+     * excludes green. Next, the bean properties 'value' and 'name' are mapped to the
14813
+     * HTML template string, note that the order here is important. The mapping and
14814
+     * the HTML template string follow vsprintf-rules. All property values are then
14815
+     * passed through the specified filter function 'strtoupper' which in this case
14816
+     * is a native PHP function to convert strings to uppercase characters only.
14817
+     * Finally the resulting HTML fragment strings are glued together using a
14818
+     * newline character specified in the last parameter for readability.
14819
+     *
14820
+     * In previous versions of RedBeanPHP you had to use:
14821
+     * R::getLook()->look() instead of R::look(). However to improve useability of the
14822
+     * library the look() function can now directly be invoked from the facade.
14823
+     *
14824
+     * @param string   $sql      query to execute
14825
+     * @param array    $bindings parameters to bind to slots mentioned in query or an empty array
14826
+     * @param array    $keys     names in result collection to map to template
14827
+     * @param string   $template HTML template to fill with values associated with keys, use printf notation (i.e. %s)
14828
+     * @param callable $filter   function to pass values through (for translation for instance)
14829
+     * @param string   $glue     optional glue to use when joining resulting strings
14830
+     *
14831
+     * @return string
14832
+     */
14833
+    public static function look( $sql, $bindings = array(), $keys = array( 'selected', 'id', 'name' ), $template = '<option %s value="%s">%s</option>', $filter = 'trim', $glue = '' )
14834
+    {
14835
+        return self::getLook()->look( $sql, $bindings, $keys, $template, $filter, $glue );
14836
+    }
14837
+
14838
+    /**
14839
+     * Calculates a diff between two beans (or arrays of beans).
14840
+     * The result of this method is an array describing the differences of the second bean compared to
14841
+     * the first, where the first bean is taken as reference. The array is keyed by type/property, id and property name, where
14842
+     * type/property is either the type (in case of the root bean) or the property of the parent bean where the type resides.
14843
+     * The diffs are mainly intended for logging, you cannot apply these diffs as patches to other beans.
14844
+     * However this functionality might be added in the future.
14845
+     *
14846
+     * The keys of the array can be formatted using the $format parameter.
14847
+     * A key will be composed of a path (1st), id (2nd) and property (3rd).
14848
+     * Using printf-style notation you can determine the exact format of the key.
14849
+     * The default format will look like:
14850
+     *
14851
+     * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
14852
+     *
14853
+     * If you only want a simple diff of one bean and you don't care about ids,
14854
+     * you might pass a format like: '%1$s.%3$s' which gives:
14855
+     *
14856
+     * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
14857
+     *
14858
+     * The filter parameter can be used to set filters, it should be an array
14859
+     * of property names that have to be skipped. By default this array is filled with
14860
+     * two strings: 'created' and 'modified'.
14861
+     *
14862
+     * @param OODBBean|array $bean    reference beans
14863
+     * @param OODBBean|array $other   beans to compare
14864
+     * @param array          $filters names of properties of all beans to skip
14865
+     * @param string         $format  the format of the key, defaults to '%s.%s.%s'
14866
+     * @param string         $type    type/property of bean to use for key generation
14867
+     *
14868
+     * @return array
14869
+     */
14870
+    public static function diff( $bean, $other, $filters = array( 'created', 'modified' ), $pattern = '%s.%s.%s' )
14871
+    {
14872
+        $diff = new Diff( self::$toolbox );
14873
+        return $diff->diff( $bean, $other, $filters, $pattern );
14874
+    }
14875
+
14876
+    /**
14877
+     * The gentleman's way to register a RedBeanPHP ToolBox instance
14878
+     * with the facade. Stores the toolbox in the static toolbox
14879
+     * registry of the facade class. This allows for a neat and
14880
+     * explicit way to register a toolbox.
14881
+     *
14882
+     * @param string  $key     key to store toolbox instance under
14883
+     * @param ToolBox $toolbox toolbox to register
14884
+     *
14885
+     * @return void
14886
+     */
14887
+    public static function addToolBoxWithKey( $key, ToolBox $toolbox )
14888
+    {
14889
+        self::$toolboxes[$key] = $toolbox;
14890
+    }
14891
+
14892
+    /**
14893
+     * The gentleman's way to remove a RedBeanPHP ToolBox instance
14894
+     * from the facade. Removes the toolbox identified by
14895
+     * the specified key in the static toolbox
14896
+     * registry of the facade class. This allows for a neat and
14897
+     * explicit way to remove a toolbox.
14898
+     * Returns TRUE if the specified toolbox was found and removed.
14899
+     * Returns FALSE otherwise.
14900
+     *
14901
+     * @param string  $key     identifier of the toolbox to remove
14902
+     *
14903
+     * @return boolean
14904
+     */
14905
+    public static function removeToolBoxByKey( $key )
14906
+    {
14907
+        if ( !array_key_exists( $key, self::$toolboxes ) ) {
14908
+            return FALSE;
14909
+        }
14910
+        return TRUE;
14911
+    }
14912
+
14913
+    /**
14914
+     * Returns the toolbox associated with the specified key.
14915
+     *
14916
+     * @param string  $key     key to store toolbox instance under
14917
+     * @param ToolBox $toolbox toolbox to register
14918
+     *
14919
+     * @return ToolBox|NULL
14920
+     */
14921
+    public static function getToolBoxByKey( $key )
14922
+    {
14923
+        if ( !array_key_exists( $key, self::$toolboxes ) ) {
14924
+            return NULL;
14925
+        }
14926
+        return self::$toolboxes[$key];
14927
+    }
14928
+
14929
+    /**
14930
+     * Toggles JSON column features.
14931
+     * Invoking this method with boolean TRUE causes 2 JSON features to be enabled.
14932
+     * Beans will automatically JSONify any array that's not in a list property and
14933
+     * the Query Writer (if capable) will attempt to create a JSON column for strings that
14934
+     * appear to contain JSON.
14935
+     *
14936
+     * Feature #1:
14937
+     * AQueryWriter::useJSONColumns
14938
+     *
14939
+     * Toggles support for automatic generation of JSON columns.
14940
+     * Using JSON columns means that strings containing JSON will
14941
+     * cause the column to be created (not modified) as a JSON column.
14942
+     * However it might also trigger exceptions if this means the DB attempts to
14943
+     * convert a non-json column to a JSON column.
14944
+     *
14945
+     * Feature #2:
14946
+     * OODBBean::convertArraysToJSON
14947
+     *
14948
+     * Toggles array to JSON conversion. If set to TRUE any array
14949
+     * set to a bean property that's not a list will be turned into
14950
+     * a JSON string. Used together with AQueryWriter::useJSONColumns this
14951
+     * extends the data type support for JSON columns.
14952
+     *
14953
+     * So invoking this method is the same as:
14954
+     *
14955
+     * AQueryWriter::useJSONColumns( $flag );
14956
+     * OODBBean::convertArraysToJSON( $flag );
14957
+     *
14958
+     * Unlike the methods above, that return the previous state, this
14959
+     * method does not return anything (void).
14960
+     *
14961
+     * @param boolean $flag feature flag (either TRUE or FALSE)
14962
+     *
14963
+     * @return void
14964
+     */
14965
+    public static function useJSONFeatures( $flag )
14966
+    {
14967
+        AQueryWriter::useJSONColumns( $flag );
14968
+        OODBBean::convertArraysToJSON( $flag );
14969
+    }
14970
+
14971
+    /**
14972
+     * @experimental
14973
+     *
14974
+     * Given a bean and an optional SQL snippet,
14975
+     * this method will return all child beans in a hierarchically structured
14976
+     * bean table.
14977
+     *
14978
+     * @note that not all database support this functionality. You'll need
14979
+     * at least MariaDB 10.2.2 or Postgres. This method does not include
14980
+     * a warning mechanism in case your database does not support this
14981
+     * functionality.
14982
+     *
14983
+     * @note that this functionality is considered 'experimental'.
14984
+     * It may still contain bugs.
14985
+     *
14986
+     * @param OODBBean $bean     bean to find children of
14987
+     * @param string   $sql      optional SQL snippet
14988
+     * @param array    $bindings SQL snippet parameter bindings
14989
+     */
14990
+    public static function children( OODBBean $bean, $sql = NULL, $bindings = array() )
14991
+    {
14992
+        return self::$tree->children( $bean, $sql, $bindings );
14993
+    }
14994
+
14995
+    /**
14996
+     * @experimental
14997
+     *
14998
+     * Given a bean and an optional SQL snippet,
14999
+     * this method will return all parent beans in a hierarchically structured
15000
+     * bean table.
15001
+     *
15002
+     * @note that not all database support this functionality. You'll need
15003
+     * at least MariaDB 10.2.2 or Postgres. This method does not include
15004
+     * a warning mechanism in case your database does not support this
15005
+     * functionality.
15006
+     *
15007
+     * @note that this functionality is considered 'experimental'.
15008
+     * It may still contain bugs.
15009
+     *
15010
+     * @param OODBBean $bean     bean to find parents of
15011
+     * @param string   $sql      optional SQL snippet
15012
+     * @param array    $bindings SQL snippet parameter bindings
15013
+     */
15014
+    public static function parents( OODBBean $bean, $sql = NULL, $bindings = array() )
15015
+    {
15016
+        return self::$tree->parents( $bean, $sql, $bindings );
15017
+    }
15018
+
15019
+    /**
15020
+     * Toggles support for nuke().
15021
+     * Can be used to turn off the nuke() feature for security reasons.
15022
+     * Returns the old flag value.
15023
+     *
15024
+     * @param boolean $flag TRUE or FALSE
15025
+     *
15026
+     * @return boolean
15027
+     */
15028
+    public static function noNuke( $yesNo ) {
15029
+        return AQueryWriter::forbidNuke( $yesNo );
15030
+    }
15031
+
15032
+    /**
15033
+     * Selects the feature set you want as specified by
15034
+     * the label.
15035
+     *
15036
+     * Usage:
15037
+     * R::useFeatureSet( 'novice/latest' );
15038
+     *
15039
+     * @param string $label label
15040
+     *
15041
+     * @return void
15042
+     */
15043
+    public static function useFeatureSet( $label ) {
15044
+        return Feature::feature($label);
15045
+    }
15046
+
15047
+    /**
15048
+     * Dynamically extends the facade with a plugin.
15049
+     * Using this method you can register your plugin with the facade and then
15050
+     * use the plugin by invoking the name specified plugin name as a method on
15051
+     * the facade.
15052
+     *
15053
+     * Usage:
15054
+     *
15055
+     * <code>
15056
+     * R::ext( 'makeTea', function() { ... }  );
15057
+     * </code>
15058
+     *
15059
+     * Now you can use your makeTea plugin like this:
15060
+     *
15061
+     * <code>
15062
+     * R::makeTea();
15063
+     * </code>
15064
+     *
15065
+     * @param string   $pluginName name of the method to call the plugin
15066
+     * @param callable $callable   a PHP callable
15067
+     *
15068
+     * @return void
15069
+     */
15070
+    public static function ext( $pluginName, $callable )
15071
+    {
15072
+        if ( !ctype_alnum( $pluginName ) ) {
15073
+            throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
15074
+        }
15075
+        self::$plugins[$pluginName] = $callable;
15076
+    }
15077
+
15078
+    /**
15079
+     * Call static for use with dynamic plugins. This magic method will
15080
+     * intercept static calls and route them to the specified plugin.
15081
+     *
15082
+     * @param string $pluginName name of the plugin
15083
+     * @param array  $params     list of arguments to pass to plugin method
15084
+     *
15085
+     * @return mixed
15086
+     */
15087
+    public static function __callStatic( $pluginName, $params )
15088
+    {
15089
+        if ( !ctype_alnum( $pluginName) ) {
15090
+            throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
15091
+        }
15092
+        if ( !isset( self::$plugins[$pluginName] ) ) {
15093
+            throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' );
15094
+        }
15095
+        return call_user_func_array( self::$plugins[$pluginName], $params );
15096
+    }
15097 15097
 }
15098 15098
 
15099 15099
 }
@@ -15131,429 +15131,429 @@  discard block
 block discarded – undo
15131 15131
  */
15132 15132
 class DuplicationManager
15133 15133
 {
15134
-	/**
15135
-	 * @var ToolBox
15136
-	 */
15137
-	protected $toolbox;
15138
-
15139
-	/**
15140
-	 * @var AssociationManager
15141
-	 */
15142
-	protected $associationManager;
15143
-
15144
-	/**
15145
-	 * @var OODB
15146
-	 */
15147
-	protected $redbean;
15148
-
15149
-	/**
15150
-	 * @var array
15151
-	 */
15152
-	protected $tables = array();
15153
-
15154
-	/**
15155
-	 * @var array
15156
-	 */
15157
-	protected $columns = array();
15158
-
15159
-	/**
15160
-	 * @var array
15161
-	 */
15162
-	protected $filters = array();
15163
-
15164
-	/**
15165
-	 * @var array
15166
-	 */
15167
-	protected $cacheTables = FALSE;
15168
-
15169
-	/**
15170
-	 * Copies the shared beans in a bean, i.e. all the sharedBean-lists.
15171
-	 *
15172
-	 * @param OODBBean $copy   target bean to copy lists to
15173
-	 * @param string   $shared name of the shared list
15174
-	 * @param array    $beans  array with shared beans to copy
15175
-	 *
15176
-	 * @return void
15177
-	 */
15178
-	private function copySharedBeans( OODBBean $copy, $shared, $beans )
15179
-	{
15180
-		$copy->$shared = array();
15181
-
15182
-		foreach ( $beans as $subBean ) {
15183
-			array_push( $copy->$shared, $subBean );
15184
-		}
15185
-	}
15186
-
15187
-	/**
15188
-	 * Copies the own beans in a bean, i.e. all the ownBean-lists.
15189
-	 * Each bean in the own-list belongs exclusively to its owner so
15190
-	 * we need to invoke the duplicate method again to duplicate each bean here.
15191
-	 *
15192
-	 * @param OODBBean $copy        target bean to copy lists to
15193
-	 * @param string   $owned       name of the own list
15194
-	 * @param array    $beans       array with shared beans to copy
15195
-	 * @param array    $trail       array with former beans to detect recursion
15196
-	 * @param boolean  $preserveIDs TRUE means preserve IDs, for export only
15197
-	 *
15198
-	 * @return void
15199
-	 */
15200
-	private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs )
15201
-	{
15202
-		$copy->$owned = array();
15203
-		foreach ( $beans as $subBean ) {
15204
-			array_push( $copy->$owned, $this->duplicate( $subBean, $trail, $preserveIDs ) );
15205
-		}
15206
-	}
15207
-
15208
-	/**
15209
-	 * Creates a copy of bean $bean and copies all primitive properties (not lists)
15210
-	 * and the parents beans to the newly created bean. Also sets the ID of the bean
15211
-	 * to 0.
15212
-	 *
15213
-	 * @param OODBBean $bean bean to copy
15214
-	 *
15215
-	 * @return OODBBean
15216
-	 */
15217
-	private function createCopy( OODBBean $bean )
15218
-	{
15219
-		$type = $bean->getMeta( 'type' );
15220
-
15221
-		$copy = $this->redbean->dispense( $type );
15222
-		$copy->setMeta( 'sys.dup-from-id', $bean->id );
15223
-		$copy->setMeta( 'sys.old-id', $bean->id );
15224
-		$copy->importFrom( $bean );
15225
-		$copy->id = 0;
15226
-
15227
-		return $copy;
15228
-	}
15229
-
15230
-	/**
15231
-	 * Generates a key from the bean type and its ID and determines if the bean
15232
-	 * occurs in the trail, if not the bean will be added to the trail.
15233
-	 * Returns TRUE if the bean occurs in the trail and FALSE otherwise.
15234
-	 *
15235
-	 * @param array    $trail list of former beans
15236
-	 * @param OODBBean $bean  currently selected bean
15237
-	 *
15238
-	 * @return boolean
15239
-	 */
15240
-	private function inTrailOrAdd( &$trail, OODBBean $bean )
15241
-	{
15242
-		$type = $bean->getMeta( 'type' );
15243
-		$key  = $type . $bean->getID();
15244
-
15245
-		if ( isset( $trail[$key] ) ) {
15246
-			return TRUE;
15247
-		}
15248
-
15249
-		$trail[$key] = $bean;
15250
-
15251
-		return FALSE;
15252
-	}
15253
-
15254
-	/**
15255
-	 * Given the type name of a bean this method returns the canonical names
15256
-	 * of the own-list and the shared-list properties respectively.
15257
-	 * Returns a list with two elements: name of the own-list, and name
15258
-	 * of the shared list.
15259
-	 *
15260
-	 * @param string $typeName bean type name
15261
-	 *
15262
-	 * @return array
15263
-	 */
15264
-	private function getListNames( $typeName )
15265
-	{
15266
-		$owned  = 'own' . ucfirst( $typeName );
15267
-		$shared = 'shared' . ucfirst( $typeName );
15268
-
15269
-		return array( $owned, $shared );
15270
-	}
15271
-
15272
-	/**
15273
-	 * Determines whether the bean has an own list based on
15274
-	 * schema inspection from realtime schema or cache.
15275
-	 *
15276
-	 * @param string $type   bean type to get list for
15277
-	 * @param string $target type of list you want to detect
15278
-	 *
15279
-	 * @return boolean
15280
-	 */
15281
-	protected function hasOwnList( $type, $target )
15282
-	{
15283
-		return isset( $this->columns[$target][$type . '_id'] );
15284
-	}
15285
-
15286
-	/**
15287
-	 * Determines whether the bea has a shared list based on
15288
-	 * schema inspection from realtime schema or cache.
15289
-	 *
15290
-	 * @param string $type   bean type to get list for
15291
-	 * @param string $target type of list you are looking for
15292
-	 *
15293
-	 * @return boolean
15294
-	 */
15295
-	protected function hasSharedList( $type, $target )
15296
-	{
15297
-		return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables );
15298
-	}
15299
-
15300
-	/**
15301
-	 * @see DuplicationManager::dup
15302
-	 *
15303
-	 * @param OODBBean $bean        bean to be copied
15304
-	 * @param array    $trail       trail to prevent infinite loops
15305
-	 * @param boolean  $preserveIDs preserve IDs
15306
-	 *
15307
-	 * @return OODBBean
15308
-	 */
15309
-	protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
15310
-	{
15311
-		if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean;
15312
-
15313
-		$type = $bean->getMeta( 'type' );
15314
-
15315
-		$copy = $this->createCopy( $bean );
15316
-		foreach ( $this->tables as $table ) {
15317
-
15318
-			if ( !empty( $this->filters ) ) {
15319
-				if ( !in_array( $table, $this->filters ) ) continue;
15320
-			}
15321
-
15322
-			list( $owned, $shared ) = $this->getListNames( $table );
15323
-
15324
-			if ( $this->hasSharedList( $type, $table ) ) {
15325
-				if ( $beans = $bean->$shared ) {
15326
-					$this->copySharedBeans( $copy, $shared, $beans );
15327
-				}
15328
-			} elseif ( $this->hasOwnList( $type, $table ) ) {
15329
-				if ( $beans = $bean->$owned ) {
15330
-					$this->copyOwnBeans( $copy, $owned, $beans, $trail, $preserveIDs );
15331
-				}
15332
-
15333
-				$copy->setMeta( 'sys.shadow.' . $owned, NULL );
15334
-			}
15335
-
15336
-			$copy->setMeta( 'sys.shadow.' . $shared, NULL );
15337
-		}
15338
-
15339
-		$copy->id = ( $preserveIDs ) ? $bean->id : $copy->id;
15340
-
15341
-		return $copy;
15342
-	}
15343
-
15344
-	/**
15345
-	 * Constructor,
15346
-	 * creates a new instance of DupManager.
15347
-	 *
15348
-	 * @param ToolBox $toolbox
15349
-	 */
15350
-	public function __construct( ToolBox $toolbox )
15351
-	{
15352
-		$this->toolbox            = $toolbox;
15353
-		$this->redbean            = $toolbox->getRedBean();
15354
-		$this->associationManager = $this->redbean->getAssociationManager();
15355
-	}
15356
-
15357
-	/**
15358
-	 * Recursively turns the keys of an array into
15359
-	 * camelCase.
15360
-	 *
15361
-	 * @param array   $array       array to camelize
15362
-	 * @param boolean $dolphinMode whether you want the exception for IDs.
15363
-	 *
15364
-	 * @return array
15365
-	 */
15366
-	public function camelfy( $array, $dolphinMode = FALSE ) {
15367
-		$newArray = array();
15368
-		foreach( $array as $key => $element ) {
15369
-			$newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){
15370
-				return strtoupper( $matches[1] );
15371
-			}, $key);
15372
-
15373
-			if ( $dolphinMode ) {
15374
-				$newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey );
15375
-			}
15376
-
15377
-			$newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element;
15378
-		}
15379
-		return $newArray;
15380
-	}
15381
-
15382
-	/**
15383
-	 * For better performance you can pass the tables in an array to this method.
15384
-	 * If the tables are available the duplication manager will not query them so
15385
-	 * this might be beneficial for performance.
15386
-	 *
15387
-	 * This method allows two array formats:
15388
-	 *
15389
-	 * <code>
15390
-	 * array( TABLE1, TABLE2 ... )
15391
-	 * </code>
15392
-	 *
15393
-	 * or
15394
-	 *
15395
-	 * <code>
15396
-	 * array( TABLE1 => array( COLUMN1, COLUMN2 ... ) ... )
15397
-	 * </code>
15398
-	 *
15399
-	 * @param array $tables a table cache array
15400
-	 *
15401
-	 * @return void
15402
-	 */
15403
-	public function setTables( $tables )
15404
-	{
15405
-		foreach ( $tables as $key => $value ) {
15406
-			if ( is_numeric( $key ) ) {
15407
-				$this->tables[] = $value;
15408
-			} else {
15409
-				$this->tables[]      = $key;
15410
-				$this->columns[$key] = $value;
15411
-			}
15412
-		}
15413
-
15414
-		$this->cacheTables = TRUE;
15415
-	}
15416
-
15417
-	/**
15418
-	 * Returns a schema array for cache.
15419
-	 * You can use the return value of this method as a cache,
15420
-	 * store it in RAM or on disk and pass it to setTables later.
15421
-	 *
15422
-	 * @return array
15423
-	 */
15424
-	public function getSchema()
15425
-	{
15426
-		return $this->columns;
15427
-	}
15428
-
15429
-	/**
15430
-	 * Indicates whether you want the duplication manager to cache the database schema.
15431
-	 * If this flag is set to TRUE the duplication manager will query the database schema
15432
-	 * only once. Otherwise the duplicationmanager will, by default, query the schema
15433
-	 * every time a duplication action is performed (dup()).
15434
-	 *
15435
-	 * @param boolean $yesNo TRUE to use caching, FALSE otherwise
15436
-	 */
15437
-	public function setCacheTables( $yesNo )
15438
-	{
15439
-		$this->cacheTables = $yesNo;
15440
-	}
15441
-
15442
-	/**
15443
-	 * A filter array is an array with table names.
15444
-	 * By setting a table filter you can make the duplication manager only take into account
15445
-	 * certain bean types. Other bean types will be ignored when exporting or making a
15446
-	 * deep copy. If no filters are set all types will be taking into account, this is
15447
-	 * the default behavior.
15448
-	 *
15449
-	 * @param array $filters list of tables to be filtered
15450
-	 *
15451
-	 * @return void
15452
-	 */
15453
-	public function setFilters( $filters )
15454
-	{
15455
-		if ( !is_array( $filters ) ) {
15456
-			$filters = array( $filters );
15457
-		}
15458
-
15459
-		$this->filters = $filters;
15460
-	}
15461
-
15462
-	/**
15463
-	 * Makes a copy of a bean. This method makes a deep copy
15464
-	 * of the bean.The copy will have the following features.
15465
-	 * - All beans in own-lists will be duplicated as well
15466
-	 * - All references to shared beans will be copied but not the shared beans themselves
15467
-	 * - All references to parent objects (_id fields) will be copied but not the parents themselves
15468
-	 * In most cases this is the desired scenario for copying beans.
15469
-	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
15470
-	 * (i.e. one that already has been processed) the ID of the bean will be returned.
15471
-	 * This should not happen though.
15472
-	 *
15473
-	 * Note:
15474
-	 * This function does a reflectional database query so it may be slow.
15475
-	 *
15476
-	 * Note:
15477
-	 * this function actually passes the arguments to a protected function called
15478
-	 * duplicate() that does all the work. This method takes care of creating a clone
15479
-	 * of the bean to avoid the bean getting tainted (triggering saving when storing it).
15480
-	 *
15481
-	 * @param OODBBean $bean        bean to be copied
15482
-	 * @param array    $trail       for internal usage, pass array()
15483
-	 * @param boolean  $preserveIDs for internal usage
15484
-	 *
15485
-	 * @return OODBBean
15486
-	 */
15487
-	public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
15488
-	{
15489
-		if ( !count( $this->tables ) ) {
15490
-			$this->tables = $this->toolbox->getWriter()->getTables();
15491
-		}
15492
-
15493
-		if ( !count( $this->columns ) ) {
15494
-			foreach ( $this->tables as $table ) {
15495
-				$this->columns[$table] = $this->toolbox->getWriter()->getColumns( $table );
15496
-			}
15497
-		}
15498
-
15499
-		$rs = $this->duplicate( ( clone $bean ), $trail, $preserveIDs );
15500
-
15501
-		if ( !$this->cacheTables ) {
15502
-			$this->tables  = array();
15503
-			$this->columns = array();
15504
-		}
15505
-
15506
-		return $rs;
15507
-	}
15508
-
15509
-	/**
15510
-	 * Exports a collection of beans recursively.
15511
-	 * This method will export an array of beans in the first argument to a
15512
-	 * set of arrays. This can be used to send JSON or XML representations
15513
-	 * of bean hierarchies to the client.
15514
-	 *
15515
-	 * For every bean in the array this method will export:
15516
-	 *
15517
-	 * - contents of the bean
15518
-	 * - all own bean lists (recursively)
15519
-	 * - all shared beans (but not THEIR own lists)
15520
-	 *
15521
-	 * If the second parameter is set to TRUE the parents of the beans in the
15522
-	 * array will be exported as well (but not THEIR parents).
15523
-	 *
15524
-	 * The third parameter can be used to provide a white-list array
15525
-	 * for filtering. This is an array of strings representing type names,
15526
-	 * only the type names in the filter list will be exported.
15527
-	 *
15528
-	 * The fourth parameter can be used to change the keys of the resulting
15529
-	 * export arrays. The default mode is 'snake case' but this leaves the
15530
-	 * keys as-is, because 'snake' is the default case style used by
15531
-	 * RedBeanPHP in the database. You can set this to 'camel' for
15532
-	 * camel cased keys or 'dolphin' (same as camelcase but id will be
15533
-	 * converted to ID instead of Id).
15534
-	 *
15535
-	 * @param array|OODBBean $beans     beans to be exported
15536
-	 * @param boolean        $parents   also export parents
15537
-	 * @param array          $filters   only these types (whitelist)
15538
-	 * @param string         $caseStyle case style identifier
15539
-	 *
15540
-	 * @return array
15541
-	 */
15542
-	public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake')
15543
-	{
15544
-		$array = array();
15545
-		if ( !is_array( $beans ) ) {
15546
-			$beans = array( $beans );
15547
-		}
15548
-		foreach ( $beans as $bean ) {
15549
-			$this->setFilters( $filters );
15550
-			$duplicate = $this->dup( $bean, array(), TRUE );
15551
-			$array[]   = $duplicate->export( FALSE, $parents, FALSE, $filters );
15552
-		}
15553
-		if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array );
15554
-		if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, TRUE );
15555
-		return $array;
15556
-	}
15134
+    /**
15135
+     * @var ToolBox
15136
+     */
15137
+    protected $toolbox;
15138
+
15139
+    /**
15140
+     * @var AssociationManager
15141
+     */
15142
+    protected $associationManager;
15143
+
15144
+    /**
15145
+     * @var OODB
15146
+     */
15147
+    protected $redbean;
15148
+
15149
+    /**
15150
+     * @var array
15151
+     */
15152
+    protected $tables = array();
15153
+
15154
+    /**
15155
+     * @var array
15156
+     */
15157
+    protected $columns = array();
15158
+
15159
+    /**
15160
+     * @var array
15161
+     */
15162
+    protected $filters = array();
15163
+
15164
+    /**
15165
+     * @var array
15166
+     */
15167
+    protected $cacheTables = FALSE;
15168
+
15169
+    /**
15170
+     * Copies the shared beans in a bean, i.e. all the sharedBean-lists.
15171
+     *
15172
+     * @param OODBBean $copy   target bean to copy lists to
15173
+     * @param string   $shared name of the shared list
15174
+     * @param array    $beans  array with shared beans to copy
15175
+     *
15176
+     * @return void
15177
+     */
15178
+    private function copySharedBeans( OODBBean $copy, $shared, $beans )
15179
+    {
15180
+        $copy->$shared = array();
15181
+
15182
+        foreach ( $beans as $subBean ) {
15183
+            array_push( $copy->$shared, $subBean );
15184
+        }
15185
+    }
15186
+
15187
+    /**
15188
+     * Copies the own beans in a bean, i.e. all the ownBean-lists.
15189
+     * Each bean in the own-list belongs exclusively to its owner so
15190
+     * we need to invoke the duplicate method again to duplicate each bean here.
15191
+     *
15192
+     * @param OODBBean $copy        target bean to copy lists to
15193
+     * @param string   $owned       name of the own list
15194
+     * @param array    $beans       array with shared beans to copy
15195
+     * @param array    $trail       array with former beans to detect recursion
15196
+     * @param boolean  $preserveIDs TRUE means preserve IDs, for export only
15197
+     *
15198
+     * @return void
15199
+     */
15200
+    private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs )
15201
+    {
15202
+        $copy->$owned = array();
15203
+        foreach ( $beans as $subBean ) {
15204
+            array_push( $copy->$owned, $this->duplicate( $subBean, $trail, $preserveIDs ) );
15205
+        }
15206
+    }
15207
+
15208
+    /**
15209
+     * Creates a copy of bean $bean and copies all primitive properties (not lists)
15210
+     * and the parents beans to the newly created bean. Also sets the ID of the bean
15211
+     * to 0.
15212
+     *
15213
+     * @param OODBBean $bean bean to copy
15214
+     *
15215
+     * @return OODBBean
15216
+     */
15217
+    private function createCopy( OODBBean $bean )
15218
+    {
15219
+        $type = $bean->getMeta( 'type' );
15220
+
15221
+        $copy = $this->redbean->dispense( $type );
15222
+        $copy->setMeta( 'sys.dup-from-id', $bean->id );
15223
+        $copy->setMeta( 'sys.old-id', $bean->id );
15224
+        $copy->importFrom( $bean );
15225
+        $copy->id = 0;
15226
+
15227
+        return $copy;
15228
+    }
15229
+
15230
+    /**
15231
+     * Generates a key from the bean type and its ID and determines if the bean
15232
+     * occurs in the trail, if not the bean will be added to the trail.
15233
+     * Returns TRUE if the bean occurs in the trail and FALSE otherwise.
15234
+     *
15235
+     * @param array    $trail list of former beans
15236
+     * @param OODBBean $bean  currently selected bean
15237
+     *
15238
+     * @return boolean
15239
+     */
15240
+    private function inTrailOrAdd( &$trail, OODBBean $bean )
15241
+    {
15242
+        $type = $bean->getMeta( 'type' );
15243
+        $key  = $type . $bean->getID();
15244
+
15245
+        if ( isset( $trail[$key] ) ) {
15246
+            return TRUE;
15247
+        }
15248
+
15249
+        $trail[$key] = $bean;
15250
+
15251
+        return FALSE;
15252
+    }
15253
+
15254
+    /**
15255
+     * Given the type name of a bean this method returns the canonical names
15256
+     * of the own-list and the shared-list properties respectively.
15257
+     * Returns a list with two elements: name of the own-list, and name
15258
+     * of the shared list.
15259
+     *
15260
+     * @param string $typeName bean type name
15261
+     *
15262
+     * @return array
15263
+     */
15264
+    private function getListNames( $typeName )
15265
+    {
15266
+        $owned  = 'own' . ucfirst( $typeName );
15267
+        $shared = 'shared' . ucfirst( $typeName );
15268
+
15269
+        return array( $owned, $shared );
15270
+    }
15271
+
15272
+    /**
15273
+     * Determines whether the bean has an own list based on
15274
+     * schema inspection from realtime schema or cache.
15275
+     *
15276
+     * @param string $type   bean type to get list for
15277
+     * @param string $target type of list you want to detect
15278
+     *
15279
+     * @return boolean
15280
+     */
15281
+    protected function hasOwnList( $type, $target )
15282
+    {
15283
+        return isset( $this->columns[$target][$type . '_id'] );
15284
+    }
15285
+
15286
+    /**
15287
+     * Determines whether the bea has a shared list based on
15288
+     * schema inspection from realtime schema or cache.
15289
+     *
15290
+     * @param string $type   bean type to get list for
15291
+     * @param string $target type of list you are looking for
15292
+     *
15293
+     * @return boolean
15294
+     */
15295
+    protected function hasSharedList( $type, $target )
15296
+    {
15297
+        return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables );
15298
+    }
15299
+
15300
+    /**
15301
+     * @see DuplicationManager::dup
15302
+     *
15303
+     * @param OODBBean $bean        bean to be copied
15304
+     * @param array    $trail       trail to prevent infinite loops
15305
+     * @param boolean  $preserveIDs preserve IDs
15306
+     *
15307
+     * @return OODBBean
15308
+     */
15309
+    protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
15310
+    {
15311
+        if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean;
15312
+
15313
+        $type = $bean->getMeta( 'type' );
15314
+
15315
+        $copy = $this->createCopy( $bean );
15316
+        foreach ( $this->tables as $table ) {
15317
+
15318
+            if ( !empty( $this->filters ) ) {
15319
+                if ( !in_array( $table, $this->filters ) ) continue;
15320
+            }
15321
+
15322
+            list( $owned, $shared ) = $this->getListNames( $table );
15323
+
15324
+            if ( $this->hasSharedList( $type, $table ) ) {
15325
+                if ( $beans = $bean->$shared ) {
15326
+                    $this->copySharedBeans( $copy, $shared, $beans );
15327
+                }
15328
+            } elseif ( $this->hasOwnList( $type, $table ) ) {
15329
+                if ( $beans = $bean->$owned ) {
15330
+                    $this->copyOwnBeans( $copy, $owned, $beans, $trail, $preserveIDs );
15331
+                }
15332
+
15333
+                $copy->setMeta( 'sys.shadow.' . $owned, NULL );
15334
+            }
15335
+
15336
+            $copy->setMeta( 'sys.shadow.' . $shared, NULL );
15337
+        }
15338
+
15339
+        $copy->id = ( $preserveIDs ) ? $bean->id : $copy->id;
15340
+
15341
+        return $copy;
15342
+    }
15343
+
15344
+    /**
15345
+     * Constructor,
15346
+     * creates a new instance of DupManager.
15347
+     *
15348
+     * @param ToolBox $toolbox
15349
+     */
15350
+    public function __construct( ToolBox $toolbox )
15351
+    {
15352
+        $this->toolbox            = $toolbox;
15353
+        $this->redbean            = $toolbox->getRedBean();
15354
+        $this->associationManager = $this->redbean->getAssociationManager();
15355
+    }
15356
+
15357
+    /**
15358
+     * Recursively turns the keys of an array into
15359
+     * camelCase.
15360
+     *
15361
+     * @param array   $array       array to camelize
15362
+     * @param boolean $dolphinMode whether you want the exception for IDs.
15363
+     *
15364
+     * @return array
15365
+     */
15366
+    public function camelfy( $array, $dolphinMode = FALSE ) {
15367
+        $newArray = array();
15368
+        foreach( $array as $key => $element ) {
15369
+            $newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){
15370
+                return strtoupper( $matches[1] );
15371
+            }, $key);
15372
+
15373
+            if ( $dolphinMode ) {
15374
+                $newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey );
15375
+            }
15376
+
15377
+            $newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element;
15378
+        }
15379
+        return $newArray;
15380
+    }
15381
+
15382
+    /**
15383
+     * For better performance you can pass the tables in an array to this method.
15384
+     * If the tables are available the duplication manager will not query them so
15385
+     * this might be beneficial for performance.
15386
+     *
15387
+     * This method allows two array formats:
15388
+     *
15389
+     * <code>
15390
+     * array( TABLE1, TABLE2 ... )
15391
+     * </code>
15392
+     *
15393
+     * or
15394
+     *
15395
+     * <code>
15396
+     * array( TABLE1 => array( COLUMN1, COLUMN2 ... ) ... )
15397
+     * </code>
15398
+     *
15399
+     * @param array $tables a table cache array
15400
+     *
15401
+     * @return void
15402
+     */
15403
+    public function setTables( $tables )
15404
+    {
15405
+        foreach ( $tables as $key => $value ) {
15406
+            if ( is_numeric( $key ) ) {
15407
+                $this->tables[] = $value;
15408
+            } else {
15409
+                $this->tables[]      = $key;
15410
+                $this->columns[$key] = $value;
15411
+            }
15412
+        }
15413
+
15414
+        $this->cacheTables = TRUE;
15415
+    }
15416
+
15417
+    /**
15418
+     * Returns a schema array for cache.
15419
+     * You can use the return value of this method as a cache,
15420
+     * store it in RAM or on disk and pass it to setTables later.
15421
+     *
15422
+     * @return array
15423
+     */
15424
+    public function getSchema()
15425
+    {
15426
+        return $this->columns;
15427
+    }
15428
+
15429
+    /**
15430
+     * Indicates whether you want the duplication manager to cache the database schema.
15431
+     * If this flag is set to TRUE the duplication manager will query the database schema
15432
+     * only once. Otherwise the duplicationmanager will, by default, query the schema
15433
+     * every time a duplication action is performed (dup()).
15434
+     *
15435
+     * @param boolean $yesNo TRUE to use caching, FALSE otherwise
15436
+     */
15437
+    public function setCacheTables( $yesNo )
15438
+    {
15439
+        $this->cacheTables = $yesNo;
15440
+    }
15441
+
15442
+    /**
15443
+     * A filter array is an array with table names.
15444
+     * By setting a table filter you can make the duplication manager only take into account
15445
+     * certain bean types. Other bean types will be ignored when exporting or making a
15446
+     * deep copy. If no filters are set all types will be taking into account, this is
15447
+     * the default behavior.
15448
+     *
15449
+     * @param array $filters list of tables to be filtered
15450
+     *
15451
+     * @return void
15452
+     */
15453
+    public function setFilters( $filters )
15454
+    {
15455
+        if ( !is_array( $filters ) ) {
15456
+            $filters = array( $filters );
15457
+        }
15458
+
15459
+        $this->filters = $filters;
15460
+    }
15461
+
15462
+    /**
15463
+     * Makes a copy of a bean. This method makes a deep copy
15464
+     * of the bean.The copy will have the following features.
15465
+     * - All beans in own-lists will be duplicated as well
15466
+     * - All references to shared beans will be copied but not the shared beans themselves
15467
+     * - All references to parent objects (_id fields) will be copied but not the parents themselves
15468
+     * In most cases this is the desired scenario for copying beans.
15469
+     * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
15470
+     * (i.e. one that already has been processed) the ID of the bean will be returned.
15471
+     * This should not happen though.
15472
+     *
15473
+     * Note:
15474
+     * This function does a reflectional database query so it may be slow.
15475
+     *
15476
+     * Note:
15477
+     * this function actually passes the arguments to a protected function called
15478
+     * duplicate() that does all the work. This method takes care of creating a clone
15479
+     * of the bean to avoid the bean getting tainted (triggering saving when storing it).
15480
+     *
15481
+     * @param OODBBean $bean        bean to be copied
15482
+     * @param array    $trail       for internal usage, pass array()
15483
+     * @param boolean  $preserveIDs for internal usage
15484
+     *
15485
+     * @return OODBBean
15486
+     */
15487
+    public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
15488
+    {
15489
+        if ( !count( $this->tables ) ) {
15490
+            $this->tables = $this->toolbox->getWriter()->getTables();
15491
+        }
15492
+
15493
+        if ( !count( $this->columns ) ) {
15494
+            foreach ( $this->tables as $table ) {
15495
+                $this->columns[$table] = $this->toolbox->getWriter()->getColumns( $table );
15496
+            }
15497
+        }
15498
+
15499
+        $rs = $this->duplicate( ( clone $bean ), $trail, $preserveIDs );
15500
+
15501
+        if ( !$this->cacheTables ) {
15502
+            $this->tables  = array();
15503
+            $this->columns = array();
15504
+        }
15505
+
15506
+        return $rs;
15507
+    }
15508
+
15509
+    /**
15510
+     * Exports a collection of beans recursively.
15511
+     * This method will export an array of beans in the first argument to a
15512
+     * set of arrays. This can be used to send JSON or XML representations
15513
+     * of bean hierarchies to the client.
15514
+     *
15515
+     * For every bean in the array this method will export:
15516
+     *
15517
+     * - contents of the bean
15518
+     * - all own bean lists (recursively)
15519
+     * - all shared beans (but not THEIR own lists)
15520
+     *
15521
+     * If the second parameter is set to TRUE the parents of the beans in the
15522
+     * array will be exported as well (but not THEIR parents).
15523
+     *
15524
+     * The third parameter can be used to provide a white-list array
15525
+     * for filtering. This is an array of strings representing type names,
15526
+     * only the type names in the filter list will be exported.
15527
+     *
15528
+     * The fourth parameter can be used to change the keys of the resulting
15529
+     * export arrays. The default mode is 'snake case' but this leaves the
15530
+     * keys as-is, because 'snake' is the default case style used by
15531
+     * RedBeanPHP in the database. You can set this to 'camel' for
15532
+     * camel cased keys or 'dolphin' (same as camelcase but id will be
15533
+     * converted to ID instead of Id).
15534
+     *
15535
+     * @param array|OODBBean $beans     beans to be exported
15536
+     * @param boolean        $parents   also export parents
15537
+     * @param array          $filters   only these types (whitelist)
15538
+     * @param string         $caseStyle case style identifier
15539
+     *
15540
+     * @return array
15541
+     */
15542
+    public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake')
15543
+    {
15544
+        $array = array();
15545
+        if ( !is_array( $beans ) ) {
15546
+            $beans = array( $beans );
15547
+        }
15548
+        foreach ( $beans as $bean ) {
15549
+            $this->setFilters( $filters );
15550
+            $duplicate = $this->dup( $bean, array(), TRUE );
15551
+            $array[]   = $duplicate->export( FALSE, $parents, FALSE, $filters );
15552
+        }
15553
+        if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array );
15554
+        if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, TRUE );
15555
+        return $array;
15556
+    }
15557 15557
 }
15558 15558
 }
15559 15559
 
@@ -15584,65 +15584,65 @@  discard block
 block discarded – undo
15584 15584
  */
15585 15585
 class ArrayTool
15586 15586
 {
15587
-	/**
15588
-	 * Generates question mark slots for an array of values.
15589
-	 * Given an array and an optional template string this method
15590
-	 * will produce string containing parameter slots for use in
15591
-	 * an SQL query string.
15592
-	 *
15593
-	 * Usage:
15594
-	 *
15595
-	 * <code>
15596
-	 * R::genSlots( array( 'a', 'b' ) );
15597
-	 * </code>
15598
-	 *
15599
-	 * The statement in the example will produce the string:
15600
-	 * '?,?'.
15601
-	 *
15602
-	 * Another example, using a template string:
15603
-	 *
15604
-	 * <code>
15605
-	 * R::genSlots( array('a', 'b'), ' IN( %s ) ' );
15606
-	 * </code>
15607
-	 *
15608
-	 * The statement in the example will produce the string:
15609
-	 * ' IN( ?,? ) '.
15610
-	 *
15611
-	 * @param array  $array    array to generate question mark slots for
15612
-	 * @param string $template template to use
15613
-	 *
15614
-	 * @return string
15615
-	 */
15616
-	public static function genSlots( $array, $template = NULL )
15617
-	{
15618
-		$str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : '';
15619
-		return ( is_null( $template ) ||  $str === '' ) ? $str : sprintf( $template, $str );
15620
-	}
15621
-
15622
-	/**
15623
-	 * Flattens a multi dimensional bindings array for use with genSlots().
15624
-	 *
15625
-	 * Usage:
15626
-	 *
15627
-	 * <code>
15628
-	 * R::flat( array( 'a', array( 'b' ), 'c' ) );
15629
-	 * </code>
15630
-	 *
15631
-	 * produces an array like: [ 'a', 'b', 'c' ]
15632
-	 *
15633
-	 * @param array $array  array to flatten
15634
-	 * @param array $result result array parameter (for recursion)
15635
-	 *
15636
-	 * @return array
15637
-	 */
15638
-	public static function flat( $array, $result = array() )
15639
-	{		
15640
-		foreach( $array as $value ) {
15641
-			if ( is_array( $value ) ) $result = self::flat( $value, $result );
15642
-			else $result[] = $value;
15643
-		}
15644
-		return $result;
15645
-	}
15587
+    /**
15588
+     * Generates question mark slots for an array of values.
15589
+     * Given an array and an optional template string this method
15590
+     * will produce string containing parameter slots for use in
15591
+     * an SQL query string.
15592
+     *
15593
+     * Usage:
15594
+     *
15595
+     * <code>
15596
+     * R::genSlots( array( 'a', 'b' ) );
15597
+     * </code>
15598
+     *
15599
+     * The statement in the example will produce the string:
15600
+     * '?,?'.
15601
+     *
15602
+     * Another example, using a template string:
15603
+     *
15604
+     * <code>
15605
+     * R::genSlots( array('a', 'b'), ' IN( %s ) ' );
15606
+     * </code>
15607
+     *
15608
+     * The statement in the example will produce the string:
15609
+     * ' IN( ?,? ) '.
15610
+     *
15611
+     * @param array  $array    array to generate question mark slots for
15612
+     * @param string $template template to use
15613
+     *
15614
+     * @return string
15615
+     */
15616
+    public static function genSlots( $array, $template = NULL )
15617
+    {
15618
+        $str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : '';
15619
+        return ( is_null( $template ) ||  $str === '' ) ? $str : sprintf( $template, $str );
15620
+    }
15621
+
15622
+    /**
15623
+     * Flattens a multi dimensional bindings array for use with genSlots().
15624
+     *
15625
+     * Usage:
15626
+     *
15627
+     * <code>
15628
+     * R::flat( array( 'a', array( 'b' ), 'c' ) );
15629
+     * </code>
15630
+     *
15631
+     * produces an array like: [ 'a', 'b', 'c' ]
15632
+     *
15633
+     * @param array $array  array to flatten
15634
+     * @param array $result result array parameter (for recursion)
15635
+     *
15636
+     * @return array
15637
+     */
15638
+    public static function flat( $array, $result = array() )
15639
+    {		
15640
+        foreach( $array as $value ) {
15641
+            if ( is_array( $value ) ) $result = self::flat( $value, $result );
15642
+            else $result[] = $value;
15643
+        }
15644
+        return $result;
15645
+    }
15646 15646
 }
15647 15647
 }
15648 15648
 
@@ -15667,191 +15667,191 @@  discard block
 block discarded – undo
15667 15667
  */
15668 15668
 class DispenseHelper
15669 15669
 {
15670
-	/**
15671
-	 * @var boolean
15672
-	 */
15673
-	private static $enforceNamingPolicy = TRUE;
15674
-
15675
-	/**
15676
-	 * Sets the enforce naming policy flag. If set to
15677
-	 * TRUE the RedBeanPHP naming policy will be enforced.
15678
-	 * Otherwise it will not. Use at your own risk.
15679
-	 * Setting this to FALSE is not recommended.
15680
-	 *
15681
-	 * @param boolean $yesNo whether to enforce RB name policy
15682
-	 *
15683
-	 * @return void
15684
-	 */
15685
-	public static function setEnforceNamingPolicy( $yesNo )
15686
-	{
15687
-		self::$enforceNamingPolicy = (boolean) $yesNo;
15688
-	}
15689
-
15690
-	/**
15691
-	 * Checks whether the bean type conforms to the RedbeanPHP
15692
-	 * naming policy. This method will throw an exception if the
15693
-	 * type does not conform to the RedBeanPHP database column naming
15694
-	 * policy.
15695
-	 *
15696
-	 * The RedBeanPHP naming policy for beans states that valid
15697
-	 * bean type names contain only:
15698
-	 *
15699
-	 * - lowercase alphanumeric characters a-z
15700
-	 * - numbers 0-9
15701
-	 * - at least one character
15702
-	 *
15703
-	 * Although there are no restrictions on length, database
15704
-	 * specific implementations may apply further restrictions
15705
-	 * regarding the length of a table which means these restrictions
15706
-	 * also apply to bean types.
15707
-	 *
15708
-	 * The RedBeanPHP naming policy ensures that, without any
15709
-	 * configuration, the core functionalities work across many
15710
-	 * databases and operating systems, including those that are
15711
-	 * case insensitive or restricted to the ASCII character set.
15712
-	 *
15713
-	 * Although these restrictions can be bypassed, this is not
15714
-	 * recommended.
15715
-	 *
15716
-	 * @param string $type type of bean
15717
-	 *
15718
-	 * @return void
15719
-	 */
15720
-	public static function checkType( $type )
15721
-	{
15722
-		if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) {
15723
-			throw new RedException( 'Invalid type: ' . $type );
15724
-		}
15725
-	}
15726
-
15727
-	/**
15728
-	 * Dispenses a new RedBean OODB Bean for use with
15729
-	 * the rest of the methods. RedBeanPHP thinks in beans, the bean is the
15730
-	 * primary way to interact with RedBeanPHP and the database managed by
15731
-	 * RedBeanPHP. To load, store and delete data from the database using RedBeanPHP
15732
-	 * you exchange these RedBeanPHP OODB Beans. The only exception to this rule
15733
-	 * are the raw query methods like R::getCell() or R::exec() and so on.
15734
-	 * The dispense method is the 'preferred way' to create a new bean.
15735
-	 *
15736
-	 * Usage:
15737
-	 *
15738
-	 * <code>
15739
-	 * $book = R::dispense( 'book' );
15740
-	 * $book->title = 'My Book';
15741
-	 * R::store( $book );
15742
-	 * </code>
15743
-	 *
15744
-	 * This method can also be used to create an entire bean graph at once.
15745
-	 * Given an array with keys specifying the property names of the beans
15746
-	 * and a special _type key to indicate the type of bean, one can
15747
-	 * make the Dispense Helper generate an entire hierarchy of beans, including
15748
-	 * lists. To make dispense() generate a list, simply add a key like:
15749
-	 * ownXList or sharedXList where X is the type of beans it contains and
15750
-	 * a set its value to an array filled with arrays representing the beans.
15751
-	 * Note that, although the type may have been hinted at in the list name,
15752
-	 * you still have to specify a _type key for every bean array in the list.
15753
-	 * Note that, if you specify an array to generate a bean graph, the number
15754
-	 * parameter will be ignored.
15755
-	 *
15756
-	 * Usage:
15757
-	 *
15758
-	 * <code>
15759
-	 *  $book = R::dispense( [
15670
+    /**
15671
+     * @var boolean
15672
+     */
15673
+    private static $enforceNamingPolicy = TRUE;
15674
+
15675
+    /**
15676
+     * Sets the enforce naming policy flag. If set to
15677
+     * TRUE the RedBeanPHP naming policy will be enforced.
15678
+     * Otherwise it will not. Use at your own risk.
15679
+     * Setting this to FALSE is not recommended.
15680
+     *
15681
+     * @param boolean $yesNo whether to enforce RB name policy
15682
+     *
15683
+     * @return void
15684
+     */
15685
+    public static function setEnforceNamingPolicy( $yesNo )
15686
+    {
15687
+        self::$enforceNamingPolicy = (boolean) $yesNo;
15688
+    }
15689
+
15690
+    /**
15691
+     * Checks whether the bean type conforms to the RedbeanPHP
15692
+     * naming policy. This method will throw an exception if the
15693
+     * type does not conform to the RedBeanPHP database column naming
15694
+     * policy.
15695
+     *
15696
+     * The RedBeanPHP naming policy for beans states that valid
15697
+     * bean type names contain only:
15698
+     *
15699
+     * - lowercase alphanumeric characters a-z
15700
+     * - numbers 0-9
15701
+     * - at least one character
15702
+     *
15703
+     * Although there are no restrictions on length, database
15704
+     * specific implementations may apply further restrictions
15705
+     * regarding the length of a table which means these restrictions
15706
+     * also apply to bean types.
15707
+     *
15708
+     * The RedBeanPHP naming policy ensures that, without any
15709
+     * configuration, the core functionalities work across many
15710
+     * databases and operating systems, including those that are
15711
+     * case insensitive or restricted to the ASCII character set.
15712
+     *
15713
+     * Although these restrictions can be bypassed, this is not
15714
+     * recommended.
15715
+     *
15716
+     * @param string $type type of bean
15717
+     *
15718
+     * @return void
15719
+     */
15720
+    public static function checkType( $type )
15721
+    {
15722
+        if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) {
15723
+            throw new RedException( 'Invalid type: ' . $type );
15724
+        }
15725
+    }
15726
+
15727
+    /**
15728
+     * Dispenses a new RedBean OODB Bean for use with
15729
+     * the rest of the methods. RedBeanPHP thinks in beans, the bean is the
15730
+     * primary way to interact with RedBeanPHP and the database managed by
15731
+     * RedBeanPHP. To load, store and delete data from the database using RedBeanPHP
15732
+     * you exchange these RedBeanPHP OODB Beans. The only exception to this rule
15733
+     * are the raw query methods like R::getCell() or R::exec() and so on.
15734
+     * The dispense method is the 'preferred way' to create a new bean.
15735
+     *
15736
+     * Usage:
15737
+     *
15738
+     * <code>
15739
+     * $book = R::dispense( 'book' );
15740
+     * $book->title = 'My Book';
15741
+     * R::store( $book );
15742
+     * </code>
15743
+     *
15744
+     * This method can also be used to create an entire bean graph at once.
15745
+     * Given an array with keys specifying the property names of the beans
15746
+     * and a special _type key to indicate the type of bean, one can
15747
+     * make the Dispense Helper generate an entire hierarchy of beans, including
15748
+     * lists. To make dispense() generate a list, simply add a key like:
15749
+     * ownXList or sharedXList where X is the type of beans it contains and
15750
+     * a set its value to an array filled with arrays representing the beans.
15751
+     * Note that, although the type may have been hinted at in the list name,
15752
+     * you still have to specify a _type key for every bean array in the list.
15753
+     * Note that, if you specify an array to generate a bean graph, the number
15754
+     * parameter will be ignored.
15755
+     *
15756
+     * Usage:
15757
+     *
15758
+     * <code>
15759
+     *  $book = R::dispense( [
15760 15760
      *   '_type' => 'book',
15761 15761
      *   'title'  => 'Gifted Programmers',
15762 15762
      *   'author' => [ '_type' => 'author', 'name' => 'Xavier' ],
15763 15763
      *   'ownPageList' => [ ['_type'=>'page', 'text' => '...'] ]
15764 15764
      * ] );
15765
-	 * </code>
15766
-	 *
15767
-	 * @param string|array $typeOrBeanArray   type or bean array to import
15768
-	 * @param integer      $num               number of beans to dispense
15769
-	 * @param boolean      $alwaysReturnArray if TRUE always returns the result as an array
15770
-	 *
15771
-	 * @return array|OODBBean
15772
-	 */
15773
-	public static function dispense( OODB $oodb, $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE ) {
15774
-
15775
-		if ( is_array($typeOrBeanArray) ) {
15776
-
15777
-			if ( !isset( $typeOrBeanArray['_type'] ) ) {
15778
-				$list = array();
15779
-				foreach( $typeOrBeanArray as $beanArray ) {
15780
-					if ( 
15781
-						!( is_array( $beanArray ) 
15782
-						&& isset( $beanArray['_type'] ) ) ) {
15783
-						throw new RedException( 'Invalid Array Bean' );
15784
-					}
15785
-				}
15786
-				foreach( $typeOrBeanArray as $beanArray ) $list[] = self::dispense( $oodb, $beanArray );
15787
-				return $list;
15788
-			}
15789
-
15790
-			$import = $typeOrBeanArray;
15791
-			$type = $import['_type'];
15792
-			unset( $import['_type'] );
15793
-		} else {
15794
-			$type = $typeOrBeanArray;
15795
-		}
15796
-
15797
-		if (self::$enforceNamingPolicy) self::checkType( $type );
15798
-
15799
-		$beanOrBeans = $oodb->dispense( $type, $num, $alwaysReturnArray );
15800
-
15801
-		if ( isset( $import ) ) {
15802
-			$beanOrBeans->import( $import );
15803
-		}
15804
-
15805
-		return $beanOrBeans;
15806
-	}
15765
+     * </code>
15766
+     *
15767
+     * @param string|array $typeOrBeanArray   type or bean array to import
15768
+     * @param integer      $num               number of beans to dispense
15769
+     * @param boolean      $alwaysReturnArray if TRUE always returns the result as an array
15770
+     *
15771
+     * @return array|OODBBean
15772
+     */
15773
+    public static function dispense( OODB $oodb, $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE ) {
15774
+
15775
+        if ( is_array($typeOrBeanArray) ) {
15776
+
15777
+            if ( !isset( $typeOrBeanArray['_type'] ) ) {
15778
+                $list = array();
15779
+                foreach( $typeOrBeanArray as $beanArray ) {
15780
+                    if ( 
15781
+                        !( is_array( $beanArray ) 
15782
+                        && isset( $beanArray['_type'] ) ) ) {
15783
+                        throw new RedException( 'Invalid Array Bean' );
15784
+                    }
15785
+                }
15786
+                foreach( $typeOrBeanArray as $beanArray ) $list[] = self::dispense( $oodb, $beanArray );
15787
+                return $list;
15788
+            }
15789
+
15790
+            $import = $typeOrBeanArray;
15791
+            $type = $import['_type'];
15792
+            unset( $import['_type'] );
15793
+        } else {
15794
+            $type = $typeOrBeanArray;
15795
+        }
15796
+
15797
+        if (self::$enforceNamingPolicy) self::checkType( $type );
15798
+
15799
+        $beanOrBeans = $oodb->dispense( $type, $num, $alwaysReturnArray );
15800
+
15801
+        if ( isset( $import ) ) {
15802
+            $beanOrBeans->import( $import );
15803
+        }
15804
+
15805
+        return $beanOrBeans;
15806
+    }
15807 15807
 	
15808 15808
 	
15809
-	/**
15810
-	 * Takes a comma separated list of bean types
15811
-	 * and dispenses these beans. For each type in the list
15812
-	 * you can specify the number of beans to be dispensed.
15813
-	 *
15814
-	 * Usage:
15815
-	 *
15816
-	 * <code>
15817
-	 * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' );
15818
-	 * </code>
15819
-	 *
15820
-	 * This will dispense a book, a page and a text. This way you can
15821
-	 * quickly dispense beans of various types in just one line of code.
15822
-	 *
15823
-	 * Usage:
15824
-	 *
15825
-	 * <code>
15826
-	 * list($book, $pages) = R::dispenseAll('book,page*100');
15827
-	 * </code>
15828
-	 *
15829
-	 * This returns an array with a book bean and then another array
15830
-	 * containing 100 page beans.
15831
-	 *
15832
-	 * @param OODB    $oodb       OODB
15833
-	 * @param string  $order      a description of the desired dispense order using the syntax above
15834
-	 * @param boolean $onlyArrays return only arrays even if amount < 2
15835
-	 *
15836
-	 * @return array
15837
-	 */
15838
-	public static function dispenseAll( OODB $oodb, $order, $onlyArrays = FALSE )
15839
-	{
15840
-		$list = array();
15841
-
15842
-		foreach( explode( ',', $order ) as $order ) {
15843
-			if ( strpos( $order, '*' ) !== FALSE ) {
15844
-				list( $type, $amount ) = explode( '*', $order );
15845
-			} else {
15846
-				$type   = $order;
15847
-				$amount = 1;
15848
-			}
15849
-
15850
-			$list[] = self::dispense( $oodb, $type, $amount, $onlyArrays );
15851
-		}
15852
-
15853
-		return $list;
15854
-	}
15809
+    /**
15810
+     * Takes a comma separated list of bean types
15811
+     * and dispenses these beans. For each type in the list
15812
+     * you can specify the number of beans to be dispensed.
15813
+     *
15814
+     * Usage:
15815
+     *
15816
+     * <code>
15817
+     * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' );
15818
+     * </code>
15819
+     *
15820
+     * This will dispense a book, a page and a text. This way you can
15821
+     * quickly dispense beans of various types in just one line of code.
15822
+     *
15823
+     * Usage:
15824
+     *
15825
+     * <code>
15826
+     * list($book, $pages) = R::dispenseAll('book,page*100');
15827
+     * </code>
15828
+     *
15829
+     * This returns an array with a book bean and then another array
15830
+     * containing 100 page beans.
15831
+     *
15832
+     * @param OODB    $oodb       OODB
15833
+     * @param string  $order      a description of the desired dispense order using the syntax above
15834
+     * @param boolean $onlyArrays return only arrays even if amount < 2
15835
+     *
15836
+     * @return array
15837
+     */
15838
+    public static function dispenseAll( OODB $oodb, $order, $onlyArrays = FALSE )
15839
+    {
15840
+        $list = array();
15841
+
15842
+        foreach( explode( ',', $order ) as $order ) {
15843
+            if ( strpos( $order, '*' ) !== FALSE ) {
15844
+                list( $type, $amount ) = explode( '*', $order );
15845
+            } else {
15846
+                $type   = $order;
15847
+                $amount = 1;
15848
+            }
15849
+
15850
+            $list[] = self::dispense( $oodb, $type, $amount, $onlyArrays );
15851
+        }
15852
+
15853
+        return $list;
15854
+    }
15855 15855
 }
15856 15856
 }
15857 15857
 
@@ -15881,47 +15881,47 @@  discard block
 block discarded – undo
15881 15881
  */
15882 15882
 class Dump
15883 15883
 {
15884
-	/**
15885
-	 * Dumps bean data to array.
15886
-	 * Given a one or more beans this method will
15887
-	 * return an array containing first part of the string
15888
-	 * representation of each item in the array.
15889
-	 *
15890
-	 * Usage:
15891
-	 *
15892
-	 * <code>
15893
-	 * echo R::dump( $bean );
15894
-	 * </code>
15895
-	 *
15896
-	 * The example shows how to echo the result of a simple
15897
-	 * dump. This will print the string representation of the
15898
-	 * specified bean to the screen, limiting the output per bean
15899
-	 * to 35 characters to improve readability. Nested beans will
15900
-	 * also be dumped.
15901
-	 *
15902
-	 * @param OODBBean|array $data either a bean or an array of beans
15903
-	 *
15904
-	 * @return array
15905
-	 */
15906
-	public static function dump( $data )
15907
-	{
15908
-		$array = array();
15909
-		if ( $data instanceof OODBBean ) {
15910
-			$str = strval( $data );
15911
-			if (strlen($str) > 35) {
15912
-				$beanStr = substr( $str, 0, 35 ).'... ';
15913
-			} else {
15914
-				$beanStr = $str;
15915
-			}
15916
-			return $beanStr;
15917
-		}
15918
-		if ( is_array( $data ) ) {
15919
-			foreach( $data as $key => $item ) {
15920
-				$array[$key] = self::dump( $item );
15921
-			}
15922
-		}
15923
-		return $array;
15924
-	}
15884
+    /**
15885
+     * Dumps bean data to array.
15886
+     * Given a one or more beans this method will
15887
+     * return an array containing first part of the string
15888
+     * representation of each item in the array.
15889
+     *
15890
+     * Usage:
15891
+     *
15892
+     * <code>
15893
+     * echo R::dump( $bean );
15894
+     * </code>
15895
+     *
15896
+     * The example shows how to echo the result of a simple
15897
+     * dump. This will print the string representation of the
15898
+     * specified bean to the screen, limiting the output per bean
15899
+     * to 35 characters to improve readability. Nested beans will
15900
+     * also be dumped.
15901
+     *
15902
+     * @param OODBBean|array $data either a bean or an array of beans
15903
+     *
15904
+     * @return array
15905
+     */
15906
+    public static function dump( $data )
15907
+    {
15908
+        $array = array();
15909
+        if ( $data instanceof OODBBean ) {
15910
+            $str = strval( $data );
15911
+            if (strlen($str) > 35) {
15912
+                $beanStr = substr( $str, 0, 35 ).'... ';
15913
+            } else {
15914
+                $beanStr = $str;
15915
+            }
15916
+            return $beanStr;
15917
+        }
15918
+        if ( is_array( $data ) ) {
15919
+            foreach( $data as $key => $item ) {
15920
+                $array[$key] = self::dump( $item );
15921
+            }
15922
+        }
15923
+        return $array;
15924
+    }
15925 15925
 }
15926 15926
 }
15927 15927
 
@@ -15951,42 +15951,42 @@  discard block
 block discarded – undo
15951 15951
  */
15952 15952
 class MultiLoader
15953 15953
 {
15954
-	/**
15955
-	 * Loads multiple types of beans with the same ID.
15956
-	 * This might look like a strange method, however it can be useful
15957
-	 * for loading a one-to-one relation. In a typical 1-1 relation,
15958
-	 * you have two records sharing the same primary key.
15959
-	 * RedBeanPHP has only limited support for 1-1 relations.
15960
-	 * In general it is recommended to use 1-N for this.
15961
-	 *
15962
-	 * Usage:
15963
-	 *
15964
-	 * <code>
15965
-	 * list( $author, $bio ) = R::loadMulti( 'author, bio', $id );
15966
-	 * </code>
15967
-	 *
15968
-	 * @param OODB         $oodb  OODB object
15969
-	 * @param string|array $types the set of types to load at once
15970
-	 * @param mixed        $id    the common ID
15971
-	 *
15972
-	 * @return OODBBean
15973
-	 */
15974
-	public static function load( OODB $oodb, $types, $id )
15975
-	{
15976
-		if ( is_string( $types ) ) {
15977
-			$types = explode( ',', $types );
15978
-		}
15979
-
15980
-		if ( !is_array( $types ) ) {
15981
-			return array();
15982
-		}
15983
-
15984
-		foreach ( $types as $k => $typeItem ) {
15985
-			$types[$k] = $oodb->load( $typeItem, $id );
15986
-		}
15987
-
15988
-		return $types;
15989
-	}
15954
+    /**
15955
+     * Loads multiple types of beans with the same ID.
15956
+     * This might look like a strange method, however it can be useful
15957
+     * for loading a one-to-one relation. In a typical 1-1 relation,
15958
+     * you have two records sharing the same primary key.
15959
+     * RedBeanPHP has only limited support for 1-1 relations.
15960
+     * In general it is recommended to use 1-N for this.
15961
+     *
15962
+     * Usage:
15963
+     *
15964
+     * <code>
15965
+     * list( $author, $bio ) = R::loadMulti( 'author, bio', $id );
15966
+     * </code>
15967
+     *
15968
+     * @param OODB         $oodb  OODB object
15969
+     * @param string|array $types the set of types to load at once
15970
+     * @param mixed        $id    the common ID
15971
+     *
15972
+     * @return OODBBean
15973
+     */
15974
+    public static function load( OODB $oodb, $types, $id )
15975
+    {
15976
+        if ( is_string( $types ) ) {
15977
+            $types = explode( ',', $types );
15978
+        }
15979
+
15980
+        if ( !is_array( $types ) ) {
15981
+            return array();
15982
+        }
15983
+
15984
+        foreach ( $types as $k => $typeItem ) {
15985
+            $types[$k] = $oodb->load( $typeItem, $id );
15986
+        }
15987
+
15988
+        return $types;
15989
+    }
15990 15990
 }
15991 15991
 }
15992 15992
 
@@ -16019,64 +16019,64 @@  discard block
 block discarded – undo
16019 16019
  */
16020 16020
 class Transaction
16021 16021
 {
16022
-	/**
16023
-	 * Wraps a transaction around a closure or string callback.
16024
-	 * If an Exception is thrown inside, the operation is automatically rolled back.
16025
-	 * If no Exception happens, it commits automatically.
16026
-	 * It also supports (simulated) nested transactions (that is useful when
16027
-	 * you have many methods that needs transactions but are unaware of
16028
-	 * each other).
16029
-	 *
16030
-	 * Example:
16031
-	 *
16032
-	 * <code>
16033
-	 * $from = 1;
16034
-	 * $to = 2;
16035
-	 * $amount = 300;
16036
-	 *
16037
-	 * R::transaction(function() use($from, $to, $amount)
16038
-	 * {
16039
-	 *   $accountFrom = R::load('account', $from);
16040
-	 *   $accountTo = R::load('account', $to);
16041
-	 *   $accountFrom->money -= $amount;
16042
-	 *   $accountTo->money += $amount;
16043
-	 *   R::store($accountFrom);
16044
-	 *   R::store($accountTo);
16045
-	 * });
16046
-	 * </code>
16047
-	 *
16048
-	 * @param Adapter  $adapter  Database Adapter providing transaction mechanisms.
16049
-	 * @param callable $callback Closure (or other callable) with the transaction logic
16050
-	 *
16051
-	 * @return mixed
16052
-	 */
16053
-	public static function transaction( Adapter $adapter, $callback )
16054
-	{
16055
-		if ( !is_callable( $callback ) ) {
16056
-			throw new RedException( 'R::transaction needs a valid callback.' );
16057
-		}
16058
-
16059
-		static $depth = 0;
16060
-		$result = null;
16061
-		try {
16062
-			if ( $depth == 0 ) {
16063
-				$adapter->startTransaction();
16064
-			}
16065
-			$depth++;
16066
-			$result = call_user_func( $callback ); //maintain 5.2 compatibility
16067
-			$depth--;
16068
-			if ( $depth == 0 ) {
16069
-				$adapter->commit();
16070
-			}
16071
-		} catch ( \Exception $exception ) {
16072
-			$depth--;
16073
-			if ( $depth == 0 ) {
16074
-				$adapter->rollback();
16075
-			}
16076
-			throw $exception;
16077
-		}
16078
-		return $result;
16079
-	}
16022
+    /**
16023
+     * Wraps a transaction around a closure or string callback.
16024
+     * If an Exception is thrown inside, the operation is automatically rolled back.
16025
+     * If no Exception happens, it commits automatically.
16026
+     * It also supports (simulated) nested transactions (that is useful when
16027
+     * you have many methods that needs transactions but are unaware of
16028
+     * each other).
16029
+     *
16030
+     * Example:
16031
+     *
16032
+     * <code>
16033
+     * $from = 1;
16034
+     * $to = 2;
16035
+     * $amount = 300;
16036
+     *
16037
+     * R::transaction(function() use($from, $to, $amount)
16038
+     * {
16039
+     *   $accountFrom = R::load('account', $from);
16040
+     *   $accountTo = R::load('account', $to);
16041
+     *   $accountFrom->money -= $amount;
16042
+     *   $accountTo->money += $amount;
16043
+     *   R::store($accountFrom);
16044
+     *   R::store($accountTo);
16045
+     * });
16046
+     * </code>
16047
+     *
16048
+     * @param Adapter  $adapter  Database Adapter providing transaction mechanisms.
16049
+     * @param callable $callback Closure (or other callable) with the transaction logic
16050
+     *
16051
+     * @return mixed
16052
+     */
16053
+    public static function transaction( Adapter $adapter, $callback )
16054
+    {
16055
+        if ( !is_callable( $callback ) ) {
16056
+            throw new RedException( 'R::transaction needs a valid callback.' );
16057
+        }
16058
+
16059
+        static $depth = 0;
16060
+        $result = null;
16061
+        try {
16062
+            if ( $depth == 0 ) {
16063
+                $adapter->startTransaction();
16064
+            }
16065
+            $depth++;
16066
+            $result = call_user_func( $callback ); //maintain 5.2 compatibility
16067
+            $depth--;
16068
+            if ( $depth == 0 ) {
16069
+                $adapter->commit();
16070
+            }
16071
+        } catch ( \Exception $exception ) {
16072
+            $depth--;
16073
+            if ( $depth == 0 ) {
16074
+                $adapter->rollback();
16075
+            }
16076
+            throw $exception;
16077
+        }
16078
+        return $result;
16079
+    }
16080 16080
 }
16081 16081
 }
16082 16082
 
@@ -16103,103 +16103,103 @@  discard block
 block discarded – undo
16103 16103
  */
16104 16104
 class QuickExport
16105 16105
 {
16106
-	/**
16107
-	 * @var Finder
16108
-	 */
16109
-	protected $toolbox;
16110
-
16111
-	/**
16112
-	 * @boolean
16113
-	 */
16114
-	private static $test = FALSE;
16115
-
16116
-	/**
16117
-	 * Constructor.
16118
-	 * The Quick Export requires a toolbox.
16119
-	 *
16120
-	 * @param ToolBox $toolbox
16121
-	 */
16122
-	public function __construct( ToolBox $toolbox )
16123
-	{
16124
-		$this->toolbox = $toolbox;
16125
-	}
16126
-
16127
-	/**
16128
-	 * Makes csv() testable.
16129
-	 */
16130
-	public static function operation( $name, $arg1, $arg2 = TRUE ) {
16131
-		$out = '';
16132
-		switch( $name ) {
16133
-			case 'test':
16134
-				self::$test = (boolean) $arg1;
16135
-				break;
16136
-			case 'header':
16137
-				$out = ( self::$test ) ? $arg1 : header( $arg1, $arg2 );
16138
-				break;
16139
-			case 'readfile':
16140
-				$out = ( self::$test ) ? file_get_contents( $arg1 ) : readfile( $arg1 );
16141
-				break;
16142
-			case 'exit':
16143
-				$out = ( self::$test ) ? 'exit' : exit();
16144
-				break;
16145
-		}
16146
-		return $out;
16147
-	}
16148
-
16149
-	/**
16150
-	 * Exposes the result of the specified SQL query as a CSV file.
16151
-	 * Usage:
16152
-	 *
16153
-	 * R::csv( 'SELECT
16154
-	 *                 `name`,
16155
-	 *                  population
16156
-	 *          FROM city
16157
-	 *          WHERE region = :region ',
16158
-	 *          array( ':region' => 'Denmark' ),
16159
-	 *          array( 'city', 'population' ),
16160
-	 *          '/tmp/cities.csv'
16161
-	 * );
16162
-	 *
16163
-	 * The command above will select all cities in Denmark
16164
-	 * and create a CSV with columns 'city' and 'population' and
16165
-	 * populate the cells under these column headers with the
16166
-	 * names of the cities and the population numbers respectively.
16167
-	 *
16168
-	 * @param string  $sql      SQL query to expose result of
16169
-	 * @param array   $bindings parameter bindings
16170
-	 * @param array   $columns  column headers for CSV file
16171
-	 * @param string  $path     path to save CSV file to
16172
-	 * @param boolean $output   TRUE to output CSV directly using readfile
16173
-	 * @param array   $options  delimiter, quote and escape character respectively
16174
-	 *
16175
-	 * @return void
16176
-	 */
16177
-	public function csv( $sql = '', $bindings = array(), $columns = NULL, $path = '/tmp/redexport_%s.csv', $output = TRUE, $options = array(',','"','\\') )
16178
-	{
16179
-		list( $delimiter, $enclosure, $escapeChar ) = $options;
16180
-		$path = sprintf( $path, date('Ymd_his') );
16181
-		$handle = fopen( $path, 'w' );
16182
-		if ($columns) if (PHP_VERSION_ID>=505040) fputcsv($handle, $columns, $delimiter, $enclosure, $escapeChar ); else fputcsv($handle, $columns, $delimiter, $enclosure );
16183
-		$cursor = $this->toolbox->getDatabaseAdapter()->getCursor( $sql, $bindings );
16184
-		while( $row = $cursor->getNextItem() ) {
16185
-			if (PHP_VERSION_ID>=505040) fputcsv($handle, $row, $delimiter, $enclosure, $escapeChar ); else fputcsv($handle, $row, $delimiter, $enclosure );
16186
-		}
16187
-		fclose($handle);
16188
-		if ( $output ) {
16189
-			$file = basename($path);
16190
-			$out = self::operation('header',"Pragma: public");
16191
-			$out .= self::operation('header',"Expires: 0");
16192
-			$out .= self::operation('header',"Cache-Control: must-revalidate, post-check=0, pre-check=0");
16193
-			$out .= self::operation('header',"Cache-Control: private", FALSE );
16194
-			$out .= self::operation('header',"Content-Type: text/csv");
16195
-			$out .= self::operation('header',"Content-Disposition: attachment; filename={$file}" );
16196
-			$out .= self::operation('header',"Content-Transfer-Encoding: binary");
16197
-			$out .= self::operation('readfile',$path );
16198
-			@unlink( $path );
16199
-			self::operation('exit', FALSE);
16200
-			return $out;
16201
-		}
16202
-	}
16106
+    /**
16107
+     * @var Finder
16108
+     */
16109
+    protected $toolbox;
16110
+
16111
+    /**
16112
+     * @boolean
16113
+     */
16114
+    private static $test = FALSE;
16115
+
16116
+    /**
16117
+     * Constructor.
16118
+     * The Quick Export requires a toolbox.
16119
+     *
16120
+     * @param ToolBox $toolbox
16121
+     */
16122
+    public function __construct( ToolBox $toolbox )
16123
+    {
16124
+        $this->toolbox = $toolbox;
16125
+    }
16126
+
16127
+    /**
16128
+     * Makes csv() testable.
16129
+     */
16130
+    public static function operation( $name, $arg1, $arg2 = TRUE ) {
16131
+        $out = '';
16132
+        switch( $name ) {
16133
+            case 'test':
16134
+                self::$test = (boolean) $arg1;
16135
+                break;
16136
+            case 'header':
16137
+                $out = ( self::$test ) ? $arg1 : header( $arg1, $arg2 );
16138
+                break;
16139
+            case 'readfile':
16140
+                $out = ( self::$test ) ? file_get_contents( $arg1 ) : readfile( $arg1 );
16141
+                break;
16142
+            case 'exit':
16143
+                $out = ( self::$test ) ? 'exit' : exit();
16144
+                break;
16145
+        }
16146
+        return $out;
16147
+    }
16148
+
16149
+    /**
16150
+     * Exposes the result of the specified SQL query as a CSV file.
16151
+     * Usage:
16152
+     *
16153
+     * R::csv( 'SELECT
16154
+     *                 `name`,
16155
+     *                  population
16156
+     *          FROM city
16157
+     *          WHERE region = :region ',
16158
+     *          array( ':region' => 'Denmark' ),
16159
+     *          array( 'city', 'population' ),
16160
+     *          '/tmp/cities.csv'
16161
+     * );
16162
+     *
16163
+     * The command above will select all cities in Denmark
16164
+     * and create a CSV with columns 'city' and 'population' and
16165
+     * populate the cells under these column headers with the
16166
+     * names of the cities and the population numbers respectively.
16167
+     *
16168
+     * @param string  $sql      SQL query to expose result of
16169
+     * @param array   $bindings parameter bindings
16170
+     * @param array   $columns  column headers for CSV file
16171
+     * @param string  $path     path to save CSV file to
16172
+     * @param boolean $output   TRUE to output CSV directly using readfile
16173
+     * @param array   $options  delimiter, quote and escape character respectively
16174
+     *
16175
+     * @return void
16176
+     */
16177
+    public function csv( $sql = '', $bindings = array(), $columns = NULL, $path = '/tmp/redexport_%s.csv', $output = TRUE, $options = array(',','"','\\') )
16178
+    {
16179
+        list( $delimiter, $enclosure, $escapeChar ) = $options;
16180
+        $path = sprintf( $path, date('Ymd_his') );
16181
+        $handle = fopen( $path, 'w' );
16182
+        if ($columns) if (PHP_VERSION_ID>=505040) fputcsv($handle, $columns, $delimiter, $enclosure, $escapeChar ); else fputcsv($handle, $columns, $delimiter, $enclosure );
16183
+        $cursor = $this->toolbox->getDatabaseAdapter()->getCursor( $sql, $bindings );
16184
+        while( $row = $cursor->getNextItem() ) {
16185
+            if (PHP_VERSION_ID>=505040) fputcsv($handle, $row, $delimiter, $enclosure, $escapeChar ); else fputcsv($handle, $row, $delimiter, $enclosure );
16186
+        }
16187
+        fclose($handle);
16188
+        if ( $output ) {
16189
+            $file = basename($path);
16190
+            $out = self::operation('header',"Pragma: public");
16191
+            $out .= self::operation('header',"Expires: 0");
16192
+            $out .= self::operation('header',"Cache-Control: must-revalidate, post-check=0, pre-check=0");
16193
+            $out .= self::operation('header',"Cache-Control: private", FALSE );
16194
+            $out .= self::operation('header',"Content-Type: text/csv");
16195
+            $out .= self::operation('header',"Content-Disposition: attachment; filename={$file}" );
16196
+            $out .= self::operation('header',"Content-Transfer-Encoding: binary");
16197
+            $out .= self::operation('readfile',$path );
16198
+            @unlink( $path );
16199
+            self::operation('exit', FALSE);
16200
+            return $out;
16201
+        }
16202
+    }
16203 16203
 }
16204 16204
 }
16205 16205
 
@@ -16230,82 +16230,82 @@  discard block
 block discarded – undo
16230 16230
  */
16231 16231
 class MatchUp
16232 16232
 {
16233
-	/**
16234
-	 * @var Toolbox
16235
-	 */
16236
-	protected $toolbox;
16237
-
16238
-	/**
16239
-	 * Constructor.
16240
-	 * The MatchUp class requires a toolbox
16241
-	 *
16242
-	 * @param ToolBox $toolbox
16243
-	 */
16244
-	public function __construct( ToolBox $toolbox )
16245
-	{
16246
-		$this->toolbox = $toolbox;
16247
-	}
16248
-
16249
-	/**
16250
-	 * MatchUp is a powerful productivity boosting method that can replace simple control
16251
-	 * scripts with a single RedBeanPHP command. Typically, matchUp() is used to
16252
-	 * replace login scripts, token generation scripts and password reset scripts.
16253
-	 * The MatchUp method takes a bean type, an SQL query snippet (starting at the WHERE clause),
16254
-	 * SQL bindings, a pair of task arrays and a bean reference.
16255
-	 *
16256
-	 * If the first 3 parameters match a bean, the first task list will be considered,
16257
-	 * otherwise the second one will be considered. On consideration, each task list,
16258
-	 * an array of keys and values will be executed. Every key in the task list should
16259
-	 * correspond to a bean property while every value can either be an expression to
16260
-	 * be evaluated or a closure (PHP 5.3+). After applying the task list to the bean
16261
-	 * it will be stored. If no bean has been found, a new bean will be dispensed.
16262
-	 *
16263
-	 * This method will return TRUE if the bean was found and FALSE if not AND
16264
-	 * there was a NOT-FOUND task list. If no bean was found AND there was also
16265
-	 * no second task list, NULL will be returned.
16266
-	 *
16267
-	 * To obtain the bean, pass a variable as the sixth parameter.
16268
-	 * The function will put the matching bean in the specified variable.
16269
-	 *
16270
-	 * @param string   $type         type of bean you're looking for
16271
-	 * @param string   $sql          SQL snippet (starting at the WHERE clause, omit WHERE-keyword)
16272
-	 * @param array    $bindings     array of parameter bindings for SQL snippet
16273
-	 * @param array    $onFoundDo    task list to be considered on finding the bean
16274
-	 * @param array    $onNotFoundDo task list to be considered on NOT finding the bean
16275
-	 * @param OODBBean &$bean        reference to obtain the found bean
16276
-	 *
16277
-	 * @return mixed
16278
-	 */
16279
-	public function matchUp( $type, $sql, $bindings = array(), $onFoundDo = NULL, $onNotFoundDo = NULL, &$bean = NULL )
16280
-	{
16281
-		$finder = new Finder( $this->toolbox );
16282
-		$oodb   = $this->toolbox->getRedBean();
16283
-		$bean = $finder->findOne( $type, $sql, $bindings );
16284
-		if ( $bean && $onFoundDo ) {
16285
-			foreach( $onFoundDo as $property => $value ) {
16286
-				if ( function_exists('is_callable') && is_callable( $value ) ) {
16287
-					$bean[$property] = call_user_func_array( $value, array( $bean ) );
16288
-				} else {
16289
-					$bean[$property] = $value;
16290
-				}
16291
-			}
16292
-			$oodb->store( $bean );
16293
-			return TRUE;
16294
-		}
16295
-		if ( $onNotFoundDo ) {
16296
-			$bean = $oodb->dispense( $type );
16297
-			foreach( $onNotFoundDo as $property => $value ) {
16298
-				if ( function_exists('is_callable') && is_callable( $value ) ) {
16299
-					$bean[$property] = call_user_func_array( $value, array( $bean ) );
16300
-				} else {
16301
-					$bean[$property] = $value;
16302
-				}
16303
-			}
16304
-			$oodb->store( $bean );
16305
-			return FALSE;
16306
-		}
16307
-		return NULL;
16308
-	}
16233
+    /**
16234
+     * @var Toolbox
16235
+     */
16236
+    protected $toolbox;
16237
+
16238
+    /**
16239
+     * Constructor.
16240
+     * The MatchUp class requires a toolbox
16241
+     *
16242
+     * @param ToolBox $toolbox
16243
+     */
16244
+    public function __construct( ToolBox $toolbox )
16245
+    {
16246
+        $this->toolbox = $toolbox;
16247
+    }
16248
+
16249
+    /**
16250
+     * MatchUp is a powerful productivity boosting method that can replace simple control
16251
+     * scripts with a single RedBeanPHP command. Typically, matchUp() is used to
16252
+     * replace login scripts, token generation scripts and password reset scripts.
16253
+     * The MatchUp method takes a bean type, an SQL query snippet (starting at the WHERE clause),
16254
+     * SQL bindings, a pair of task arrays and a bean reference.
16255
+     *
16256
+     * If the first 3 parameters match a bean, the first task list will be considered,
16257
+     * otherwise the second one will be considered. On consideration, each task list,
16258
+     * an array of keys and values will be executed. Every key in the task list should
16259
+     * correspond to a bean property while every value can either be an expression to
16260
+     * be evaluated or a closure (PHP 5.3+). After applying the task list to the bean
16261
+     * it will be stored. If no bean has been found, a new bean will be dispensed.
16262
+     *
16263
+     * This method will return TRUE if the bean was found and FALSE if not AND
16264
+     * there was a NOT-FOUND task list. If no bean was found AND there was also
16265
+     * no second task list, NULL will be returned.
16266
+     *
16267
+     * To obtain the bean, pass a variable as the sixth parameter.
16268
+     * The function will put the matching bean in the specified variable.
16269
+     *
16270
+     * @param string   $type         type of bean you're looking for
16271
+     * @param string   $sql          SQL snippet (starting at the WHERE clause, omit WHERE-keyword)
16272
+     * @param array    $bindings     array of parameter bindings for SQL snippet
16273
+     * @param array    $onFoundDo    task list to be considered on finding the bean
16274
+     * @param array    $onNotFoundDo task list to be considered on NOT finding the bean
16275
+     * @param OODBBean &$bean        reference to obtain the found bean
16276
+     *
16277
+     * @return mixed
16278
+     */
16279
+    public function matchUp( $type, $sql, $bindings = array(), $onFoundDo = NULL, $onNotFoundDo = NULL, &$bean = NULL )
16280
+    {
16281
+        $finder = new Finder( $this->toolbox );
16282
+        $oodb   = $this->toolbox->getRedBean();
16283
+        $bean = $finder->findOne( $type, $sql, $bindings );
16284
+        if ( $bean && $onFoundDo ) {
16285
+            foreach( $onFoundDo as $property => $value ) {
16286
+                if ( function_exists('is_callable') && is_callable( $value ) ) {
16287
+                    $bean[$property] = call_user_func_array( $value, array( $bean ) );
16288
+                } else {
16289
+                    $bean[$property] = $value;
16290
+                }
16291
+            }
16292
+            $oodb->store( $bean );
16293
+            return TRUE;
16294
+        }
16295
+        if ( $onNotFoundDo ) {
16296
+            $bean = $oodb->dispense( $type );
16297
+            foreach( $onNotFoundDo as $property => $value ) {
16298
+                if ( function_exists('is_callable') && is_callable( $value ) ) {
16299
+                    $bean[$property] = call_user_func_array( $value, array( $bean ) );
16300
+                } else {
16301
+                    $bean[$property] = $value;
16302
+                }
16303
+            }
16304
+            $oodb->store( $bean );
16305
+            return FALSE;
16306
+        }
16307
+        return NULL;
16308
+    }
16309 16309
 }
16310 16310
 }
16311 16311
 
@@ -16333,88 +16333,88 @@  discard block
 block discarded – undo
16333 16333
  */
16334 16334
 class Look
16335 16335
 {
16336
-	/**
16337
-	 * @var Toolbox
16338
-	 */
16339
-	protected $toolbox;
16340
-
16341
-	/**
16342
-	 * Constructor.
16343
-	 * The MatchUp class requires a toolbox
16344
-	 *
16345
-	 * @param ToolBox $toolbox
16346
-	 */
16347
-	public function __construct( ToolBox $toolbox )
16348
-	{
16349
-		$this->toolbox = $toolbox;
16350
-	}
16351
-
16352
-	/**
16353
-	 * Takes an full SQL query with optional bindings, a series of keys, a template
16354
-	 * and optionally a filter function and glue and assembles a view from all this.
16355
-	 * This is the fastest way from SQL to view. Typically this function is used to
16356
-	 * generate pulldown (select tag) menus with options queried from the database.
16357
-	 *
16358
-	 * Usage:
16359
-	 *
16360
-	 * <code>
16361
-	 * $htmlPulldown = R::look(
16362
-	 *   'SELECT * FROM color WHERE value != ? ORDER BY value ASC',
16363
-	 *   [ 'g' ],
16364
-	 *   [ 'value', 'name' ],
16365
-	 *   '<option value="%s">%s</option>',
16366
-	 *   'strtoupper',
16367
-	 *   "\n"
16368
-	 * );
16369
-	 *</code>
16370
-	 *
16371
-	 * The example above creates an HTML fragment like this:
16372
-	 *
16373
-	 * <option value="B">BLUE</option>
16374
-	 * <option value="R">RED</option>
16375
-	 *
16376
-	 * to pick a color from a palette. The HTML fragment gets constructed by
16377
-	 * an SQL query that selects all colors that do not have value 'g' - this
16378
-	 * excludes green. Next, the bean properties 'value' and 'name' are mapped to the
16379
-	 * HTML template string, note that the order here is important. The mapping and
16380
-	 * the HTML template string follow vsprintf-rules. All property values are then
16381
-	 * passed through the specified filter function 'strtoupper' which in this case
16382
-	 * is a native PHP function to convert strings to uppercase characters only.
16383
-	 * Finally the resulting HTML fragment strings are glued together using a
16384
-	 * newline character specified in the last parameter for readability.
16385
-	 *
16386
-	 * In previous versions of RedBeanPHP you had to use:
16387
-	 * R::getLook()->look() instead of R::look(). However to improve useability of the
16388
-	 * library the look() function can now directly be invoked from the facade.
16389
-	 *
16390
-	 * @param string   $sql      query to execute
16391
-	 * @param array    $bindings parameters to bind to slots mentioned in query or an empty array
16392
-	 * @param array    $keys     names in result collection to map to template
16393
-	 * @param string   $template HTML template to fill with values associated with keys, use printf notation (i.e. %s)
16394
-	 * @param callable $filter   function to pass values through (for translation for instance)
16395
-	 * @param string   $glue     optional glue to use when joining resulting strings
16396
-	 *
16397
-	 * @return string
16398
-	 */
16399
-	public function look( $sql, $bindings = array(), $keys = array( 'selected', 'id', 'name' ), $template = '<option %s value="%s">%s</option>', $filter = 'trim', $glue = '' )
16400
-	{
16401
-		$adapter = $this->toolbox->getDatabaseAdapter();
16402
-		$lines = array();
16403
-		$rows = $adapter->get( $sql, $bindings );
16404
-		foreach( $rows as $row ) {
16405
-			$values = array();
16406
-			foreach( $keys as $key ) {
16407
-				if (!empty($filter)) {
16408
-					$values[] = call_user_func_array( $filter, array( $row[$key] ) );
16409
-				} else {
16410
-					$values[] = $row[$key];
16411
-				}
16412
-			}
16413
-			$lines[] = vsprintf( $template, $values );
16414
-		}
16415
-		$string = implode( $glue, $lines );
16416
-		return $string;
16417
-	}
16336
+    /**
16337
+     * @var Toolbox
16338
+     */
16339
+    protected $toolbox;
16340
+
16341
+    /**
16342
+     * Constructor.
16343
+     * The MatchUp class requires a toolbox
16344
+     *
16345
+     * @param ToolBox $toolbox
16346
+     */
16347
+    public function __construct( ToolBox $toolbox )
16348
+    {
16349
+        $this->toolbox = $toolbox;
16350
+    }
16351
+
16352
+    /**
16353
+     * Takes an full SQL query with optional bindings, a series of keys, a template
16354
+     * and optionally a filter function and glue and assembles a view from all this.
16355
+     * This is the fastest way from SQL to view. Typically this function is used to
16356
+     * generate pulldown (select tag) menus with options queried from the database.
16357
+     *
16358
+     * Usage:
16359
+     *
16360
+     * <code>
16361
+     * $htmlPulldown = R::look(
16362
+     *   'SELECT * FROM color WHERE value != ? ORDER BY value ASC',
16363
+     *   [ 'g' ],
16364
+     *   [ 'value', 'name' ],
16365
+     *   '<option value="%s">%s</option>',
16366
+     *   'strtoupper',
16367
+     *   "\n"
16368
+     * );
16369
+     *</code>
16370
+     *
16371
+     * The example above creates an HTML fragment like this:
16372
+     *
16373
+     * <option value="B">BLUE</option>
16374
+     * <option value="R">RED</option>
16375
+     *
16376
+     * to pick a color from a palette. The HTML fragment gets constructed by
16377
+     * an SQL query that selects all colors that do not have value 'g' - this
16378
+     * excludes green. Next, the bean properties 'value' and 'name' are mapped to the
16379
+     * HTML template string, note that the order here is important. The mapping and
16380
+     * the HTML template string follow vsprintf-rules. All property values are then
16381
+     * passed through the specified filter function 'strtoupper' which in this case
16382
+     * is a native PHP function to convert strings to uppercase characters only.
16383
+     * Finally the resulting HTML fragment strings are glued together using a
16384
+     * newline character specified in the last parameter for readability.
16385
+     *
16386
+     * In previous versions of RedBeanPHP you had to use:
16387
+     * R::getLook()->look() instead of R::look(). However to improve useability of the
16388
+     * library the look() function can now directly be invoked from the facade.
16389
+     *
16390
+     * @param string   $sql      query to execute
16391
+     * @param array    $bindings parameters to bind to slots mentioned in query or an empty array
16392
+     * @param array    $keys     names in result collection to map to template
16393
+     * @param string   $template HTML template to fill with values associated with keys, use printf notation (i.e. %s)
16394
+     * @param callable $filter   function to pass values through (for translation for instance)
16395
+     * @param string   $glue     optional glue to use when joining resulting strings
16396
+     *
16397
+     * @return string
16398
+     */
16399
+    public function look( $sql, $bindings = array(), $keys = array( 'selected', 'id', 'name' ), $template = '<option %s value="%s">%s</option>', $filter = 'trim', $glue = '' )
16400
+    {
16401
+        $adapter = $this->toolbox->getDatabaseAdapter();
16402
+        $lines = array();
16403
+        $rows = $adapter->get( $sql, $bindings );
16404
+        foreach( $rows as $row ) {
16405
+            $values = array();
16406
+            foreach( $keys as $key ) {
16407
+                if (!empty($filter)) {
16408
+                    $values[] = call_user_func_array( $filter, array( $row[$key] ) );
16409
+                } else {
16410
+                    $values[] = $row[$key];
16411
+                }
16412
+            }
16413
+            $lines[] = vsprintf( $template, $values );
16414
+        }
16415
+        $string = implode( $glue, $lines );
16416
+        return $string;
16417
+    }
16418 16418
 }
16419 16419
 }
16420 16420
 
@@ -16442,86 +16442,86 @@  discard block
 block discarded – undo
16442 16442
  */
16443 16443
 class Diff
16444 16444
 {
16445
-	/**
16446
-	 * @var Toolbox
16447
-	 */
16448
-	protected $toolbox;
16449
-
16450
-	/**
16451
-	 * Constructor.
16452
-	 * The MatchUp class requires a toolbox
16453
-	 *
16454
-	 * @param ToolBox $toolbox
16455
-	 */
16456
-	public function __construct( ToolBox $toolbox )
16457
-	{
16458
-		$this->toolbox = $toolbox;
16459
-	}
16460
-
16461
-	/**
16462
-	 * Calculates a diff between two beans (or arrays of beans).
16463
-	 * The result of this method is an array describing the differences of the second bean compared to
16464
-	 * the first, where the first bean is taken as reference. The array is keyed by type/property, id and property name, where
16465
-	 * type/property is either the type (in case of the root bean) or the property of the parent bean where the type resides.
16466
-	 * The diffs are mainly intended for logging, you cannot apply these diffs as patches to other beans.
16467
-	 * However this functionality might be added in the future.
16468
-	 *
16469
-	 * The keys of the array can be formatted using the $format parameter.
16470
-	 * A key will be composed of a path (1st), id (2nd) and property (3rd).
16471
-	 * Using printf-style notation you can determine the exact format of the key.
16472
-	 * The default format will look like:
16473
-	 *
16474
-	 * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
16475
-	 *
16476
-	 * If you only want a simple diff of one bean and you don't care about ids,
16477
-	 * you might pass a format like: '%1$s.%3$s' which gives:
16478
-	 *
16479
-	 * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
16480
-	 *
16481
-	 * The filter parameter can be used to set filters, it should be an array
16482
-	 * of property names that have to be skipped. By default this array is filled with
16483
-	 * two strings: 'created' and 'modified'.
16484
-	 *
16485
-	 * @param OODBBean|array $beans   reference beans
16486
-	 * @param OODBBean|array $others  beans to compare
16487
-	 * @param array          $filters names of properties of all beans to skip
16488
-	 * @param string         $format  the format of the key, defaults to '%s.%s.%s'
16489
-	 * @param string         $type    type/property of bean to use for key generation
16490
-	 *
16491
-	 * @return array
16492
-	 */
16493
-	public function diff( $beans, $others, $filters = array( 'created', 'modified' ), $format = '%s.%s.%s', $type = NULL )
16494
-	{
16495
-		$diff = array();
16496
-		if ( !is_array( $beans ) ) $beans = array( $beans );
16497
-		if ( !is_array( $others ) ) $others = array( $others );
16498
-		foreach( $beans as $bean ) {
16499
-			if ( !is_object( $bean ) ) continue;
16500
-			if ( !( $bean instanceof OODBBean ) ) continue;
16501
-			if ( $type == NULL ) $type = $bean->getMeta( 'type' );
16502
-			foreach( $others as $other ) {
16503
-				if ( !is_object( $other ) ) continue;
16504
-				if ( !( $other instanceof OODBBean ) ) continue;
16505
-				if ( $other->id == $bean->id ) {
16506
-					foreach( $bean as $property => $value ) {
16507
-						if ( in_array( $property, $filters ) ) continue;
16508
-						$key = vsprintf( $format, array( $type, $bean->id, $property ) );
16509
-						$compare = $other->{$property};
16510
-						if ( !is_object( $value ) && !is_array( $value ) ) {
16511
-							if ( $value != $compare ) {
16512
-								$diff[$key] = array( $value, $compare );
16513
-							}
16514
-							continue;
16515
-						} else {
16516
-							$diff = array_merge( $diff, $this->diff( $value, $compare, $filters, $format, $key ) );
16517
-							continue;
16518
-						}
16519
-					}
16520
-				}
16521
-			}
16522
-		}
16523
-		return $diff;
16524
-	}
16445
+    /**
16446
+     * @var Toolbox
16447
+     */
16448
+    protected $toolbox;
16449
+
16450
+    /**
16451
+     * Constructor.
16452
+     * The MatchUp class requires a toolbox
16453
+     *
16454
+     * @param ToolBox $toolbox
16455
+     */
16456
+    public function __construct( ToolBox $toolbox )
16457
+    {
16458
+        $this->toolbox = $toolbox;
16459
+    }
16460
+
16461
+    /**
16462
+     * Calculates a diff between two beans (or arrays of beans).
16463
+     * The result of this method is an array describing the differences of the second bean compared to
16464
+     * the first, where the first bean is taken as reference. The array is keyed by type/property, id and property name, where
16465
+     * type/property is either the type (in case of the root bean) or the property of the parent bean where the type resides.
16466
+     * The diffs are mainly intended for logging, you cannot apply these diffs as patches to other beans.
16467
+     * However this functionality might be added in the future.
16468
+     *
16469
+     * The keys of the array can be formatted using the $format parameter.
16470
+     * A key will be composed of a path (1st), id (2nd) and property (3rd).
16471
+     * Using printf-style notation you can determine the exact format of the key.
16472
+     * The default format will look like:
16473
+     *
16474
+     * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
16475
+     *
16476
+     * If you only want a simple diff of one bean and you don't care about ids,
16477
+     * you might pass a format like: '%1$s.%3$s' which gives:
16478
+     *
16479
+     * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
16480
+     *
16481
+     * The filter parameter can be used to set filters, it should be an array
16482
+     * of property names that have to be skipped. By default this array is filled with
16483
+     * two strings: 'created' and 'modified'.
16484
+     *
16485
+     * @param OODBBean|array $beans   reference beans
16486
+     * @param OODBBean|array $others  beans to compare
16487
+     * @param array          $filters names of properties of all beans to skip
16488
+     * @param string         $format  the format of the key, defaults to '%s.%s.%s'
16489
+     * @param string         $type    type/property of bean to use for key generation
16490
+     *
16491
+     * @return array
16492
+     */
16493
+    public function diff( $beans, $others, $filters = array( 'created', 'modified' ), $format = '%s.%s.%s', $type = NULL )
16494
+    {
16495
+        $diff = array();
16496
+        if ( !is_array( $beans ) ) $beans = array( $beans );
16497
+        if ( !is_array( $others ) ) $others = array( $others );
16498
+        foreach( $beans as $bean ) {
16499
+            if ( !is_object( $bean ) ) continue;
16500
+            if ( !( $bean instanceof OODBBean ) ) continue;
16501
+            if ( $type == NULL ) $type = $bean->getMeta( 'type' );
16502
+            foreach( $others as $other ) {
16503
+                if ( !is_object( $other ) ) continue;
16504
+                if ( !( $other instanceof OODBBean ) ) continue;
16505
+                if ( $other->id == $bean->id ) {
16506
+                    foreach( $bean as $property => $value ) {
16507
+                        if ( in_array( $property, $filters ) ) continue;
16508
+                        $key = vsprintf( $format, array( $type, $bean->id, $property ) );
16509
+                        $compare = $other->{$property};
16510
+                        if ( !is_object( $value ) && !is_array( $value ) ) {
16511
+                            if ( $value != $compare ) {
16512
+                                $diff[$key] = array( $value, $compare );
16513
+                            }
16514
+                            continue;
16515
+                        } else {
16516
+                            $diff = array_merge( $diff, $this->diff( $value, $compare, $filters, $format, $key ) );
16517
+                            continue;
16518
+                        }
16519
+                    }
16520
+                }
16521
+            }
16522
+        }
16523
+        return $diff;
16524
+    }
16525 16525
 }
16526 16526
 }
16527 16527
 
@@ -16549,79 +16549,79 @@  discard block
 block discarded – undo
16549 16549
  */
16550 16550
 class Tree {
16551 16551
 
16552
-	/**
16553
-	 * @var ToolBox
16554
-	 */
16555
-	protected $toolbox;
16556
-
16557
-	/**
16558
-	 * @var QueryWriter
16559
-	 */
16560
-	protected $writer;
16561
-
16562
-	/**
16563
-	 * @var OODB
16564
-	 */
16565
-	protected $oodb;
16566
-
16567
-	/**
16568
-	 * Constructor, creates a new instance of
16569
-	 * the Tree.
16570
-	 *
16571
-	 * @param ToolBox $toolbox toolbox
16572
-	 */
16573
-	public function __construct( ToolBox $toolbox )
16574
-	{
16575
-		$this->toolbox = $toolbox;
16576
-		$this->writer  = $toolbox->getWriter();
16577
-		$this->oodb    = $toolbox->getRedBean();
16578
-	}
16579
-
16580
-	/**
16581
-	 * Returns all child beans associates with the specified
16582
-	 * bean in a tree structure.
16583
-	 *
16584
-	 * @note this only works for databases that support
16585
-	 * recusrive common table expressions.
16586
-	 *
16587
-	 * @param OODBBean $bean     reference bean to find children of
16588
-	 * @param string   $sql      optional SQL snippet
16589
-	 * @param array    $bindings optional parameter bindings for SQL snippet
16590
-	 *
16591
-	 * @return array
16592
-	 */
16593
-	public function children( OODBBean $bean, $sql = NULL, $bindings = array() )
16594
-	{
16595
-		$type = $bean->getMeta('type');
16596
-		$id   = $bean->id;
16597
-
16598
-		$rows = $this->writer->queryRecursiveCommonTableExpression( $type, $id, FALSE, $sql, $bindings );
16599
-
16600
-		return $this->oodb->convertToBeans( $type, $rows );
16601
-	}
16552
+    /**
16553
+     * @var ToolBox
16554
+     */
16555
+    protected $toolbox;
16556
+
16557
+    /**
16558
+     * @var QueryWriter
16559
+     */
16560
+    protected $writer;
16561
+
16562
+    /**
16563
+     * @var OODB
16564
+     */
16565
+    protected $oodb;
16566
+
16567
+    /**
16568
+     * Constructor, creates a new instance of
16569
+     * the Tree.
16570
+     *
16571
+     * @param ToolBox $toolbox toolbox
16572
+     */
16573
+    public function __construct( ToolBox $toolbox )
16574
+    {
16575
+        $this->toolbox = $toolbox;
16576
+        $this->writer  = $toolbox->getWriter();
16577
+        $this->oodb    = $toolbox->getRedBean();
16578
+    }
16579
+
16580
+    /**
16581
+     * Returns all child beans associates with the specified
16582
+     * bean in a tree structure.
16583
+     *
16584
+     * @note this only works for databases that support
16585
+     * recusrive common table expressions.
16586
+     *
16587
+     * @param OODBBean $bean     reference bean to find children of
16588
+     * @param string   $sql      optional SQL snippet
16589
+     * @param array    $bindings optional parameter bindings for SQL snippet
16590
+     *
16591
+     * @return array
16592
+     */
16593
+    public function children( OODBBean $bean, $sql = NULL, $bindings = array() )
16594
+    {
16595
+        $type = $bean->getMeta('type');
16596
+        $id   = $bean->id;
16597
+
16598
+        $rows = $this->writer->queryRecursiveCommonTableExpression( $type, $id, FALSE, $sql, $bindings );
16599
+
16600
+        return $this->oodb->convertToBeans( $type, $rows );
16601
+    }
16602 16602
 	
16603
-	/**
16604
-	 * Returns all parent beans associates with the specified
16605
-	 * bean in a tree structure.
16606
-	 *
16607
-	 * @note this only works for databases that support
16608
-	 * recusrive common table expressions.
16609
-	 *
16610
-	 * @param OODBBean $bean     reference bean to find parents of
16611
-	 * @param string   $sql      optional SQL snippet
16612
-	 * @param array    $bindings optional parameter bindings for SQL snippet
16613
-	 *
16614
-	 * @return array
16615
-	 */
16616
-	public function parents( OODBBean $bean, $sql = NULL, $bindings = array() )
16617
-	{
16618
-		$type = $bean->getMeta('type');
16619
-		$id   = $bean->id;
16620
-
16621
-		$rows = $this->writer->queryRecursiveCommonTableExpression( $type, $id, TRUE, $sql, $bindings );
16622
-
16623
-		return $this->oodb->convertToBeans( $type, $rows );		
16624
-	}	
16603
+    /**
16604
+     * Returns all parent beans associates with the specified
16605
+     * bean in a tree structure.
16606
+     *
16607
+     * @note this only works for databases that support
16608
+     * recusrive common table expressions.
16609
+     *
16610
+     * @param OODBBean $bean     reference bean to find parents of
16611
+     * @param string   $sql      optional SQL snippet
16612
+     * @param array    $bindings optional parameter bindings for SQL snippet
16613
+     *
16614
+     * @return array
16615
+     */
16616
+    public function parents( OODBBean $bean, $sql = NULL, $bindings = array() )
16617
+    {
16618
+        $type = $bean->getMeta('type');
16619
+        $id   = $bean->id;
16620
+
16621
+        $rows = $this->writer->queryRecursiveCommonTableExpression( $type, $id, TRUE, $sql, $bindings );
16622
+
16623
+        return $this->oodb->convertToBeans( $type, $rows );		
16624
+    }	
16625 16625
 }
16626 16626
 }
16627 16627
 
@@ -16646,40 +16646,40 @@  discard block
 block discarded – undo
16646 16646
  */
16647 16647
 class Feature
16648 16648
 {
16649
-	/**
16650
-	 * Selects the feature set you want as specified by
16651
-	 * the label.
16652
-	 *
16653
-	 * Usage:
16654
-	 * R::useFeatureSet( 'novice/latest' );
16655
-	 *
16656
-	 * @param string $label label
16657
-	 *
16658
-	 * @return void
16659
-	 */
16660
-	public static function feature( $label ) {
16661
-		switch( $label ) {
16662
-			case "novice/latest":
16663
-				R::noNuke( TRUE );
16664
-				R::setAutoResolve( TRUE );
16665
-				break;
16666
-			case "latest":
16667
-				R::noNuke( FALSE );
16668
-				R::setAutoResolve( TRUE );
16669
-				break;
16670
-			case "novice/5.3":
16671
-				R::noNuke( TRUE );
16672
-				R::setAutoResolve( TRUE );
16673
-				break;
16674
-			case "5.3":
16675
-				R::noNuke( FALSE );
16676
-				R::setAutoResolve( TRUE );
16677
-				break;
16678
-			default:
16679
-				throw new \Exception("Unknown feature set label.");
16680
-				break;
16681
-		}
16682
-	}
16649
+    /**
16650
+     * Selects the feature set you want as specified by
16651
+     * the label.
16652
+     *
16653
+     * Usage:
16654
+     * R::useFeatureSet( 'novice/latest' );
16655
+     *
16656
+     * @param string $label label
16657
+     *
16658
+     * @return void
16659
+     */
16660
+    public static function feature( $label ) {
16661
+        switch( $label ) {
16662
+            case "novice/latest":
16663
+                R::noNuke( TRUE );
16664
+                R::setAutoResolve( TRUE );
16665
+                break;
16666
+            case "latest":
16667
+                R::noNuke( FALSE );
16668
+                R::setAutoResolve( TRUE );
16669
+                break;
16670
+            case "novice/5.3":
16671
+                R::noNuke( TRUE );
16672
+                R::setAutoResolve( TRUE );
16673
+                break;
16674
+            case "5.3":
16675
+                R::noNuke( FALSE );
16676
+                R::setAutoResolve( TRUE );
16677
+                break;
16678
+            default:
16679
+                throw new \Exception("Unknown feature set label.");
16680
+                break;
16681
+        }
16682
+    }
16683 16683
 }
16684 16684
 }
16685 16685
 
@@ -16714,7 +16714,7 @@  discard block
 block discarded – undo
16714 16714
 class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {};
16715 16715
 
16716 16716
 if (!class_exists('R')) {
16717
-	class R extends \RedBeanPHP\Facade{};
16717
+    class R extends \RedBeanPHP\Facade{};
16718 16718
 }
16719 16719
 
16720 16720
 
@@ -16751,10 +16751,10 @@  discard block
 block discarded – undo
16751 16751
  */
16752 16752
 if (!function_exists('EID')) {
16753 16753
 
16754
-	function EID($enumName)
16755
-	{
16756
-		return \RedBeanPHP\Facade::enum( $enumName )->id;
16757
-	}
16754
+    function EID($enumName)
16755
+    {
16756
+        return \RedBeanPHP\Facade::enum( $enumName )->id;
16757
+    }
16758 16758
 
16759 16759
 }
16760 16760
 
@@ -16768,10 +16768,10 @@  discard block
 block discarded – undo
16768 16768
  */
16769 16769
 if ( !function_exists( 'dmp' ) ) {
16770 16770
 
16771
-	function dmp( $list )
16772
-	{
16773
-		print_r( \RedBeanPHP\Facade::dump( $list ) );
16774
-	}
16771
+    function dmp( $list )
16772
+    {
16773
+        print_r( \RedBeanPHP\Facade::dump( $list ) );
16774
+    }
16775 16775
 }
16776 16776
 
16777 16777
 /**
@@ -16779,10 +16779,10 @@  discard block
 block discarded – undo
16779 16779
  */
16780 16780
 if ( !function_exists( 'genslots' ) ) {
16781 16781
 
16782
-	function genslots( $slots, $tpl = NULL )
16783
-	{
16784
-		return \RedBeanPHP\Facade::genSlots( $slots, $tpl );
16785
-	}
16782
+    function genslots( $slots, $tpl = NULL )
16783
+    {
16784
+        return \RedBeanPHP\Facade::genSlots( $slots, $tpl );
16785
+    }
16786 16786
 }
16787 16787
 
16788 16788
 /**
@@ -16790,10 +16790,10 @@  discard block
 block discarded – undo
16790 16790
  */
16791 16791
 if ( !function_exists( 'array_flatten' ) ) {
16792 16792
 
16793
-	function array_flatten( $array )
16794
-	{
16795
-		return \RedBeanPHP\Facade::flat( $array );
16796
-	}
16793
+    function array_flatten( $array )
16794
+    {
16795
+        return \RedBeanPHP\Facade::flat( $array );
16796
+    }
16797 16797
 }
16798 16798
 
16799 16799
 
Please login to merge, or discard this patch.