GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 19c271...bb79ab )
by Calima
04:02
created
Sistema/Ayudantes/CFPHPAyuda.php 1 patch
Indentation   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -43,7 +43,7 @@  discard block
 block discarded – undo
43 43
     }
44 44
     
45 45
     
46
-     // filtros Email
46
+        // filtros Email
47 47
     
48 48
     function filtroEmail($email){
49 49
         if (!filter_input(INPUT_POST, $email, FILTER_VALIDATE_EMAIL)){
@@ -109,7 +109,7 @@  discard block
 block discarded – undo
109 109
     //texto
110 110
     public function filtrarEntero($int){
111 111
         
112
-       return filter_var($int, FILTER_VALIDATE_INT);
112
+        return filter_var($int, FILTER_VALIDATE_INT);
113 113
        
114 114
     }
115 115
     
Please login to merge, or discard this patch.
Sistema/Ayudantes/CFPHPCache.php 1 patch
Indentation   +95 added lines, -95 removed lines patch added patch discarded remove patch
@@ -26,100 +26,100 @@
 block discarded – undo
26 26
 namespace Sistema\Ayudantes;
27 27
 
28 28
 class CFPHPCache {
29
-	/**
30
-	 * Configuracion
31
-	 *
32
-	 * @access public
33
-	 */
34
-	public static $configuracion = array(
35
-		'cache_dir' => 'cache',
36
-		// por defecto el tiempo expira en  *minutos*
37
-		'expires' => 180,
38
-	);
29
+    /**
30
+     * Configuracion
31
+     *
32
+     * @access public
33
+     */
34
+    public static $configuracion = array(
35
+        'cache_dir' => 'cache',
36
+        // por defecto el tiempo expira en  *minutos*
37
+        'expires' => 180,
38
+    );
39 39
 	
40
-	public static function configurar($clave, $valor = null) {
41
-		if( is_array($clave) ) {
42
-			foreach ($clave as $config_name => $config_value) {
43
-				self::$configuracion[$config_name] = $config_value;
44
-			}
45
-		} else {
46
-			self::$configuracion[$clave] = $valor;
47
-		}
48
-	}
49
-	/**
50
-	 * obtiene la ruta del archivo asociado a la clave.
51
-	 *
52
-	 * @access public
53
-	 * @param string $clave
54
-	 * @return string el nombre del archivo php
55
-	 */
56
-	public static function obtenerRuta($clave) {
57
-		return static::$configuracion['cache_path'] . '/' . md5($clave) . '.php';
58
-	}
59
-	/**
60
-	 * obtiene los datos asociados a la clave
61
-	 *
62
-	 * @access public
63
-	 * @param string $clave	 
64
-	 */
65
-	public static function obtener($clave, $raw = false, $tiempo_personalizado = null) {
66
-		if( ! self::file_expired($archivo = self::obtenerRuta($clave), $tiempo_personalizado)) {
67
-			$contenido = file_get_contents($archivo);
68
-			return $raw ? $contenido : unserialize($contenido);
69
-		}
70
-		return null;
71
-	}
72
-	/**
73
-	 * Envia el contenido dentro de la cache
74
-	 *
75
-	 * @access public
76
-	 * @param string $clave
77
-	 * @param mixed $content the the content you want to store
78
-	 * @param bool $raw whether if you want to store raw data or not. If it is true, $content *must* be a string
79
-	 *        It can be useful for static html caching.
80
-	 * @return bool whether if the operation was successful or not
81
-	 */
82
-	public static function enviar($clave, $contenido, $raw = false) {
83
-		return @file_put_contents(self::obtenerRuta($clave), $raw ? $contenido : serialize($contenido)) !== false;
84
-	}
85
-	/**
86
-	 * Delete data from cache
87
-	 *
88
-	 * @access public
89
-	 * @param string $clave
90
-	 * @return bool true if the data was removed successfully
91
-	 */
92
-	public static function eliminar($clave) {
93
-		if( ! file_exists($archivo = self::obtenerRuta($clave)) ) {
94
-			return true;
95
-		}
96
-		return @unlink($archivo);
97
-	}
98
-	/**
99
-	 * limpia la cache
100
-	 *
101
-	 * @access public
102
-	 * @return bool always true
103
-	 */
104
-	public static function limpiar() {
105
-		$cache_files = glob(self::$configuracion['cache_path'] . '/*.php', GLOB_NOSORT);
106
-		foreach ($cache_files as $archivo) {
107
-			@unlink($archivo);
108
-		}
109
-		return true;
110
-	}
111
-	/**
112
-	 * Check if a file has expired or not.
113
-	 *
114
-	 * @access public
115
-	 * @param $archivo the rout to the file
116
-	 * @param int $fecha the number of minutes it was set to expire
117
-	 * @return bool if the file has expired or not
118
-	 */
119
-	public static function file_expired($archivo, $fecha = null) {
120
-		if( ! file_exists($archivo) ) {
121
-			return true;
122
-		}
123
-		return (time() > (filemtime($archivo) + 60 * ($fecha ? $fecha : self::$configuracion['expires'])));
124
-	}
40
+    public static function configurar($clave, $valor = null) {
41
+        if( is_array($clave) ) {
42
+            foreach ($clave as $config_name => $config_value) {
43
+                self::$configuracion[$config_name] = $config_value;
44
+            }
45
+        } else {
46
+            self::$configuracion[$clave] = $valor;
47
+        }
48
+    }
49
+    /**
50
+     * obtiene la ruta del archivo asociado a la clave.
51
+     *
52
+     * @access public
53
+     * @param string $clave
54
+     * @return string el nombre del archivo php
55
+     */
56
+    public static function obtenerRuta($clave) {
57
+        return static::$configuracion['cache_path'] . '/' . md5($clave) . '.php';
58
+    }
59
+    /**
60
+     * obtiene los datos asociados a la clave
61
+     *
62
+     * @access public
63
+     * @param string $clave	 
64
+     */
65
+    public static function obtener($clave, $raw = false, $tiempo_personalizado = null) {
66
+        if( ! self::file_expired($archivo = self::obtenerRuta($clave), $tiempo_personalizado)) {
67
+            $contenido = file_get_contents($archivo);
68
+            return $raw ? $contenido : unserialize($contenido);
69
+        }
70
+        return null;
71
+    }
72
+    /**
73
+     * Envia el contenido dentro de la cache
74
+     *
75
+     * @access public
76
+     * @param string $clave
77
+     * @param mixed $content the the content you want to store
78
+     * @param bool $raw whether if you want to store raw data or not. If it is true, $content *must* be a string
79
+     *        It can be useful for static html caching.
80
+     * @return bool whether if the operation was successful or not
81
+     */
82
+    public static function enviar($clave, $contenido, $raw = false) {
83
+        return @file_put_contents(self::obtenerRuta($clave), $raw ? $contenido : serialize($contenido)) !== false;
84
+    }
85
+    /**
86
+     * Delete data from cache
87
+     *
88
+     * @access public
89
+     * @param string $clave
90
+     * @return bool true if the data was removed successfully
91
+     */
92
+    public static function eliminar($clave) {
93
+        if( ! file_exists($archivo = self::obtenerRuta($clave)) ) {
94
+            return true;
95
+        }
96
+        return @unlink($archivo);
97
+    }
98
+    /**
99
+     * limpia la cache
100
+     *
101
+     * @access public
102
+     * @return bool always true
103
+     */
104
+    public static function limpiar() {
105
+        $cache_files = glob(self::$configuracion['cache_path'] . '/*.php', GLOB_NOSORT);
106
+        foreach ($cache_files as $archivo) {
107
+            @unlink($archivo);
108
+        }
109
+        return true;
110
+    }
111
+    /**
112
+     * Check if a file has expired or not.
113
+     *
114
+     * @access public
115
+     * @param $archivo the rout to the file
116
+     * @param int $fecha the number of minutes it was set to expire
117
+     * @return bool if the file has expired or not
118
+     */
119
+    public static function file_expired($archivo, $fecha = null) {
120
+        if( ! file_exists($archivo) ) {
121
+            return true;
122
+        }
123
+        return (time() > (filemtime($archivo) + 60 * ($fecha ? $fecha : self::$configuracion['expires'])));
124
+    }
125 125
 }
Please login to merge, or discard this patch.
Sistema/Ayudantes/CFPHPFormularios.php 1 patch
Indentation   +823 added lines, -823 removed lines patch added patch discarded remove patch
@@ -25,26 +25,26 @@  discard block
 block discarded – undo
25 25
 
26 26
 // se agradece a ArrayZone por el aporte de esta clase
27 27
 
28
- /**
29
- * @name Form Generator for Kernel Web
30
- * @version A.1.0
31
- * @copyright ArrayZone 2014
32
- * @license AZPL or later; see License.txt or http://arrayzone.com/license
33
- * @category plugin
34
- * 
35
- * Description: This script write a FORM HTML automatically from array (it will be generated trhoug functions).
36
- * It write any type of items basics of HTML4 and some of HTML5.
37
- * Once user clic "submit", all information  will be validated via PHP
38
- * Some types like "required" and "email" validated trhough PHP to avoid problems with navigator compatibilities or hacker attacks
39
- *
40
- * NEXT VERSION: If you can use JavaScript and have JQuery implemented, the form can write JavaScript code to validate all
41
- * Read the documentation for all information in kernel.arrayzone.com
42
- * or all coments of all parameters and functions.
43
- * 
44
- * IMPORTANT: If you use a specific system to get POST and GET, you have to edit "private function getData()"
45
- * IMPORTANT 2: If you use DISABLE, control it manually when you save it
46
- * IMPORTANT 3: Currently aren't supported array names (name="MyField[]"), you can use: name="MyField_1"
47
- */
28
+    /**
29
+     * @name Form Generator for Kernel Web
30
+     * @version A.1.0
31
+     * @copyright ArrayZone 2014
32
+     * @license AZPL or later; see License.txt or http://arrayzone.com/license
33
+     * @category plugin
34
+     * 
35
+     * Description: This script write a FORM HTML automatically from array (it will be generated trhoug functions).
36
+     * It write any type of items basics of HTML4 and some of HTML5.
37
+     * Once user clic "submit", all information  will be validated via PHP
38
+     * Some types like "required" and "email" validated trhough PHP to avoid problems with navigator compatibilities or hacker attacks
39
+     *
40
+     * NEXT VERSION: If you can use JavaScript and have JQuery implemented, the form can write JavaScript code to validate all
41
+     * Read the documentation for all information in kernel.arrayzone.com
42
+     * or all coments of all parameters and functions.
43
+     * 
44
+     * IMPORTANT: If you use a specific system to get POST and GET, you have to edit "private function getData()"
45
+     * IMPORTANT 2: If you use DISABLE, control it manually when you save it
46
+     * IMPORTANT 3: Currently aren't supported array names (name="MyField[]"), you can use: name="MyField_1"
47
+     */
48 48
 
49 49
  
50 50
  
@@ -52,855 +52,855 @@  discard block
 block discarded – undo
52 52
 
53 53
 class CFPHPFormularios
54 54
 {
55
-	/*
55
+    /*
56 56
 	 * Form configuration. It don't need description
57 57
 	 */
58
-	public $method = 'post'; // post / get (CAUTION!!: Case sensitive)
59
-	public $action = '';
60
-	public $name = '';
61
-	public $on_submit = '';
62
-	public $id = '';
63
-	public $class = '';
64
-	
65
-	/**
66
-	 * Logging control
67
-	 * @logErrors boolean If is true, $errors will store errors
68
-	 * @errors string Contain al errors
69
-	 */
70
-	public $logErrors = true;
71
-	public $errors = '';
72
-	
73
-	/*
58
+    public $method = 'post'; // post / get (CAUTION!!: Case sensitive)
59
+    public $action = '';
60
+    public $name = '';
61
+    public $on_submit = '';
62
+    public $id = '';
63
+    public $class = '';
64
+	
65
+    /**
66
+     * Logging control
67
+     * @logErrors boolean If is true, $errors will store errors
68
+     * @errors string Contain al errors
69
+     */
70
+    public $logErrors = true;
71
+    public $errors = '';
72
+	
73
+    /*
74 74
 	 * Other configurations about form
75 75
 	 */
76 76
 	
77
-	/**
78
-	 * @show_labels boolean
79
-	 * @tutorial If is true, it show label text of some inputs before input tag
80
-	 */
81
-	public $show_labels = true;
77
+    /**
78
+     * @show_labels boolean
79
+     * @tutorial If is true, it show label text of some inputs before input tag
80
+     */
81
+    public $show_labels = true;
82 82
 	
83
-	/** IN DEVELOPMENT
84
-	 * @self_show boolean
85
-	 * @tutorial if is true, it show the input when function addX is called
86
-	 */
87
-	//public $self_show = false;
83
+    /** IN DEVELOPMENT
84
+     * @self_show boolean
85
+     * @tutorial if is true, it show the input when function addX is called
86
+     */
87
+    //public $self_show = false;
88 88
 	
89 89
 	
90
-	/** IN DEVELOPMENT
91
-	 * @only_show boolean
92
-	 * @tutorial If is true, it don't save the configuration to input (so showForm() and validateData() don't work)
93
-	 */
94
-	//public $only_show = false;
90
+    /** IN DEVELOPMENT
91
+     * @only_show boolean
92
+     * @tutorial If is true, it don't save the configuration to input (so showForm() and validateData() don't work)
93
+     */
94
+    //public $only_show = false;
95 95
 
96 96
 	
97
-	/*
97
+    /*
98 98
 	 * Content
99 99
 	 * It can be set manually or automatically trhough functions
100 100
 	 */
101
-	public $content = array();
101
+    public $content = array();
102 102
 	
103 103
 /*
104 104
  * Start functions
105 105
  */
106 106
 	
107
-	/**
108
-	 * @name showArray
109
-	 * @tutorial This function show array result, it can be copied and you can replace all generator functions to this array
110
-	 * @example $fg->showArray();
111
-	 */
112
-	public function showArray($array = false) {
113
-		print_r($this->content);
114
-	}
115
-	
116
-	
117
-	/**
118
-	 * @name showForm
119
-	 * @tutorial This function generate the form
120
-	 * @example $fg->showForm();
121
-	 */
122
-	public function showForm() {
123
-		// Loading all arrays
124
-		$toShow = $this->content;
125
-		
126
-		// Showing form
127
-		echo $this->showStartForm();
128
-		
129
-		
130
-		// Itearate all inputs
131
-		foreach ($toShow as $input) {
132
-			// Reading data
133
-			if (isset($input['data'])) $data = $input['data'];
107
+    /**
108
+     * @name showArray
109
+     * @tutorial This function show array result, it can be copied and you can replace all generator functions to this array
110
+     * @example $fg->showArray();
111
+     */
112
+    public function showArray($array = false) {
113
+        print_r($this->content);
114
+    }
115
+	
116
+	
117
+    /**
118
+     * @name showForm
119
+     * @tutorial This function generate the form
120
+     * @example $fg->showForm();
121
+     */
122
+    public function showForm() {
123
+        // Loading all arrays
124
+        $toShow = $this->content;
125
+		
126
+        // Showing form
127
+        echo $this->showStartForm();
128
+		
129
+		
130
+        // Itearate all inputs
131
+        foreach ($toShow as $input) {
132
+            // Reading data
133
+            if (isset($input['data'])) $data = $input['data'];
134 134
 			
135
-			// New row
136
-			echo '<div>';
137
-				// Showing labels
138
-				if ($this->show_labels) {
139
-					echo '<div>';
140
-						if (isset($input['label']) and $input['label'] != '') echo $input['label'];
141
-					echo '</div>';
142
-				}
135
+            // New row
136
+            echo '<div>';
137
+                // Showing labels
138
+                if ($this->show_labels) {
139
+                    echo '<div>';
140
+                        if (isset($input['label']) and $input['label'] != '') echo $input['label'];
141
+                    echo '</div>';
142
+                }
143 143
 				
144
-				// Showing content
145
-				echo '<div>';		
146
-					switch ($input['type']) {
147
-						case 'input':
148
-							echo $this->showInput($data);
149
-							break;
150
-						case 'radio':
151
-							echo $this->showRadio($data, $input['values'], $input['selected']);
152
-							break;
153
-						case 'select';
154
-							echo $this->showSelect($data, $input['values'], $input['selected']);
155
-							break;
156
-						case 'textarea':
157
-							echo $this->showTextArea($input['text'], $data);
158
-							break;
159
-						case 'separator';
160
-							echo '<br />';
161
-							break;
162
-					}
163
-				echo '</div>';
164
-			echo '</div>'.PHP_EOL;
165
-		}
166
-		
167
-		// Closing form
168
-		echo $this->showEndForm();
169
-	}
170
-	
171
-	
172
-	/* 
144
+                // Showing content
145
+                echo '<div>';		
146
+                    switch ($input['type']) {
147
+                        case 'input':
148
+                            echo $this->showInput($data);
149
+                            break;
150
+                        case 'radio':
151
+                            echo $this->showRadio($data, $input['values'], $input['selected']);
152
+                            break;
153
+                        case 'select';
154
+                            echo $this->showSelect($data, $input['values'], $input['selected']);
155
+                            break;
156
+                        case 'textarea':
157
+                            echo $this->showTextArea($input['text'], $data);
158
+                            break;
159
+                        case 'separator';
160
+                            echo '<br />';
161
+                            break;
162
+                    }
163
+                echo '</div>';
164
+            echo '</div>'.PHP_EOL;
165
+        }
166
+		
167
+        // Closing form
168
+        echo $this->showEndForm();
169
+    }
170
+	
171
+	
172
+    /* 
173 173
 	 * The following "showX" functions are autoloaded and show the different parts of form
174 174
 	 */ 
175 175
 	
176
-	/**
177
-	 * @name showInput
178
-	 * @tutorial This function show <input> tag with parameters specifics
179
-	 * @param array $data All data to show in the input
180
-	 * @return string
181
-	 */
182
-	private function showInput($data) {
183
-		$r = '';
184
-		// Number start if is range
185
-		if ($data['type'] == 'range' and isset($data['min'])) $r .= $data['min'] . ' '; 
176
+    /**
177
+     * @name showInput
178
+     * @tutorial This function show <input> tag with parameters specifics
179
+     * @param array $data All data to show in the input
180
+     * @return string
181
+     */
182
+    private function showInput($data) {
183
+        $r = '';
184
+        // Number start if is range
185
+        if ($data['type'] == 'range' and isset($data['min'])) $r .= $data['min'] . ' '; 
186 186
 		
187
-		$r .= '<input ';
187
+        $r .= '<input ';
188 188
 		
189
-		// Reading value sended by user, if are any, we load it temporaly to show in form
190
-		$data['value'] = $this->getData($data['name'], $data['value']);
189
+        // Reading value sended by user, if are any, we load it temporaly to show in form
190
+        $data['value'] = $this->getData($data['name'], $data['value']);
191 191
 		
192
-		// Asignamos las claves conviritiendo el array
193
-		foreach ($data as $attr=>$value) {
194
-			$r .= $attr.'="'.$value.'" ';
195
-		}
192
+        // Asignamos las claves conviritiendo el array
193
+        foreach ($data as $attr=>$value) {
194
+            $r .= $attr.'="'.$value.'" ';
195
+        }
196 196
 		
197
-		$r .= '/>';
197
+        $r .= '/>';
198 198
 		
199
-		// Number end if is range
200
-		if ($data['type'] == 'range' and isset($data['max'])) $r .= $data['max'] . ' ';
199
+        // Number end if is range
200
+        if ($data['type'] == 'range' and isset($data['max'])) $r .= $data['max'] . ' ';
201 201
 		
202
-		// Return
203
-		return $r;
204
-	}
202
+        // Return
203
+        return $r;
204
+    }
205 205
 	
206
-	private function showRadio($data, $values, $selected) {		
207
-		// Primero generamos un esquema
208
-		$base = '<label><input type="radio"';
206
+    private function showRadio($data, $values, $selected) {		
207
+        // Primero generamos un esquema
208
+        $base = '<label><input type="radio"';
209 209
 		
210
-		// Asignamos las claves conviritiendo el array
211
-		foreach ($data as $attr=>$value) {
212
-			$base .= $attr.'="'.$value.'" ';
213
-		}
210
+        // Asignamos las claves conviritiendo el array
211
+        foreach ($data as $attr=>$value) {
212
+            $base .= $attr.'="'.$value.'" ';
213
+        }
214 214
 		
215 215
 		
216
-		// Leemos el valor enviado por el usuario (si lo hay) y si hay, reemplazamos $select por el valor 
217
-		$selected = $this->getData($data['name'], $selected);
216
+        // Leemos el valor enviado por el usuario (si lo hay) y si hay, reemplazamos $select por el valor 
217
+        $selected = $this->getData($data['name'], $selected);
218 218
 
219
-		//echo $sendValue;
220
-		// Si no tiene m�ltiples valores (es un string), lo retornamos de golpe 
221
-		if (!is_array($values)) {
222
-			// Comprobamos el value, es posible que el usuario este intentando crear los Radio (option) de forma separada
223
-			if (!is_array($values) and $selected == $values) $base .= ' checked="checked" ';
224
-			return $base . ' value="'.$values.'" />';
225
-		} 
219
+        //echo $sendValue;
220
+        // Si no tiene m�ltiples valores (es un string), lo retornamos de golpe 
221
+        if (!is_array($values)) {
222
+            // Comprobamos el value, es posible que el usuario este intentando crear los Radio (option) de forma separada
223
+            if (!is_array($values) and $selected == $values) $base .= ' checked="checked" ';
224
+            return $base . ' value="'.$values.'" />';
225
+        } 
226 226
 
227
-		// Por el contrario, tenemos 1 o m�s value con lo que es un posible texto
228
-		// Ahora preparamos todos los input
229
-		$r = '';
230
-		foreach ($values as $id=>$text) {
231
-			$r .= $base;
232
-			if ($selected !== null and $id == $selected) $r .= ' checked="checked" ';
233
-			$r .= ' value="'. $id .'" />'.$text.'</label>';
234
-		}
235
-		
236
-		return $r;
237
-		
238
-	}
239
-	
240
-	/**
241
-	 * @name showSelect
242
-	 * @tutorial This function show <select> tag with all values
243
-	 * @param array $data All data to show in the input
244
-	 * @param array $values Values to show in select
245
-	 * @param string $selected Specify selected option
246
-	 * @return string
247
-	 */
248
-	private function showSelect($data, $values, $selected = null) {
249
-		$r = '<select ';
250
-			// Convert array to input string
251
-			foreach ($data as $attr=>$value) {
252
-				$r .= $attr.'="'.$value.'" ';
253
-			}
254
-			// Return end input
255
-		$r .= '>';
256
-		
257
-		// Leemos el valor enviado por el usuario (si lo hay) y si hay, reemplazamos $select por el valor
258
-		$selected = $this->getData($data['name'], $selected);
259
-		
260
-		
261
-		// Loading options
262
-		// To speed up processes, we have two whiles depending if are any selected value
263
-		if ($selected != '') {
264
-			foreach ($values as $val=>$txt) {
265
-				$r .= '<option value="' . $val . '"';
266
-					if ($val == $selected) $r .= ' selected ';
267
-				$r .= '>' . $txt . '</option>';
268
-			}
269
-		} else {
270
-			foreach ($values as $val=>$txt) {
271
-				$r .= '<option value="' . $val . '">' . $txt . '</option>';
272
-			}	
273
-		}
274
-		
275
-		return $r . '</select>';
276
-	}
277
-	
278
-	/**
279
-	 * @name showTextArea
280
-	 * @tutorial This function show <textarea> tag with all values
281
-	 * @param array $data All data to show in the input
282
-	 * @return string
283
-	 */
284
-	private function showTextArea($text, $data) {
285
-		$r = '';
286
-		$r .= '<textarea ';
287
-		
288
-		// Asignamos las claves conviritiendo el array
289
-		foreach ($data as $attr=>$value) {
290
-			$r .= $attr.'="'.$value.'" ';
291
-		}
292
-		
293
-		$r .= '>';
294
-		// Reading value sended by user, if are any, we load it temporaly to show in form
295
-		$r .= $this->getData($data['name'], $text) . '</textarea>';
227
+        // Por el contrario, tenemos 1 o m�s value con lo que es un posible texto
228
+        // Ahora preparamos todos los input
229
+        $r = '';
230
+        foreach ($values as $id=>$text) {
231
+            $r .= $base;
232
+            if ($selected !== null and $id == $selected) $r .= ' checked="checked" ';
233
+            $r .= ' value="'. $id .'" />'.$text.'</label>';
234
+        }
235
+		
236
+        return $r;
237
+		
238
+    }
239
+	
240
+    /**
241
+     * @name showSelect
242
+     * @tutorial This function show <select> tag with all values
243
+     * @param array $data All data to show in the input
244
+     * @param array $values Values to show in select
245
+     * @param string $selected Specify selected option
246
+     * @return string
247
+     */
248
+    private function showSelect($data, $values, $selected = null) {
249
+        $r = '<select ';
250
+            // Convert array to input string
251
+            foreach ($data as $attr=>$value) {
252
+                $r .= $attr.'="'.$value.'" ';
253
+            }
254
+            // Return end input
255
+        $r .= '>';
256
+		
257
+        // Leemos el valor enviado por el usuario (si lo hay) y si hay, reemplazamos $select por el valor
258
+        $selected = $this->getData($data['name'], $selected);
259
+		
260
+		
261
+        // Loading options
262
+        // To speed up processes, we have two whiles depending if are any selected value
263
+        if ($selected != '') {
264
+            foreach ($values as $val=>$txt) {
265
+                $r .= '<option value="' . $val . '"';
266
+                    if ($val == $selected) $r .= ' selected ';
267
+                $r .= '>' . $txt . '</option>';
268
+            }
269
+        } else {
270
+            foreach ($values as $val=>$txt) {
271
+                $r .= '<option value="' . $val . '">' . $txt . '</option>';
272
+            }	
273
+        }
274
+		
275
+        return $r . '</select>';
276
+    }
277
+	
278
+    /**
279
+     * @name showTextArea
280
+     * @tutorial This function show <textarea> tag with all values
281
+     * @param array $data All data to show in the input
282
+     * @return string
283
+     */
284
+    private function showTextArea($text, $data) {
285
+        $r = '';
286
+        $r .= '<textarea ';
287
+		
288
+        // Asignamos las claves conviritiendo el array
289
+        foreach ($data as $attr=>$value) {
290
+            $r .= $attr.'="'.$value.'" ';
291
+        }
292
+		
293
+        $r .= '>';
294
+        // Reading value sended by user, if are any, we load it temporaly to show in form
295
+        $r .= $this->getData($data['name'], $text) . '</textarea>';
296 296
 
297
-		// Return
298
-		return $r;
299
-	}
300
-	
301
-	/**
302
-	 * @name showStartForm This function return the start part of the form
303
-	 * @return string
304
-	 */
305
-	private function showStartForm() {
306
-		$r = '<form ';
307
-		if ($this->action!= '') $r .= 'action="'.$this->action.'" ';
308
-		if ($this->method != '') $r .= 'method="'.$this->method .'" ';
309
-		if ($this->name != '') $r .= 'name="'.$this->name .'" ';
310
-		if ($this->on_submit != '') $r .= 'onSubmit="'.$this->on_submit  .'" ';
311
-		if ($this->id != '') $r .= 'id="'.$this->id .'" ';
312
-		if ($this->class != '') $r .= 'class="'.$this->class .'" ';
313
-	
314
-		return $r . '>'.PHP_EOL;
315
-	}
316
-	
317
-	/**
318
-	 * @name showEndForm This show the end of the form
319
-	 * @return string
320
-	 */
321
-	private function showEndForm() {
322
-		return '</form>';
323
-	}
324
-	
325
-	
326
-	/*
297
+        // Return
298
+        return $r;
299
+    }
300
+	
301
+    /**
302
+     * @name showStartForm This function return the start part of the form
303
+     * @return string
304
+     */
305
+    private function showStartForm() {
306
+        $r = '<form ';
307
+        if ($this->action!= '') $r .= 'action="'.$this->action.'" ';
308
+        if ($this->method != '') $r .= 'method="'.$this->method .'" ';
309
+        if ($this->name != '') $r .= 'name="'.$this->name .'" ';
310
+        if ($this->on_submit != '') $r .= 'onSubmit="'.$this->on_submit  .'" ';
311
+        if ($this->id != '') $r .= 'id="'.$this->id .'" ';
312
+        if ($this->class != '') $r .= 'class="'.$this->class .'" ';
313
+	
314
+        return $r . '>'.PHP_EOL;
315
+    }
316
+	
317
+    /**
318
+     * @name showEndForm This show the end of the form
319
+     * @return string
320
+     */
321
+    private function showEndForm() {
322
+        return '</form>';
323
+    }
324
+	
325
+	
326
+    /*
327 327
 	 * VALIDATORS
328 328
 	 */
329
-	/**
330
-	 * @name validate
331
-	 * @tutorial this function validate items sends trhough form. It DON'T FILTER
332
-	 * 	Basically simulate HTML5 trhoguh PHP for full compatibility
333
-	 * @param boolean $error_list If is true, it generate in $this->error_list a list with data required 
334
-	 */
335
-	public function validate() {
336
-		$this->error_list = ''; // Clean error list
337
-		$total = 0; // Total counted params
338
-		$success = 0; // Total correct
339
-		
340
-		// load all inputs
341
-		$params = $this->content;
342
-		
343
-		// reading all inputs
344
-		foreach ($params as $param) {
345
-			++$total;
329
+    /**
330
+     * @name validate
331
+     * @tutorial this function validate items sends trhough form. It DON'T FILTER
332
+     * 	Basically simulate HTML5 trhoguh PHP for full compatibility
333
+     * @param boolean $error_list If is true, it generate in $this->error_list a list with data required 
334
+     */
335
+    public function validate() {
336
+        $this->error_list = ''; // Clean error list
337
+        $total = 0; // Total counted params
338
+        $success = 0; // Total correct
339
+		
340
+        // load all inputs
341
+        $params = $this->content;
342
+		
343
+        // reading all inputs
344
+        foreach ($params as $param) {
345
+            ++$total;
346 346
 			
347
-			// Skip separators and bad generated arrays
348
-			if (!isset($param['data'])) {
349
-				++$success;
350
-				continue;
351
-			}
347
+            // Skip separators and bad generated arrays
348
+            if (!isset($param['data'])) {
349
+                ++$success;
350
+                continue;
351
+            }
352 352
 			
353
-			// Start validation
354
-			$data = $param['data'];
353
+            // Start validation
354
+            $data = $param['data'];
355 355
 			
356
-			// Checking type input
357
-			switch ($param['type']) {
356
+            // Checking type input
357
+            switch ($param['type']) {
358 358
 				
359
-				case 'input':
360
-					$success += $this->validateInput($param['data']);
361
-					break;
362
-				case 'radio':
363
-					$success += $this->validateRadio($param);
364
-					break;
365
-				case 'select':
366
-					$success += $this->validateSelect($param);
367
-					break;
368
-				default:
369
-					$success++;
370
-			}
371
-		}
372
-		
373
-		if ($success >= $total) return true;
374
-		return false;
375
-	}
376
-	
377
-	/**
378
-	 * @name validateInput
379
-	 * @tutorial This function test if an input (text, password, checkbox, ...) is valid depending assigned values
380
-	 * If you need other that is not invented, you can use "pattern" for example
381
-	 * It checks:
382
-	 * 	- required
383
-	 *  - date
384
-	 *  - min
385
-	 *  - max
386
-	 *  - number
387
-	 * @param array $data Contains all information about input
388
-	 * @return boolean If return true, its okay
389
-	 */
390
-	private function validateInput($data) {
391
-		// Obtaining value send by user
392
-		$readValue = $this->getData($data['name']);
359
+                case 'input':
360
+                    $success += $this->validateInput($param['data']);
361
+                    break;
362
+                case 'radio':
363
+                    $success += $this->validateRadio($param);
364
+                    break;
365
+                case 'select':
366
+                    $success += $this->validateSelect($param);
367
+                    break;
368
+                default:
369
+                    $success++;
370
+            }
371
+        }
372
+		
373
+        if ($success >= $total) return true;
374
+        return false;
375
+    }
376
+	
377
+    /**
378
+     * @name validateInput
379
+     * @tutorial This function test if an input (text, password, checkbox, ...) is valid depending assigned values
380
+     * If you need other that is not invented, you can use "pattern" for example
381
+     * It checks:
382
+     * 	- required
383
+     *  - date
384
+     *  - min
385
+     *  - max
386
+     *  - number
387
+     * @param array $data Contains all information about input
388
+     * @return boolean If return true, its okay
389
+     */
390
+    private function validateInput($data) {
391
+        // Obtaining value send by user
392
+        $readValue = $this->getData($data['name']);
393 393
 
394
-		// Empty/not send and required?
395
-		// TODO: Add require --> file (uses $_FILE)
396
-		
397
-		if (isset($data['required']) and ($readValue === null or $readValue == '')) {
398
-			$this->log('is required', $data);
399
-			return false;
400
-		} elseif ($readValue == '') {
401
-			return true;
402
-		}
403
-		
404
-		// Checking type input
405
-		switch ($data['type']) {
406
-			case 'text':
407
-				// Maxlenght fail
408
-				if (isset($data['maxlength']) and is_numeric($data['maxlength']) and $data['maxlength'] > -1 
409
-					and strlen($readValue) > $data['maxlength']) {
394
+        // Empty/not send and required?
395
+        // TODO: Add require --> file (uses $_FILE)
396
+		
397
+        if (isset($data['required']) and ($readValue === null or $readValue == '')) {
398
+            $this->log('is required', $data);
399
+            return false;
400
+        } elseif ($readValue == '') {
401
+            return true;
402
+        }
403
+		
404
+        // Checking type input
405
+        switch ($data['type']) {
406
+            case 'text':
407
+                // Maxlenght fail
408
+                if (isset($data['maxlength']) and is_numeric($data['maxlength']) and $data['maxlength'] > -1 
409
+                    and strlen($readValue) > $data['maxlength']) {
410 410
 					
411
-					$this->log('is too long. Maximum' . ' ' . $data['maxlength'] . ' ' . 'characters', $data );
412
-					return false;
413
-				}
411
+                    $this->log('is too long. Maximum' . ' ' . $data['maxlength'] . ' ' . 'characters', $data );
412
+                    return false;
413
+                }
414 414
 				
415
-				if (isset($data['pattern']) and is_numeric($data['pattern']) and $data['maxlength'] != '' 
416
-					and preg_match($data['pattern'], $readValue) === FALSE) {
415
+                if (isset($data['pattern']) and is_numeric($data['pattern']) and $data['maxlength'] != '' 
416
+                    and preg_match($data['pattern'], $readValue) === FALSE) {
417 417
 					
418
-					$this->log('pattern error' . ' (' . $data['pattern'] . ')' , $data);
419
-					return false;
420
-				}
421
-				break;
422
-			case 'number':
423
-			case 'range':
424
-				// IS NUMERIC
425
-				if ($readValue != '' and !is_numeric($readValue)) {
426
-					$this->log('Not numeric', $data);
427
-					return false;
428
-				}
418
+                    $this->log('pattern error' . ' (' . $data['pattern'] . ')' , $data);
419
+                    return false;
420
+                }
421
+                break;
422
+            case 'number':
423
+            case 'range':
424
+                // IS NUMERIC
425
+                if ($readValue != '' and !is_numeric($readValue)) {
426
+                    $this->log('Not numeric', $data);
427
+                    return false;
428
+                }
429 429
 				
430
-				// MIN
431
-				if (isset($data['min']) and $readValue < $data['min']) {
432
-					$this->log('The number have to be greather than' . ' ' . $data['min'].'.', $data);
433
-					return false;
434
-				}
430
+                // MIN
431
+                if (isset($data['min']) and $readValue < $data['min']) {
432
+                    $this->log('The number have to be greather than' . ' ' . $data['min'].'.', $data);
433
+                    return false;
434
+                }
435 435
 				
436
-				// MAX
437
-				if (isset($data['max']) and $readValue > $data['max']) {
438
-					$this->log('The number have to be less than' . ' ' . $data['max'].'.', $data);
439
-					return false;
440
-				}
436
+                // MAX
437
+                if (isset($data['max']) and $readValue > $data['max']) {
438
+                    $this->log('The number have to be less than' . ' ' . $data['max'].'.', $data);
439
+                    return false;
440
+                }
441 441
 				
442
-				// STEP http://www.w3schools.com/tags/att_input_step.asp
443
-				// Value 0 ever is valid (and if you try Divide to Zero, it will take error because the result is inifinite
444
-				if (isset($data['step']) and $readValue != 0 and $readValue % $data['step'] !== 0) {
445
-					$this->log('The number have to be multiple of' . ' ' . $data['step'].'.', $data);
446
-					return false;
447
-				}
442
+                // STEP http://www.w3schools.com/tags/att_input_step.asp
443
+                // Value 0 ever is valid (and if you try Divide to Zero, it will take error because the result is inifinite
444
+                if (isset($data['step']) and $readValue != 0 and $readValue % $data['step'] !== 0) {
445
+                    $this->log('The number have to be multiple of' . ' ' . $data['step'].'.', $data);
446
+                    return false;
447
+                }
448 448
 				
449
-				break;
450
-			case 'date';
451
-				//min | max
452
-				echo $readValue;
453
-				if (!is_date($readValue)) {
454
-					$this->log('The date' . ' ' .$readValue.' '. 'must have format' . ' mm/dd/yyyy '.'.', $data);
455
-					return false;
456
-				}
457
-				break;
449
+                break;
450
+            case 'date';
451
+                //min | max
452
+                echo $readValue;
453
+                if (!is_date($readValue)) {
454
+                    $this->log('The date' . ' ' .$readValue.' '. 'must have format' . ' mm/dd/yyyy '.'.', $data);
455
+                    return false;
456
+                }
457
+                break;
458 458
 				
459
-			case 'email':
460
-				if (!filter_var($readValue, FILTER_VALIDATE_EMAIL)) {
461
-					$this->log('Email invalid', $data);
462
-					return false;
463
-				}
464
-			case 'file':
465
-				//accept="image/*" (http://www.w3schools.com/tags/att_input_accept.asp)
466
-				break;
459
+            case 'email':
460
+                if (!filter_var($readValue, FILTER_VALIDATE_EMAIL)) {
461
+                    $this->log('Email invalid', $data);
462
+                    return false;
463
+                }
464
+            case 'file':
465
+                //accept="image/*" (http://www.w3schools.com/tags/att_input_accept.asp)
466
+                break;
467 467
 			
468
-			case 'url':
469
-				if (!filter_var($readValue, FILTER_VALIDATE_URL)) {
470
-					$this->log('Invalid url', $data);
471
-					return false;
472
-				}
473
-				break;	
474
-		}
475
-		// Validamos el resto como cierto (no podemos parametrizar el 100 % de cosas nuevas que salgan de HTML)
476
-		return true;
477
-	}
478
-	
479
-	/**
480
-	 * @name validateArray
481
-	 * This functions validate an Array, is a complement to validateRadio, select...
482
-	 * @param array/string $values List of values to validate
483
-	 * @param array/string $value/s selecteds by user 
484
-	 */
485
-	private function validateArray($values, $value, $data = array()) {
486
-		if (is_array($values)) {
487
-			// Is array (serach all "$value" in "$values"
488
-			if (!array_key_exists($value, $values)) {
489
-				if (is_array($value)) {
490
-					$this->log('Values don\'t match.', $data);
491
-				} else {
492
-					$this->log('ID ' .$value.' ' . 'don\'t match', $data);
493
-				}
494
-				return false;
495
-			}
496
-		} else {
497
-			// Is string
498
-			if ($readValue == $values) {
499
-				$this->log('The value' . ' ' . $value.' ' . 'is not available', $data);
500
-				return false;
501
-			}
502
-		}
503
-		
504
-		return true;
505
-	}
506
-	
507
-	
508
-	/**
509
-	 * @name validateRadio
510
-	 * @tutorial This function test if an radio is valid depending assigned values
511
-	 * @param array $data Contains all information about input
512
-	 * @return boolean If return true, its okay
513
-	 */
514
-	private function validateRadio($params) {
515
-		$data = $params['data'];
516
-		// Obtaining value send by user
517
-		$readValue = $this->getData($data['name']);
518
-		
519
-		// Is required?
520
-		if (isset($data['required']) and ($readValue === null or $readValue == '')) {
521
-			$this->log('is required', $data);
522
-			return false;
523
-		} elseif ($readValue == '') {
524
-			return true;
525
-		}
526
-		
527
-		
528
-		// Seleccionamos que tipo de analisis (dependiendo si se ha creado como input o como radio)
529
-		// Esto no deberia estar aqui porque no es necesario, pero esta hecho por posibles despistes de usuarios finales
530
-		if (isset($params['values'])) {
531
-			return $this->validateArray($params['values'], $readValue, $data);
532
-		} elseif ($data['value']) {
533
-			// If user try to add radio like normal input... (into 'value' in index data)
534
-			return $this->validateArray($params['value'], $readValue, $data);
535
-		}
536
-		
537
-		return false;
538
-	}
539
-	
540
-	/**
541
-	 * @name validateSelect
542
-	 * @tutorial This function test if an select is valid depending assigned values
543
-	 * @param array $data Contains all information about input
544
-	 * @return boolean If return true, its okay
545
-	 */
546
-	private function validateSelect($param) {
547
-		$data = $param['data'];
548
-		// Obtaining value send by user
549
-		$readValue = $this->getData($data['name']);
550
-	
551
-		// Is required?
552
-		if (isset($data['required']) and ($readValue === null or $readValue == '')) {
553
-			$this->log('is required', $data);
554
-			return false;
555
-		} elseif ($readValue == '') {
556
-			return true;
557
-		}
558
-	
559
-		// Seleccionamos que tipo de analisis (dependiendo si se ha creado como input o como radio)
560
-		// Esto no deberia estar aqui porque no es necesario, pero esta hecho por posibles despistes de usuarios finales
561
-		return $this->validateArray($param['values'], $readValue, $data);
562
-	}
563
-	
564
-	
565
-	/**
566
-	 * @name getData This function get value from POST/GET trhough ID ($key)
567
-	 * 	If you use other system to load GET and POST, you have to edit this
568
-	 * @param string $key Object Index
569
-	 * @param string $default Default item if not data
570
-	 * @return string/null Return the value, if don't exist, return null
571
-	 */
572
-	private function getData($key, $default = null) {
573
-		if ($this->method == "post") return isset($_POST[$key]) ? $_POST[$key] : $default;
574
-		else return isset($_GET[$key]) ? $_GET[$key] : $default;
575
-	}
576
-	
577
-	/*
468
+            case 'url':
469
+                if (!filter_var($readValue, FILTER_VALIDATE_URL)) {
470
+                    $this->log('Invalid url', $data);
471
+                    return false;
472
+                }
473
+                break;	
474
+        }
475
+        // Validamos el resto como cierto (no podemos parametrizar el 100 % de cosas nuevas que salgan de HTML)
476
+        return true;
477
+    }
478
+	
479
+    /**
480
+     * @name validateArray
481
+     * This functions validate an Array, is a complement to validateRadio, select...
482
+     * @param array/string $values List of values to validate
483
+     * @param array/string $value/s selecteds by user 
484
+     */
485
+    private function validateArray($values, $value, $data = array()) {
486
+        if (is_array($values)) {
487
+            // Is array (serach all "$value" in "$values"
488
+            if (!array_key_exists($value, $values)) {
489
+                if (is_array($value)) {
490
+                    $this->log('Values don\'t match.', $data);
491
+                } else {
492
+                    $this->log('ID ' .$value.' ' . 'don\'t match', $data);
493
+                }
494
+                return false;
495
+            }
496
+        } else {
497
+            // Is string
498
+            if ($readValue == $values) {
499
+                $this->log('The value' . ' ' . $value.' ' . 'is not available', $data);
500
+                return false;
501
+            }
502
+        }
503
+		
504
+        return true;
505
+    }
506
+	
507
+	
508
+    /**
509
+     * @name validateRadio
510
+     * @tutorial This function test if an radio is valid depending assigned values
511
+     * @param array $data Contains all information about input
512
+     * @return boolean If return true, its okay
513
+     */
514
+    private function validateRadio($params) {
515
+        $data = $params['data'];
516
+        // Obtaining value send by user
517
+        $readValue = $this->getData($data['name']);
518
+		
519
+        // Is required?
520
+        if (isset($data['required']) and ($readValue === null or $readValue == '')) {
521
+            $this->log('is required', $data);
522
+            return false;
523
+        } elseif ($readValue == '') {
524
+            return true;
525
+        }
526
+		
527
+		
528
+        // Seleccionamos que tipo de analisis (dependiendo si se ha creado como input o como radio)
529
+        // Esto no deberia estar aqui porque no es necesario, pero esta hecho por posibles despistes de usuarios finales
530
+        if (isset($params['values'])) {
531
+            return $this->validateArray($params['values'], $readValue, $data);
532
+        } elseif ($data['value']) {
533
+            // If user try to add radio like normal input... (into 'value' in index data)
534
+            return $this->validateArray($params['value'], $readValue, $data);
535
+        }
536
+		
537
+        return false;
538
+    }
539
+	
540
+    /**
541
+     * @name validateSelect
542
+     * @tutorial This function test if an select is valid depending assigned values
543
+     * @param array $data Contains all information about input
544
+     * @return boolean If return true, its okay
545
+     */
546
+    private function validateSelect($param) {
547
+        $data = $param['data'];
548
+        // Obtaining value send by user
549
+        $readValue = $this->getData($data['name']);
550
+	
551
+        // Is required?
552
+        if (isset($data['required']) and ($readValue === null or $readValue == '')) {
553
+            $this->log('is required', $data);
554
+            return false;
555
+        } elseif ($readValue == '') {
556
+            return true;
557
+        }
558
+	
559
+        // Seleccionamos que tipo de analisis (dependiendo si se ha creado como input o como radio)
560
+        // Esto no deberia estar aqui porque no es necesario, pero esta hecho por posibles despistes de usuarios finales
561
+        return $this->validateArray($param['values'], $readValue, $data);
562
+    }
563
+	
564
+	
565
+    /**
566
+     * @name getData This function get value from POST/GET trhough ID ($key)
567
+     * 	If you use other system to load GET and POST, you have to edit this
568
+     * @param string $key Object Index
569
+     * @param string $default Default item if not data
570
+     * @return string/null Return the value, if don't exist, return null
571
+     */
572
+    private function getData($key, $default = null) {
573
+        if ($this->method == "post") return isset($_POST[$key]) ? $_POST[$key] : $default;
574
+        else return isset($_GET[$key]) ? $_GET[$key] : $default;
575
+    }
576
+	
577
+    /*
578 578
 	 * Array constructors
579 579
 	 * This functions will be called to self construct array easy
580 580
 	 */
581 581
 	
582
-	/**
583
-	 * @name separator
584
-	 * @tutorial This function add separation (<hr/>) between parts
585
-	 */
586
-	public function addSeparator() {
587
-		$this->content[] = array('type' => 'separator');
588
-	}
589
-	
590
-	/**
591
-	 * @name addInput
592
-	 * @tutorial This class generate a generic INPUT
593
-	 * @param string $label If is not empty, it show a text "label" with the text
594
-	 * @param string $type Specify input type. It will be validated once use validate();
595
-	 * Types that are current supported to validate:
596
-	 * 		text, password, hidden, file
597
-	 * 		Its supported use ALL type supported (http://www.w3schools.com/tags/att_input_type.asp), 
598
-	 * 	Some type input you can declare directly via functions (afther this function are more)
599
-	 * @param string $name Element name
600
-	 * @param string $value Element value
601
-	 * @param string $required Element required?
602
-	 * @param array $attributes Optional atributes not listened (in array).
603
-	 * 		EX: You like use "date", then you can put array('min' => '1979-12-31', 'max' => '2000-01-02')
604
-	 * 		All parameters currently working: min, max (date and number), pattern,   
605
-	 * 
606
-	 * @example $fr->addInput('text', 'Username', '', true, 'Type here your username', array('class'=>'text red', id => 'text1'), array())
607
-	 */
608
-	public function addInput($label = '', $type = 'text', $name = '', $value = '', $required = false, $placeholder = '',  $attributes = array()) {
609
-		// Creating main data
610
-		$data = array(
611
-			'type' => $type,
612
-			'name' => $name,
613
-			'value' => $value,
614
-		);
615
-		
616
-		
617
-		if ($required) $data['required'] = 'required';
618
-		if ($placeholder != '') $data['placeholder'] = $placeholder;
619
-		if (!empty($attributes)) array_merge($data, $attributes);
620
-		
621
-		
622
-		// Saving data to object
623
-		$content = array(
624
-			'type' => 'input',
625
-			'data' => $data	
626
-		);
627
-		if ($label != '') $content['label'] = $label;
582
+    /**
583
+     * @name separator
584
+     * @tutorial This function add separation (<hr/>) between parts
585
+     */
586
+    public function addSeparator() {
587
+        $this->content[] = array('type' => 'separator');
588
+    }
589
+	
590
+    /**
591
+     * @name addInput
592
+     * @tutorial This class generate a generic INPUT
593
+     * @param string $label If is not empty, it show a text "label" with the text
594
+     * @param string $type Specify input type. It will be validated once use validate();
595
+     * Types that are current supported to validate:
596
+     * 		text, password, hidden, file
597
+     * 		Its supported use ALL type supported (http://www.w3schools.com/tags/att_input_type.asp), 
598
+     * 	Some type input you can declare directly via functions (afther this function are more)
599
+     * @param string $name Element name
600
+     * @param string $value Element value
601
+     * @param string $required Element required?
602
+     * @param array $attributes Optional atributes not listened (in array).
603
+     * 		EX: You like use "date", then you can put array('min' => '1979-12-31', 'max' => '2000-01-02')
604
+     * 		All parameters currently working: min, max (date and number), pattern,   
605
+     * 
606
+     * @example $fr->addInput('text', 'Username', '', true, 'Type here your username', array('class'=>'text red', id => 'text1'), array())
607
+     */
608
+    public function addInput($label = '', $type = 'text', $name = '', $value = '', $required = false, $placeholder = '',  $attributes = array()) {
609
+        // Creating main data
610
+        $data = array(
611
+            'type' => $type,
612
+            'name' => $name,
613
+            'value' => $value,
614
+        );
615
+		
616
+		
617
+        if ($required) $data['required'] = 'required';
618
+        if ($placeholder != '') $data['placeholder'] = $placeholder;
619
+        if (!empty($attributes)) array_merge($data, $attributes);
620
+		
621
+		
622
+        // Saving data to object
623
+        $content = array(
624
+            'type' => 'input',
625
+            'data' => $data	
626
+        );
627
+        if ($label != '') $content['label'] = $label;
628 628
 		 
629
-		$this->content[] = $content;
630
-	}
631
-	
632
-	/**
633
-	 * @name addNumber This function add input "number" and "date" with min, max and step (if you like)
634
-	 * @param string $label If is not empty, it show a text "label" with the text
635
-	 * @param string $name Element name
636
-	 * @param string $value Default value
637
-	 * @param boolen $range If is true, then show "range" instead of text with number
638
-	 * @param boolen $required Is required?
639
-	 * @param number $min Minium value. Empty nothing
640
-	 * @param number $max Max value. Empty nothing
641
-	 * @param number $step Step (multiply). Empty nothing (ex: step 2: -2, 0, 2, 4 ...)
642
-	 * @param string $placeholder Default text to show if box is empty
643
-	 * @param unknown $attributes Additional attributes
644
-	 */
645
-	public function addNumber($label = '', $name = '', $value = '', $range = false, $required = false, $min = '', $max = '', $step = '',  $placeholder = '',  $attributes = array()) {
646
-		// Creating main data
647
-		$data = array(
648
-			'type' => (! $range) ? 'number' : 'range',
649
-			'name' => $name,
650
-			'value' => $value,
651
-		);
652
-	
653
-		
654
-		if ($required) $data['required'] = 'required';
655
-		if ($min) $data['min'] = $min;
656
-		if ($max) $data['max'] = $max;
657
-		if ($step) $data['step'] = $step;
658
-		if ($placeholder != '') $data['placeholder'] = $placeholder;
659
-		if (!empty($attributes)) array_merge($data, $attributes);
660
-	
661
-		// Saving data to object
662
-		$content = array(
663
-			'type' => 'input',
664
-			'data' => $data
665
-		);
666
-		if ($label != '') $content['label'] = $label;
667
-		
668
-		$this->content[] = $content;
669
-	}
629
+        $this->content[] = $content;
630
+    }
631
+	
632
+    /**
633
+     * @name addNumber This function add input "number" and "date" with min, max and step (if you like)
634
+     * @param string $label If is not empty, it show a text "label" with the text
635
+     * @param string $name Element name
636
+     * @param string $value Default value
637
+     * @param boolen $range If is true, then show "range" instead of text with number
638
+     * @param boolen $required Is required?
639
+     * @param number $min Minium value. Empty nothing
640
+     * @param number $max Max value. Empty nothing
641
+     * @param number $step Step (multiply). Empty nothing (ex: step 2: -2, 0, 2, 4 ...)
642
+     * @param string $placeholder Default text to show if box is empty
643
+     * @param unknown $attributes Additional attributes
644
+     */
645
+    public function addNumber($label = '', $name = '', $value = '', $range = false, $required = false, $min = '', $max = '', $step = '',  $placeholder = '',  $attributes = array()) {
646
+        // Creating main data
647
+        $data = array(
648
+            'type' => (! $range) ? 'number' : 'range',
649
+            'name' => $name,
650
+            'value' => $value,
651
+        );
652
+	
653
+		
654
+        if ($required) $data['required'] = 'required';
655
+        if ($min) $data['min'] = $min;
656
+        if ($max) $data['max'] = $max;
657
+        if ($step) $data['step'] = $step;
658
+        if ($placeholder != '') $data['placeholder'] = $placeholder;
659
+        if (!empty($attributes)) array_merge($data, $attributes);
660
+	
661
+        // Saving data to object
662
+        $content = array(
663
+            'type' => 'input',
664
+            'data' => $data
665
+        );
666
+        if ($label != '') $content['label'] = $label;
667
+		
668
+        $this->content[] = $content;
669
+    }
670 670
 
671
-	/**
672
-	 * @name addDate This function add input "number" with min, max and step (if you like)
673
-	 * @param string $label If is not empty, it show a text "label" with the text
674
-	 * @param string $name Element name
675
-	 * @param string $value Default value
676
-	 * @param string $required Is required?
677
-	 * @param string $min Minium value. Empty nothing
678
-	 * @param string $max Max value. Empty nothing
679
-	 * @param string $step Step (multiply). Empty nothing (ex: step 2: -2, 0, 2, 4 ...)
680
-	 * @param string $placeholder Default text to show if box is empty
681
-	 * @param unknown $attributes Additional attributes
682
-	 */
683
-	public function addDate($label = '', $name = '', $value = '', $required = false, $min = '', $max = '', $step = '',  $placeholder = '',  $attributes = array()) {
684
-		// Creating main data
685
-		$data = array(
686
-			'type' => 'date',
687
-			'name' => $name,
688
-			'value' => $value,
689
-		);
690
-	
691
-		if ($required) $data['required'] = 'required';
692
-		if ($min) $data['min'] = $min;
693
-		if ($max) $data['max'] = $max;
694
-		if ($step) $data['step'] = $step;
695
-		if ($placeholder != '') $data['placeholder'] = $placeholder;
696
-		if (!empty($attributes)) array_merge($data, $attributes);
697
-	
698
-		// Saving data to object
699
-		$content = array(
700
-			'type' => 'input',
701
-			'data' => $data
702
-		);
703
-		if ($label != '') $content['label'] = $label;
704
-		
705
-		$this->content[] = $content;
706
-	}
707
-	
708
-	
709
-	/**
710
-	 * @name addCheck
711
-	 * @tutorial Use this function to add a checkBox
712
-	 * @param string $label If is not empty, it show a text "label" with the text
713
-	 * @param string $name Check name
714
-	 * @param string $value Check value
715
-	 * @param boolean $required Specify if check is required
716
-	 * @param array $attributes Optional atributes not listened (in array).
717
-	 */
718
-	public function addCheck($label = '', $name = '', $value = '', $required = false, $attributes = array()) {
719
-		$data = array(
720
-			'type' => 'checkbox',
721
-			'name' => $name,
722
-			'value' => $value
723
-		);
724
-		if ($required) $data['required'] = 'required';
725
-		if (!empty($attributes)) array_merge($data, $attributes);
726
-		
727
-		$content = array(
728
-			'type' => 'input',
729
-			'data' => $data 
730
-		);
731
-		if ($label != '') $content['label'] = $label;
732
-		
733
-		$this->content[] = $content;
734
-	}
735
-	
736
-	
737
-	/**
738
-	 * @name addRadio
739
-	 * @tutorial Use this function to add a radio button
740
-	 * @param string $label If is not empty, it show a text "label" with the text
741
-	 * @param string $name Radio button name
742
-	 * @param array $values Contain all radio with values and text to show 
743
-	 * 	EX: array('val1'=>'text1', 'val2'=>'text2'); 
744
-	 * @param string $selected Specify wich option (ID) will be checked by default 
745
-	 * @param boolean $required Specify if radio is required
746
-	 * @param array $attributes Optional atributes not listened (in array). This will be applicated to all radioButton
747
-	 */
748
-	public function addRadio($label = '', $name = '', $values = array(), $selected = null, $required = false, $attributes = array()) {
749
-		if (!is_array($values)) die('FATAL ERROR: Trying to create "RadioButton" with string VALUE, is requried use ARRAY()');
750
-		$data = array(
751
-			'name' => $name,
752
-		);
753
-		
754
-		if ($required) $data['required'] = 'required';		
755
-		if (!empty($attributes)) array_merge($data, $attributes);
756
-		
757
-		
758
-		
759
-		$content = array(
760
-			'type' => 'radio',
761
-			'data' => $data,
762
-			'values' => $values,
763
-			'selected' => $selected
764
-		);
765
-		if ($label != '') $content['label'] = $label;
766
-		
767
-		$this->content[] = $content;
768
-	}
769
-	
770
-	
771
-	/**
772
-	 * @name addSelect
773
-	 * @tutorial Use this function to add a radio button
774
-	 * @param string $label Text to show before input
775
-	 * @param string $name Select name
776
-	 * @param array $values Array('value'=>'text to show')
777
-	 * @param boolean $required Specify if select is required
778
-	 * @param string $id ID Specify ID to manipulate via javascript or CSS
779
-	 * @param array $attributes Optional atributes not listened (in array).
780
-	 */
781
-	public function addSelect($label = '', $name = '', $values = '', $selected = null, $required = false, $multiple = false, $attributes = array()) {
782
-		$data = array(
783
-			'name' => $name,
784
-			'required' => $required,
785
-		);
786
-		if ($required) $data['required'] = 'required';
787
-		if ($multiple) $data['multiple'] = 'multiple';
788
-		if (!empty($attributes)) array_merge($data, $attributes);
789
-		
790
-		
791
-		// In this case, the values are saved in the main of array to speed up select loader
792
-		$content = array(
793
-			'type' => 'select',
794
-			'data' => $data,
795
-			'values' => $values,
796
-			'selected' => $selected
797
-		);
798
-		if ($label != '') $content['label'] = $label;
799
-		
800
-		$this->content[] = $content;
801
-	}
802
-	
803
-	/**
804
-	 * @name addSubmit
805
-	 * @tutorial Use this function to add a submit button
806
-	 * @param string $name Select name (optional, if leav blank it will not send in POST / GET)
807
-	 * @param string $value Text to show in button and ID
808
-	 * @param array $attributes Optional atributes not listened (in array).
809
-	 */
810
-	public function addSubmit($name = '', $value = '', $attributes = array()) {
811
-		$data = array(
812
-			'type' => 'submit',
813
-			'name' => $name,
814
-			'value' => $value
815
-		);
816
-		if (!empty($attributes)) array_merge($data, $attributes);
817
-		
818
-		$this->content[] = array(
819
-			'type' => 'input',
820
-			'data' => $data
821
-		);
822
-		
823
-		
824
-	}
825
-	
826
-	/**
827
-	 * @name addButton
828
-	 * @tutorial Use this function to add a button with "onclick" action (or id/class to call via jquery) 
829
-	 * @param string $value Name to show in button and ID
830
-	 * @param string $onClick Action to load when button is clicked 
831
-	 * @param array $attributes Optional atributes not listened (in array).
832
-	 */
833
-	public function addButton($value = '', $onClick = '', $attributes = array()) {
834
-		$data = array(
835
-			'type' => 'button',
836
-			'value' => $value,
837
-			'onClick' => $onClick
838
-		);
839
-		if (!empty($attributes)) array_merge($data, $attributes);
840
-		
841
-		$this->content[] = array(
842
-			'type' => 'input',
843
-			'data' => $data
844
-		);
845
-	}
846
-	
847
-	
848
-	/**
849
-	 * @name addButton
850
-	 * @tutorial Use this function to add a button with "onclick" action (or id/class to call via jquery)
851
-	 * @param string $value Name to show in button and ID
852
-	 * @param string $onClick Action to load when button is clicked
853
-	 * @param array $attributes Optional atributes not listened (in array).
854
-	 */
855
-	public function addTextArea($label = '', $name = '', $value = '', $required = false, $attributes = array()) {
856
-		// Creating main data
857
-		$data = array(
858
-			'name' => $name,
859
-		);
860
-		
861
-		if ($required) $data['required'] = 'required';
862
-		if (!empty($attributes)) array_merge($data, $attributes);
863
-		
864
-		// Saving data to object
865
-		$content = array(
866
-			'type' => 'textarea',
867
-			'text' => $value,
868
-			'data' => $data	
869
-		);
870
-		if ($label != '') $content['label'] = $label;
671
+    /**
672
+     * @name addDate This function add input "number" with min, max and step (if you like)
673
+     * @param string $label If is not empty, it show a text "label" with the text
674
+     * @param string $name Element name
675
+     * @param string $value Default value
676
+     * @param string $required Is required?
677
+     * @param string $min Minium value. Empty nothing
678
+     * @param string $max Max value. Empty nothing
679
+     * @param string $step Step (multiply). Empty nothing (ex: step 2: -2, 0, 2, 4 ...)
680
+     * @param string $placeholder Default text to show if box is empty
681
+     * @param unknown $attributes Additional attributes
682
+     */
683
+    public function addDate($label = '', $name = '', $value = '', $required = false, $min = '', $max = '', $step = '',  $placeholder = '',  $attributes = array()) {
684
+        // Creating main data
685
+        $data = array(
686
+            'type' => 'date',
687
+            'name' => $name,
688
+            'value' => $value,
689
+        );
690
+	
691
+        if ($required) $data['required'] = 'required';
692
+        if ($min) $data['min'] = $min;
693
+        if ($max) $data['max'] = $max;
694
+        if ($step) $data['step'] = $step;
695
+        if ($placeholder != '') $data['placeholder'] = $placeholder;
696
+        if (!empty($attributes)) array_merge($data, $attributes);
697
+	
698
+        // Saving data to object
699
+        $content = array(
700
+            'type' => 'input',
701
+            'data' => $data
702
+        );
703
+        if ($label != '') $content['label'] = $label;
704
+		
705
+        $this->content[] = $content;
706
+    }
707
+	
708
+	
709
+    /**
710
+     * @name addCheck
711
+     * @tutorial Use this function to add a checkBox
712
+     * @param string $label If is not empty, it show a text "label" with the text
713
+     * @param string $name Check name
714
+     * @param string $value Check value
715
+     * @param boolean $required Specify if check is required
716
+     * @param array $attributes Optional atributes not listened (in array).
717
+     */
718
+    public function addCheck($label = '', $name = '', $value = '', $required = false, $attributes = array()) {
719
+        $data = array(
720
+            'type' => 'checkbox',
721
+            'name' => $name,
722
+            'value' => $value
723
+        );
724
+        if ($required) $data['required'] = 'required';
725
+        if (!empty($attributes)) array_merge($data, $attributes);
726
+		
727
+        $content = array(
728
+            'type' => 'input',
729
+            'data' => $data 
730
+        );
731
+        if ($label != '') $content['label'] = $label;
732
+		
733
+        $this->content[] = $content;
734
+    }
735
+	
736
+	
737
+    /**
738
+     * @name addRadio
739
+     * @tutorial Use this function to add a radio button
740
+     * @param string $label If is not empty, it show a text "label" with the text
741
+     * @param string $name Radio button name
742
+     * @param array $values Contain all radio with values and text to show 
743
+     * 	EX: array('val1'=>'text1', 'val2'=>'text2'); 
744
+     * @param string $selected Specify wich option (ID) will be checked by default 
745
+     * @param boolean $required Specify if radio is required
746
+     * @param array $attributes Optional atributes not listened (in array). This will be applicated to all radioButton
747
+     */
748
+    public function addRadio($label = '', $name = '', $values = array(), $selected = null, $required = false, $attributes = array()) {
749
+        if (!is_array($values)) die('FATAL ERROR: Trying to create "RadioButton" with string VALUE, is requried use ARRAY()');
750
+        $data = array(
751
+            'name' => $name,
752
+        );
753
+		
754
+        if ($required) $data['required'] = 'required';		
755
+        if (!empty($attributes)) array_merge($data, $attributes);
756
+		
757
+		
758
+		
759
+        $content = array(
760
+            'type' => 'radio',
761
+            'data' => $data,
762
+            'values' => $values,
763
+            'selected' => $selected
764
+        );
765
+        if ($label != '') $content['label'] = $label;
766
+		
767
+        $this->content[] = $content;
768
+    }
769
+	
770
+	
771
+    /**
772
+     * @name addSelect
773
+     * @tutorial Use this function to add a radio button
774
+     * @param string $label Text to show before input
775
+     * @param string $name Select name
776
+     * @param array $values Array('value'=>'text to show')
777
+     * @param boolean $required Specify if select is required
778
+     * @param string $id ID Specify ID to manipulate via javascript or CSS
779
+     * @param array $attributes Optional atributes not listened (in array).
780
+     */
781
+    public function addSelect($label = '', $name = '', $values = '', $selected = null, $required = false, $multiple = false, $attributes = array()) {
782
+        $data = array(
783
+            'name' => $name,
784
+            'required' => $required,
785
+        );
786
+        if ($required) $data['required'] = 'required';
787
+        if ($multiple) $data['multiple'] = 'multiple';
788
+        if (!empty($attributes)) array_merge($data, $attributes);
789
+		
790
+		
791
+        // In this case, the values are saved in the main of array to speed up select loader
792
+        $content = array(
793
+            'type' => 'select',
794
+            'data' => $data,
795
+            'values' => $values,
796
+            'selected' => $selected
797
+        );
798
+        if ($label != '') $content['label'] = $label;
799
+		
800
+        $this->content[] = $content;
801
+    }
802
+	
803
+    /**
804
+     * @name addSubmit
805
+     * @tutorial Use this function to add a submit button
806
+     * @param string $name Select name (optional, if leav blank it will not send in POST / GET)
807
+     * @param string $value Text to show in button and ID
808
+     * @param array $attributes Optional atributes not listened (in array).
809
+     */
810
+    public function addSubmit($name = '', $value = '', $attributes = array()) {
811
+        $data = array(
812
+            'type' => 'submit',
813
+            'name' => $name,
814
+            'value' => $value
815
+        );
816
+        if (!empty($attributes)) array_merge($data, $attributes);
817
+		
818
+        $this->content[] = array(
819
+            'type' => 'input',
820
+            'data' => $data
821
+        );
822
+		
823
+		
824
+    }
825
+	
826
+    /**
827
+     * @name addButton
828
+     * @tutorial Use this function to add a button with "onclick" action (or id/class to call via jquery) 
829
+     * @param string $value Name to show in button and ID
830
+     * @param string $onClick Action to load when button is clicked 
831
+     * @param array $attributes Optional atributes not listened (in array).
832
+     */
833
+    public function addButton($value = '', $onClick = '', $attributes = array()) {
834
+        $data = array(
835
+            'type' => 'button',
836
+            'value' => $value,
837
+            'onClick' => $onClick
838
+        );
839
+        if (!empty($attributes)) array_merge($data, $attributes);
840
+		
841
+        $this->content[] = array(
842
+            'type' => 'input',
843
+            'data' => $data
844
+        );
845
+    }
846
+	
847
+	
848
+    /**
849
+     * @name addButton
850
+     * @tutorial Use this function to add a button with "onclick" action (or id/class to call via jquery)
851
+     * @param string $value Name to show in button and ID
852
+     * @param string $onClick Action to load when button is clicked
853
+     * @param array $attributes Optional atributes not listened (in array).
854
+     */
855
+    public function addTextArea($label = '', $name = '', $value = '', $required = false, $attributes = array()) {
856
+        // Creating main data
857
+        $data = array(
858
+            'name' => $name,
859
+        );
860
+		
861
+        if ($required) $data['required'] = 'required';
862
+        if (!empty($attributes)) array_merge($data, $attributes);
863
+		
864
+        // Saving data to object
865
+        $content = array(
866
+            'type' => 'textarea',
867
+            'text' => $value,
868
+            'data' => $data	
869
+        );
870
+        if ($label != '') $content['label'] = $label;
871 871
 		 
872
-		$this->content[] = $content;
873
-	}
872
+        $this->content[] = $content;
873
+    }
874 874
 	
875
-	/*
875
+    /*
876 876
 	 * OTHER
877 877
 	 */
878
-	/**
879
-	 * @name log
880
-	 * @tutorial Save a log if is enabled logging
881
-	 * @param boolean $save If is true, save the log
882
-	 * @param string $message Message to log
883
-	 */
884
-	private function log($message, $data = array()) {
885
-		if ($this->logErrors) {
886
-			// Try to get a name (label or name)
887
-			if (class_exists('kw')) {
888
-				if (isset($data['label'])) $this->errors .= 'Found an error in field' . ': "' . $data['label'] . '": ';
889
-				elseif (isset($data['name'])) $this->errors .= 'Found an error in field' . ': "' . $data['name'] . '": ';
890
-			} else {
891
-				if (isset($data['label'])) $this->errors .= 'Found an error in field' . $data['label'] . '": ';
892
-				elseif (isset($data['name'])) $this->errors .= 'Found an error in field' . ': "' . $data['name'] . '": ';
893
-			}
878
+    /**
879
+     * @name log
880
+     * @tutorial Save a log if is enabled logging
881
+     * @param boolean $save If is true, save the log
882
+     * @param string $message Message to log
883
+     */
884
+    private function log($message, $data = array()) {
885
+        if ($this->logErrors) {
886
+            // Try to get a name (label or name)
887
+            if (class_exists('kw')) {
888
+                if (isset($data['label'])) $this->errors .= 'Found an error in field' . ': "' . $data['label'] . '": ';
889
+                elseif (isset($data['name'])) $this->errors .= 'Found an error in field' . ': "' . $data['name'] . '": ';
890
+            } else {
891
+                if (isset($data['label'])) $this->errors .= 'Found an error in field' . $data['label'] . '": ';
892
+                elseif (isset($data['name'])) $this->errors .= 'Found an error in field' . ': "' . $data['name'] . '": ';
893
+            }
894 894
 			
895
-			// preparing message
896
-			$this->errors .= ' ' . $message . '.';
895
+            // preparing message
896
+            $this->errors .= ' ' . $message . '.';
897 897
 			
898
-			// Extra message (title attribute)
899
-			if (isset($data['title']) and $data['title'] != '') $this->errors .= ' | '. 'MESSAGE' .': ' . $data['title'];
898
+            // Extra message (title attribute)
899
+            if (isset($data['title']) and $data['title'] != '') $this->errors .= ' | '. 'MESSAGE' .': ' . $data['title'];
900 900
 			
901
-			$this->errors .= '<br />';
902
-		}
903
-	}
901
+            $this->errors .= '<br />';
902
+        }
903
+    }
904 904
 }
905 905
 
906 906
 
@@ -914,23 +914,23 @@  discard block
 block discarded – undo
914 914
 // More examples:
915 915
 // http://us2.php.net/manual/es/function.checkdate.php
916 916
 function is_date( $str ) {
917
-	// Try to execute date creator
918
-	try {
919
-		$dt = new DateTime( trim($str) );
920
-	} catch( Exception $e ) {
921
-		// If fails, it return false, date is incorrect
922
-		return false;
923
-	}
924
-	
925
-	// Checking date
926
-	$month = $dt->format('m');
927
-	$day = $dt->format('d');
928
-	$year = $dt->format('Y');
929
-	
930
-	// Date is ok
931
-	if (checkdate($month, $day, $year)) {
932
-		return true;
933
-	} else {
934
-		return false;
935
-	}
917
+    // Try to execute date creator
918
+    try {
919
+        $dt = new DateTime( trim($str) );
920
+    } catch( Exception $e ) {
921
+        // If fails, it return false, date is incorrect
922
+        return false;
923
+    }
924
+	
925
+    // Checking date
926
+    $month = $dt->format('m');
927
+    $day = $dt->format('d');
928
+    $year = $dt->format('Y');
929
+	
930
+    // Date is ok
931
+    if (checkdate($month, $day, $year)) {
932
+        return true;
933
+    } else {
934
+        return false;
935
+    }
936 936
 }
937 937
\ No newline at end of file
Please login to merge, or discard this patch.
Sistema/Ayudantes/CFPHPMenu.php 1 patch
Indentation   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -30,7 +30,7 @@  discard block
 block discarded – undo
30 30
     
31 31
     
32 32
     function menu_ul($sel = 'inicio')
33
-   {
33
+    {
34 34
         $items = array(
35 35
             'inicio' => array(
36 36
                 'id'     => 'inicio',
@@ -65,7 +65,7 @@  discard block
 block discarded – undo
65 65
     }
66 66
     
67 67
     function menu_p($sel = 'inicio', $separador = '')
68
-   {
68
+    {
69 69
         //$CI =& get_instance();
70 70
         $items = array(
71 71
             'inicio' => array(
Please login to merge, or discard this patch.
Sistema/Ayudantes/CFPHPPaginador.php 1 patch
Indentation   +236 added lines, -236 removed lines patch added patch discarded remove patch
@@ -39,311 +39,311 @@
 block discarded – undo
39 39
  * (you can pass the most critical GETs manually if you like)
40 40
  */
41 41
  
42
- //Esta clase fue editada por programadores de Cf con la aprobacion de Ruben Arroyo
42
+    //Esta clase fue editada por programadores de Cf con la aprobacion de Ruben Arroyo
43 43
 
44 44
 namespace Sistema\Ayudantes;
45 45
 
46 46
 class Cf_PHPPaginator {
47
-	/*
47
+    /*
48 48
 	 * MAIN Settings (required)
49 49
 	 */
50 50
 	
51
-	/**
52
-	 * 
53
-	 * @total_records string Total of records to paginate (specify manually)
54
-	 */
55
-	public $total_records = 0;
51
+    /**
52
+     * 
53
+     * @total_records string Total of records to paginate (specify manually)
54
+     */
55
+    public $total_records = 0;
56 56
 	
57 57
 	
58
-	/*
58
+    /*
59 59
 	 * MAIN Settings (optional)
60 60
 	 */
61 61
 	
62
-	/**
63
-	 * @style string  Specify Class to define
64
-	 * @tutorial If is specified, it would create <div class="style"> otherwise it not create div
65
-	 */
66
-	public $style = '';
62
+    /**
63
+     * @style string  Specify Class to define
64
+     * @tutorial If is specified, it would create <div class="style"> otherwise it not create div
65
+     */
66
+    public $style = '';
67 67
 	
68
-	/**
69
-	 * @records number Max records to show on every page
70
-	 */
71
-	public $records = 20;
68
+    /**
69
+     * @records number Max records to show on every page
70
+     */
71
+    public $records = 20;
72 72
 	
73
-	/** 
74
-	 * @max_links number Maxium quantity of links to show before and after actual page
75
-	 * @example If current page is 4 and MAX is 2, it show "2 3 4 5 6"
76
-	 */
77
-	public $max_links = 3;
73
+    /** 
74
+     * @max_links number Maxium quantity of links to show before and after actual page
75
+     * @example If current page is 4 and MAX is 2, it show "2 3 4 5 6"
76
+     */
77
+    public $max_links = 3;
78 78
 	
79
-	/**
80
-	 * @get_name string Specify INDEX of GET that is used
81
-	 * 
82
-	 * It will load automatically the current page. 
83
-	 * If you like, you can specify manually in "$cur_page" 
84
-	 * (if you like filter previously or the script will not load directly from URL)  
85
-	 */
86
-	public $get_name = 'pag';
79
+    /**
80
+     * @get_name string Specify INDEX of GET that is used
81
+     * 
82
+     * It will load automatically the current page. 
83
+     * If you like, you can specify manually in "$cur_page" 
84
+     * (if you like filter previously or the script will not load directly from URL)  
85
+     */
86
+    public $get_name = 'pag';
87 87
 	
88
-	/**
89
-	 * @max_records number Specify maxium quantity of registers will be loaded. "0" disable this
90
-	 */
91
-	public $max_records = 0;
88
+    /**
89
+     * @max_records number Specify maxium quantity of registers will be loaded. "0" disable this
90
+     */
91
+    public $max_records = 0;
92 92
 	
93 93
 	
94
-	/**
95
-	 * @recicle_url boolean If is true, modify directly tue current URL to put the pagination (if not exist any, add &pag= to the end)
96
-	 * If is true, $url_start and $url_end will be ignored
97
-	 */
98
-	public $recicle_url = true;
94
+    /**
95
+     * @recicle_url boolean If is true, modify directly tue current URL to put the pagination (if not exist any, add &pag= to the end)
96
+     * If is true, $url_start and $url_end will be ignored
97
+     */
98
+    public $recicle_url = true;
99 99
 	
100
-	/**
101
-	 * @specific_get array Specify what GETs (and order) you like recicle.
102
-	 * Set array() to ALL (not recomended to avoid atacks DoS)  
103
-	 * Set '' for any
104
-	 * @example array('section', 'pag', 'subsection');
105
-	 * If you don't need 'pag' in the midle, you can ignore it.
106
-	 * WARNING: If the order is very important, use the pag at THE END of url   
107
-	 */
108
-	public $specific_get = '';
100
+    /**
101
+     * @specific_get array Specify what GETs (and order) you like recicle.
102
+     * Set array() to ALL (not recomended to avoid atacks DoS)  
103
+     * Set '' for any
104
+     * @example array('section', 'pag', 'subsection');
105
+     * If you don't need 'pag' in the midle, you can ignore it.
106
+     * WARNING: If the order is very important, use the pag at THE END of url   
107
+     */
108
+    public $specific_get = '';
109 109
 	
110
-	/**
111
-	 * @url_start string specify how the URL of every link start. IF $recicle_url is TRUE, this will be ignored 
112
-	 * @example If you use MOD_REWRITE or JAVASCRIPT, you can put "http://mywebsite.com/subdir/pag-" or "javascript:paginate("
113
-	 * 
114
-	 * This value will auto completed if you leave empty 
115
-	 */
116
-	public $url_start = '';
110
+    /**
111
+     * @url_start string specify how the URL of every link start. IF $recicle_url is TRUE, this will be ignored 
112
+     * @example If you use MOD_REWRITE or JAVASCRIPT, you can put "http://mywebsite.com/subdir/pag-" or "javascript:paginate("
113
+     * 
114
+     * This value will auto completed if you leave empty 
115
+     */
116
+    public $url_start = '';
117 117
 	
118
-	/**
119
-	 * @url_end string specify how the URL of every link ends. IF $recicle_url is TRUE, this will be ignored
120
-	 * @example If you use MOD_REWRITE or JAVASCRIPT, you can put "/" or ");"
121
-	 * 
122
-	 * This value will auto completed if you leave empty
123
-	 */
124
-	public $url_end = '';
118
+    /**
119
+     * @url_end string specify how the URL of every link ends. IF $recicle_url is TRUE, this will be ignored
120
+     * @example If you use MOD_REWRITE or JAVASCRIPT, you can put "/" or ");"
121
+     * 
122
+     * This value will auto completed if you leave empty
123
+     */
124
+    public $url_end = '';
125 125
 	
126 126
 	
127 127
 	
128
-	/*
128
+    /*
129 129
 	 * Design settings
130 130
 	 */
131 131
 	
132
-	// Icons to show "First | Previous | Next | Last"
133
-	//& laquo; = � | & lt; = <
132
+    // Icons to show "First | Previous | Next | Last"
133
+    //& laquo; = � | & lt; = <
134 134
     //& raquo; = � | & gt; = >
135 135
     // Leave empty '' to not show or ' ' to show empty
136
-	public $first = '[&lt;&lt;]';
137
-	public $previous = '[&lt;]';
138
-	public $next = '[&gt;]';
139
-	public $last = '[&gt;&gt;]';
136
+    public $first = '[&lt;&lt;]';
137
+    public $previous = '[&lt;]';
138
+    public $next = '[&gt;]';
139
+    public $last = '[&gt;&gt;]';
140 140
 	
141 141
 	
142
-	/*
142
+    /*
143 143
 	 * RETURNS FROM PAGINATOR and internal use
144 144
 	 */
145
-	// Return current page (auto loaded)
146
-	public $cur_page;
145
+    // Return current page (auto loaded)
146
+    public $cur_page;
147 147
 	
148
-	// original page loaded
149
-	// it contains the real value in GET (ex: -1, 5...) to compare later
150
-	public $original_page;
148
+    // original page loaded
149
+    // it contains the real value in GET (ex: -1, 5...) to compare later
150
+    public $original_page;
151 151
 	
152
-	// Return total pages
153
-	public $total_pages;
152
+    // Return total pages
153
+    public $total_pages;
154 154
 	
155
-	// LIMIT_START / Specify the first record of the page (ex, in page 3 and 20 records is record num 40)
156
-	// Limit max is $records
157
-	public $first_record;
155
+    // LIMIT_START / Specify the first record of the page (ex, in page 3 and 20 records is record num 40)
156
+    // Limit max is $records
157
+    public $first_record;
158 158
 	
159
-	// Specify limit to put directly in mysql query ( LIMIT 0,20 )
160
-	public $limit;
159
+    // Specify limit to put directly in mysql query ( LIMIT 0,20 )
160
+    public $limit;
161 161
 	
162 162
 	
163 163
 /*
164 164
  * Functions
165 165
  */
166
-	/**
167
-	 * @name paginate
168
-	 * @tutorial This function is called directly to load all params
169
-	 * @example $pag->paginate();
170
-	 */
171
-	public function paginate() {
172
-		$this->get(); // Filtering and obtaining variables
173
-		$this->calculate(); // Calc current page
174
-		$this->prepareUrl(); // Preparing URL to show
175
-	}
166
+    /**
167
+     * @name paginate
168
+     * @tutorial This function is called directly to load all params
169
+     * @example $pag->paginate();
170
+     */
171
+    public function paginate() {
172
+        $this->get(); // Filtering and obtaining variables
173
+        $this->calculate(); // Calc current page
174
+        $this->prepareUrl(); // Preparing URL to show
175
+    }
176 176
 	
177
-	/**
178
-	 * @name show
179
-	 * @tutorial Show links of pagination
180
-	 * @example $pag->show();
181
-	 */
182
-	public function show() {
183
-		// Prepare string with links
184
-		// Reading here variables is faster than "$this->var"
185
-		$cur_page = $this->cur_page; 
186
-		$url_start = $this->url_start;
187
-		$url_end = $this->url_end;
177
+    /**
178
+     * @name show
179
+     * @tutorial Show links of pagination
180
+     * @example $pag->show();
181
+     */
182
+    public function show() {
183
+        // Prepare string with links
184
+        // Reading here variables is faster than "$this->var"
185
+        $cur_page = $this->cur_page; 
186
+        $url_start = $this->url_start;
187
+        $url_end = $this->url_end;
188 188
 		
189
-		$start = $cur_page - $this->max_links;
190
-		$end = $cur_page + $this->max_links;
189
+        $start = $cur_page - $this->max_links;
190
+        $end = $cur_page + $this->max_links;
191 191
 		
192
-		if ($start < 1) $start = 1;
193
-		if ($end > $this->total_pages) $end = $this->total_pages;
192
+        if ($start < 1) $start = 1;
193
+        if ($end > $this->total_pages) $end = $this->total_pages;
194 194
 		
195
-		// Showing all clickable pages (create div if class is defined)
196
-		if ($this->style != '') $r = '<div class="'.$this->style.'">';
197
-		else $r = '';
195
+        // Showing all clickable pages (create div if class is defined)
196
+        if ($this->style != '') $r = '<div class="'.$this->style.'">';
197
+        else $r = '';
198 198
 		
199 199
 		
200
-		// First / previous
201
-		if ($this->cur_page > 1) {
202
-			if ($this->first != '') $r .= '<a class="first" href="' . $url_start . '1' . $url_end . '">'.$this->first.'</a> ';
203
-			if ($this->previous != '') $r .= '<a class="previous" href="' . $url_start . ($cur_page - 1) . $url_end . '">'.$this->previous.'</a> ';
204
-		}
200
+        // First / previous
201
+        if ($this->cur_page > 1) {
202
+            if ($this->first != '') $r .= '<a class="first" href="' . $url_start . '1' . $url_end . '">'.$this->first.'</a> ';
203
+            if ($this->previous != '') $r .= '<a class="previous" href="' . $url_start . ($cur_page - 1) . $url_end . '">'.$this->previous.'</a> ';
204
+        }
205 205
 		
206
-		// You can optimize this separating BEFORE and AFTER current page in two for (to avoid load "if" in each loop)
207
-		// but it would be difficult on changes
208
-		for ($n=$start; $n<=$end; $n++) {
209
-			if ($n != $cur_page) $r .= '<a class="link" href="'. $url_start . $n . $url_end . '">'.$n.'</a> ';
210
-			else $r .= '<a class="current">'.$n.'</a> ';
211
-		}
206
+        // You can optimize this separating BEFORE and AFTER current page in two for (to avoid load "if" in each loop)
207
+        // but it would be difficult on changes
208
+        for ($n=$start; $n<=$end; $n++) {
209
+            if ($n != $cur_page) $r .= '<a class="link" href="'. $url_start . $n . $url_end . '">'.$n.'</a> ';
210
+            else $r .= '<a class="current">'.$n.'</a> ';
211
+        }
212 212
 		
213
-		// next / last
214
-		if ($this->cur_page < $this->total_pages) {
215
-			if ($this->next != '') $r .= '<a class="next" href="' . $url_start . ($cur_page + 1) . $url_end . '">'.$this->next.'</a> ';
216
-			if ($this->last != '') $r .= '<a class="last" href="' . $url_start . $this->total_pages . $url_end .'">'.$this->last.'</a> ';
217
-		}
213
+        // next / last
214
+        if ($this->cur_page < $this->total_pages) {
215
+            if ($this->next != '') $r .= '<a class="next" href="' . $url_start . ($cur_page + 1) . $url_end . '">'.$this->next.'</a> ';
216
+            if ($this->last != '') $r .= '<a class="last" href="' . $url_start . $this->total_pages . $url_end .'">'.$this->last.'</a> ';
217
+        }
218 218
 		
219
-		// End div (if exist)
220
-		if ($this->style != '') $r .= '</div>';
219
+        // End div (if exist)
220
+        if ($this->style != '') $r .= '</div>';
221 221
 		
222
-		return $r;
223
-	}
222
+        return $r;
223
+    }
224 224
 	
225 225
 	
226
-	/**
227
-	 * @name get
228
-	 * @tutorial This function is autoloaded, it get the current page and filter number to avoid hackers
229
-	 */	 
230
-	private function get() {
231
-		if (!is_numeric($this->cur_page)) {
232
-			// First get the actual page, by default 1
233
-			$cur_page = isset($_GET[$this->get_name]) ? $_GET[$this->get_name] : 1;
234
-			$this->original_page = $cur_page;
226
+    /**
227
+     * @name get
228
+     * @tutorial This function is autoloaded, it get the current page and filter number to avoid hackers
229
+     */	 
230
+    private function get() {
231
+        if (!is_numeric($this->cur_page)) {
232
+            // First get the actual page, by default 1
233
+            $cur_page = isset($_GET[$this->get_name]) ? $_GET[$this->get_name] : 1;
234
+            $this->original_page = $cur_page;
235 235
 			
236
-			// Filter values
237
-			if (!is_numeric($cur_page) or $cur_page < 1) $cur_page = 1;
236
+            // Filter values
237
+            if (!is_numeric($cur_page) or $cur_page < 1) $cur_page = 1;
238 238
 			
239
-			// Set new filtered values (is faster this method)
240
-			$this->cur_page = $cur_page;
241
-		} else {
242
-			$this->original_page = $cur_page;
243
-		}
244
-	}
239
+            // Set new filtered values (is faster this method)
240
+            $this->cur_page = $cur_page;
241
+        } else {
242
+            $this->original_page = $cur_page;
243
+        }
244
+    }
245 245
 	
246
-	/**
247
-	 * @name calculate
248
-	 * @tutorial Do all calcs about current and last page
249
-	 */
250
-	private function calculate() {
251
-		// This vars are very used, so is faster do this
252
-		$max_records = $this->max_records;
253
-		$records = $this->records;
246
+    /**
247
+     * @name calculate
248
+     * @tutorial Do all calcs about current and last page
249
+     */
250
+    private function calculate() {
251
+        // This vars are very used, so is faster do this
252
+        $max_records = $this->max_records;
253
+        $records = $this->records;
254 254
 		
255
-		// Force maxium records loaded (only if is specified by user)
256
-		if ($max_records  > 0 and $max_records  > $total_records) 
257
-			$this->total_records = $max_records;
255
+        // Force maxium records loaded (only if is specified by user)
256
+        if ($max_records  > 0 and $max_records  > $total_records) 
257
+            $this->total_records = $max_records;
258 258
 		
259
-		// Calculate total pages that have
260
-		$total_pages = ceil($this->total_records / $records);
259
+        // Calculate total pages that have
260
+        $total_pages = ceil($this->total_records / $records);
261 261
 		
262
-		// Is correct current page?
263
-		if ($this->cur_page > $total_pages) $this->cur_page = $total_pages;
264
-		$this->total_pages = $total_pages;
262
+        // Is correct current page?
263
+        if ($this->cur_page > $total_pages) $this->cur_page = $total_pages;
264
+        $this->total_pages = $total_pages;
265 265
 		
266
-		// Specify LIMIT to do a query
267
-		$start = ($this->cur_page - 1) * $records;
266
+        // Specify LIMIT to do a query
267
+        $start = ($this->cur_page - 1) * $records;
268 268
 		
269
-		// Forcing maixum records to show (only if is specified by user)
270
-		if ($max_records > 0 and $records > $max_records) {
271
-			$records = $max_records;
272
-			$this->records = $records;
273
-		}
269
+        // Forcing maixum records to show (only if is specified by user)
270
+        if ($max_records > 0 and $records > $max_records) {
271
+            $records = $max_records;
272
+            $this->records = $records;
273
+        }
274 274
 			
275 275
 		
276
-		// Saving changes
277
-		$this->first_record = $start;
278
-		$this->limit = ' LIMIT '.$start.','.$records.' ';
279
-	}
276
+        // Saving changes
277
+        $this->first_record = $start;
278
+        $this->limit = ' LIMIT '.$start.','.$records.' ';
279
+    }
280 280
 	
281
-	/**
282
-	 * @name prepareUrl
283
-	 * @tutorial It prepare the url to show in each link, specified by user
284
-	 * If recicle URL is false, it will auto load $url_start and $url_end
285
-	 */
286
-	private function prepareUrl() {
287
-		// This script use three methods to recicle GET depending user selection
288
-		if ($this->recicle_url) {
289
-			// gonna to recicle the URL
290
-			$gets = $this->specific_get;
291
-			$get_name = $this->get_name;
281
+    /**
282
+     * @name prepareUrl
283
+     * @tutorial It prepare the url to show in each link, specified by user
284
+     * If recicle URL is false, it will auto load $url_start and $url_end
285
+     */
286
+    private function prepareUrl() {
287
+        // This script use three methods to recicle GET depending user selection
288
+        if ($this->recicle_url) {
289
+            // gonna to recicle the URL
290
+            $gets = $this->specific_get;
291
+            $get_name = $this->get_name;
292 292
 			
293
-			// If user specified an array
294
-			if (is_array($gets)) {
295
-				if (empty($gets)) {
296
-					// And is empty, we have to Recicle ALL GET
297
-					// To know that page needs to be replaced we will use the "original_page" (the real get)
298
-					// Cortamos "pag=2" para obtener lo que va antes y despues, as� poder modificar directamente ese pag=2 por la pagina actual
299
-					$query_string = explode($this->get_name.'='.$this->original_page, $_SERVER['QUERY_STRING']);
293
+            // If user specified an array
294
+            if (is_array($gets)) {
295
+                if (empty($gets)) {
296
+                    // And is empty, we have to Recicle ALL GET
297
+                    // To know that page needs to be replaced we will use the "original_page" (the real get)
298
+                    // Cortamos "pag=2" para obtener lo que va antes y despues, as� poder modificar directamente ese pag=2 por la pagina actual
299
+                    $query_string = explode($this->get_name.'='.$this->original_page, $_SERVER['QUERY_STRING']);
300 300
 					
301
-					// How start the new URL? To know it, we need to see the last character from current string
302
-					if (!in_array(substr($query_string[0], -1), array('?', '&'))) {
303
-						// Current string haven't any at end and it isn't "&" or "?" ?
304
-						// If initial string have one character at least it means that not is the first index 
305
-						if (isset($query_string[0][0])) $this->url_start = '?'.$query_string[0].'&';
306
-						else $this->url_start = '?'.$query_string[0];
307
-					} else {
308
-						// Current string already have ? or &,
309
-						$this->url_start = '?'.$query_string[0];
310
-					}
301
+                    // How start the new URL? To know it, we need to see the last character from current string
302
+                    if (!in_array(substr($query_string[0], -1), array('?', '&'))) {
303
+                        // Current string haven't any at end and it isn't "&" or "?" ?
304
+                        // If initial string have one character at least it means that not is the first index 
305
+                        if (isset($query_string[0][0])) $this->url_start = '?'.$query_string[0].'&';
306
+                        else $this->url_start = '?'.$query_string[0];
307
+                    } else {
308
+                        // Current string already have ? or &,
309
+                        $this->url_start = '?'.$query_string[0];
310
+                    }
311 311
 					
312
-					$this->url_end = isset($query_string[1]) ? $query_string[1] : '';
313
-				} else {
314
-					// Only specifics GET, reading all and finally leave in the same order
315
-					// With this we can clean all GET that we don't need (like hacker attempts)
316
-					$tmp = '';
317
-					$tmp_start = ''; // If we found the current page, we move here all $tmp
318
-					foreach ($gets as $get) {
319
-						if ($get != $get_name) {
320
-							// Trying to get the GET
321
-							if (isset($_GET[$get])) $tmp .= $get.'='.$_GET[$get].'&';
322
-						} else {
323
-							// Pour the $tmp content to $tmp_start
324
-							if ($tmp_start == '') $tmp_start .= '?';
325
-							$tmp_start .= $tmp;
326
-							$tmp = '';
327
-						}
328
-					}
312
+                    $this->url_end = isset($query_string[1]) ? $query_string[1] : '';
313
+                } else {
314
+                    // Only specifics GET, reading all and finally leave in the same order
315
+                    // With this we can clean all GET that we don't need (like hacker attempts)
316
+                    $tmp = '';
317
+                    $tmp_start = ''; // If we found the current page, we move here all $tmp
318
+                    foreach ($gets as $get) {
319
+                        if ($get != $get_name) {
320
+                            // Trying to get the GET
321
+                            if (isset($_GET[$get])) $tmp .= $get.'='.$_GET[$get].'&';
322
+                        } else {
323
+                            // Pour the $tmp content to $tmp_start
324
+                            if ($tmp_start == '') $tmp_start .= '?';
325
+                            $tmp_start .= $tmp;
326
+                            $tmp = '';
327
+                        }
328
+                    }
329 329
 					
330
-					// Finally, write the changes in the object
331
-					if ($tmp_start != '') {
332
-						// If have start and end
333
-						$this->url_start = $tmp_start;
334
-						$this->url_end = $tmp;
335
-					}
336
-					else $this->url_start = '?'.$tmp;
337
-				}
338
-			} else{
339
-				// Non recicle
340
-				$this->url_start = '?';
341
-				$this->url_end = ''; 
342
-			}
330
+                    // Finally, write the changes in the object
331
+                    if ($tmp_start != '') {
332
+                        // If have start and end
333
+                        $this->url_start = $tmp_start;
334
+                        $this->url_end = $tmp;
335
+                    }
336
+                    else $this->url_start = '?'.$tmp;
337
+                }
338
+            } else{
339
+                // Non recicle
340
+                $this->url_start = '?';
341
+                $this->url_end = ''; 
342
+            }
343 343
 			
344
-			// Add the pagination
345
-			$this->url_start .= $this->get_name.'=';
346
-		}
347
-	}
344
+            // Add the pagination
345
+            $this->url_start .= $this->get_name.'=';
346
+        }
347
+    }
348 348
 }
349 349
 ?>
350 350
\ No newline at end of file
Please login to merge, or discard this patch.
Sistema/Ayudantes/CFPHPSeguridad.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -31,10 +31,10 @@  discard block
 block discarded – undo
31 31
     
32 32
     //xss funciones de mitigacion
33 33
     function xsseguro($texto,$encoding='UTF-8'){
34
-       return htmlspecialchars($texto,ENT_QUOTES | ENT_HTML401,$encoding);
34
+        return htmlspecialchars($texto,ENT_QUOTES | ENT_HTML401,$encoding);
35 35
     }
36 36
     function xecho($texto){
37
-       echo xsseguro($texto);
37
+        echo xsseguro($texto);
38 38
     }
39 39
     
40 40
     public function filtrarTexto($texto){
@@ -54,7 +54,7 @@  discard block
 block discarded – undo
54 54
         $longitudCaracteres = strlen($caracteres);
55 55
         $cadenaAleatoria = '';
56 56
         for ($i = 0; $i < $length; $i++) {
57
-             $cadenaAleatoria .= $caracteres[rand(0, $longitudCaracteres - 1)];
57
+                $cadenaAleatoria .= $caracteres[rand(0, $longitudCaracteres - 1)];
58 58
         }
59 59
         return $cadenaAleatoria;
60 60
     }
@@ -80,14 +80,14 @@  discard block
 block discarded – undo
80 80
     
81 81
     function obtenerDireccionIP(){
82 82
     if (!empty($_SERVER ['HTTP_CLIENT_IP'] ))
83
-      $ip=$_SERVER ['HTTP_CLIENT_IP'];
83
+        $ip=$_SERVER ['HTTP_CLIENT_IP'];
84 84
     elseif (!empty($_SERVER ['HTTP_X_FORWARDED_FOR'] ))
85
-      $ip=$_SERVER ['HTTP_X_FORWARDED_FOR'];
85
+        $ip=$_SERVER ['HTTP_X_FORWARDED_FOR'];
86 86
     else
87
-      $ip=$_SERVER ['REMOTE_ADDR'];
87
+        $ip=$_SERVER ['REMOTE_ADDR'];
88 88
 
89 89
     return $ip;
90
-	}
90
+    }
91 91
 	
92 92
     function restringirIp($ip){
93 93
         $ipCliente = $this->obtenerDireccionIP();
@@ -101,7 +101,7 @@  discard block
 block discarded – undo
101 101
         }
102 102
     }
103 103
 	
104
-	function restringirConjuntoIps($ips){
104
+    function restringirConjuntoIps($ips){
105 105
     $ipCliente = obtenerDireccionIP();
106 106
 
107 107
     if (in_array($ipCliente,$ips)){
@@ -111,9 +111,9 @@  discard block
 block discarded – undo
111 111
         header('location: http://direccion_envio_salida');
112 112
         exit;
113 113
     }
114
-	}
114
+    }
115 115
 	
116
-	function restringirRango(){
116
+    function restringirRango(){
117 117
     $ipCliente = obtenerDireccionIP();
118 118
 
119 119
     if(substr($ipCliente, 0, 8 ) == "150.214."){
@@ -123,20 +123,20 @@  discard block
 block discarded – undo
123 123
         header('location: http://direccion_envio_salida');
124 124
         exit;
125 125
     }
126
-	}
126
+    }
127 127
         
128 128
         
129 129
     /**
130
- *
131
- * @strip injection chars from email headers
132
- *
133
- * @param string $string
134
- *
135
- * return string
136
- *
137
- */
130
+     *
131
+     * @strip injection chars from email headers
132
+     *
133
+     * @param string $string
134
+     *
135
+     * return string
136
+     *
137
+     */
138 138
 function emailSeguro($cadena) {
139
-     return  preg_replace( '((?:\n|\r|\t|%0A|%0D|%08|%09)+)i' , '', $cadena );
139
+        return  preg_replace( '((?:\n|\r|\t|%0A|%0D|%08|%09)+)i' , '', $cadena );
140 140
 }
141 141
 
142 142
 /*** example usage 
Please login to merge, or discard this patch.
Sistema/Ayudantes/CFPHPVinculos.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -31,5 +31,5 @@
 block discarded – undo
31 31
     
32 32
     $dominio = "http://www.my-site-domain.com/";
33 33
     return $dominio;
34
-   }
34
+    }
35 35
 }
36 36
\ No newline at end of file
Please login to merge, or discard this patch.
Sistema/Nucleo/CFControlador.php 1 patch
Indentation   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -38,7 +38,7 @@  discard block
 block discarded – undo
38 38
         $rutaLib = RUTA_LIBS . $libreria . '.php';
39 39
         if(is_readable($rutaLib)){
40 40
             require_once $rutaLib;
41
-           //echo 'libreria cargada';
41
+            //echo 'libreria cargada';
42 42
         }
43 43
         else {
44 44
             throw new Exception("houston tenemos un problema! al cargar libreria");
@@ -47,12 +47,12 @@  discard block
 block discarded – undo
47 47
     
48 48
     protected function cargaModelo($modelo){
49 49
         $modelo=$modelo.'Modelo';
50
-       $rutaMod = RUTA_MOD . $modelo . '.php';
50
+        $rutaMod = RUTA_MOD . $modelo . '.php';
51 51
         if(is_readable($rutaMod)){
52 52
             require_once $rutaMod;
53 53
             $modelo = new $modelo;
54 54
             return $modelo;
55
-           //echo 'modelo cargado';
55
+            //echo 'modelo cargado';
56 56
         }
57 57
         else {
58 58
             //echo $rutaMod;
@@ -64,10 +64,10 @@  discard block
 block discarded – undo
64 64
     }
65 65
     
66 66
     protected function cargaAyudante($ayudante){
67
-       $rutaAyudante = RUTA_AYUDANTES . $ayudante . '.php';
67
+        $rutaAyudante = RUTA_AYUDANTES . $ayudante . '.php';
68 68
         if(is_readable($rutaAyudante)){
69 69
             require_once $rutaAyudante;
70
-           //echo 'libreria cargada';
70
+            //echo 'libreria cargada';
71 71
         }
72 72
         else {
73 73
             throw new Exception("houston tenemos un problema! al cargar ayudante");
Please login to merge, or discard this patch.
Sistema/librerias/rb.php 1 patch
Indentation   +11048 added lines, -11048 removed lines patch added patch discarded remove patch
@@ -19,18 +19,18 @@  discard block
 block discarded – undo
19 19
 interface Logger
20 20
 {
21 21
 
22
-	/**
23
-	 * A logger (for\PDO or OCI driver) needs to implement the log method.
24
-	 * The log method will receive logging data. Note that the number of parameters is 0, this means
25
-	 * all parameters are optional and the number may vary. This way the logger can be used in a very
26
-	 * flexible way. Sometimes the logger is used to log a simple error message and in other
27
-	 * situations sql and bindings are passed.
28
-	 * The log method should be able to accept all kinds of parameters and data by using
29
-	 * functions like func_num_args/func_get_args.
30
-	 * 
31
-	 * @return void
32
-	 */
33
-	public function log();
22
+    /**
23
+     * A logger (for\PDO or OCI driver) needs to implement the log method.
24
+     * The log method will receive logging data. Note that the number of parameters is 0, this means
25
+     * all parameters are optional and the number may vary. This way the logger can be used in a very
26
+     * flexible way. Sometimes the logger is used to log a simple error message and in other
27
+     * situations sql and bindings are passed.
28
+     * The log method should be able to accept all kinds of parameters and data by using
29
+     * functions like func_num_args/func_get_args.
30
+     * 
31
+     * @return void
32
+     */
33
+    public function log();
34 34
 }
35 35
 } 
36 36
 
@@ -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 = print_r( $argument, TRUE );
90
-				if ( $this->mode === self::C_LOGGER_ECHO ) {
91
-					echo $log;
92
-				} else {
93
-					$this->logs[] = $log;
94
-				}
95
-			} else {
96
-				if ( $this->mode === self::C_LOGGER_ECHO ) {
97
-					echo $argument;
98
-				} else {
99
-					$this->logs[] = $argument;
100
-				}
101
-			}
102
-
103
-			if ( $this->mode === self::C_LOGGER_ECHO ) echo "<br>\n";
104
-		}
105
-	}
57
+    /**
58
+     * Logger modes
59
+     */
60
+    const C_LOGGER_ECHO  = 0;
61
+    const C_LOGGER_ARRAY = 1;
62
+
63
+    /**
64
+     * @var integer
65
+     */
66
+    protected $mode = 0;
67
+
68
+    /**
69
+     * @var array
70
+     */
71
+    protected $logs = array();
72
+
73
+    /**
74
+     * Default logger method logging to STDOUT.
75
+     * This is the default/reference implementation of a logger.
76
+     * This method will write the message value to STDOUT (screen) unless
77
+     * you have changed the mode of operation to C_LOGGER_ARRAY.
78
+     *
79
+     * @param $message (optional) message to log (might also be data or output)
80
+     *
81
+     * @return void
82
+     */
83
+    public function log()
84
+    {
85
+        if ( func_num_args() < 1 ) return;
86
+
87
+        foreach ( func_get_args() as $argument ) {
88
+            if ( is_array( $argument ) ) {
89
+                $log = print_r( $argument, TRUE );
90
+                if ( $this->mode === self::C_LOGGER_ECHO ) {
91
+                    echo $log;
92
+                } else {
93
+                    $this->logs[] = $log;
94
+                }
95
+            } else {
96
+                if ( $this->mode === self::C_LOGGER_ECHO ) {
97
+                    echo $argument;
98
+                } else {
99
+                    $this->logs[] = $argument;
100
+                }
101
+            }
102
+
103
+            if ( $this->mode === self::C_LOGGER_ECHO ) echo "<br>\n";
104
+        }
105
+    }
106 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
-	}
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 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
-	}
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 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
-	}
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 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
-	}
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
 
@@ -191,166 +191,166 @@  discard block
 block discarded – undo
191 191
  */
192 192
 class Debug extends RDefault implements Logger
193 193
 {
194
-	/**
195
-	 * @var integer
196
-	 */
197
-	private $strLen = 40;
198
-
199
-	/**
200
-	 * Writes a query for logging with all bindings / params filled
201
-	 * in.
202
-	 *
203
-	 * @param string $newSql   the query
204
-	 * @param array  $bindings the bindings to process (key-value pairs)
205
-	 *
206
-	 * @return string
207
-	 */
208
-	private function writeQuery( $newSql, $newBindings )
209
-	{
210
-		//avoid str_replace collisions: slot1 and slot10 (issue 407).
211
-		uksort( $newBindings, function( $a, $b ) {
212
-			return ( strlen( $b ) - strlen( $a ) );
213
-		} );
214
-
215
-		$newStr = $newSql;
216
-		foreach( $newBindings as $slot => $value ) {
217
-			if ( strpos( $slot, ':' ) === 0 ) {
218
-				$newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr );
219
-			}
220
-		}
221
-		return $newStr;
222
-	}
223
-
224
-	/**
225
-	 * Fills in a value of a binding and truncates the
226
-	 * resulting string if necessary.
227
-	 *
228
-	 * @param mixed $value
229
-	 *
230
-	 * @return string
231
-	 */
232
-	protected function fillInValue( $value )
233
-	{
234
-		if ( is_null( $value ) ) $value = 'NULL';
235
-
236
-		$value = strval( $value );
237
-		if ( strlen( $value ) > ( $this->strLen ) ) {
238
-			$value = substr( $value, 0, ( $this->strLen ) ).'... ';
239
-		}
240
-
241
-		if ( !is_numeric( $value ) && $value !== 'NULL') {
242
-			$value = '\''.$value.'\'';
243
-		}
244
-
245
-		return $value;
246
-	}
247
-
248
-	/**
249
-	 * Dependending on the current mode of operation,
250
-	 * this method will either log and output to STDIN or
251
-	 * just log.
252
-	 *
253
-	 * @param string $str string to log or output and log
254
-	 *
255
-	 * @return void
256
-	 */
257
-	protected function output( $str )
258
-	{
259
-		$this->logs[] = $str;
260
-		if ( !$this->mode ) echo $str .'<br />';
261
-	}
262
-
263
-	/**
264
-	 * Normalizes the slots in an SQL string.
265
-	 * Replaces question mark slots with :slot1 :slot2 etc.
266
-	 *
267
-	 * @param string $sql sql to normalize
268
-	 *
269
-	 * @return string
270
-	 */
271
-	protected function normalizeSlots( $sql )
272
-	{
273
-		$i = 0;
274
-		$newSql = $sql;
275
-		while($i < 20 && strpos($newSql, '?') !== FALSE ){
276
-			$pos   = strpos( $newSql, '?' );
277
-			$slot  = ':slot'.$i;
278
-			$begin = substr( $newSql, 0, $pos );
279
-			$end   = substr( $newSql, $pos+1 );
280
-			$newSql = $begin . $slot . $end;
281
-			$i++;
282
-		}
283
-		return $newSql;
284
-	}
285
-
286
-	/**
287
-	 * Normalizes the bindings.
288
-	 * Replaces numeric binding keys with :slot1 :slot2 etc.
289
-	 *
290
-	 * @param array $bindings bindings to normalize
291
-	 *
292
-	 * @return array
293
-	 */
294
-	protected function normalizeBindings( $bindings )
295
-	{
296
-		$i = 0;
297
-		$newBindings = array();
298
-		foreach( $bindings as $key => $value ) {
299
-			if ( is_numeric($key) ) {
300
-				$newKey = ':slot'.$i;
301
-				$newBindings[$newKey] = $value;
302
-				$i++;
303
-			} else {
304
-				$newBindings[$key] = $value;
305
-			}
306
-		}
307
-		return $newBindings;
308
-	}
309
-
310
-	/**
311
-	 * Logger method.
312
-	 *
313
-	 * Takes a number of arguments tries to create
314
-	 * a proper debug log based on the available data.
315
-	 *
316
-	 * @return void
317
-	 */
318
-	public function log()
319
-	{
320
-		if ( func_num_args() < 1 ) return;
321
-
322
-		$sql = func_get_arg( 0 );
323
-
324
-		if ( func_num_args() < 2) {
325
-			$bindings = array();
326
-		} else {
327
-			$bindings = func_get_arg( 1 );
328
-		}
329
-
330
-		if ( !is_array( $bindings ) ) {
331
-			return $this->output( $sql );
332
-		}
333
-
334
-		$newSql = $this->normalizeSlots( $sql );
335
-		$newBindings = $this->normalizeBindings( $bindings );
336
-		$newStr = $this->writeQuery( $newSql, $newBindings );
337
-		$this->output( $newStr );
338
-	}
339
-
340
-	/**
341
-	 * Sets the max string length for the parameter output in
342
-	 * SQL queries. Set this value to a reasonable number to
343
-	 * keep you SQL queries readable.
344
-	 *
345
-	 * @param integer $len string length
346
-	 *
347
-	 * @return self
348
-	 */
349
-	public function setParamStringLength( $len = 20 )
350
-	{
351
-		$this->strLen = max(0, $len);
352
-		return $this;
353
-	}
194
+    /**
195
+     * @var integer
196
+     */
197
+    private $strLen = 40;
198
+
199
+    /**
200
+     * Writes a query for logging with all bindings / params filled
201
+     * in.
202
+     *
203
+     * @param string $newSql   the query
204
+     * @param array  $bindings the bindings to process (key-value pairs)
205
+     *
206
+     * @return string
207
+     */
208
+    private function writeQuery( $newSql, $newBindings )
209
+    {
210
+        //avoid str_replace collisions: slot1 and slot10 (issue 407).
211
+        uksort( $newBindings, function( $a, $b ) {
212
+            return ( strlen( $b ) - strlen( $a ) );
213
+        } );
214
+
215
+        $newStr = $newSql;
216
+        foreach( $newBindings as $slot => $value ) {
217
+            if ( strpos( $slot, ':' ) === 0 ) {
218
+                $newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr );
219
+            }
220
+        }
221
+        return $newStr;
222
+    }
223
+
224
+    /**
225
+     * Fills in a value of a binding and truncates the
226
+     * resulting string if necessary.
227
+     *
228
+     * @param mixed $value
229
+     *
230
+     * @return string
231
+     */
232
+    protected function fillInValue( $value )
233
+    {
234
+        if ( is_null( $value ) ) $value = 'NULL';
235
+
236
+        $value = strval( $value );
237
+        if ( strlen( $value ) > ( $this->strLen ) ) {
238
+            $value = substr( $value, 0, ( $this->strLen ) ).'... ';
239
+        }
240
+
241
+        if ( !is_numeric( $value ) && $value !== 'NULL') {
242
+            $value = '\''.$value.'\'';
243
+        }
244
+
245
+        return $value;
246
+    }
247
+
248
+    /**
249
+     * Dependending on the current mode of operation,
250
+     * this method will either log and output to STDIN or
251
+     * just log.
252
+     *
253
+     * @param string $str string to log or output and log
254
+     *
255
+     * @return void
256
+     */
257
+    protected function output( $str )
258
+    {
259
+        $this->logs[] = $str;
260
+        if ( !$this->mode ) echo $str .'<br />';
261
+    }
262
+
263
+    /**
264
+     * Normalizes the slots in an SQL string.
265
+     * Replaces question mark slots with :slot1 :slot2 etc.
266
+     *
267
+     * @param string $sql sql to normalize
268
+     *
269
+     * @return string
270
+     */
271
+    protected function normalizeSlots( $sql )
272
+    {
273
+        $i = 0;
274
+        $newSql = $sql;
275
+        while($i < 20 && strpos($newSql, '?') !== FALSE ){
276
+            $pos   = strpos( $newSql, '?' );
277
+            $slot  = ':slot'.$i;
278
+            $begin = substr( $newSql, 0, $pos );
279
+            $end   = substr( $newSql, $pos+1 );
280
+            $newSql = $begin . $slot . $end;
281
+            $i++;
282
+        }
283
+        return $newSql;
284
+    }
285
+
286
+    /**
287
+     * Normalizes the bindings.
288
+     * Replaces numeric binding keys with :slot1 :slot2 etc.
289
+     *
290
+     * @param array $bindings bindings to normalize
291
+     *
292
+     * @return array
293
+     */
294
+    protected function normalizeBindings( $bindings )
295
+    {
296
+        $i = 0;
297
+        $newBindings = array();
298
+        foreach( $bindings as $key => $value ) {
299
+            if ( is_numeric($key) ) {
300
+                $newKey = ':slot'.$i;
301
+                $newBindings[$newKey] = $value;
302
+                $i++;
303
+            } else {
304
+                $newBindings[$key] = $value;
305
+            }
306
+        }
307
+        return $newBindings;
308
+    }
309
+
310
+    /**
311
+     * Logger method.
312
+     *
313
+     * Takes a number of arguments tries to create
314
+     * a proper debug log based on the available data.
315
+     *
316
+     * @return void
317
+     */
318
+    public function log()
319
+    {
320
+        if ( func_num_args() < 1 ) return;
321
+
322
+        $sql = func_get_arg( 0 );
323
+
324
+        if ( func_num_args() < 2) {
325
+            $bindings = array();
326
+        } else {
327
+            $bindings = func_get_arg( 1 );
328
+        }
329
+
330
+        if ( !is_array( $bindings ) ) {
331
+            return $this->output( $sql );
332
+        }
333
+
334
+        $newSql = $this->normalizeSlots( $sql );
335
+        $newBindings = $this->normalizeBindings( $bindings );
336
+        $newStr = $this->writeQuery( $newSql, $newBindings );
337
+        $this->output( $newStr );
338
+    }
339
+
340
+    /**
341
+     * Sets the max string length for the parameter output in
342
+     * SQL queries. Set this value to a reasonable number to
343
+     * keep you SQL queries readable.
344
+     *
345
+     * @param integer $len string length
346
+     *
347
+     * @return self
348
+     */
349
+    public function setParamStringLength( $len = 20 )
350
+    {
351
+        $this->strLen = max(0, $len);
352
+        return $this;
353
+    }
354 354
 }
355 355
 } 
356 356
 
@@ -372,148 +372,148 @@  discard block
 block discarded – undo
372 372
  */
373 373
 interface Driver
374 374
 {
375
-	/**
376
-	 * Runs a query and fetches results as a multi dimensional array.
377
-	 *
378
-	 * @param string $sql      SQL to be executed
379
-	 * @param array  $bindings list of values to bind to SQL snippet
380
-	 *
381
-	 * @return array
382
-	 */
383
-	public function GetAll( $sql, $bindings = array() );
384
-
385
-	/**
386
-	 * Runs a query and fetches results as a column.
387
-	 *
388
-	 * @param string $sql      SQL Code to execute
389
-	 * @param array  $bindings list of values to bind to SQL snippet
390
-	 *
391
-	 * @return array
392
-	 */
393
-	public function GetCol( $sql, $bindings = array() );
394
-
395
-	/**
396
-	 * Runs a query and returns results as a single cell.
397
-	 *
398
-	 * @param string $sql      SQL to execute
399
-	 * @param array  $bindings list of values to bind to SQL snippet
400
-	 *
401
-	 * @return mixed
402
-	 */
403
-	public function GetOne( $sql, $bindings = array() );
375
+    /**
376
+     * Runs a query and fetches results as a multi dimensional array.
377
+     *
378
+     * @param string $sql      SQL to be executed
379
+     * @param array  $bindings list of values to bind to SQL snippet
380
+     *
381
+     * @return array
382
+     */
383
+    public function GetAll( $sql, $bindings = array() );
384
+
385
+    /**
386
+     * Runs a query and fetches results as a column.
387
+     *
388
+     * @param string $sql      SQL Code to execute
389
+     * @param array  $bindings list of values to bind to SQL snippet
390
+     *
391
+     * @return array
392
+     */
393
+    public function GetCol( $sql, $bindings = array() );
394
+
395
+    /**
396
+     * Runs a query and returns results as a single cell.
397
+     *
398
+     * @param string $sql      SQL to execute
399
+     * @param array  $bindings list of values to bind to SQL snippet
400
+     *
401
+     * @return mixed
402
+     */
403
+    public function GetOne( $sql, $bindings = array() );
404 404
 	
405
-	/**
406
-	 * Runs a query and returns results as an associative array
407
-	 * indexed by the first column.
408
-	 *
409
-	 * @param string $sql      SQL to execute
410
-	 * @param array  $bindings list of values to bind to SQL snippet
411
-	 *
412
-	 * @return mixed
413
-	 */
414
-	public function GetAssocRow( $sql, $bindings = array() );
405
+    /**
406
+     * Runs a query and returns results as an associative array
407
+     * indexed by the first column.
408
+     *
409
+     * @param string $sql      SQL to execute
410
+     * @param array  $bindings list of values to bind to SQL snippet
411
+     *
412
+     * @return mixed
413
+     */
414
+    public function GetAssocRow( $sql, $bindings = array() );
415 415
 	
416
-	/**
417
-	 * Runs a query and returns a flat array containing the values of
418
-	 * one row.
419
-	 *
420
-	 * @param string $sql      SQL to execute
421
-	 * @param array  $bindings list of values to bind to SQL snippet
422
-	 * 
423
-	 * @return array
424
-	 */
425
-	public function GetRow( $sql, $bindings = array() );
426
-
427
-	/**
428
-	 * Executes SQL code and allows key-value binding.
429
-	 * This function allows you to provide an array with values to bind
430
-	 * to query parameters. For instance you can bind values to question
431
-	 * marks in the query. Each value in the array corresponds to the
432
-	 * question mark in the query that matches the position of the value in the
433
-	 * array. You can also bind values using explicit keys, for instance
434
-	 * array(":key"=>123) will bind the integer 123 to the key :key in the
435
-	 * SQL. This method has no return value.
436
-	 *
437
-	 * @param string $sql      SQL Code to execute
438
-	 * @param array  $bindings list of values to bind to SQL snippet
439
-	 *
440
-	 * @return array Affected Rows
441
-	 */
442
-	public function Execute( $sql, $bindings = array() );
443
-
444
-	/**
445
-	 * Returns the latest insert ID if driver does support this
446
-	 * feature.
447
-	 *
448
-	 * @return integer
449
-	 */
450
-	public function GetInsertID();
451
-
452
-	/**
453
-	 * Returns the number of rows affected by the most recent query
454
-	 * if the currently selected driver driver supports this feature.
455
-	 *
456
-	 * @return integer
457
-	 */
458
-	public function Affected_Rows();
459
-
460
-	/**
461
-	 * Returns a cursor-like object from the database.
462
-	 *
463
-	 * @param string $sql      SQL code to execute
464
-	 * @param array  $bindings Bindings
465
-	 *
466
-	 * @return mixed
467
-	 */
468
-	public function GetCursor( $sql, $bindings = array() );
469
-
470
-	/**
471
-	 * Toggles debug mode. In debug mode the driver will print all
472
-	 * SQL to the screen together with some information about the
473
-	 * results. All SQL code that passes through the driver will be
474
-	 * passes on to the screen for inspection.
475
-	 * This method has no return value.
476
-	 *
477
-	 * @param boolean $trueFalse turn on/off
478
-	 *
479
-	 * @return void
480
-	 */
481
-	public function setDebugMode( $tf );
482
-
483
-	/**
484
-	 * Starts a transaction.
485
-	 *
486
-	 * @return void
487
-	 */
488
-	public function CommitTrans();
489
-
490
-	/**
491
-	 * Commits a transaction.
492
-	 *
493
-	 * @return void
494
-	 */
495
-	public function StartTrans();
496
-
497
-	/**
498
-	 * Rolls back a transaction.
499
-	 *
500
-	 * @return void
501
-	 */
502
-	public function FailTrans();
503
-
504
-	/**
505
-	 * Resets the internal Query Counter.
506
-	 *
507
-	 * @return self
508
-	 */
509
-	public function resetCounter();
510
-
511
-	/**
512
-	 * Returns the number of SQL queries processed.
513
-	 *
514
-	 * @return integer
515
-	 */
516
-	public function getQueryCount();
416
+    /**
417
+     * Runs a query and returns a flat array containing the values of
418
+     * one row.
419
+     *
420
+     * @param string $sql      SQL to execute
421
+     * @param array  $bindings list of values to bind to SQL snippet
422
+     * 
423
+     * @return array
424
+     */
425
+    public function GetRow( $sql, $bindings = array() );
426
+
427
+    /**
428
+     * Executes SQL code and allows key-value binding.
429
+     * This function allows you to provide an array with values to bind
430
+     * to query parameters. For instance you can bind values to question
431
+     * marks in the query. Each value in the array corresponds to the
432
+     * question mark in the query that matches the position of the value in the
433
+     * array. You can also bind values using explicit keys, for instance
434
+     * array(":key"=>123) will bind the integer 123 to the key :key in the
435
+     * SQL. This method has no return value.
436
+     *
437
+     * @param string $sql      SQL Code to execute
438
+     * @param array  $bindings list of values to bind to SQL snippet
439
+     *
440
+     * @return array Affected Rows
441
+     */
442
+    public function Execute( $sql, $bindings = array() );
443
+
444
+    /**
445
+     * Returns the latest insert ID if driver does support this
446
+     * feature.
447
+     *
448
+     * @return integer
449
+     */
450
+    public function GetInsertID();
451
+
452
+    /**
453
+     * Returns the number of rows affected by the most recent query
454
+     * if the currently selected driver driver supports this feature.
455
+     *
456
+     * @return integer
457
+     */
458
+    public function Affected_Rows();
459
+
460
+    /**
461
+     * Returns a cursor-like object from the database.
462
+     *
463
+     * @param string $sql      SQL code to execute
464
+     * @param array  $bindings Bindings
465
+     *
466
+     * @return mixed
467
+     */
468
+    public function GetCursor( $sql, $bindings = array() );
469
+
470
+    /**
471
+     * Toggles debug mode. In debug mode the driver will print all
472
+     * SQL to the screen together with some information about the
473
+     * results. All SQL code that passes through the driver will be
474
+     * passes on to the screen for inspection.
475
+     * This method has no return value.
476
+     *
477
+     * @param boolean $trueFalse turn on/off
478
+     *
479
+     * @return void
480
+     */
481
+    public function setDebugMode( $tf );
482
+
483
+    /**
484
+     * Starts a transaction.
485
+     *
486
+     * @return void
487
+     */
488
+    public function CommitTrans();
489
+
490
+    /**
491
+     * Commits a transaction.
492
+     *
493
+     * @return void
494
+     */
495
+    public function StartTrans();
496
+
497
+    /**
498
+     * Rolls back a transaction.
499
+     *
500
+     * @return void
501
+     */
502
+    public function FailTrans();
503
+
504
+    /**
505
+     * Resets the internal Query Counter.
506
+     *
507
+     * @return self
508
+     */
509
+    public function resetCounter();
510
+
511
+    /**
512
+     * Returns the number of SQL queries processed.
513
+     *
514
+     * @return integer
515
+     */
516
+    public function getQueryCount();
517 517
 }
518 518
 }
519 519
 
@@ -544,571 +544,571 @@  discard block
 block discarded – undo
544 544
  */
545 545
 class RPDO implements Driver
546 546
 {
547
-	/**
548
-	 * @var integer
549
-	 */
550
-	protected $max;
551
-
552
-	/**
553
-	 * @var string
554
-	 */
555
-	protected $dsn;
556
-
557
-	/**
558
-	 * @var boolean
559
-	 */
560
-	protected $loggingEnabled = FALSE;
561
-
562
-	/**
563
-	 * @var Logger
564
-	 */
565
-	protected $logger = NULL;
566
-
567
-	/**
568
-	 * @var PDO
569
-	 */
570
-	protected $pdo;
571
-
572
-	/**
573
-	 * @var integer
574
-	 */
575
-	protected $affectedRows;
576
-
577
-	/**
578
-	 * @var integer
579
-	 */
580
-	protected $resultArray;
581
-
582
-	/**
583
-	 * @var array
584
-	 */
585
-	protected $connectInfo = array();
586
-
587
-	/**
588
-	 * @var boolean
589
-	 */
590
-	protected $isConnected = FALSE;
591
-
592
-	/**
593
-	 * @var bool
594
-	 */
595
-	protected $flagUseStringOnlyBinding = FALSE;
596
-
597
-	/**
598
-	 * @var integer
599
-	 */
600
-	protected $queryCounter = 0;
601
-
602
-	/**
603
-	 * @var string
604
-	 */
605
-	protected $mysqlEncoding = '';
606
-
607
-	/**
608
-	 * Binds parameters. This method binds parameters to a PDOStatement for
609
-	 * Query Execution. This method binds parameters as NULL, INTEGER or STRING
610
-	 * and supports both named keys and question mark keys.
611
-	 *
612
-	 * @param PDOStatement $statement PDO Statement instance
613
-	 * @param array        $bindings  values that need to get bound to the statement
614
-	 *
615
-	 * @return void
616
-	 */
617
-	protected function bindParams( $statement, $bindings )
618
-	{
619
-		foreach ( $bindings as $key => &$value ) {
620
-			if ( is_integer( $key ) ) {
621
-				if ( is_null( $value ) ) {
622
-					$statement->bindValue( $key + 1, NULL, \PDO::PARAM_NULL );
623
-				} elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
624
-					$statement->bindParam( $key + 1, $value, \PDO::PARAM_INT );
625
-				} else {
626
-					$statement->bindParam( $key + 1, $value, \PDO::PARAM_STR );
627
-				}
628
-			} else {
629
-				if ( is_null( $value ) ) {
630
-					$statement->bindValue( $key, NULL, \PDO::PARAM_NULL );
631
-				} elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
632
-					$statement->bindParam( $key, $value, \PDO::PARAM_INT );
633
-				} else {
634
-					$statement->bindParam( $key, $value, \PDO::PARAM_STR );
635
-				}
636
-			}
637
-		}
638
-	}
639
-
640
-	/**
641
-	 * This method runs the actual SQL query and binds a list of parameters to the query.
642
-	 * slots. The result of the query will be stored in the protected property
643
-	 * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
644
-	 * is stored in protected property $affectedRows. If the debug flag is set
645
-	 * this function will send debugging output to screen buffer.
646
-	 *
647
-	 * @param string $sql      the SQL string to be send to database server
648
-	 * @param array  $bindings the values that need to get bound to the query slots
649
-	 *
650
-	 * @return void
651
-	 *
652
-	 * @throws SQL
653
-	 */
654
-	protected function runQuery( $sql, $bindings, $options = array() )
655
-	{
656
-		$this->connect();
657
-		if ( $this->loggingEnabled && $this->logger ) {
658
-			$this->logger->log( $sql, $bindings );
659
-		}
660
-		try {
661
-			if ( strpos( 'pgsql', $this->dsn ) === 0 ) {
662
-				$statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) );
663
-			} else {
664
-				$statement = $this->pdo->prepare( $sql );
665
-			}
666
-			$this->bindParams( $statement, $bindings );
667
-			$statement->execute();
668
-			$this->queryCounter ++;
669
-			$this->affectedRows = $statement->rowCount();
670
-			if ( $statement->columnCount() ) {
671
-				$fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL;
672
-				if ( isset( $options['noFetch'] ) && $options['noFetch'] ) {
673
-					$this->resultArray = array();
674
-					return $statement;
675
-				}
676
-				$this->resultArray = $statement->fetchAll( $fetchStyle );
677
-				if ( $this->loggingEnabled && $this->logger ) {
678
-					$this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' );
679
-				}
680
-			} else {
681
-				$this->resultArray = array();
682
-			}
683
-		} catch (\PDOException $e ) {
684
-			//Unfortunately the code field is supposed to be int by default (php)
685
-			//So we need a property to convey the SQL State code.
686
-			$err = $e->getMessage();
687
-			if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err );
688
-			$exception = new SQL( $err, 0 );
689
-			$exception->setSQLState( $e->getCode() );
690
-			throw $exception;
691
-		}
692
-	}
693
-
694
-	/**
695
-	 * Try to fix MySQL character encoding problems.
696
-	 * MySQL < 5.5 does not support proper 4 byte unicode but they
697
-	 * seem to have added it with version 5.5 under a different label: utf8mb4.
698
-	 * We try to select the best possible charset based on your version data.
699
-	 */
700
-	protected function setEncoding()
701
-	{
702
-		$driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
703
-		$version = floatval( $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION ) );
704
-		if ($driver === 'mysql') {
705
-			$encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8';
706
-			$this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect
707
-			$this->pdo->exec(' SET NAMES '. $encoding); //also for current connection
708
-			$this->mysqlEncoding = $encoding;
709
-		}
710
-	}
711
-
712
-	/**
713
-	 * Constructor. You may either specify dsn, user and password or
714
-	 * just give an existing PDO connection.
715
-	 *
716
-	 * Examples:
717
-	 *    $driver = new RPDO($dsn, $user, $password);
718
-	 *    $driver = new RPDO($existingConnection);
719
-	 *
720
-	 * @param string|object $dsn    database connection string
721
-	 * @param string        $user   optional, usename to sign in
722
-	 * @param string        $pass   optional, password for connection login
723
-	 *
724
-	 */
725
-	public function __construct( $dsn, $user = NULL, $pass = NULL )
726
-	{
727
-		if ( is_object( $dsn ) ) {
728
-			$this->pdo = $dsn;
729
-			$this->isConnected = TRUE;
730
-			$this->setEncoding();
731
-			$this->pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
732
-			$this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
733
-			// make sure that the dsn at least contains the type
734
-			$this->dsn = $this->getDatabaseType();
735
-		} else {
736
-			$this->dsn = $dsn;
737
-			$this->connectInfo = array( 'pass' => $pass, 'user' => $user );
738
-		}
739
-
740
-		//PHP 5.3 PDO SQLite has a bug with large numbers:
741
-		if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) || $this->dsn === 'test-sqlite-53' ) {
742
-			$this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis.
743
-		} elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) {
744
-			$this->max = 2147483647; //bindParam in pdo_cubrid also fails...
745
-		} else {
746
-			$this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause)
747
-		}
748
-	}
749
-
750
-	/**
751
-	 * Returns the best possible encoding for MySQL based on version data.
752
-	 *
753
-	 * @return string
754
-	 */
755
-	public function getMysqlEncoding()
756
-	{
757
-		return $this->mysqlEncoding;
758
-	}
759
-
760
-	/**
761
-	 * Whether to bind all parameters as strings.
762
-	 * If set to TRUE this will cause all integers to be bound as STRINGS.
763
-	 * This will NOT affect NULL values.
764
-	 *
765
-	 * @param boolean $yesNo pass TRUE to bind all parameters as strings.
766
-	 *
767
-	 * @return void
768
-	 */
769
-	public function setUseStringOnlyBinding( $yesNo )
770
-	{
771
-		$this->flagUseStringOnlyBinding = (boolean) $yesNo;
772
-	}
773
-
774
-	/**
775
-	 * Establishes a connection to the database using PHP\PDO
776
-	 * functionality. If a connection has already been established this
777
-	 * method will simply return directly. This method also turns on
778
-	 * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as
779
-	 * PDO-FETCH-ASSOC.
780
-	 *
781
-	 * @throws\PDOException
782
-	 *
783
-	 * @return void
784
-	 */
785
-	public function connect()
786
-	{
787
-		if ( $this->isConnected ) return;
788
-		try {
789
-			$user = $this->connectInfo['user'];
790
-			$pass = $this->connectInfo['pass'];
791
-			$this->pdo = new \PDO(
792
-				$this->dsn,
793
-				$user,
794
-				$pass
795
-			);
796
-			$this->setEncoding();
797
-			$this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, TRUE );
798
-			//cant pass these as argument to constructor, CUBRID driver does not understand...
799
-			$this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
800
-			$this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC );
801
-			$this->isConnected = TRUE;
802
-		} catch ( \PDOException $exception ) {
803
-			$matches = array();
804
-			$dbname  = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?';
805
-			throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() );
806
-		}
807
-	}
808
-
809
-	/**
810
-	 * Directly sets PDO instance into driver.
811
-	 * This method might improve performance, however since the driver does
812
-	 * not configure this instance terrible things may happen... only use
813
-	 * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and
814
-	 * you know your database server VERY WELL.
815
-	 *
816
-	 * @param PDO $pdo PDO instance
817
-	 *
818
-	 * @return void
819
-	 */
820
-	public function setPDO( \PDO $pdo ) {
821
-		$this->pdo = $pdo;
822
-	}
823
-
824
-	/**
825
-	 * @see Driver::GetAll
826
-	 */
827
-	public function GetAll( $sql, $bindings = array() )
828
-	{
829
-		$this->runQuery( $sql, $bindings );
830
-		return $this->resultArray;
831
-	}
832
-
833
-	/**
834
-	 * @see Driver::GetAssocRow
835
-	 */
836
-	public function GetAssocRow( $sql, $bindings = array() )
837
-	{
838
-		$this->runQuery( $sql, $bindings, array(
839
-				'fetchStyle' => \PDO::FETCH_ASSOC
840
-			)
841
-		);
842
-		return $this->resultArray;
843
-	}
844
-
845
-	/**
846
-	 * @see Driver::GetCol
847
-	 */
848
-	public function GetCol( $sql, $bindings = array() )
849
-	{
850
-		$rows = $this->GetAll( $sql, $bindings );
851
-		$cols = array();
852
-		if ( $rows && is_array( $rows ) && count( $rows ) > 0 ) {
853
-			foreach ( $rows as $row ) {
854
-				$cols[] = array_shift( $row );
855
-			}
856
-		}
857
-
858
-		return $cols;
859
-	}
860
-
861
-	/**
862
-	 * @see Driver::GetOne
863
-	 */
864
-	public function GetOne( $sql, $bindings = array() )
865
-	{
866
-		$arr = $this->GetAll( $sql, $bindings );
867
-		$res = NULL;
868
-		if ( !is_array( $arr ) ) return NULL;
869
-		if ( count( $arr ) === 0 ) return NULL;
870
-		$row1 = array_shift( $arr );
871
-		if ( !is_array( $row1 ) ) return NULL;
872
-		if ( count( $row1 ) === 0 ) return NULL;
873
-		$col1 = array_shift( $row1 );
874
-		return $col1;
875
-	}
876
-
877
-	/**
878
-	 * Alias for getOne().
879
-	 * Backward compatibility.
880
-	 *
881
-	 * @param string $sql      SQL
882
-	 * @param array  $bindings bindings
883
-	 *
884
-	 * @return mixed
885
-	 */
886
-	public function GetCell( $sql, $bindings = array() )
887
-	{
888
-		return $this->GetOne( $sql, $bindings );
889
-	}
890
-
891
-	/**
892
-	 * @see Driver::GetRow
893
-	 */
894
-	public function GetRow( $sql, $bindings = array() )
895
-	{
896
-		$arr = $this->GetAll( $sql, $bindings );
897
-		return array_shift( $arr );
898
-	}
899
-
900
-	/**
901
-	 * @see Driver::Excecute
902
-	 */
903
-	public function Execute( $sql, $bindings = array() )
904
-	{
905
-		$this->runQuery( $sql, $bindings );
906
-		return $this->affectedRows;
907
-	}
908
-
909
-	/**
910
-	 * @see Driver::GetInsertID
911
-	 */
912
-	public function GetInsertID()
913
-	{
914
-		$this->connect();
915
-
916
-		return (int) $this->pdo->lastInsertId();
917
-	}
918
-
919
-	/**
920
-	 * @see Driver::GetCursor
921
-	 */
922
-	public function GetCursor( $sql, $bindings = array() )
923
-	{
924
-		$statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) );
925
-		$cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC );
926
-		return $cursor;
927
-	}
928
-
929
-	/**
930
-	 * @see Driver::Affected_Rows
931
-	 */
932
-	public function Affected_Rows()
933
-	{
934
-		$this->connect();
935
-		return (int) $this->affectedRows;
936
-	}
937
-
938
-	/**
939
-	 * Toggles debug mode. In debug mode the driver will print all
940
-	 * SQL to the screen together with some information about the
941
-	 * results.
942
-	 *
943
-	 * @param boolean $trueFalse turn on/off
944
-	 * @param Logger  $logger    logger instance
945
-	 *
946
-	 * @return void
947
-	 */
948
-	public function setDebugMode( $tf, $logger = NULL )
949
-	{
950
-		$this->connect();
951
-		$this->loggingEnabled = (bool) $tf;
952
-		if ( $this->loggingEnabled and !$logger ) {
953
-			$logger = new RDefault();
954
-		}
955
-		$this->setLogger( $logger );
956
-	}
957
-
958
-	/**
959
-	 * Injects Logger object.
960
-	 * Sets the logger instance you wish to use.
961
-	 *
962
-	 * @param Logger $logger the logger instance to be used for logging
963
-	 *
964
-	 * @return void
965
-	 */
966
-	public function setLogger( Logger $logger )
967
-	{
968
-		$this->logger = $logger;
969
-	}
970
-
971
-	/**
972
-	 * Gets Logger object.
973
-	 * Returns the currently active Logger instance.
974
-	 *
975
-	 * @return Logger
976
-	 */
977
-	public function getLogger()
978
-	{
979
-		return $this->logger;
980
-	}
981
-
982
-	/**
983
-	 * @see Driver::StartTrans
984
-	 */
985
-	public function StartTrans()
986
-	{
987
-		$this->connect();
988
-		$this->pdo->beginTransaction();
989
-	}
990
-
991
-	/**
992
-	 * @see Driver::CommitTrans
993
-	 */
994
-	public function CommitTrans()
995
-	{
996
-		$this->connect();
997
-		$this->pdo->commit();
998
-	}
999
-
1000
-	/**
1001
-	 * @see Driver::FailTrans
1002
-	 */
1003
-	public function FailTrans()
1004
-	{
1005
-		$this->connect();
1006
-		$this->pdo->rollback();
1007
-	}
1008
-
1009
-	/**
1010
-	 * Returns the name of database driver for PDO.
1011
-	 * Uses the PDO attribute DRIVER NAME to obtain the name of the
1012
-	 * PDO driver.
1013
-	 *
1014
-	 * @return string
1015
-	 */
1016
-	public function getDatabaseType()
1017
-	{
1018
-		$this->connect();
1019
-
1020
-		return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
1021
-	}
1022
-
1023
-	/**
1024
-	 * Returns the version number of the database.
1025
-	 *
1026
-	 * @return mixed $version version number of the database
1027
-	 */
1028
-	public function getDatabaseVersion()
1029
-	{
1030
-		$this->connect();
1031
-		return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
1032
-	}
1033
-
1034
-	/**
1035
-	 * Returns the underlying PHP PDO instance.
1036
-	 *
1037
-	 * @return PDO
1038
-	 */
1039
-	public function getPDO()
1040
-	{
1041
-		$this->connect();
1042
-		return $this->pdo;
1043
-	}
1044
-
1045
-	/**
1046
-	 * Closes database connection by destructing\PDO.
1047
-	 *
1048
-	 * @return void
1049
-	 */
1050
-	public function close()
1051
-	{
1052
-		$this->pdo         = NULL;
1053
-		$this->isConnected = FALSE;
1054
-	}
1055
-
1056
-	/**
1057
-	 * Returns TRUE if the current\PDO instance is connected.
1058
-	 *
1059
-	 * @return boolean
1060
-	 */
1061
-	public function isConnected()
1062
-	{
1063
-		return $this->isConnected && $this->pdo;
1064
-	}
1065
-
1066
-	/**
1067
-	 * Toggles logging, enables or disables logging.
1068
-	 *
1069
-	 * @param boolean $enable TRUE to enable logging
1070
-	 *
1071
-	 * @return self
1072
-	 */
1073
-	public function setEnableLogging( $enable )
1074
-	{
1075
-		$this->loggingEnabled = (boolean) $enable;
1076
-	}
1077
-
1078
-	/**
1079
-	 * Resets the internal Query Counter.
1080
-	 *
1081
-	 * @return self
1082
-	 */
1083
-	public function resetCounter()
1084
-	{
1085
-		$this->queryCounter = 0;
1086
-		return $this;
1087
-	}
1088
-
1089
-	/**
1090
-	 * Returns the number of SQL queries processed.
1091
-	 *
1092
-	 * @return integer
1093
-	 */
1094
-	public function getQueryCount()
1095
-	{
1096
-		return $this->queryCounter;
1097
-	}
1098
-
1099
-	/**
1100
-	 * Returns the maximum value treated as integer parameter
1101
-	 * binding.
1102
-	 *
1103
-	 * This method is mainly for testing purposes but it can help
1104
-	 * you solve some issues relating to integer bindings.
1105
-	 *
1106
-	 * @return integer
1107
-	 */
1108
-	public function getIntegerBindingMax()
1109
-	{
1110
-		return $this->max;
1111
-	}
547
+    /**
548
+     * @var integer
549
+     */
550
+    protected $max;
551
+
552
+    /**
553
+     * @var string
554
+     */
555
+    protected $dsn;
556
+
557
+    /**
558
+     * @var boolean
559
+     */
560
+    protected $loggingEnabled = FALSE;
561
+
562
+    /**
563
+     * @var Logger
564
+     */
565
+    protected $logger = NULL;
566
+
567
+    /**
568
+     * @var PDO
569
+     */
570
+    protected $pdo;
571
+
572
+    /**
573
+     * @var integer
574
+     */
575
+    protected $affectedRows;
576
+
577
+    /**
578
+     * @var integer
579
+     */
580
+    protected $resultArray;
581
+
582
+    /**
583
+     * @var array
584
+     */
585
+    protected $connectInfo = array();
586
+
587
+    /**
588
+     * @var boolean
589
+     */
590
+    protected $isConnected = FALSE;
591
+
592
+    /**
593
+     * @var bool
594
+     */
595
+    protected $flagUseStringOnlyBinding = FALSE;
596
+
597
+    /**
598
+     * @var integer
599
+     */
600
+    protected $queryCounter = 0;
601
+
602
+    /**
603
+     * @var string
604
+     */
605
+    protected $mysqlEncoding = '';
606
+
607
+    /**
608
+     * Binds parameters. This method binds parameters to a PDOStatement for
609
+     * Query Execution. This method binds parameters as NULL, INTEGER or STRING
610
+     * and supports both named keys and question mark keys.
611
+     *
612
+     * @param PDOStatement $statement PDO Statement instance
613
+     * @param array        $bindings  values that need to get bound to the statement
614
+     *
615
+     * @return void
616
+     */
617
+    protected function bindParams( $statement, $bindings )
618
+    {
619
+        foreach ( $bindings as $key => &$value ) {
620
+            if ( is_integer( $key ) ) {
621
+                if ( is_null( $value ) ) {
622
+                    $statement->bindValue( $key + 1, NULL, \PDO::PARAM_NULL );
623
+                } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
624
+                    $statement->bindParam( $key + 1, $value, \PDO::PARAM_INT );
625
+                } else {
626
+                    $statement->bindParam( $key + 1, $value, \PDO::PARAM_STR );
627
+                }
628
+            } else {
629
+                if ( is_null( $value ) ) {
630
+                    $statement->bindValue( $key, NULL, \PDO::PARAM_NULL );
631
+                } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
632
+                    $statement->bindParam( $key, $value, \PDO::PARAM_INT );
633
+                } else {
634
+                    $statement->bindParam( $key, $value, \PDO::PARAM_STR );
635
+                }
636
+            }
637
+        }
638
+    }
639
+
640
+    /**
641
+     * This method runs the actual SQL query and binds a list of parameters to the query.
642
+     * slots. The result of the query will be stored in the protected property
643
+     * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
644
+     * is stored in protected property $affectedRows. If the debug flag is set
645
+     * this function will send debugging output to screen buffer.
646
+     *
647
+     * @param string $sql      the SQL string to be send to database server
648
+     * @param array  $bindings the values that need to get bound to the query slots
649
+     *
650
+     * @return void
651
+     *
652
+     * @throws SQL
653
+     */
654
+    protected function runQuery( $sql, $bindings, $options = array() )
655
+    {
656
+        $this->connect();
657
+        if ( $this->loggingEnabled && $this->logger ) {
658
+            $this->logger->log( $sql, $bindings );
659
+        }
660
+        try {
661
+            if ( strpos( 'pgsql', $this->dsn ) === 0 ) {
662
+                $statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) );
663
+            } else {
664
+                $statement = $this->pdo->prepare( $sql );
665
+            }
666
+            $this->bindParams( $statement, $bindings );
667
+            $statement->execute();
668
+            $this->queryCounter ++;
669
+            $this->affectedRows = $statement->rowCount();
670
+            if ( $statement->columnCount() ) {
671
+                $fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL;
672
+                if ( isset( $options['noFetch'] ) && $options['noFetch'] ) {
673
+                    $this->resultArray = array();
674
+                    return $statement;
675
+                }
676
+                $this->resultArray = $statement->fetchAll( $fetchStyle );
677
+                if ( $this->loggingEnabled && $this->logger ) {
678
+                    $this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' );
679
+                }
680
+            } else {
681
+                $this->resultArray = array();
682
+            }
683
+        } catch (\PDOException $e ) {
684
+            //Unfortunately the code field is supposed to be int by default (php)
685
+            //So we need a property to convey the SQL State code.
686
+            $err = $e->getMessage();
687
+            if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err );
688
+            $exception = new SQL( $err, 0 );
689
+            $exception->setSQLState( $e->getCode() );
690
+            throw $exception;
691
+        }
692
+    }
693
+
694
+    /**
695
+     * Try to fix MySQL character encoding problems.
696
+     * MySQL < 5.5 does not support proper 4 byte unicode but they
697
+     * seem to have added it with version 5.5 under a different label: utf8mb4.
698
+     * We try to select the best possible charset based on your version data.
699
+     */
700
+    protected function setEncoding()
701
+    {
702
+        $driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
703
+        $version = floatval( $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION ) );
704
+        if ($driver === 'mysql') {
705
+            $encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8';
706
+            $this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect
707
+            $this->pdo->exec(' SET NAMES '. $encoding); //also for current connection
708
+            $this->mysqlEncoding = $encoding;
709
+        }
710
+    }
711
+
712
+    /**
713
+     * Constructor. You may either specify dsn, user and password or
714
+     * just give an existing PDO connection.
715
+     *
716
+     * Examples:
717
+     *    $driver = new RPDO($dsn, $user, $password);
718
+     *    $driver = new RPDO($existingConnection);
719
+     *
720
+     * @param string|object $dsn    database connection string
721
+     * @param string        $user   optional, usename to sign in
722
+     * @param string        $pass   optional, password for connection login
723
+     *
724
+     */
725
+    public function __construct( $dsn, $user = NULL, $pass = NULL )
726
+    {
727
+        if ( is_object( $dsn ) ) {
728
+            $this->pdo = $dsn;
729
+            $this->isConnected = TRUE;
730
+            $this->setEncoding();
731
+            $this->pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
732
+            $this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
733
+            // make sure that the dsn at least contains the type
734
+            $this->dsn = $this->getDatabaseType();
735
+        } else {
736
+            $this->dsn = $dsn;
737
+            $this->connectInfo = array( 'pass' => $pass, 'user' => $user );
738
+        }
739
+
740
+        //PHP 5.3 PDO SQLite has a bug with large numbers:
741
+        if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) || $this->dsn === 'test-sqlite-53' ) {
742
+            $this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis.
743
+        } elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) {
744
+            $this->max = 2147483647; //bindParam in pdo_cubrid also fails...
745
+        } else {
746
+            $this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause)
747
+        }
748
+    }
749
+
750
+    /**
751
+     * Returns the best possible encoding for MySQL based on version data.
752
+     *
753
+     * @return string
754
+     */
755
+    public function getMysqlEncoding()
756
+    {
757
+        return $this->mysqlEncoding;
758
+    }
759
+
760
+    /**
761
+     * Whether to bind all parameters as strings.
762
+     * If set to TRUE this will cause all integers to be bound as STRINGS.
763
+     * This will NOT affect NULL values.
764
+     *
765
+     * @param boolean $yesNo pass TRUE to bind all parameters as strings.
766
+     *
767
+     * @return void
768
+     */
769
+    public function setUseStringOnlyBinding( $yesNo )
770
+    {
771
+        $this->flagUseStringOnlyBinding = (boolean) $yesNo;
772
+    }
773
+
774
+    /**
775
+     * Establishes a connection to the database using PHP\PDO
776
+     * functionality. If a connection has already been established this
777
+     * method will simply return directly. This method also turns on
778
+     * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as
779
+     * PDO-FETCH-ASSOC.
780
+     *
781
+     * @throws\PDOException
782
+     *
783
+     * @return void
784
+     */
785
+    public function connect()
786
+    {
787
+        if ( $this->isConnected ) return;
788
+        try {
789
+            $user = $this->connectInfo['user'];
790
+            $pass = $this->connectInfo['pass'];
791
+            $this->pdo = new \PDO(
792
+                $this->dsn,
793
+                $user,
794
+                $pass
795
+            );
796
+            $this->setEncoding();
797
+            $this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, TRUE );
798
+            //cant pass these as argument to constructor, CUBRID driver does not understand...
799
+            $this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
800
+            $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC );
801
+            $this->isConnected = TRUE;
802
+        } catch ( \PDOException $exception ) {
803
+            $matches = array();
804
+            $dbname  = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?';
805
+            throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() );
806
+        }
807
+    }
808
+
809
+    /**
810
+     * Directly sets PDO instance into driver.
811
+     * This method might improve performance, however since the driver does
812
+     * not configure this instance terrible things may happen... only use
813
+     * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and
814
+     * you know your database server VERY WELL.
815
+     *
816
+     * @param PDO $pdo PDO instance
817
+     *
818
+     * @return void
819
+     */
820
+    public function setPDO( \PDO $pdo ) {
821
+        $this->pdo = $pdo;
822
+    }
823
+
824
+    /**
825
+     * @see Driver::GetAll
826
+     */
827
+    public function GetAll( $sql, $bindings = array() )
828
+    {
829
+        $this->runQuery( $sql, $bindings );
830
+        return $this->resultArray;
831
+    }
832
+
833
+    /**
834
+     * @see Driver::GetAssocRow
835
+     */
836
+    public function GetAssocRow( $sql, $bindings = array() )
837
+    {
838
+        $this->runQuery( $sql, $bindings, array(
839
+                'fetchStyle' => \PDO::FETCH_ASSOC
840
+            )
841
+        );
842
+        return $this->resultArray;
843
+    }
844
+
845
+    /**
846
+     * @see Driver::GetCol
847
+     */
848
+    public function GetCol( $sql, $bindings = array() )
849
+    {
850
+        $rows = $this->GetAll( $sql, $bindings );
851
+        $cols = array();
852
+        if ( $rows && is_array( $rows ) && count( $rows ) > 0 ) {
853
+            foreach ( $rows as $row ) {
854
+                $cols[] = array_shift( $row );
855
+            }
856
+        }
857
+
858
+        return $cols;
859
+    }
860
+
861
+    /**
862
+     * @see Driver::GetOne
863
+     */
864
+    public function GetOne( $sql, $bindings = array() )
865
+    {
866
+        $arr = $this->GetAll( $sql, $bindings );
867
+        $res = NULL;
868
+        if ( !is_array( $arr ) ) return NULL;
869
+        if ( count( $arr ) === 0 ) return NULL;
870
+        $row1 = array_shift( $arr );
871
+        if ( !is_array( $row1 ) ) return NULL;
872
+        if ( count( $row1 ) === 0 ) return NULL;
873
+        $col1 = array_shift( $row1 );
874
+        return $col1;
875
+    }
876
+
877
+    /**
878
+     * Alias for getOne().
879
+     * Backward compatibility.
880
+     *
881
+     * @param string $sql      SQL
882
+     * @param array  $bindings bindings
883
+     *
884
+     * @return mixed
885
+     */
886
+    public function GetCell( $sql, $bindings = array() )
887
+    {
888
+        return $this->GetOne( $sql, $bindings );
889
+    }
890
+
891
+    /**
892
+     * @see Driver::GetRow
893
+     */
894
+    public function GetRow( $sql, $bindings = array() )
895
+    {
896
+        $arr = $this->GetAll( $sql, $bindings );
897
+        return array_shift( $arr );
898
+    }
899
+
900
+    /**
901
+     * @see Driver::Excecute
902
+     */
903
+    public function Execute( $sql, $bindings = array() )
904
+    {
905
+        $this->runQuery( $sql, $bindings );
906
+        return $this->affectedRows;
907
+    }
908
+
909
+    /**
910
+     * @see Driver::GetInsertID
911
+     */
912
+    public function GetInsertID()
913
+    {
914
+        $this->connect();
915
+
916
+        return (int) $this->pdo->lastInsertId();
917
+    }
918
+
919
+    /**
920
+     * @see Driver::GetCursor
921
+     */
922
+    public function GetCursor( $sql, $bindings = array() )
923
+    {
924
+        $statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) );
925
+        $cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC );
926
+        return $cursor;
927
+    }
928
+
929
+    /**
930
+     * @see Driver::Affected_Rows
931
+     */
932
+    public function Affected_Rows()
933
+    {
934
+        $this->connect();
935
+        return (int) $this->affectedRows;
936
+    }
937
+
938
+    /**
939
+     * Toggles debug mode. In debug mode the driver will print all
940
+     * SQL to the screen together with some information about the
941
+     * results.
942
+     *
943
+     * @param boolean $trueFalse turn on/off
944
+     * @param Logger  $logger    logger instance
945
+     *
946
+     * @return void
947
+     */
948
+    public function setDebugMode( $tf, $logger = NULL )
949
+    {
950
+        $this->connect();
951
+        $this->loggingEnabled = (bool) $tf;
952
+        if ( $this->loggingEnabled and !$logger ) {
953
+            $logger = new RDefault();
954
+        }
955
+        $this->setLogger( $logger );
956
+    }
957
+
958
+    /**
959
+     * Injects Logger object.
960
+     * Sets the logger instance you wish to use.
961
+     *
962
+     * @param Logger $logger the logger instance to be used for logging
963
+     *
964
+     * @return void
965
+     */
966
+    public function setLogger( Logger $logger )
967
+    {
968
+        $this->logger = $logger;
969
+    }
970
+
971
+    /**
972
+     * Gets Logger object.
973
+     * Returns the currently active Logger instance.
974
+     *
975
+     * @return Logger
976
+     */
977
+    public function getLogger()
978
+    {
979
+        return $this->logger;
980
+    }
981
+
982
+    /**
983
+     * @see Driver::StartTrans
984
+     */
985
+    public function StartTrans()
986
+    {
987
+        $this->connect();
988
+        $this->pdo->beginTransaction();
989
+    }
990
+
991
+    /**
992
+     * @see Driver::CommitTrans
993
+     */
994
+    public function CommitTrans()
995
+    {
996
+        $this->connect();
997
+        $this->pdo->commit();
998
+    }
999
+
1000
+    /**
1001
+     * @see Driver::FailTrans
1002
+     */
1003
+    public function FailTrans()
1004
+    {
1005
+        $this->connect();
1006
+        $this->pdo->rollback();
1007
+    }
1008
+
1009
+    /**
1010
+     * Returns the name of database driver for PDO.
1011
+     * Uses the PDO attribute DRIVER NAME to obtain the name of the
1012
+     * PDO driver.
1013
+     *
1014
+     * @return string
1015
+     */
1016
+    public function getDatabaseType()
1017
+    {
1018
+        $this->connect();
1019
+
1020
+        return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
1021
+    }
1022
+
1023
+    /**
1024
+     * Returns the version number of the database.
1025
+     *
1026
+     * @return mixed $version version number of the database
1027
+     */
1028
+    public function getDatabaseVersion()
1029
+    {
1030
+        $this->connect();
1031
+        return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
1032
+    }
1033
+
1034
+    /**
1035
+     * Returns the underlying PHP PDO instance.
1036
+     *
1037
+     * @return PDO
1038
+     */
1039
+    public function getPDO()
1040
+    {
1041
+        $this->connect();
1042
+        return $this->pdo;
1043
+    }
1044
+
1045
+    /**
1046
+     * Closes database connection by destructing\PDO.
1047
+     *
1048
+     * @return void
1049
+     */
1050
+    public function close()
1051
+    {
1052
+        $this->pdo         = NULL;
1053
+        $this->isConnected = FALSE;
1054
+    }
1055
+
1056
+    /**
1057
+     * Returns TRUE if the current\PDO instance is connected.
1058
+     *
1059
+     * @return boolean
1060
+     */
1061
+    public function isConnected()
1062
+    {
1063
+        return $this->isConnected && $this->pdo;
1064
+    }
1065
+
1066
+    /**
1067
+     * Toggles logging, enables or disables logging.
1068
+     *
1069
+     * @param boolean $enable TRUE to enable logging
1070
+     *
1071
+     * @return self
1072
+     */
1073
+    public function setEnableLogging( $enable )
1074
+    {
1075
+        $this->loggingEnabled = (boolean) $enable;
1076
+    }
1077
+
1078
+    /**
1079
+     * Resets the internal Query Counter.
1080
+     *
1081
+     * @return self
1082
+     */
1083
+    public function resetCounter()
1084
+    {
1085
+        $this->queryCounter = 0;
1086
+        return $this;
1087
+    }
1088
+
1089
+    /**
1090
+     * Returns the number of SQL queries processed.
1091
+     *
1092
+     * @return integer
1093
+     */
1094
+    public function getQueryCount()
1095
+    {
1096
+        return $this->queryCounter;
1097
+    }
1098
+
1099
+    /**
1100
+     * Returns the maximum value treated as integer parameter
1101
+     * binding.
1102
+     *
1103
+     * This method is mainly for testing purposes but it can help
1104
+     * you solve some issues relating to integer bindings.
1105
+     *
1106
+     * @return integer
1107
+     */
1108
+    public function getIntegerBindingMax()
1109
+    {
1110
+        return $this->max;
1111
+    }
1112 1112
 }
1113 1113
 }
1114 1114
 
@@ -1139,1765 +1139,1765 @@  discard block
 block discarded – undo
1139 1139
  */
1140 1140
 class OODBBean implements\IteratorAggregate,\ArrayAccess,\Countable
1141 1141
 {
1142
-	/**
1143
-	 * FUSE error modes.
1144
-	 */
1145
-	const C_ERR_IGNORE    = FALSE;
1146
-	const C_ERR_LOG       = 1;
1147
-	const C_ERR_NOTICE    = 2;
1148
-	const C_ERR_WARN      = 3;
1149
-	const C_ERR_EXCEPTION = 4;
1150
-	const C_ERR_FUNC      = 5;
1151
-	const C_ERR_FATAL     = 6;
1152
-
1153
-	/**
1154
-	 * @var boolean
1155
-	 */
1156
-	protected static $errorHandlingFUSE = FALSE;
1157
-
1158
-	/**
1159
-	 * @var callable|NULL
1160
-	 */
1161
-	protected static $errorHandler = NULL;
1162
-
1163
-	/**
1164
-	 * @var array
1165
-	 */
1166
-	protected static $aliases = array();
1167
-
1168
-	/**
1169
-	 * @var boolean
1170
-	 */
1171
-	protected static $autoResolve = FALSE;
1172
-
1173
-	/**
1174
-	 * Sets the error mode for FUSE.
1175
-	 * What to do if a FUSE model method does not exist?
1176
-	 * You can set the following options:
1177
-	 *
1178
-	 * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
1179
-	 * OODBBean::C_ERR_LOG, logs the incident using error_log
1180
-	 * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
1181
-	 * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
1182
-	 * OODBBean::C_ERR_EXCEPTION, throws an exception
1183
-	 * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
1184
-	 * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
1185
-	 *
1186
-	 * Custom handler method signature: handler( array (
1187
-	 * 	'message' => string
1188
-	 * 	'bean' => OODBBean
1189
-	 * 	'method' => string
1190
-	 * ) )
1191
-	 *
1192
-	 * This method returns the old mode and handler as an array.
1193
-	 *
1194
-	 * @param integer       $mode mode
1195
-	 * @param callable|NULL $func custom handler
1196
-	 *
1197
-	 * @return array
1198
-	 */
1199
-	public static function setErrorHandlingFUSE($mode, $func = NULL) {
1200
-		if (
1201
-			   $mode !== self::C_ERR_IGNORE
1202
-			&& $mode !== self::C_ERR_LOG
1203
-			&& $mode !== self::C_ERR_NOTICE
1204
-			&& $mode !== self::C_ERR_WARN
1205
-			&& $mode !== self::C_ERR_EXCEPTION
1206
-			&& $mode !== self::C_ERR_FUNC
1207
-			&& $mode !== self::C_ERR_FATAL
1208
-		) throw new \Exception( 'Invalid error mode selected' );
1209
-
1210
-		if ( $mode === self::C_ERR_FUNC && !is_callable( $func ) ) {
1211
-			throw new \Exception( 'Invalid error handler' );
1212
-		}
1213
-
1214
-		$old = array( self::$errorHandlingFUSE, self::$errorHandler );
1215
-		self::$errorHandlingFUSE = $mode;
1216
-		if ( is_callable( $func ) ) {
1217
-			self::$errorHandler = $func;
1218
-		} else {
1219
-			self::$errorHandler = NULL;
1220
-		}
1221
-		return $old;
1222
-	}
1223
-
1224
-	/**
1225
-	 * Sets aliases.
1226
-	 *
1227
-	 * @param array $list
1228
-	 *
1229
-	 * @return void
1230
-	 */
1231
-	public static function aliases( $list )
1232
-	{
1233
-		self::$aliases = $list;
1234
-	}
1235
-
1236
-	/**
1237
-	 * Enables or disables auto-resolving fetch types.
1238
-	 * Auto-resolving aliased parent beans is convenient but can
1239
-	 * be slower and can create infinite recursion if you
1240
-	 * used aliases to break cyclic relations in your domain.
1241
-	 *
1242
-	 * @param boolean $automatic TRUE to enable automatic resolving aliased parents
1243
-	 *
1244
-	 * @return void
1245
-	 */
1246
-	public static function setAutoResolve( $automatic = TRUE )
1247
-	{
1248
-		self::$autoResolve = (boolean) $automatic;
1249
-	}
1250
-
1251
-	/**
1252
-	 * This is where the real properties of the bean live. They are stored and retrieved
1253
-	 * by the magic getter and setter (__get and __set).
1254
-	 *
1255
-	 * @var array $properties
1256
-	 */
1257
-	protected $properties = array();
1258
-
1259
-	/**
1260
-	 * Here we keep the meta data of a bean.
1261
-	 *
1262
-	 * @var array
1263
-	 */
1264
-	protected $__info = array();
1265
-
1266
-	/**
1267
-	 * The BeanHelper allows the bean to access the toolbox objects to implement
1268
-	 * rich functionality, otherwise you would have to do everything with R or
1269
-	 * external objects.
1270
-	 *
1271
-	 * @var BeanHelper
1272
-	 */
1273
-	protected $beanHelper = NULL;
1274
-
1275
-	/**
1276
-	 * @var null
1277
-	 */
1278
-	protected $fetchType = NULL;
1279
-
1280
-	/**
1281
-	 * @var string
1282
-	 */
1283
-	protected $withSql = '';
1284
-
1285
-	/**
1286
-	 * @var array
1287
-	 */
1288
-	protected $withParams = array();
1289
-
1290
-	/**
1291
-	 * @var string
1292
-	 */
1293
-	protected $aliasName = NULL;
1294
-
1295
-	/**
1296
-	 * @var string
1297
-	 */
1298
-	protected $via = NULL;
1299
-
1300
-	/**
1301
-	 * @var boolean
1302
-	 */
1303
-	protected $noLoad = FALSE;
1304
-
1305
-	/**
1306
-	 * @var boolean
1307
-	 */
1308
-	protected $all = FALSE;
1309
-
1310
-	/**
1311
-	 * Sets a meta property for all beans. This is a quicker way to set
1312
-	 * the meta properties for a collection of beans because this method
1313
-	 * can directly access the property arrays of the beans.
1314
-	 * This method returns the beans.
1315
-	 *
1316
-	 * @param array  $beans    beans to set the meta property of
1317
-	 * @param string $property property to set
1318
-	 * @param mixed  $value    value
1319
-	 *
1320
-	 * @return array
1321
-	 */
1322
-	public static function setMetaAll( $beans, $property, $value )
1323
-	{
1324
-		foreach( $beans as $bean ) {
1325
-			if ( $bean instanceof OODBBean ) $bean->__info[ $property ] = $value;
1326
-		}
1327
-
1328
-		return $beans;
1329
-	}
1330
-
1331
-	/**
1332
-	 * Parses the join in the with-snippet.
1333
-	 * For instance:
1334
-	 *
1335
-	 * $author
1336
-	 * 	->withCondition(' @joined.detail.title LIKE ? ')
1337
-	 *  ->ownBookList;
1338
-	 *
1339
-	 * will automatically join 'detail' on book to
1340
-	 * access the title field.
1341
-	 *
1342
-	 * @note this feature requires Narrow Field Mode and Join Feature
1343
-	 * to be both activated (default).
1344
-	 *
1345
-	 * @param string $type the source type for the join
1346
-	 *
1347
-	 * @return string $joinSql
1348
-	 */
1349
-	private function parseJoin( $type )
1350
-	{
1351
-		$joinSql = '';
1352
-		$joins = array();
1353
-		if ( strpos($this->withSql, '@joined.' ) !== FALSE ) {
1354
-			$writer   = $this->beanHelper->getToolBox()->getWriter();
1355
-			$oldParts = $parts = explode( '@joined.', $this->withSql );
1356
-			array_shift( $parts );
1357
-			foreach($parts as $part) {
1358
-				$explosion = explode( '.', $part );
1359
-				$joinInfo  = array_shift( $explosion );
1360
-				//Dont join more than once..
1361
-				if ( !isset( $joins[$joinInfo] ) ) {
1362
-					$joins[ $joinInfo ] = true;
1363
-					$joinSql  .= $writer->writeJoin( $type, $joinInfo, 'LEFT' );
1364
-				}
1365
-			}
1366
-			$this->withSql = implode( '', $oldParts );
1367
-			$joinSql      .= ' WHERE ';
1368
-		}
1369
-		return $joinSql;
1370
-	}
1371
-
1372
-	/**
1373
-	 * Internal method.
1374
-	 * Obtains a shared list for a certain type.
1375
-	 *
1376
-	 * @param string $type the name of the list you want to retrieve.
1377
-	 *
1378
-	 * @return array
1379
-	 */
1380
-	private function getSharedList( $type, $redbean, $toolbox )
1381
-	{
1382
-		$writer = $toolbox->getWriter();
1383
-
1384
-		if ( $this->via ) {
1385
-			$oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
1386
-			if ( $oldName !== $this->via ) {
1387
-				//set the new renaming rule
1388
-				$writer->renameAssocTable( $oldName, $this->via );
1389
-			}
1390
-			$this->via = NULL;
1391
-		}
1392
-
1393
-		$beans = array();
1394
-		if ($this->getID()) {
1395
-			$type             = $this->beau( $type );
1396
-			$assocManager     = $redbean->getAssociationManager();
1397
-			$beans            = $assocManager->related( $this, $type, $this->withSql, $this->withParams );
1398
-		}
1399
-
1400
-		$this->withSql    = '';
1401
-		$this->withParams = array();
1402
-
1403
-		return $beans;
1404
-	}
1405
-
1406
-	/**
1407
-	 * Internal method.
1408
-	 * Obtains the own list of a certain type.
1409
-	 *
1410
-	 * @param string      $type   name of the list you want to retrieve
1411
-	 * @param OODB        $oodb   The RB OODB object database instance
1412
-	 *
1413
-	 * @return array
1414
-	 */
1415
-	private function getOwnList( $type, $redbean )
1416
-	{
1417
-		$type = $this->beau( $type );
1418
-
1419
-		if ( $this->aliasName ) {
1420
-			$parentField = $this->aliasName;
1421
-			$myFieldLink = $parentField . '_id';
1422
-
1423
-			$this->__info['sys.alias.' . $type] = $this->aliasName;
1424
-
1425
-			$this->aliasName = NULL;
1426
-		} else {
1427
-			$parentField = $this->__info['type'];
1428
-			$myFieldLink = $parentField . '_id';
1429
-		}
1430
-
1431
-		$beans = array();
1432
-
1433
-		if ( $this->getID() ) {
1434
-
1435
-			$firstKey = NULL;
1436
-			if ( count( $this->withParams ) > 0 ) {
1437
-				reset( $this->withParams );
1438
-
1439
-				$firstKey = key( $this->withParams );
1440
-			}
1441
-
1442
-			$joinSql = $this->parseJoin( $type );
1443
-
1444
-			if ( !is_numeric( $firstKey ) || $firstKey === NULL ) {
1445
-				$bindings           = $this->withParams;
1446
-				$bindings[':slot0'] = $this->getID();
1447
-
1448
-				$beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
1449
-			} else {
1450
-				$bindings = array_merge( array( $this->getID() ), $this->withParams );
1451
-
1452
-				$beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
1453
-			}
1454
-		}
1455
-
1456
-		$this->withSql    = '';
1457
-		$this->withParams = array();
1458
-
1459
-		foreach ( $beans as $beanFromList ) {
1460
-			$beanFromList->__info['sys.parentcache.' . $parentField] = $this;
1461
-		}
1462
-
1463
-		return $beans;
1464
-	}
1465
-
1466
-	/**
1467
-	 * Initializes a bean. Used by OODB for dispensing beans.
1468
-	 * It is not recommended to use this method to initialize beans. Instead
1469
-	 * use the OODB object to dispense new beans. You can use this method
1470
-	 * if you build your own bean dispensing mechanism.
1471
-	 *
1472
-	 * @param string             $type       type of the new bean
1473
-	 * @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model
1474
-	 *
1475
-	 * @return void
1476
-	 */
1477
-	public function initializeForDispense( $type, BeanHelper $beanhelper )
1478
-	{
1479
-		$this->beanHelper         = $beanhelper;
1480
-		$this->__info['type']     = $type;
1481
-		$this->__info['sys.id']   = 'id';
1482
-		$this->__info['sys.orig'] = array( 'id' => 0 );
1483
-		$this->__info['tainted']  = TRUE;
1484
-		$this->__info['changed']  = TRUE;
1485
-		$this->properties['id']   = 0;
1486
-	}
1487
-
1488
-	/**
1489
-	 * Sets the Bean Helper. Normally the Bean Helper is set by OODB.
1490
-	 * Here you can change the Bean Helper. The Bean Helper is an object
1491
-	 * providing access to a toolbox for the bean necessary to retrieve
1492
-	 * nested beans (bean lists: ownBean, sharedBean) without the need to
1493
-	 * rely on static calls to the facade (or make this class dep. on OODB).
1494
-	 *
1495
-	 * @param BeanHelper $helper
1496
-	 *
1497
-	 * @return void
1498
-	 */
1499
-	public function setBeanHelper( BeanHelper $helper )
1500
-	{
1501
-		$this->beanHelper = $helper;
1502
-	}
1503
-
1504
-	/**
1505
-	 * Returns an\ArrayIterator so you can treat the bean like
1506
-	 * an array with the properties container as its contents.
1507
-	 * This method is meant for PHP and allows you to access beans as if
1508
-	 * they were arrays, i.e. using array notation:
1509
-	 *
1510
-	 * $bean[$key] = $value;
1511
-	 *
1512
-	 * Note that not all PHP functions work with the array interface.
1513
-	 *
1514
-	 * @return \ArrayIterator
1515
-	 */
1516
-	public function getIterator()
1517
-	{
1518
-		return new\ArrayIterator( $this->properties );
1519
-	}
1520
-
1521
-	/**
1522
-	 * Imports all values from an associative array $array. Chainable.
1523
-	 * This method imports the values in the first argument as bean
1524
-	 * propery and value pairs. Use the second parameter to provide a
1525
-	 * selection. If a selection array is passed, only the entries
1526
-	 * having keys mentioned in the selection array will be imported.
1527
-	 * Set the third parameter to TRUE to preserve spaces in selection keys.
1528
-	 *
1529
-	 * @param array        $array     what you want to import
1530
-	 * @param string|array $selection selection of values
1531
-	 * @param boolean      $notrim    if TRUE selection keys will NOT be trimmed
1532
-	 *
1533
-	 * @return OODBBean
1534
-	 */
1535
-	public function import( $array, $selection = FALSE, $notrim = FALSE )
1536
-	{
1537
-		if ( is_string( $selection ) ) {
1538
-			$selection = explode( ',', $selection );
1539
-		}
1540
-
1541
-		if ( !$notrim && is_array( $selection ) ) {
1542
-			foreach ( $selection as $key => $selected ) {
1543
-				$selection[$key] = trim( $selected );
1544
-			}
1545
-		}
1546
-
1547
-		foreach ( $array as $key => $value ) {
1548
-			if ( $key != '__info' ) {
1549
-				if ( !$selection || ( $selection && in_array( $key, $selection ) ) ) {
1550
-					if ( is_array($value ) ) {
1551
-						if ( isset( $value['_type'] ) ) {
1552
-							$bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] );
1553
-							unset( $value['_type'] );
1554
-							$bean->import($value);
1555
-							$this->$key = $bean;
1556
-						} else {
1557
-							$listBeans = array();
1558
-							foreach( $value as $listKey => $listItem ) {
1559
-								$bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] );
1560
-								unset( $listItem['_type'] );
1561
-								$bean->import($listItem);
1562
-								$list = &$this->$key;
1563
-								$list[ $listKey ] = $bean;
1564
-							}
1565
-						}
1566
-					} else {
1567
-						$this->$key = $value;
1568
-					}
1569
-				}
1570
-			}
1571
-		}
1572
-
1573
-		return $this;
1574
-	}
1575
-
1576
-	/**
1577
-	* Fast way to import a row.
1578
-	* Does not perform any checks.
1579
-	*
1580
-	* @param array $row a database row
1581
-	*
1582
-	* @return self
1583
-	*/
1584
-	public function importRow( $row )
1585
-	{
1586
-		$this->properties = $row;
1587
-		$this->__info['sys.orig'] = $row;
1588
-		$this->__info['changed'] = FALSE;
1589
-		return $this;
1590
-	}
1591
-
1592
-	/**
1593
-	 * Imports data from another bean. Chainable.
1594
-	 * Copies the properties from the source bean to the internal
1595
-	 * property list.
1596
-	 *
1597
-	 * @param OODBBean $sourceBean the source bean to take properties from
1598
-	 *
1599
-	 * @return OODBBean
1600
-	 */
1601
-	public function importFrom( OODBBean $sourceBean )
1602
-	{
1603
-		$this->__info['tainted'] = TRUE;
1604
-		$this->__info['changed'] = TRUE;
1605
-		$this->properties = $sourceBean->properties;
1606
-
1607
-		return $this;
1608
-	}
1609
-
1610
-	/**
1611
-	 * Injects the properties of another bean but keeps the original ID.
1612
-	 * Just like import() but keeps the original ID.
1613
-	 * Chainable.
1614
-	 *
1615
-	 * @param OODBBean $otherBean the bean whose properties you would like to copy
1616
-	 *
1617
-	 * @return OODBBean
1618
-	 */
1619
-	public function inject( OODBBean $otherBean )
1620
-	{
1621
-		$myID = $this->properties['id'];
1622
-
1623
-		$this->import( $otherBean->export() );
1624
-
1625
-		$this->id = $myID;
1626
-
1627
-		return $this;
1628
-	}
1629
-
1630
-	/**
1631
-	 * Exports the bean as an array.
1632
-	 * This function exports the contents of a bean to an array and returns
1633
-	 * the resulting array.
1634
-	 *
1635
-	 * @param boolean $meta    set to TRUE if you want to export meta data as well
1636
-	 * @param boolean $parents set to TRUE if you want to export parents as well
1637
-	 * @param boolean $onlyMe  set to TRUE if you want to export only this bean
1638
-	 * @param array   $filters optional whitelist for export
1639
-	 *
1640
-	 * @return array
1641
-	 */
1642
-	public function export( $meta = FALSE, $parents = FALSE, $onlyMe = FALSE, $filters = array() )
1643
-	{
1644
-		$arr = array();
1645
-
1646
-		if ( $parents ) {
1647
-			foreach ( $this as $key => $value ) {
1648
-				if ( substr( $key, -3 ) != '_id' ) continue;
1649
-
1650
-				$prop = substr( $key, 0, strlen( $key ) - 3 );
1651
-				$this->$prop;
1652
-			}
1653
-		}
1654
-
1655
-		$hasFilters = is_array( $filters ) && count( $filters );
1656
-
1657
-		foreach ( $this as $key => $value ) {
1658
-			if ( !$onlyMe && is_array( $value ) ) {
1659
-				$vn = array();
1660
-
1661
-				foreach ( $value as $i => $b ) {
1662
-					if ( !( $b instanceof OODBBean ) ) continue;
1663
-					$vn[] = $b->export( $meta, FALSE, FALSE, $filters );
1664
-					$value = $vn;
1665
-				}
1666
-			} elseif ( $value instanceof OODBBean ) {
1667
-				if ( $hasFilters ) {
1668
-					if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue;
1669
-				}
1670
-
1671
-				$value = $value->export( $meta, $parents, FALSE, $filters );
1672
-			}
1673
-
1674
-			$arr[$key] = $value;
1675
-		}
1676
-
1677
-		if ( $meta ) {
1678
-			$arr['__info'] = $this->__info;
1679
-		}
1680
-
1681
-		return $arr;
1682
-	}
1683
-
1684
-	/**
1685
-	 * Implements isset() function for use as an array.
1686
-	 *
1687
-	 * @param string $property name of the property you want to check
1688
-	 *
1689
-	 * @return boolean
1690
-	 */
1691
-	public function __isset( $property )
1692
-	{
1693
-		$property = $this->beau( $property );
1694
-
1695
-		if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
1696
-			$property = substr($property, 1);
1697
-		}
1698
-		return isset( $this->properties[$property] );
1699
-	}
1700
-
1701
-	/**
1702
-	 * Returns the ID of the bean no matter what the ID field is.
1703
-	 *
1704
-	 * @return string|null
1705
-	 */
1706
-	public function getID()
1707
-	{
1708
-		return ( isset( $this->properties['id'] ) ) ? (string) $this->properties['id'] : NULL;
1709
-	}
1710
-
1711
-	/**
1712
-	 * Unsets a property of a bean.
1713
-	 * Magic method, gets called implicitly when performing the unset() operation
1714
-	 * on a bean property.
1715
-	 * 
1716
-	 * @param  string $property property to unset
1717
-	 *
1718
-	 * @return void
1719
-	 */
1720
-	public function __unset( $property )
1721
-	{
1722
-		$property = $this->beau( $property );
1723
-
1724
-		if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
1725
-			$property = substr($property, 1);
1726
-		}
1727
-
1728
-		unset( $this->properties[$property] );
1729
-
1730
-		$shadowKey = 'sys.shadow.'.$property;
1731
-		if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] );
1732
-
1733
-		//also clear modifiers
1734
-		$this->withSql    = '';
1735
-		$this->withParams = array();
1736
-		$this->aliasName  = NULL;
1737
-		$this->fetchType  = NULL;
1738
-		$this->noLoad     = FALSE;
1739
-		$this->all        = FALSE;
1740
-		$this->via        = NULL;
1741
-
1742
-		return;
1743
-	}
1744
-
1745
-	/**
1746
-	 * Adds WHERE clause conditions to ownList retrieval.
1747
-	 * For instance to get the pages that belong to a book you would
1748
-	 * issue the following command: $book->ownPage
1749
-	 * However, to order these pages by number use:
1750
-	 *
1751
-	 * $book->with(' ORDER BY `number` ASC ')->ownPage
1752
-	 *
1753
-	 * the additional SQL snippet will be merged into the final
1754
-	 * query.
1755
-	 *
1756
-	 * @param string $sql SQL to be added to retrieval query.
1757
-	 * @param array       $bindings array with parameters to bind to SQL snippet
1758
-	 *
1759
-	 * @return OODBBean
1760
-	 */
1761
-	public function with( $sql, $bindings = array() )
1762
-	{
1763
-		$this->withSql    = $sql;
1764
-		$this->withParams = $bindings;
1765
-		return $this;
1766
-	}
1767
-
1768
-	/**
1769
-	 * Just like with(). Except that this method prepends the SQL query snippet
1770
-	 * with AND which makes it slightly more comfortable to use a conditional
1771
-	 * SQL snippet. For instance to filter an own-list with pages (belonging to
1772
-	 * a book) on specific chapters you can use:
1773
-	 *
1774
-	 * $book->withCondition(' chapter = 3 ')->ownPage
1775
-	 *
1776
-	 * This will return in the own list only the pages having 'chapter == 3'.
1777
-	 *
1778
-	 * @param string $sql      SQL to be added to retrieval query (prefixed by AND)
1779
-	 * @param array  $bindings array with parameters to bind to SQL snippet
1780
-	 *
1781
-	 * @return OODBBean
1782
-	 */
1783
-	public function withCondition( $sql, $bindings = array() )
1784
-	{
1785
-		$this->withSql    = ' AND ' . $sql;
1786
-		$this->withParams = $bindings;
1787
-		return $this;
1788
-	}
1789
-
1790
-	/**
1791
-	 * Tells the bean to (re)load the following list without any
1792
-	 * conditions. If you have an ownList or sharedList with a
1793
-	 * condition you can use this method to reload the entire list.
1794
-	 *
1795
-	 * Usage:
1796
-	 *
1797
-	 * $bean->with( ' LIMIT 3 ' )->ownPage; //Just 3
1798
-	 * $bean->all()->ownPage; //Reload all pages
1799
-	 *
1800
-	 * @return self
1801
-	 */
1802
-	public function all()
1803
-	{
1804
-		$this->all = TRUE;
1805
-		return $this;
1806
-	}
1807
-
1808
-	/**
1809
-	 * Tells the bean to only access the list but not load
1810
-	 * its contents. Use this if you only want to add something to a list
1811
-	 * and you have no interest in retrieving its contents from the database.
1812
-	 *
1813
-	 * @return self
1814
-	 */
1815
-	public function noLoad()
1816
-	{
1817
-		$this->noLoad = TRUE;
1818
-		return $this;
1819
-	}
1820
-
1821
-	/**
1822
-	 * Prepares an own-list to use an alias. This is best explained using
1823
-	 * an example. Imagine a project and a person. The project always involves
1824
-	 * two persons: a teacher and a student. The person beans have been aliased in this
1825
-	 * case, so to the project has a teacher_id pointing to a person, and a student_id
1826
-	 * also pointing to a person. Given a project, we obtain the teacher like this:
1827
-	 *
1828
-	 * $project->fetchAs('person')->teacher;
1829
-	 *
1830
-	 * Now, if we want all projects of a teacher we cant say:
1831
-	 *
1832
-	 * $teacher->ownProject
1833
-	 *
1834
-	 * because the $teacher is a bean of type 'person' and no project has been
1835
-	 * assigned to a person. Instead we use the alias() method like this:
1836
-	 *
1837
-	 * $teacher->alias('teacher')->ownProject
1838
-	 *
1839
-	 * now we get the projects associated with the person bean aliased as
1840
-	 * a teacher.
1841
-	 *
1842
-	 * @param string $aliasName the alias name to use
1843
-	 *
1844
-	 * @return OODBBean
1845
-	 */
1846
-	public function alias( $aliasName )
1847
-	{
1848
-		$this->aliasName = $this->beau( $aliasName );
1849
-
1850
-		return $this;
1851
-	}
1852
-
1853
-	/**
1854
-	 * Returns properties of bean as an array.
1855
-	 * This method returns the raw internal property list of the
1856
-	 * bean. Only use this method for optimization purposes. Otherwise
1857
-	 * use the export() method to export bean data to arrays.
1858
-	 *
1859
-	 * @return array
1860
-	 */
1861
-	public function getProperties()
1862
-	{
1863
-		return $this->properties;
1864
-	}
1865
-
1866
-	/**
1867
-	 * Returns properties of bean as an array.
1868
-	 * This method returns the raw internal property list of the
1869
-	 * bean. Only use this method for optimization purposes. Otherwise
1870
-	 * use the export() method to export bean data to arrays.
1871
-	 * This method returns an array with the properties array and
1872
-	 * the type (string).
1873
-	 *
1874
-	 * @return array
1875
-	 */
1876
-	public function getPropertiesAndType()
1877
-	{
1878
-		return array( $this->properties, $this->__info['type'] );
1879
-	}
1880
-
1881
-	/**
1882
-	 * Turns a camelcase property name into an underscored property name.
1883
-	 *
1884
-	 * Examples:
1885
-	 *    oneACLRoute -> one_acl_route
1886
-	 *    camelCase -> camel_case
1887
-	 *
1888
-	 * Also caches the result to improve performance.
1889
-	 *
1890
-	 * @param string $property
1891
-	 *
1892
-	 * @return string
1893
-	 */
1894
-	public function beau( $property )
1895
-	{
1896
-		static $beautifulColumns = array();
1897
-
1898
-		if ( ctype_lower( $property ) ) return $property;
1899
-
1900
-		if (
1901
-			( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )
1902
-			|| ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) )
1903
-			|| ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) )
1904
-		) {
1905
-
1906
-			$property = preg_replace( '/List$/', '', $property );
1907
-			return $property;
1908
-		}
1909
-
1910
-		if ( !isset( $beautifulColumns[$property] ) ) {
1911
-			$beautifulColumns[$property] = AQueryWriter::camelsSnake( $property );
1912
-		}
1913
-
1914
-		return $beautifulColumns[$property];
1915
-	}
1916
-
1917
-	/**
1918
-	 * Clears all modifiers.
1919
-	 *
1920
-	 * @return self
1921
-	 */
1922
-	public function clearModifiers()
1923
-	{
1924
-		$this->withSql    = '';
1925
-		$this->withParams = array();
1926
-		$this->aliasName  = NULL;
1927
-		$this->fetchType  = NULL;
1928
-		$this->noLoad     = FALSE;
1929
-		$this->all        = FALSE;
1930
-		$this->via        = NULL;
1931
-		return $this;
1932
-	}
1933
-
1934
-	/**
1935
-	 * Determines whether a list is opened in exclusive mode or not.
1936
-	 * If a list has been opened in exclusive mode this method will return TRUE,
1937
-	 * othwerwise it will return FALSE.
1938
-	 *
1939
-	 * @param string $listName name of the list to check
1940
-	 *
1941
-	 * @return boolean
1942
-	 */
1943
-	public function isListInExclusiveMode( $listName )
1944
-	{
1945
-		$listName = $this->beau( $listName );
1946
-
1947
-		if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) {
1948
-			$listName = substr($listName, 1);
1949
-		}
1950
-
1951
-		$listName = lcfirst( substr( $listName, 3 ) );
1952
-
1953
-		return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] );
1954
-	}
1955
-
1956
-	/**
1957
-	 * Magic Getter. Gets the value for a specific property in the bean.
1958
-	 * If the property does not exist this getter will make sure no error
1959
-	 * occurs. This is because RedBean allows you to query (probe) for
1960
-	 * properties. If the property can not be found this method will
1961
-	 * return NULL instead.
1962
-	 *
1963
-	 * @param string $property name of the property you wish to obtain the value of
1964
-	 *
1965
-	 * @return mixed
1966
-	 */
1967
-	public function &__get( $property )
1968
-	{
1969
-		$isEx          = FALSE;
1970
-		$isOwn         = FALSE;
1971
-		$isShared      = FALSE;
1972
-
1973
-		if ( !ctype_lower( $property ) ) {
1974
-			$property = $this->beau( $property );
1975
-			if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
1976
-				$property = substr($property, 1);
1977
-				$listName = lcfirst( substr( $property, 3 ) );
1978
-				$isEx     = TRUE;
1979
-				$isOwn    = TRUE;
1980
-				$this->__info['sys.exclusive-'.$listName] = TRUE;
1981
-			} elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
1982
-				$isOwn    = TRUE;
1983
-				$listName = lcfirst( substr( $property, 3 ) );
1984
-			} elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
1985
-				$isShared = TRUE;
1986
-			}
1987
-		}
1988
-
1989
-		$fieldLink      = $property . '_id';
1990
-		$exists         = isset( $this->properties[$property] );
1991
-
1992
-		//If not exists and no field link and no list, bail out.
1993
-		if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) {
1994
-
1995
-			$this->withSql    = '';
1996
-			$this->withParams = array();
1997
-			$this->aliasName  = NULL;
1998
-			$this->fetchType  = NULL;
1999
-			$this->noLoad     = FALSE;
2000
-			$this->all        = FALSE;
2001
-			$this->via        = NULL;
2002
-
2003
-			$NULL = NULL;
2004
-			return $NULL;
2005
-		}
2006
-
2007
-		$hasAlias       = (!is_null($this->aliasName));
2008
-		$differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
2009
-								($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2010
-		$hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2011
-		$hasAll         = (boolean) ($this->all);
2012
-
2013
-		//If exists and no list or exits and list not changed, bail out.
2014
-		if ( $exists && ((!$isOwn && !$isShared ) ||  (!$hasSQL && !$differentAlias && !$hasAll)) ) {
2015
-
2016
-			$this->withSql    = '';
2017
-			$this->withParams = array();
2018
-			$this->aliasName  = NULL;
2019
-			$this->fetchType  = NULL;
2020
-			$this->noLoad     = FALSE;
2021
-			$this->all        = FALSE;
2022
-			$this->via        = NULL;
2023
-			return $this->properties[$property];
2024
-		}
2025
-
2026
-
2027
-		list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2028
-
2029
-		if ( isset( $this->$fieldLink ) ) {
2030
-			$this->__info['tainted'] = TRUE;
2031
-
2032
-			if ( isset( $this->__info["sys.parentcache.$property"] ) ) {
2033
-				$bean = $this->__info["sys.parentcache.$property"];
2034
-			} else {
2035
-				if ( isset( self::$aliases[$property] ) ) {
2036
-					$type = self::$aliases[$property];
2037
-				} elseif ( $this->fetchType ) {
2038
-					$type = $this->fetchType;
2039
-					$this->fetchType = NULL;
2040
-				} else {
2041
-					$type = $property;
2042
-				}
2043
-				$bean = NULL;
2044
-				if ( !is_null( $this->properties[$fieldLink] ) ) {
2045
-					$bean = $redbean->load( $type, $this->properties[$fieldLink] );
2046
-					//If the IDs dont match, we failed to load, so try autoresolv in that case...
2047
-					if ( $bean->id !== $this->properties[$fieldLink] && self::$autoResolve ) {
2048
-						$type = $this->beanHelper->getToolbox()->getWriter()->inferFetchType( $this->__info['type'], $property );
2049
-						if ( !is_null( $type) ) {
2050
-							$bean = $redbean->load( $type, $this->properties[$fieldLink] );
2051
-							$this->__info["sys.autoresolved.{$property}"] = $type;
2052
-						}
2053
-					}
2054
-				}
2055
-			}
2056
-
2057
-			$this->properties[$property] = $bean;
2058
-			$this->withSql               = '';
2059
-			$this->withParams            = array();
2060
-			$this->aliasName             = NULL;
2061
-			$this->fetchType             = NULL;
2062
-			$this->noLoad                = FALSE;
2063
-			$this->all                   = FALSE;
2064
-			$this->via                   = NULL;
2065
-
2066
-			return $this->properties[$property];
2067
-
2068
-		}
2069
-		//Implicit: elseif ( $isOwn || $isShared ) {
2070
-		if ( $this->noLoad ) {
2071
-			$beans = array();
2072
-		} elseif ( $isOwn ) {
2073
-			$beans = $this->getOwnList( $listName, $redbean );
2074
-		} else {
2075
-			$beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2076
-		}
2077
-
2078
-		$this->properties[$property]          = $beans;
2079
-		$this->__info["sys.shadow.$property"] = $beans;
2080
-		$this->__info['tainted']              = TRUE;
2081
-
2082
-		$this->withSql    = '';
2083
-		$this->withParams = array();
2084
-		$this->aliasName  = NULL;
2085
-		$this->fetchType  = NULL;
2086
-		$this->noLoad     = FALSE;
2087
-		$this->all        = FALSE;
2088
-		$this->via        = NULL;
2089
-
2090
-		return $this->properties[$property];
2091
-	}
2092
-
2093
-	/**
2094
-	 * Magic Setter. Sets the value for a specific property.
2095
-	 * This setter acts as a hook for OODB to mark beans as tainted.
2096
-	 * The tainted meta property can be retrieved using getMeta("tainted").
2097
-	 * The tainted meta property indicates whether a bean has been modified and
2098
-	 * can be used in various caching mechanisms.
2099
-	 *
2100
-	 * @param string $property name of the property you wish to assign a value to
2101
-	 * @param  mixed $value    the value you want to assign
2102
-	 *
2103
-	 * @return void
2104
-	 *
2105
-	 * @throws Security
2106
-	 */
2107
-	public function __set( $property, $value )
2108
-	{
2109
-		$isEx          = FALSE;
2110
-		$isOwn         = FALSE;
2111
-		$isShared      = FALSE;
2112
-
2113
-		if ( !ctype_lower( $property ) ) {
2114
-			$property = $this->beau( $property );
2115
-			if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2116
-				$property = substr($property, 1);
2117
-				$listName = lcfirst( substr( $property, 3 ) );
2118
-				$isEx     = TRUE;
2119
-				$isOwn    = TRUE;
2120
-				$this->__info['sys.exclusive-'.$listName] = TRUE;
2121
-			} elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
2122
-				$isOwn    = TRUE;
2123
-				$listName = lcfirst( substr( $property, 3 ) );
2124
-			} elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
2125
-				$isShared = TRUE;
2126
-			}
2127
-		}
2128
-
2129
-		$hasAlias       = (!is_null($this->aliasName));
2130
-		$differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
2131
-								($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2132
-		$hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2133
-		$exists         = isset( $this->properties[$property] );
2134
-		$fieldLink      = $property . '_id';
2135
-
2136
-		if ( ($isOwn || $isShared) &&  (!$exists || $hasSQL || $differentAlias) ) {
2137
-
2138
-			if ( !$this->noLoad ) {
2139
-				list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2140
-				if ( $isOwn ) {
2141
-					$beans = $this->getOwnList( $listName, $redbean );
2142
-				} else {
2143
-					$beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2144
-				}
2145
-				$this->__info["sys.shadow.$property"] = $beans;
2146
-			}
2147
-		}
2148
-
2149
-		$this->withSql    = '';
2150
-		$this->withParams = array();
2151
-		$this->aliasName  = NULL;
2152
-		$this->fetchType  = NULL;
2153
-		$this->noLoad     = FALSE;
2154
-		$this->all        = FALSE;
2155
-		$this->via        = NULL;
2156
-
2157
-		$this->__info['tainted'] = TRUE;
2158
-		$this->__info['changed'] = TRUE;
2159
-
2160
-		if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) {
2161
-			if ( is_null( $value ) || $value === FALSE ) {
2162
-
2163
-				unset( $this->properties[ $property ]);
2164
-				$this->properties[ $fieldLink ] = NULL;
2165
-
2166
-				return;
2167
-			} else {
2168
-				throw new RedException( 'Cannot cast to bean.' );
2169
-			}
2170
-		}
2171
-
2172
-		if ( $value === FALSE ) {
2173
-			$value = '0';
2174
-		} elseif ( $value === TRUE ) {
2175
-			$value = '1';
2176
-		} elseif ( $value instanceof \DateTime ) {
2177
-			$value = $value->format( 'Y-m-d H:i:s' );
2178
-		}
2179
-
2180
-		$this->properties[$property] = $value;
2181
-	}
2182
-
2183
-	/**
2184
-	 * Sets a property directly, for internal use only.
2185
-	 *
2186
-	 * @param string  $property     property
2187
-	 * @param mixed   $value        value
2188
-	 * @param boolean $updateShadow whether you want to update the shadow
2189
-	 * @param boolean $taint        whether you want to mark the bean as tainted
2190
-	 *
2191
-	 * @return void
2192
-	 */
2193
-	public function setProperty( $property, $value, $updateShadow = FALSE, $taint = FALSE )
2194
-	{
2195
-		$this->properties[$property] = $value;
2196
-
2197
-		if ( $updateShadow ) {
2198
-			$this->__info['sys.shadow.' . $property] = $value;
2199
-		}
2200
-
2201
-		if ( $taint ) {
2202
-			$this->__info['tainted'] = TRUE;
2203
-			$this->__info['changed'] = TRUE;
2204
-		}
2205
-	}
2206
-
2207
-	/**
2208
-	 * Returns the value of a meta property. A meta property
2209
-	 * contains additional information about the bean object that will not
2210
-	 * be stored in the database. Meta information is used to instruct
2211
-	 * RedBeanPHP as well as other systems how to deal with the bean.
2212
-	 * If the property cannot be found this getter will return NULL instead.
2213
-	 *
2214
-	 * Example:
2215
-	 *
2216
-	 * $bean->setMeta( 'flush-cache', TRUE );
2217
-	 *
2218
-	 * RedBeanPHP also stores meta data in beans, this meta data uses
2219
-	 * keys prefixed with 'sys.' (system).
2220
-	 *
2221
-	 * @param string $path    path
2222
-	 * @param mixed  $default default value
2223
-	 *
2224
-	 * @return mixed
2225
-	 */
2226
-	public function getMeta( $path, $default = NULL )
2227
-	{
2228
-		return ( isset( $this->__info[$path] ) ) ? $this->__info[$path] : $default;
2229
-	}
2230
-
2231
-	/**
2232
-	 * Gets and unsets a meta property.
2233
-	 * Moves a meta property out of the bean.
2234
-	 * This is a short-cut method that can be used instead
2235
-	 * of combining a get/unset.
2236
-	 *
2237
-	 * @param string $path    path
2238
-	 * @param mixed  $default default value
2239
-	 *
2240
-	 * @return mixed
2241
-	 */
2242
-	public function moveMeta( $path, $value = NULL )
2243
-	{
2244
-		if ( isset( $this->__info[$path] ) ) {
2245
-			$value = $this->__info[ $path ];
2246
-			unset( $this->__info[ $path ] );
2247
-		}
2248
-		return $value;
2249
-	}
2250
-
2251
-	/**
2252
-	 * Stores a value in the specified Meta information property.
2253
-	 * The first argument should be the key to store the value under,
2254
-	 * the second argument should be the value. It is common to use
2255
-	 * a path-like notation for meta data in RedBeanPHP like:
2256
-	 * 'my.meta.data', however the dots are purely for readability, the
2257
-	 * meta data methods do not store nested structures or hierarchies.
2258
-	 *
2259
-	 * @param string $path  path / key to store value under
2260
-	 * @param mixed  $value value to store in bean (not in database) as meta data
2261
-	 *
2262
-	 * @return OODBBean
2263
-	 */
2264
-	public function setMeta( $path, $value )
2265
-	{
2266
-		$this->__info[$path] = $value;
2267
-
2268
-		return $this;
2269
-	}
2270
-
2271
-	/**
2272
-	 * Copies the meta information of the specified bean
2273
-	 * This is a convenience method to enable you to
2274
-	 * exchange meta information easily.
2275
-	 *
2276
-	 * @param OODBBean $bean
2277
-	 *
2278
-	 * @return OODBBean
2279
-	 */
2280
-	public function copyMetaFrom( OODBBean $bean )
2281
-	{
2282
-		$this->__info = $bean->__info;
2283
-
2284
-		return $this;
2285
-	}
2286
-
2287
-	/**
2288
-	 * Sends the call to the registered model.
2289
-	 *
2290
-	 * @param string $method name of the method
2291
-	 * @param array  $args   argument list
2292
-	 *
2293
-	 * @return mixed
2294
-	 */
2295
-	public function __call( $method, $args )
2296
-	{
2297
-		if ( !isset( $this->__info['model'] ) ) {
2298
-			$model = $this->beanHelper->getModelForBean( $this );
2299
-
2300
-			if ( !$model ) {
2301
-				return NULL;
2302
-			}
2303
-
2304
-			$this->__info['model'] = $model;
2305
-		}
2306
-		if ( !method_exists( $this->__info['model'], $method ) ) {
2307
-
2308
-			if ( self::$errorHandlingFUSE === FALSE ) {
2309
-				return NULL;
2310
-			}
2311
-
2312
-			if ( in_array( $method, array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ), TRUE ) ) {
2313
-				return NULL;
2314
-			}
2315
-
2316
-			$message = "FUSE: method does not exist in model: $method";
2317
-			if ( self::$errorHandlingFUSE === self::C_ERR_LOG ) {
2318
-				error_log( $message );
2319
-				return NULL;
2320
-			} elseif ( self::$errorHandlingFUSE === self::C_ERR_NOTICE ) {
2321
-				trigger_error( $message, E_USER_NOTICE );
2322
-				return NULL;
2323
-			} elseif ( self::$errorHandlingFUSE === self::C_ERR_WARN ) {
2324
-				trigger_error( $message, E_USER_WARNING );
2325
-				return NULL;
2326
-			} elseif ( self::$errorHandlingFUSE === self::C_ERR_EXCEPTION ) {
2327
-				throw new \Exception( $message );
2328
-			} elseif ( self::$errorHandlingFUSE === self::C_ERR_FUNC ) {
2329
-				$func = self::$errorHandler;
2330
-				return $func(array(
2331
-					'message' => $message,
2332
-					'method' => $method,
2333
-					'args' => $args,
2334
-					'bean' => $this
2335
-				));
2336
-			}
2337
-			trigger_error( $message, E_USER_ERROR );
2338
-			return NULL;
2339
-		}
2340
-
2341
-		return call_user_func_array( array( $this->__info['model'], $method ), $args );
2342
-	}
2343
-
2344
-	/**
2345
-	 * Implementation of __toString Method
2346
-	 * Routes call to Model. If the model implements a __toString() method this
2347
-	 * method will be called and the result will be returned. In case of an
2348
-	 * echo-statement this result will be printed. If the model does not
2349
-	 * implement a __toString method, this method will return a JSON
2350
-	 * representation of the current bean.
2351
-	 *
2352
-	 * @return string
2353
-	 */
2354
-	public function __toString()
2355
-	{
2356
-		$string = $this->__call( '__toString', array() );
2357
-
2358
-		if ( $string === NULL ) {
2359
-			return json_encode( $this->properties );
2360
-		} else {
2361
-			return $string;
2362
-		}
2363
-	}
2364
-
2365
-	/**
2366
-	 * Implementation of Array Access Interface, you can access bean objects
2367
-	 * like an array.
2368
-	 * Call gets routed to __set.
2369
-	 *
2370
-	 * @param  mixed $offset offset string
2371
-	 * @param  mixed $value  value
2372
-	 *
2373
-	 * @return void
2374
-	 */
2375
-	public function offsetSet( $offset, $value )
2376
-	{
2377
-		$this->__set( $offset, $value );
2378
-	}
2379
-
2380
-	/**
2381
-	 * Implementation of Array Access Interface, you can access bean objects
2382
-	 * like an array.
2383
-	 *
2384
-	 * Array functions do not reveal x-own-lists and list-alias because
2385
-	 * you dont want duplicate entries in foreach-loops.
2386
-	 * Also offers a slight performance improvement for array access.
2387
-	 *
2388
-	 * @param  mixed $offset property
2389
-	 *
2390
-	 * @return boolean
2391
-	 */
2392
-	public function offsetExists( $offset )
2393
-	{
2394
-		return $this->__isset( $offset );
2395
-	}
2396
-
2397
-	/**
2398
-	 * Implementation of Array Access Interface, you can access bean objects
2399
-	 * like an array.
2400
-	 * Unsets a value from the array/bean.
2401
-	 *
2402
-	 * Array functions do not reveal x-own-lists and list-alias because
2403
-	 * you dont want duplicate entries in foreach-loops.
2404
-	 * Also offers a slight performance improvement for array access.
2405
-	 *
2406
-	 * @param  mixed $offset property
2407
-	 *
2408
-	 * @return void
2409
-	 */
2410
-	public function offsetUnset( $offset )
2411
-	{
2412
-		$this->__unset( $offset );
2413
-	}
2414
-
2415
-	/**
2416
-	 * Implementation of Array Access Interface, you can access bean objects
2417
-	 * like an array.
2418
-	 * Returns value of a property.
2419
-	 *
2420
-	 * Array functions do not reveal x-own-lists and list-alias because
2421
-	 * you dont want duplicate entries in foreach-loops.
2422
-	 * Also offers a slight performance improvement for array access.
2423
-	 *
2424
-	 * @param  mixed $offset property
2425
-	 *
2426
-	 * @return mixed
2427
-	 */
2428
-	public function &offsetGet( $offset )
2429
-	{
2430
-		return $this->__get( $offset );
2431
-	}
2432
-
2433
-	/**
2434
-	 * Chainable method to cast a certain ID to a bean; for instance:
2435
-	 * $person = $club->fetchAs('person')->member;
2436
-	 * This will load a bean of type person using member_id as ID.
2437
-	 *
2438
-	 * @param  string $type preferred fetch type
2439
-	 *
2440
-	 * @return OODBBean
2441
-	 */
2442
-	public function fetchAs( $type )
2443
-	{
2444
-		$this->fetchType = $type;
2445
-
2446
-		return $this;
2447
-	}
2448
-
2449
-	/**
2450
-	 * For polymorphic bean relations.
2451
-	 * Same as fetchAs but uses a column instead of a direct value.
2452
-	 *
2453
-	 * @param string $column
2454
-	 *
2455
-	 * @return OODBBean
2456
-	 */
2457
-	public function poly( $field )
2458
-	{
2459
-		return $this->fetchAs( $this->$field );
2460
-	}
2461
-
2462
-	/**
2463
-	 * Traverses a bean property with the specified function.
2464
-	 * Recursively iterates through the property invoking the
2465
-	 * function for each bean along the way passing the bean to it.
2466
-	 *
2467
-	 * Can be used together with with, withCondition, alias and fetchAs.
2468
-	 *
2469
-	 * @param string  $property property
2470
-	 * @param closure $function function
2471
-	 *
2472
-	 * @return OODBBean
2473
-	 */
2474
-	public function traverse( $property, $function, $maxDepth = NULL )
2475
-	{
2476
-		$this->via = NULL;
2477
-		if ( strpos( $property, 'shared' ) !== FALSE ) {
2478
-			throw new RedException( 'Traverse only works with (x)own-lists.' );
2479
-		}
2480
-
2481
-		if ( !is_null( $maxDepth ) ) {
2482
-			if ( !$maxDepth-- ) return $this;
2483
-		}
2484
-
2485
-		$oldFetchType = $this->fetchType;
2486
-		$oldAliasName = $this->aliasName;
2487
-		$oldWith      = $this->withSql;
2488
-		$oldBindings  = $this->withParams;
2489
-
2490
-		$beans = $this->$property;
2491
-
2492
-		if ( $beans === NULL ) return $this;
2493
-
2494
-		if ( !is_array( $beans ) ) $beans = array( $beans );
2495
-
2496
-		foreach( $beans as $bean ) {
2497
-
2498
-			$function( $bean );
2499
-
2500
-			$bean->fetchType  = $oldFetchType;
2501
-			$bean->aliasName  = $oldAliasName;
2502
-			$bean->withSql    = $oldWith;
2503
-			$bean->withParams = $oldBindings;
2504
-
2505
-			$bean->traverse( $property, $function, $maxDepth );
2506
-		}
2507
-
2508
-		return $this;
2509
-	}
2510
-
2511
-	/**
2512
-	 * Implementation of\Countable interface. Makes it possible to use
2513
-	 * count() function on a bean.
2514
-	 *
2515
-	 * @return integer
2516
-	 */
2517
-	public function count()
2518
-	{
2519
-		return count( $this->properties );
2520
-	}
2521
-
2522
-	/**
2523
-	 * Checks whether a bean is empty or not.
2524
-	 * A bean is empty if it has no other properties than the id field OR
2525
-	 * if all the other property are empty().
2526
-	 *
2527
-	 * @return boolean
2528
-	 */
2529
-	public function isEmpty()
2530
-	{
2531
-		$empty = TRUE;
2532
-		foreach ( $this->properties as $key => $value ) {
2533
-			if ( $key == 'id' ) {
2534
-				continue;
2535
-			}
2536
-			if ( !empty( $value ) ) {
2537
-				$empty = FALSE;
2538
-			}
2539
-		}
2540
-
2541
-		return $empty;
2542
-	}
2543
-
2544
-	/**
2545
-	 * Chainable setter.
2546
-	 *
2547
-	 * @param string $property the property of the bean
2548
-	 * @param mixed  $value    the value you want to set
2549
-	 *
2550
-	 * @return OODBBean
2551
-	 */
2552
-	public function setAttr( $property, $value )
2553
-	{
2554
-		$this->$property = $value;
2555
-
2556
-		return $this;
2557
-	}
2558
-
2559
-	/**
2560
-	 * Comfort method.
2561
-	 * Unsets all properties in array.
2562
-	 *
2563
-	 * @param array $properties properties you want to unset.
2564
-	 *
2565
-	 * @return OODBBean
2566
-	 */
2567
-	public function unsetAll( $properties )
2568
-	{
2569
-		foreach ( $properties as $prop ) {
2570
-			if ( isset( $this->properties[$prop] ) ) {
2571
-				unset( $this->properties[$prop] );
2572
-			}
2573
-		}
2574
-
2575
-		return $this;
2576
-	}
2577
-
2578
-	/**
2579
-	 * Returns original (old) value of a property.
2580
-	 * You can use this method to see what has changed in a
2581
-	 * bean.
2582
-	 *
2583
-	 * @param string $property name of the property you want the old value of
2584
-	 *
2585
-	 * @return mixed
2586
-	 */
2587
-	public function old( $property )
2588
-	{
2589
-		$old = $this->getMeta( 'sys.orig', array() );
2590
-
2591
-		if ( array_key_exists( $property, $old ) ) {
2592
-			return $old[$property];
2593
-		}
2594
-
2595
-		return NULL;
2596
-	}
2597
-
2598
-	/**
2599
-	 * Convenience method.
2600
-	 * Returns TRUE if the bean has been changed, or FALSE otherwise.
2601
-	 * Same as $bean->getMeta('tainted');
2602
-	 * Note that a bean becomes tainted as soon as you retrieve a list from
2603
-	 * the bean. This is because the bean lists are arrays and the bean cannot
2604
-	 * determine whether you have made modifications to a list so RedBeanPHP
2605
-	 * will mark the whole bean as tainted.
2606
-	 *
2607
-	 * @return boolean
2608
-	 */
2609
-	public function isTainted()
2610
-	{
2611
-		return $this->getMeta( 'tainted' );
2612
-	}
2613
-
2614
-	/**
2615
-	 * Returns TRUE if the value of a certain property of the bean has been changed and
2616
-	 * FALSE otherwise.
2617
-	 *
2618
-	 * Note that this method will return TRUE if applied to a loaded list.
2619
-	 * Also note that this method keeps track of the bean's history regardless whether
2620
-	 * it has been stored or not. Storing a bean does not undo it's history,
2621
-	 * to clean the history of a bean use: clearHistory().
2622
-	 *
2623
-	 * @param string  $property  name of the property you want the change-status of
2624
-	 *
2625
-	 * @return boolean
2626
-	 */
2627
-	public function hasChanged( $property )
2628
-	{
2629
-		return ( array_key_exists( $property, $this->properties ) ) ?
2630
-			$this->old( $property ) != $this->properties[$property] : FALSE;
2631
-	}
2632
-
2633
-	/**
2634
-	 * Returns TRUE if the specified list exists, has been loaded and has been changed:
2635
-	 * beans have been added or deleted. This method will not tell you anything about
2636
-	 * the state of the beans in the list.
2637
-	 *
2638
-	 * @param string $property name of the list to check
2639
-	 *
2640
-	 * @return boolean
2641
-	 */
2642
-	public function hasListChanged( $property )
2643
-	{
2644
-		if ( !array_key_exists( $property, $this->properties ) ) return FALSE;
2645
-		$diffAdded = array_diff_assoc( $this->properties[$property], $this->__info['sys.shadow.'.$property] );
2646
-		if ( count( $diffAdded ) ) return TRUE;
2647
-		$diffMissing = array_diff_assoc( $this->__info['sys.shadow.'.$property], $this->properties[$property] );
2648
-		if ( count( $diffMissing ) ) return TRUE;
2649
-		return FALSE;
2650
-	}
2651
-
2652
-	/**
2653
-	 * Clears (syncs) the history of the bean.
2654
-	 * Resets all shadow values of the bean to their current value.
2655
-	 *
2656
-	 * @return self
2657
-	 */
2658
-	public function clearHistory()
2659
-	{
2660
-		$this->__info['sys.orig'] = array();
2661
-		foreach( $this->properties as $key => $value ) {
2662
-			if ( is_scalar($value) ) {
2663
-				$this->__info['sys.orig'][$key] = $value;
2664
-			} else {
2665
-				$this->__info['sys.shadow.'.$key] = $value;
2666
-			}
2667
-		}
2668
-		return $this;
2669
-	}
2670
-
2671
-	/**
2672
-	 * Creates a N-M relation by linking an intermediate bean.
2673
-	 * This method can be used to quickly connect beans using indirect
2674
-	 * relations. For instance, given an album and a song you can connect the two
2675
-	 * using a track with a number like this:
2676
-	 *
2677
-	 * Usage:
2678
-	 *
2679
-	 * $album->link('track', array('number'=>1))->song = $song;
2680
-	 *
2681
-	 * or:
2682
-	 *
2683
-	 * $album->link($trackBean)->song = $song;
2684
-	 *
2685
-	 * What this method does is adding the link bean to the own-list, in this case
2686
-	 * ownTrack. If the first argument is a string and the second is an array or
2687
-	 * a JSON string then the linking bean gets dispensed on-the-fly as seen in
2688
-	 * example #1. After preparing the linking bean, the bean is returned thus
2689
-	 * allowing the chained setter: ->song = $song.
2690
-	 *
2691
-	 * @param string|OODBBean $type          type of bean to dispense or the full bean
2692
-	 * @param string|array            $qualification JSON string or array (optional)
2693
-	 *
2694
-	 * @return OODBBean
2695
-	 */
2696
-	public function link( $typeOrBean, $qualification = array() )
2697
-	{
2698
-		if ( is_string( $typeOrBean ) ) {
2699
-
2700
-			$typeOrBean = AQueryWriter::camelsSnake( $typeOrBean );
2701
-
2702
-			$bean = $this->beanHelper->getToolBox()->getRedBean()->dispense( $typeOrBean );
2703
-
2704
-			if ( is_string( $qualification ) ) {
2705
-				$data = json_decode( $qualification, TRUE );
2706
-			} else {
2707
-				$data = $qualification;
2708
-			}
2709
-
2710
-			foreach ( $data as $key => $value ) {
2711
-				$bean->$key = $value;
2712
-			}
2713
-		} else {
2714
-			$bean = $typeOrBean;
2715
-		}
2716
-
2717
-		$list = 'own' . ucfirst( $bean->getMeta( 'type' ) );
2718
-
2719
-		array_push( $this->$list, $bean );
2720
-
2721
-		return $bean;
2722
-	}
2723
-
2724
-	/**
2725
-	 * Returns the same bean freshly loaded from the database.
2726
-	 *
2727
-	 * @return OODBBean
2728
-	 */
2729
-	public function fresh()
2730
-	{
2731
-		return $this->beanHelper->getToolbox()->getRedBean()->load( $this->getMeta( 'type' ), $this->properties['id'] );
2732
-	}
2733
-
2734
-	/**
2735
-	 * Registers a association renaming globally.
2736
-	 *
2737
-	 * @param string $via type you wish to use for shared lists
2738
-	 *
2739
-	 * @return OODBBean
2740
-	 */
2741
-	public function via( $via )
2742
-	{
2743
-		$this->via = AQueryWriter::camelsSnake( $via );
2744
-
2745
-		return $this;
2746
-	}
2747
-
2748
-	/**
2749
-	 * Counts all own beans of type $type.
2750
-	 * Also works with alias(), with() and withCondition().
2751
-	 *
2752
-	 * @param string $type the type of bean you want to count
2753
-	 *
2754
-	 * @return integer
2755
-	 */
2756
-	public function countOwn( $type )
2757
-	{
2758
-		$type = $this->beau( $type );
2759
-
2760
-		if ( $this->aliasName ) {
2761
-			$myFieldLink     = $this->aliasName . '_id';
2762
-
2763
-			$this->aliasName = NULL;
2764
-		} else {
2765
-			$myFieldLink = $this->__info['type'] . '_id';
2766
-		}
2767
-
2768
-		$count = 0;
2769
-
2770
-		if ( $this->getID() ) {
2771
-
2772
-			$firstKey = NULL;
2773
-			if ( count( $this->withParams ) > 0 ) {
2774
-				reset( $this->withParams );
2775
-				$firstKey = key( $this->withParams );
2776
-			}
2777
-
2778
-			$joinSql = $this->parseJoin( $type );
2779
-
2780
-			if ( !is_numeric( $firstKey ) || $firstKey === NULL ) {
2781
-					$bindings           = $this->withParams;
2782
-					$bindings[':slot0'] = $this->getID();
2783
-					$count              = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
2784
-			} else {
2785
-					$bindings = array_merge( array( $this->getID() ), $this->withParams );
2786
-					$count    = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
2787
-			}
2788
-
2789
-		}
2790
-
2791
-		$this->clearModifiers();
2792
-		return (int) $count;
2793
-	}
2794
-
2795
-	/**
2796
-	 * Counts all shared beans of type $type.
2797
-	 * Also works with via(), with() and withCondition().
2798
-	 *
2799
-	 * @param string $type type of bean you wish to count
2800
-	 *
2801
-	 * @return integer
2802
-	 */
2803
-	public function countShared( $type )
2804
-	{
2805
-		$toolbox = $this->beanHelper->getToolbox();
2806
-		$redbean = $toolbox->getRedBean();
2807
-		$writer  = $toolbox->getWriter();
2808
-
2809
-		if ( $this->via ) {
2810
-			$oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
2811
-
2812
-			if ( $oldName !== $this->via ) {
2813
-				//set the new renaming rule
2814
-				$writer->renameAssocTable( $oldName, $this->via );
2815
-				$this->via = NULL;
2816
-			}
2817
-		}
2818
-
2819
-		$type  = $this->beau( $type );
2820
-		$count = 0;
2821
-
2822
-		if ( $this->getID() ) {
2823
-			$count = $redbean->getAssociationManager()->relatedCount( $this, $type, $this->withSql, $this->withParams, TRUE );
2824
-		}
2825
-
2826
-		$this->clearModifiers();
2827
-		return (integer) $count;
2828
-	}
2829
-
2830
-	/**
2831
-	 * Iterates through the specified own-list and
2832
-	 * fetches all properties (with their type) and
2833
-	 * returns the references.
2834
-	 * Use this method to quickly load indirectly related
2835
-	 * beans in an own-list. Whenever you cannot use a
2836
-	 * shared-list this method offers the same convenience
2837
-	 * by aggregating the parent beans of all children in
2838
-	 * the specified own-list.
2839
-	 *
2840
-	 * Example:
2841
-	 *
2842
-	 * $quest->aggr( 'xownQuestTarget', 'target', 'quest' );
2843
-	 *
2844
-	 * Loads (in batch) and returns references to all
2845
-	 * quest beans residing in the $questTarget->target properties
2846
-	 * of each element in the xownQuestTargetList.
2847
-	 *
2848
-	 * @param string $list     the list you wish to process
2849
-	 * @param string $property the property to load
2850
-	 * @param string $type     the type of bean residing in this property (optional)
2851
-	 *
2852
-	 * @return array
2853
-	 */
2854
-	public function &aggr( $list, $property, $type = NULL )
2855
-	{
2856
-		$this->via = NULL;
2857
-		$ids = $beanIndex = $references = array();
2858
-
2859
-		if ( strlen( $list ) < 4 ) throw new RedException('Invalid own-list.');
2860
-		if ( strpos( $list, 'own') !== 0 ) throw new RedException('Only own-lists can be aggregated.');
2861
-		if ( !ctype_upper( substr( $list, 3, 1 ) ) ) throw new RedException('Invalid own-list.');
2862
-
2863
-		if ( is_null( $type ) ) $type = $property;
2864
-
2865
-		foreach( $this->$list as $bean ) {
2866
-			$field = $property . '_id';
2867
-			if ( isset( $bean->$field)  ) {
2868
-				$ids[] = $bean->$field;
2869
-				$beanIndex[$bean->$field] = $bean;
2870
-			}
2871
-		}
2872
-
2873
-		$beans = $this->beanHelper->getToolBox()->getRedBean()->batch( $type, $ids );
2874
-
2875
-		//now preload the beans as well
2876
-		foreach( $beans as $bean ) {
2877
-			$beanIndex[$bean->id]->setProperty( $property, $bean );
2878
-		}
2879
-
2880
-		foreach( $beanIndex as $indexedBean ) {
2881
-			$references[] = $indexedBean->$property;
2882
-		}
2883
-
2884
-		return $references;
2885
-	}
2886
-
2887
-	/**
2888
-	 * Tests whether the database identities of two beans are equal.
2889
-	 *
2890
-	 * @param OODBBean $bean other bean
2891
-	 *
2892
-	 * @return boolean
2893
-	 */
2894
-	public function equals(OODBBean $bean)
2895
-	{
2896
-		return (bool) (
2897
-			   ( (string) $this->properties['id'] === (string) $bean->properties['id'] )
2898
-			&& ( (string) $this->__info['type']   === (string) $bean->__info['type']   )
2899
-		);
2900
-	}
1142
+    /**
1143
+     * FUSE error modes.
1144
+     */
1145
+    const C_ERR_IGNORE    = FALSE;
1146
+    const C_ERR_LOG       = 1;
1147
+    const C_ERR_NOTICE    = 2;
1148
+    const C_ERR_WARN      = 3;
1149
+    const C_ERR_EXCEPTION = 4;
1150
+    const C_ERR_FUNC      = 5;
1151
+    const C_ERR_FATAL     = 6;
1152
+
1153
+    /**
1154
+     * @var boolean
1155
+     */
1156
+    protected static $errorHandlingFUSE = FALSE;
1157
+
1158
+    /**
1159
+     * @var callable|NULL
1160
+     */
1161
+    protected static $errorHandler = NULL;
1162
+
1163
+    /**
1164
+     * @var array
1165
+     */
1166
+    protected static $aliases = array();
1167
+
1168
+    /**
1169
+     * @var boolean
1170
+     */
1171
+    protected static $autoResolve = FALSE;
1172
+
1173
+    /**
1174
+     * Sets the error mode for FUSE.
1175
+     * What to do if a FUSE model method does not exist?
1176
+     * You can set the following options:
1177
+     *
1178
+     * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
1179
+     * OODBBean::C_ERR_LOG, logs the incident using error_log
1180
+     * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
1181
+     * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
1182
+     * OODBBean::C_ERR_EXCEPTION, throws an exception
1183
+     * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
1184
+     * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
1185
+     *
1186
+     * Custom handler method signature: handler( array (
1187
+     * 	'message' => string
1188
+     * 	'bean' => OODBBean
1189
+     * 	'method' => string
1190
+     * ) )
1191
+     *
1192
+     * This method returns the old mode and handler as an array.
1193
+     *
1194
+     * @param integer       $mode mode
1195
+     * @param callable|NULL $func custom handler
1196
+     *
1197
+     * @return array
1198
+     */
1199
+    public static function setErrorHandlingFUSE($mode, $func = NULL) {
1200
+        if (
1201
+                $mode !== self::C_ERR_IGNORE
1202
+            && $mode !== self::C_ERR_LOG
1203
+            && $mode !== self::C_ERR_NOTICE
1204
+            && $mode !== self::C_ERR_WARN
1205
+            && $mode !== self::C_ERR_EXCEPTION
1206
+            && $mode !== self::C_ERR_FUNC
1207
+            && $mode !== self::C_ERR_FATAL
1208
+        ) throw new \Exception( 'Invalid error mode selected' );
1209
+
1210
+        if ( $mode === self::C_ERR_FUNC && !is_callable( $func ) ) {
1211
+            throw new \Exception( 'Invalid error handler' );
1212
+        }
1213
+
1214
+        $old = array( self::$errorHandlingFUSE, self::$errorHandler );
1215
+        self::$errorHandlingFUSE = $mode;
1216
+        if ( is_callable( $func ) ) {
1217
+            self::$errorHandler = $func;
1218
+        } else {
1219
+            self::$errorHandler = NULL;
1220
+        }
1221
+        return $old;
1222
+    }
1223
+
1224
+    /**
1225
+     * Sets aliases.
1226
+     *
1227
+     * @param array $list
1228
+     *
1229
+     * @return void
1230
+     */
1231
+    public static function aliases( $list )
1232
+    {
1233
+        self::$aliases = $list;
1234
+    }
1235
+
1236
+    /**
1237
+     * Enables or disables auto-resolving fetch types.
1238
+     * Auto-resolving aliased parent beans is convenient but can
1239
+     * be slower and can create infinite recursion if you
1240
+     * used aliases to break cyclic relations in your domain.
1241
+     *
1242
+     * @param boolean $automatic TRUE to enable automatic resolving aliased parents
1243
+     *
1244
+     * @return void
1245
+     */
1246
+    public static function setAutoResolve( $automatic = TRUE )
1247
+    {
1248
+        self::$autoResolve = (boolean) $automatic;
1249
+    }
1250
+
1251
+    /**
1252
+     * This is where the real properties of the bean live. They are stored and retrieved
1253
+     * by the magic getter and setter (__get and __set).
1254
+     *
1255
+     * @var array $properties
1256
+     */
1257
+    protected $properties = array();
1258
+
1259
+    /**
1260
+     * Here we keep the meta data of a bean.
1261
+     *
1262
+     * @var array
1263
+     */
1264
+    protected $__info = array();
1265
+
1266
+    /**
1267
+     * The BeanHelper allows the bean to access the toolbox objects to implement
1268
+     * rich functionality, otherwise you would have to do everything with R or
1269
+     * external objects.
1270
+     *
1271
+     * @var BeanHelper
1272
+     */
1273
+    protected $beanHelper = NULL;
1274
+
1275
+    /**
1276
+     * @var null
1277
+     */
1278
+    protected $fetchType = NULL;
1279
+
1280
+    /**
1281
+     * @var string
1282
+     */
1283
+    protected $withSql = '';
1284
+
1285
+    /**
1286
+     * @var array
1287
+     */
1288
+    protected $withParams = array();
1289
+
1290
+    /**
1291
+     * @var string
1292
+     */
1293
+    protected $aliasName = NULL;
1294
+
1295
+    /**
1296
+     * @var string
1297
+     */
1298
+    protected $via = NULL;
1299
+
1300
+    /**
1301
+     * @var boolean
1302
+     */
1303
+    protected $noLoad = FALSE;
1304
+
1305
+    /**
1306
+     * @var boolean
1307
+     */
1308
+    protected $all = FALSE;
1309
+
1310
+    /**
1311
+     * Sets a meta property for all beans. This is a quicker way to set
1312
+     * the meta properties for a collection of beans because this method
1313
+     * can directly access the property arrays of the beans.
1314
+     * This method returns the beans.
1315
+     *
1316
+     * @param array  $beans    beans to set the meta property of
1317
+     * @param string $property property to set
1318
+     * @param mixed  $value    value
1319
+     *
1320
+     * @return array
1321
+     */
1322
+    public static function setMetaAll( $beans, $property, $value )
1323
+    {
1324
+        foreach( $beans as $bean ) {
1325
+            if ( $bean instanceof OODBBean ) $bean->__info[ $property ] = $value;
1326
+        }
1327
+
1328
+        return $beans;
1329
+    }
1330
+
1331
+    /**
1332
+     * Parses the join in the with-snippet.
1333
+     * For instance:
1334
+     *
1335
+     * $author
1336
+     * 	->withCondition(' @joined.detail.title LIKE ? ')
1337
+     *  ->ownBookList;
1338
+     *
1339
+     * will automatically join 'detail' on book to
1340
+     * access the title field.
1341
+     *
1342
+     * @note this feature requires Narrow Field Mode and Join Feature
1343
+     * to be both activated (default).
1344
+     *
1345
+     * @param string $type the source type for the join
1346
+     *
1347
+     * @return string $joinSql
1348
+     */
1349
+    private function parseJoin( $type )
1350
+    {
1351
+        $joinSql = '';
1352
+        $joins = array();
1353
+        if ( strpos($this->withSql, '@joined.' ) !== FALSE ) {
1354
+            $writer   = $this->beanHelper->getToolBox()->getWriter();
1355
+            $oldParts = $parts = explode( '@joined.', $this->withSql );
1356
+            array_shift( $parts );
1357
+            foreach($parts as $part) {
1358
+                $explosion = explode( '.', $part );
1359
+                $joinInfo  = array_shift( $explosion );
1360
+                //Dont join more than once..
1361
+                if ( !isset( $joins[$joinInfo] ) ) {
1362
+                    $joins[ $joinInfo ] = true;
1363
+                    $joinSql  .= $writer->writeJoin( $type, $joinInfo, 'LEFT' );
1364
+                }
1365
+            }
1366
+            $this->withSql = implode( '', $oldParts );
1367
+            $joinSql      .= ' WHERE ';
1368
+        }
1369
+        return $joinSql;
1370
+    }
1371
+
1372
+    /**
1373
+     * Internal method.
1374
+     * Obtains a shared list for a certain type.
1375
+     *
1376
+     * @param string $type the name of the list you want to retrieve.
1377
+     *
1378
+     * @return array
1379
+     */
1380
+    private function getSharedList( $type, $redbean, $toolbox )
1381
+    {
1382
+        $writer = $toolbox->getWriter();
1383
+
1384
+        if ( $this->via ) {
1385
+            $oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
1386
+            if ( $oldName !== $this->via ) {
1387
+                //set the new renaming rule
1388
+                $writer->renameAssocTable( $oldName, $this->via );
1389
+            }
1390
+            $this->via = NULL;
1391
+        }
1392
+
1393
+        $beans = array();
1394
+        if ($this->getID()) {
1395
+            $type             = $this->beau( $type );
1396
+            $assocManager     = $redbean->getAssociationManager();
1397
+            $beans            = $assocManager->related( $this, $type, $this->withSql, $this->withParams );
1398
+        }
1399
+
1400
+        $this->withSql    = '';
1401
+        $this->withParams = array();
1402
+
1403
+        return $beans;
1404
+    }
1405
+
1406
+    /**
1407
+     * Internal method.
1408
+     * Obtains the own list of a certain type.
1409
+     *
1410
+     * @param string      $type   name of the list you want to retrieve
1411
+     * @param OODB        $oodb   The RB OODB object database instance
1412
+     *
1413
+     * @return array
1414
+     */
1415
+    private function getOwnList( $type, $redbean )
1416
+    {
1417
+        $type = $this->beau( $type );
1418
+
1419
+        if ( $this->aliasName ) {
1420
+            $parentField = $this->aliasName;
1421
+            $myFieldLink = $parentField . '_id';
1422
+
1423
+            $this->__info['sys.alias.' . $type] = $this->aliasName;
1424
+
1425
+            $this->aliasName = NULL;
1426
+        } else {
1427
+            $parentField = $this->__info['type'];
1428
+            $myFieldLink = $parentField . '_id';
1429
+        }
1430
+
1431
+        $beans = array();
1432
+
1433
+        if ( $this->getID() ) {
1434
+
1435
+            $firstKey = NULL;
1436
+            if ( count( $this->withParams ) > 0 ) {
1437
+                reset( $this->withParams );
1438
+
1439
+                $firstKey = key( $this->withParams );
1440
+            }
1441
+
1442
+            $joinSql = $this->parseJoin( $type );
1443
+
1444
+            if ( !is_numeric( $firstKey ) || $firstKey === NULL ) {
1445
+                $bindings           = $this->withParams;
1446
+                $bindings[':slot0'] = $this->getID();
1447
+
1448
+                $beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
1449
+            } else {
1450
+                $bindings = array_merge( array( $this->getID() ), $this->withParams );
1451
+
1452
+                $beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
1453
+            }
1454
+        }
1455
+
1456
+        $this->withSql    = '';
1457
+        $this->withParams = array();
1458
+
1459
+        foreach ( $beans as $beanFromList ) {
1460
+            $beanFromList->__info['sys.parentcache.' . $parentField] = $this;
1461
+        }
1462
+
1463
+        return $beans;
1464
+    }
1465
+
1466
+    /**
1467
+     * Initializes a bean. Used by OODB for dispensing beans.
1468
+     * It is not recommended to use this method to initialize beans. Instead
1469
+     * use the OODB object to dispense new beans. You can use this method
1470
+     * if you build your own bean dispensing mechanism.
1471
+     *
1472
+     * @param string             $type       type of the new bean
1473
+     * @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model
1474
+     *
1475
+     * @return void
1476
+     */
1477
+    public function initializeForDispense( $type, BeanHelper $beanhelper )
1478
+    {
1479
+        $this->beanHelper         = $beanhelper;
1480
+        $this->__info['type']     = $type;
1481
+        $this->__info['sys.id']   = 'id';
1482
+        $this->__info['sys.orig'] = array( 'id' => 0 );
1483
+        $this->__info['tainted']  = TRUE;
1484
+        $this->__info['changed']  = TRUE;
1485
+        $this->properties['id']   = 0;
1486
+    }
1487
+
1488
+    /**
1489
+     * Sets the Bean Helper. Normally the Bean Helper is set by OODB.
1490
+     * Here you can change the Bean Helper. The Bean Helper is an object
1491
+     * providing access to a toolbox for the bean necessary to retrieve
1492
+     * nested beans (bean lists: ownBean, sharedBean) without the need to
1493
+     * rely on static calls to the facade (or make this class dep. on OODB).
1494
+     *
1495
+     * @param BeanHelper $helper
1496
+     *
1497
+     * @return void
1498
+     */
1499
+    public function setBeanHelper( BeanHelper $helper )
1500
+    {
1501
+        $this->beanHelper = $helper;
1502
+    }
1503
+
1504
+    /**
1505
+     * Returns an\ArrayIterator so you can treat the bean like
1506
+     * an array with the properties container as its contents.
1507
+     * This method is meant for PHP and allows you to access beans as if
1508
+     * they were arrays, i.e. using array notation:
1509
+     *
1510
+     * $bean[$key] = $value;
1511
+     *
1512
+     * Note that not all PHP functions work with the array interface.
1513
+     *
1514
+     * @return \ArrayIterator
1515
+     */
1516
+    public function getIterator()
1517
+    {
1518
+        return new\ArrayIterator( $this->properties );
1519
+    }
1520
+
1521
+    /**
1522
+     * Imports all values from an associative array $array. Chainable.
1523
+     * This method imports the values in the first argument as bean
1524
+     * propery and value pairs. Use the second parameter to provide a
1525
+     * selection. If a selection array is passed, only the entries
1526
+     * having keys mentioned in the selection array will be imported.
1527
+     * Set the third parameter to TRUE to preserve spaces in selection keys.
1528
+     *
1529
+     * @param array        $array     what you want to import
1530
+     * @param string|array $selection selection of values
1531
+     * @param boolean      $notrim    if TRUE selection keys will NOT be trimmed
1532
+     *
1533
+     * @return OODBBean
1534
+     */
1535
+    public function import( $array, $selection = FALSE, $notrim = FALSE )
1536
+    {
1537
+        if ( is_string( $selection ) ) {
1538
+            $selection = explode( ',', $selection );
1539
+        }
1540
+
1541
+        if ( !$notrim && is_array( $selection ) ) {
1542
+            foreach ( $selection as $key => $selected ) {
1543
+                $selection[$key] = trim( $selected );
1544
+            }
1545
+        }
1546
+
1547
+        foreach ( $array as $key => $value ) {
1548
+            if ( $key != '__info' ) {
1549
+                if ( !$selection || ( $selection && in_array( $key, $selection ) ) ) {
1550
+                    if ( is_array($value ) ) {
1551
+                        if ( isset( $value['_type'] ) ) {
1552
+                            $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] );
1553
+                            unset( $value['_type'] );
1554
+                            $bean->import($value);
1555
+                            $this->$key = $bean;
1556
+                        } else {
1557
+                            $listBeans = array();
1558
+                            foreach( $value as $listKey => $listItem ) {
1559
+                                $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] );
1560
+                                unset( $listItem['_type'] );
1561
+                                $bean->import($listItem);
1562
+                                $list = &$this->$key;
1563
+                                $list[ $listKey ] = $bean;
1564
+                            }
1565
+                        }
1566
+                    } else {
1567
+                        $this->$key = $value;
1568
+                    }
1569
+                }
1570
+            }
1571
+        }
1572
+
1573
+        return $this;
1574
+    }
1575
+
1576
+    /**
1577
+     * Fast way to import a row.
1578
+     * Does not perform any checks.
1579
+     *
1580
+     * @param array $row a database row
1581
+     *
1582
+     * @return self
1583
+     */
1584
+    public function importRow( $row )
1585
+    {
1586
+        $this->properties = $row;
1587
+        $this->__info['sys.orig'] = $row;
1588
+        $this->__info['changed'] = FALSE;
1589
+        return $this;
1590
+    }
1591
+
1592
+    /**
1593
+     * Imports data from another bean. Chainable.
1594
+     * Copies the properties from the source bean to the internal
1595
+     * property list.
1596
+     *
1597
+     * @param OODBBean $sourceBean the source bean to take properties from
1598
+     *
1599
+     * @return OODBBean
1600
+     */
1601
+    public function importFrom( OODBBean $sourceBean )
1602
+    {
1603
+        $this->__info['tainted'] = TRUE;
1604
+        $this->__info['changed'] = TRUE;
1605
+        $this->properties = $sourceBean->properties;
1606
+
1607
+        return $this;
1608
+    }
1609
+
1610
+    /**
1611
+     * Injects the properties of another bean but keeps the original ID.
1612
+     * Just like import() but keeps the original ID.
1613
+     * Chainable.
1614
+     *
1615
+     * @param OODBBean $otherBean the bean whose properties you would like to copy
1616
+     *
1617
+     * @return OODBBean
1618
+     */
1619
+    public function inject( OODBBean $otherBean )
1620
+    {
1621
+        $myID = $this->properties['id'];
1622
+
1623
+        $this->import( $otherBean->export() );
1624
+
1625
+        $this->id = $myID;
1626
+
1627
+        return $this;
1628
+    }
1629
+
1630
+    /**
1631
+     * Exports the bean as an array.
1632
+     * This function exports the contents of a bean to an array and returns
1633
+     * the resulting array.
1634
+     *
1635
+     * @param boolean $meta    set to TRUE if you want to export meta data as well
1636
+     * @param boolean $parents set to TRUE if you want to export parents as well
1637
+     * @param boolean $onlyMe  set to TRUE if you want to export only this bean
1638
+     * @param array   $filters optional whitelist for export
1639
+     *
1640
+     * @return array
1641
+     */
1642
+    public function export( $meta = FALSE, $parents = FALSE, $onlyMe = FALSE, $filters = array() )
1643
+    {
1644
+        $arr = array();
1645
+
1646
+        if ( $parents ) {
1647
+            foreach ( $this as $key => $value ) {
1648
+                if ( substr( $key, -3 ) != '_id' ) continue;
1649
+
1650
+                $prop = substr( $key, 0, strlen( $key ) - 3 );
1651
+                $this->$prop;
1652
+            }
1653
+        }
1654
+
1655
+        $hasFilters = is_array( $filters ) && count( $filters );
1656
+
1657
+        foreach ( $this as $key => $value ) {
1658
+            if ( !$onlyMe && is_array( $value ) ) {
1659
+                $vn = array();
1660
+
1661
+                foreach ( $value as $i => $b ) {
1662
+                    if ( !( $b instanceof OODBBean ) ) continue;
1663
+                    $vn[] = $b->export( $meta, FALSE, FALSE, $filters );
1664
+                    $value = $vn;
1665
+                }
1666
+            } elseif ( $value instanceof OODBBean ) {
1667
+                if ( $hasFilters ) {
1668
+                    if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue;
1669
+                }
1670
+
1671
+                $value = $value->export( $meta, $parents, FALSE, $filters );
1672
+            }
1673
+
1674
+            $arr[$key] = $value;
1675
+        }
1676
+
1677
+        if ( $meta ) {
1678
+            $arr['__info'] = $this->__info;
1679
+        }
1680
+
1681
+        return $arr;
1682
+    }
1683
+
1684
+    /**
1685
+     * Implements isset() function for use as an array.
1686
+     *
1687
+     * @param string $property name of the property you want to check
1688
+     *
1689
+     * @return boolean
1690
+     */
1691
+    public function __isset( $property )
1692
+    {
1693
+        $property = $this->beau( $property );
1694
+
1695
+        if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
1696
+            $property = substr($property, 1);
1697
+        }
1698
+        return isset( $this->properties[$property] );
1699
+    }
1700
+
1701
+    /**
1702
+     * Returns the ID of the bean no matter what the ID field is.
1703
+     *
1704
+     * @return string|null
1705
+     */
1706
+    public function getID()
1707
+    {
1708
+        return ( isset( $this->properties['id'] ) ) ? (string) $this->properties['id'] : NULL;
1709
+    }
1710
+
1711
+    /**
1712
+     * Unsets a property of a bean.
1713
+     * Magic method, gets called implicitly when performing the unset() operation
1714
+     * on a bean property.
1715
+     * 
1716
+     * @param  string $property property to unset
1717
+     *
1718
+     * @return void
1719
+     */
1720
+    public function __unset( $property )
1721
+    {
1722
+        $property = $this->beau( $property );
1723
+
1724
+        if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
1725
+            $property = substr($property, 1);
1726
+        }
1727
+
1728
+        unset( $this->properties[$property] );
1729
+
1730
+        $shadowKey = 'sys.shadow.'.$property;
1731
+        if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] );
1732
+
1733
+        //also clear modifiers
1734
+        $this->withSql    = '';
1735
+        $this->withParams = array();
1736
+        $this->aliasName  = NULL;
1737
+        $this->fetchType  = NULL;
1738
+        $this->noLoad     = FALSE;
1739
+        $this->all        = FALSE;
1740
+        $this->via        = NULL;
1741
+
1742
+        return;
1743
+    }
1744
+
1745
+    /**
1746
+     * Adds WHERE clause conditions to ownList retrieval.
1747
+     * For instance to get the pages that belong to a book you would
1748
+     * issue the following command: $book->ownPage
1749
+     * However, to order these pages by number use:
1750
+     *
1751
+     * $book->with(' ORDER BY `number` ASC ')->ownPage
1752
+     *
1753
+     * the additional SQL snippet will be merged into the final
1754
+     * query.
1755
+     *
1756
+     * @param string $sql SQL to be added to retrieval query.
1757
+     * @param array       $bindings array with parameters to bind to SQL snippet
1758
+     *
1759
+     * @return OODBBean
1760
+     */
1761
+    public function with( $sql, $bindings = array() )
1762
+    {
1763
+        $this->withSql    = $sql;
1764
+        $this->withParams = $bindings;
1765
+        return $this;
1766
+    }
1767
+
1768
+    /**
1769
+     * Just like with(). Except that this method prepends the SQL query snippet
1770
+     * with AND which makes it slightly more comfortable to use a conditional
1771
+     * SQL snippet. For instance to filter an own-list with pages (belonging to
1772
+     * a book) on specific chapters you can use:
1773
+     *
1774
+     * $book->withCondition(' chapter = 3 ')->ownPage
1775
+     *
1776
+     * This will return in the own list only the pages having 'chapter == 3'.
1777
+     *
1778
+     * @param string $sql      SQL to be added to retrieval query (prefixed by AND)
1779
+     * @param array  $bindings array with parameters to bind to SQL snippet
1780
+     *
1781
+     * @return OODBBean
1782
+     */
1783
+    public function withCondition( $sql, $bindings = array() )
1784
+    {
1785
+        $this->withSql    = ' AND ' . $sql;
1786
+        $this->withParams = $bindings;
1787
+        return $this;
1788
+    }
1789
+
1790
+    /**
1791
+     * Tells the bean to (re)load the following list without any
1792
+     * conditions. If you have an ownList or sharedList with a
1793
+     * condition you can use this method to reload the entire list.
1794
+     *
1795
+     * Usage:
1796
+     *
1797
+     * $bean->with( ' LIMIT 3 ' )->ownPage; //Just 3
1798
+     * $bean->all()->ownPage; //Reload all pages
1799
+     *
1800
+     * @return self
1801
+     */
1802
+    public function all()
1803
+    {
1804
+        $this->all = TRUE;
1805
+        return $this;
1806
+    }
1807
+
1808
+    /**
1809
+     * Tells the bean to only access the list but not load
1810
+     * its contents. Use this if you only want to add something to a list
1811
+     * and you have no interest in retrieving its contents from the database.
1812
+     *
1813
+     * @return self
1814
+     */
1815
+    public function noLoad()
1816
+    {
1817
+        $this->noLoad = TRUE;
1818
+        return $this;
1819
+    }
1820
+
1821
+    /**
1822
+     * Prepares an own-list to use an alias. This is best explained using
1823
+     * an example. Imagine a project and a person. The project always involves
1824
+     * two persons: a teacher and a student. The person beans have been aliased in this
1825
+     * case, so to the project has a teacher_id pointing to a person, and a student_id
1826
+     * also pointing to a person. Given a project, we obtain the teacher like this:
1827
+     *
1828
+     * $project->fetchAs('person')->teacher;
1829
+     *
1830
+     * Now, if we want all projects of a teacher we cant say:
1831
+     *
1832
+     * $teacher->ownProject
1833
+     *
1834
+     * because the $teacher is a bean of type 'person' and no project has been
1835
+     * assigned to a person. Instead we use the alias() method like this:
1836
+     *
1837
+     * $teacher->alias('teacher')->ownProject
1838
+     *
1839
+     * now we get the projects associated with the person bean aliased as
1840
+     * a teacher.
1841
+     *
1842
+     * @param string $aliasName the alias name to use
1843
+     *
1844
+     * @return OODBBean
1845
+     */
1846
+    public function alias( $aliasName )
1847
+    {
1848
+        $this->aliasName = $this->beau( $aliasName );
1849
+
1850
+        return $this;
1851
+    }
1852
+
1853
+    /**
1854
+     * Returns properties of bean as an array.
1855
+     * This method returns the raw internal property list of the
1856
+     * bean. Only use this method for optimization purposes. Otherwise
1857
+     * use the export() method to export bean data to arrays.
1858
+     *
1859
+     * @return array
1860
+     */
1861
+    public function getProperties()
1862
+    {
1863
+        return $this->properties;
1864
+    }
1865
+
1866
+    /**
1867
+     * Returns properties of bean as an array.
1868
+     * This method returns the raw internal property list of the
1869
+     * bean. Only use this method for optimization purposes. Otherwise
1870
+     * use the export() method to export bean data to arrays.
1871
+     * This method returns an array with the properties array and
1872
+     * the type (string).
1873
+     *
1874
+     * @return array
1875
+     */
1876
+    public function getPropertiesAndType()
1877
+    {
1878
+        return array( $this->properties, $this->__info['type'] );
1879
+    }
1880
+
1881
+    /**
1882
+     * Turns a camelcase property name into an underscored property name.
1883
+     *
1884
+     * Examples:
1885
+     *    oneACLRoute -> one_acl_route
1886
+     *    camelCase -> camel_case
1887
+     *
1888
+     * Also caches the result to improve performance.
1889
+     *
1890
+     * @param string $property
1891
+     *
1892
+     * @return string
1893
+     */
1894
+    public function beau( $property )
1895
+    {
1896
+        static $beautifulColumns = array();
1897
+
1898
+        if ( ctype_lower( $property ) ) return $property;
1899
+
1900
+        if (
1901
+            ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )
1902
+            || ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) )
1903
+            || ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) )
1904
+        ) {
1905
+
1906
+            $property = preg_replace( '/List$/', '', $property );
1907
+            return $property;
1908
+        }
1909
+
1910
+        if ( !isset( $beautifulColumns[$property] ) ) {
1911
+            $beautifulColumns[$property] = AQueryWriter::camelsSnake( $property );
1912
+        }
1913
+
1914
+        return $beautifulColumns[$property];
1915
+    }
1916
+
1917
+    /**
1918
+     * Clears all modifiers.
1919
+     *
1920
+     * @return self
1921
+     */
1922
+    public function clearModifiers()
1923
+    {
1924
+        $this->withSql    = '';
1925
+        $this->withParams = array();
1926
+        $this->aliasName  = NULL;
1927
+        $this->fetchType  = NULL;
1928
+        $this->noLoad     = FALSE;
1929
+        $this->all        = FALSE;
1930
+        $this->via        = NULL;
1931
+        return $this;
1932
+    }
1933
+
1934
+    /**
1935
+     * Determines whether a list is opened in exclusive mode or not.
1936
+     * If a list has been opened in exclusive mode this method will return TRUE,
1937
+     * othwerwise it will return FALSE.
1938
+     *
1939
+     * @param string $listName name of the list to check
1940
+     *
1941
+     * @return boolean
1942
+     */
1943
+    public function isListInExclusiveMode( $listName )
1944
+    {
1945
+        $listName = $this->beau( $listName );
1946
+
1947
+        if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) {
1948
+            $listName = substr($listName, 1);
1949
+        }
1950
+
1951
+        $listName = lcfirst( substr( $listName, 3 ) );
1952
+
1953
+        return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] );
1954
+    }
1955
+
1956
+    /**
1957
+     * Magic Getter. Gets the value for a specific property in the bean.
1958
+     * If the property does not exist this getter will make sure no error
1959
+     * occurs. This is because RedBean allows you to query (probe) for
1960
+     * properties. If the property can not be found this method will
1961
+     * return NULL instead.
1962
+     *
1963
+     * @param string $property name of the property you wish to obtain the value of
1964
+     *
1965
+     * @return mixed
1966
+     */
1967
+    public function &__get( $property )
1968
+    {
1969
+        $isEx          = FALSE;
1970
+        $isOwn         = FALSE;
1971
+        $isShared      = FALSE;
1972
+
1973
+        if ( !ctype_lower( $property ) ) {
1974
+            $property = $this->beau( $property );
1975
+            if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
1976
+                $property = substr($property, 1);
1977
+                $listName = lcfirst( substr( $property, 3 ) );
1978
+                $isEx     = TRUE;
1979
+                $isOwn    = TRUE;
1980
+                $this->__info['sys.exclusive-'.$listName] = TRUE;
1981
+            } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
1982
+                $isOwn    = TRUE;
1983
+                $listName = lcfirst( substr( $property, 3 ) );
1984
+            } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
1985
+                $isShared = TRUE;
1986
+            }
1987
+        }
1988
+
1989
+        $fieldLink      = $property . '_id';
1990
+        $exists         = isset( $this->properties[$property] );
1991
+
1992
+        //If not exists and no field link and no list, bail out.
1993
+        if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) {
1994
+
1995
+            $this->withSql    = '';
1996
+            $this->withParams = array();
1997
+            $this->aliasName  = NULL;
1998
+            $this->fetchType  = NULL;
1999
+            $this->noLoad     = FALSE;
2000
+            $this->all        = FALSE;
2001
+            $this->via        = NULL;
2002
+
2003
+            $NULL = NULL;
2004
+            return $NULL;
2005
+        }
2006
+
2007
+        $hasAlias       = (!is_null($this->aliasName));
2008
+        $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
2009
+                                ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2010
+        $hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2011
+        $hasAll         = (boolean) ($this->all);
2012
+
2013
+        //If exists and no list or exits and list not changed, bail out.
2014
+        if ( $exists && ((!$isOwn && !$isShared ) ||  (!$hasSQL && !$differentAlias && !$hasAll)) ) {
2015
+
2016
+            $this->withSql    = '';
2017
+            $this->withParams = array();
2018
+            $this->aliasName  = NULL;
2019
+            $this->fetchType  = NULL;
2020
+            $this->noLoad     = FALSE;
2021
+            $this->all        = FALSE;
2022
+            $this->via        = NULL;
2023
+            return $this->properties[$property];
2024
+        }
2025
+
2026
+
2027
+        list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2028
+
2029
+        if ( isset( $this->$fieldLink ) ) {
2030
+            $this->__info['tainted'] = TRUE;
2031
+
2032
+            if ( isset( $this->__info["sys.parentcache.$property"] ) ) {
2033
+                $bean = $this->__info["sys.parentcache.$property"];
2034
+            } else {
2035
+                if ( isset( self::$aliases[$property] ) ) {
2036
+                    $type = self::$aliases[$property];
2037
+                } elseif ( $this->fetchType ) {
2038
+                    $type = $this->fetchType;
2039
+                    $this->fetchType = NULL;
2040
+                } else {
2041
+                    $type = $property;
2042
+                }
2043
+                $bean = NULL;
2044
+                if ( !is_null( $this->properties[$fieldLink] ) ) {
2045
+                    $bean = $redbean->load( $type, $this->properties[$fieldLink] );
2046
+                    //If the IDs dont match, we failed to load, so try autoresolv in that case...
2047
+                    if ( $bean->id !== $this->properties[$fieldLink] && self::$autoResolve ) {
2048
+                        $type = $this->beanHelper->getToolbox()->getWriter()->inferFetchType( $this->__info['type'], $property );
2049
+                        if ( !is_null( $type) ) {
2050
+                            $bean = $redbean->load( $type, $this->properties[$fieldLink] );
2051
+                            $this->__info["sys.autoresolved.{$property}"] = $type;
2052
+                        }
2053
+                    }
2054
+                }
2055
+            }
2056
+
2057
+            $this->properties[$property] = $bean;
2058
+            $this->withSql               = '';
2059
+            $this->withParams            = array();
2060
+            $this->aliasName             = NULL;
2061
+            $this->fetchType             = NULL;
2062
+            $this->noLoad                = FALSE;
2063
+            $this->all                   = FALSE;
2064
+            $this->via                   = NULL;
2065
+
2066
+            return $this->properties[$property];
2067
+
2068
+        }
2069
+        //Implicit: elseif ( $isOwn || $isShared ) {
2070
+        if ( $this->noLoad ) {
2071
+            $beans = array();
2072
+        } elseif ( $isOwn ) {
2073
+            $beans = $this->getOwnList( $listName, $redbean );
2074
+        } else {
2075
+            $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2076
+        }
2077
+
2078
+        $this->properties[$property]          = $beans;
2079
+        $this->__info["sys.shadow.$property"] = $beans;
2080
+        $this->__info['tainted']              = TRUE;
2081
+
2082
+        $this->withSql    = '';
2083
+        $this->withParams = array();
2084
+        $this->aliasName  = NULL;
2085
+        $this->fetchType  = NULL;
2086
+        $this->noLoad     = FALSE;
2087
+        $this->all        = FALSE;
2088
+        $this->via        = NULL;
2089
+
2090
+        return $this->properties[$property];
2091
+    }
2092
+
2093
+    /**
2094
+     * Magic Setter. Sets the value for a specific property.
2095
+     * This setter acts as a hook for OODB to mark beans as tainted.
2096
+     * The tainted meta property can be retrieved using getMeta("tainted").
2097
+     * The tainted meta property indicates whether a bean has been modified and
2098
+     * can be used in various caching mechanisms.
2099
+     *
2100
+     * @param string $property name of the property you wish to assign a value to
2101
+     * @param  mixed $value    the value you want to assign
2102
+     *
2103
+     * @return void
2104
+     *
2105
+     * @throws Security
2106
+     */
2107
+    public function __set( $property, $value )
2108
+    {
2109
+        $isEx          = FALSE;
2110
+        $isOwn         = FALSE;
2111
+        $isShared      = FALSE;
2112
+
2113
+        if ( !ctype_lower( $property ) ) {
2114
+            $property = $this->beau( $property );
2115
+            if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
2116
+                $property = substr($property, 1);
2117
+                $listName = lcfirst( substr( $property, 3 ) );
2118
+                $isEx     = TRUE;
2119
+                $isOwn    = TRUE;
2120
+                $this->__info['sys.exclusive-'.$listName] = TRUE;
2121
+            } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) )  {
2122
+                $isOwn    = TRUE;
2123
+                $listName = lcfirst( substr( $property, 3 ) );
2124
+            } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
2125
+                $isShared = TRUE;
2126
+            }
2127
+        }
2128
+
2129
+        $hasAlias       = (!is_null($this->aliasName));
2130
+        $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
2131
+                                ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
2132
+        $hasSQL         = ($this->withSql !== '' || $this->via !== NULL);
2133
+        $exists         = isset( $this->properties[$property] );
2134
+        $fieldLink      = $property . '_id';
2135
+
2136
+        if ( ($isOwn || $isShared) &&  (!$exists || $hasSQL || $differentAlias) ) {
2137
+
2138
+            if ( !$this->noLoad ) {
2139
+                list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
2140
+                if ( $isOwn ) {
2141
+                    $beans = $this->getOwnList( $listName, $redbean );
2142
+                } else {
2143
+                    $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
2144
+                }
2145
+                $this->__info["sys.shadow.$property"] = $beans;
2146
+            }
2147
+        }
2148
+
2149
+        $this->withSql    = '';
2150
+        $this->withParams = array();
2151
+        $this->aliasName  = NULL;
2152
+        $this->fetchType  = NULL;
2153
+        $this->noLoad     = FALSE;
2154
+        $this->all        = FALSE;
2155
+        $this->via        = NULL;
2156
+
2157
+        $this->__info['tainted'] = TRUE;
2158
+        $this->__info['changed'] = TRUE;
2159
+
2160
+        if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) {
2161
+            if ( is_null( $value ) || $value === FALSE ) {
2162
+
2163
+                unset( $this->properties[ $property ]);
2164
+                $this->properties[ $fieldLink ] = NULL;
2165
+
2166
+                return;
2167
+            } else {
2168
+                throw new RedException( 'Cannot cast to bean.' );
2169
+            }
2170
+        }
2171
+
2172
+        if ( $value === FALSE ) {
2173
+            $value = '0';
2174
+        } elseif ( $value === TRUE ) {
2175
+            $value = '1';
2176
+        } elseif ( $value instanceof \DateTime ) {
2177
+            $value = $value->format( 'Y-m-d H:i:s' );
2178
+        }
2179
+
2180
+        $this->properties[$property] = $value;
2181
+    }
2182
+
2183
+    /**
2184
+     * Sets a property directly, for internal use only.
2185
+     *
2186
+     * @param string  $property     property
2187
+     * @param mixed   $value        value
2188
+     * @param boolean $updateShadow whether you want to update the shadow
2189
+     * @param boolean $taint        whether you want to mark the bean as tainted
2190
+     *
2191
+     * @return void
2192
+     */
2193
+    public function setProperty( $property, $value, $updateShadow = FALSE, $taint = FALSE )
2194
+    {
2195
+        $this->properties[$property] = $value;
2196
+
2197
+        if ( $updateShadow ) {
2198
+            $this->__info['sys.shadow.' . $property] = $value;
2199
+        }
2200
+
2201
+        if ( $taint ) {
2202
+            $this->__info['tainted'] = TRUE;
2203
+            $this->__info['changed'] = TRUE;
2204
+        }
2205
+    }
2206
+
2207
+    /**
2208
+     * Returns the value of a meta property. A meta property
2209
+     * contains additional information about the bean object that will not
2210
+     * be stored in the database. Meta information is used to instruct
2211
+     * RedBeanPHP as well as other systems how to deal with the bean.
2212
+     * If the property cannot be found this getter will return NULL instead.
2213
+     *
2214
+     * Example:
2215
+     *
2216
+     * $bean->setMeta( 'flush-cache', TRUE );
2217
+     *
2218
+     * RedBeanPHP also stores meta data in beans, this meta data uses
2219
+     * keys prefixed with 'sys.' (system).
2220
+     *
2221
+     * @param string $path    path
2222
+     * @param mixed  $default default value
2223
+     *
2224
+     * @return mixed
2225
+     */
2226
+    public function getMeta( $path, $default = NULL )
2227
+    {
2228
+        return ( isset( $this->__info[$path] ) ) ? $this->__info[$path] : $default;
2229
+    }
2230
+
2231
+    /**
2232
+     * Gets and unsets a meta property.
2233
+     * Moves a meta property out of the bean.
2234
+     * This is a short-cut method that can be used instead
2235
+     * of combining a get/unset.
2236
+     *
2237
+     * @param string $path    path
2238
+     * @param mixed  $default default value
2239
+     *
2240
+     * @return mixed
2241
+     */
2242
+    public function moveMeta( $path, $value = NULL )
2243
+    {
2244
+        if ( isset( $this->__info[$path] ) ) {
2245
+            $value = $this->__info[ $path ];
2246
+            unset( $this->__info[ $path ] );
2247
+        }
2248
+        return $value;
2249
+    }
2250
+
2251
+    /**
2252
+     * Stores a value in the specified Meta information property.
2253
+     * The first argument should be the key to store the value under,
2254
+     * the second argument should be the value. It is common to use
2255
+     * a path-like notation for meta data in RedBeanPHP like:
2256
+     * 'my.meta.data', however the dots are purely for readability, the
2257
+     * meta data methods do not store nested structures or hierarchies.
2258
+     *
2259
+     * @param string $path  path / key to store value under
2260
+     * @param mixed  $value value to store in bean (not in database) as meta data
2261
+     *
2262
+     * @return OODBBean
2263
+     */
2264
+    public function setMeta( $path, $value )
2265
+    {
2266
+        $this->__info[$path] = $value;
2267
+
2268
+        return $this;
2269
+    }
2270
+
2271
+    /**
2272
+     * Copies the meta information of the specified bean
2273
+     * This is a convenience method to enable you to
2274
+     * exchange meta information easily.
2275
+     *
2276
+     * @param OODBBean $bean
2277
+     *
2278
+     * @return OODBBean
2279
+     */
2280
+    public function copyMetaFrom( OODBBean $bean )
2281
+    {
2282
+        $this->__info = $bean->__info;
2283
+
2284
+        return $this;
2285
+    }
2286
+
2287
+    /**
2288
+     * Sends the call to the registered model.
2289
+     *
2290
+     * @param string $method name of the method
2291
+     * @param array  $args   argument list
2292
+     *
2293
+     * @return mixed
2294
+     */
2295
+    public function __call( $method, $args )
2296
+    {
2297
+        if ( !isset( $this->__info['model'] ) ) {
2298
+            $model = $this->beanHelper->getModelForBean( $this );
2299
+
2300
+            if ( !$model ) {
2301
+                return NULL;
2302
+            }
2303
+
2304
+            $this->__info['model'] = $model;
2305
+        }
2306
+        if ( !method_exists( $this->__info['model'], $method ) ) {
2307
+
2308
+            if ( self::$errorHandlingFUSE === FALSE ) {
2309
+                return NULL;
2310
+            }
2311
+
2312
+            if ( in_array( $method, array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ), TRUE ) ) {
2313
+                return NULL;
2314
+            }
2315
+
2316
+            $message = "FUSE: method does not exist in model: $method";
2317
+            if ( self::$errorHandlingFUSE === self::C_ERR_LOG ) {
2318
+                error_log( $message );
2319
+                return NULL;
2320
+            } elseif ( self::$errorHandlingFUSE === self::C_ERR_NOTICE ) {
2321
+                trigger_error( $message, E_USER_NOTICE );
2322
+                return NULL;
2323
+            } elseif ( self::$errorHandlingFUSE === self::C_ERR_WARN ) {
2324
+                trigger_error( $message, E_USER_WARNING );
2325
+                return NULL;
2326
+            } elseif ( self::$errorHandlingFUSE === self::C_ERR_EXCEPTION ) {
2327
+                throw new \Exception( $message );
2328
+            } elseif ( self::$errorHandlingFUSE === self::C_ERR_FUNC ) {
2329
+                $func = self::$errorHandler;
2330
+                return $func(array(
2331
+                    'message' => $message,
2332
+                    'method' => $method,
2333
+                    'args' => $args,
2334
+                    'bean' => $this
2335
+                ));
2336
+            }
2337
+            trigger_error( $message, E_USER_ERROR );
2338
+            return NULL;
2339
+        }
2340
+
2341
+        return call_user_func_array( array( $this->__info['model'], $method ), $args );
2342
+    }
2343
+
2344
+    /**
2345
+     * Implementation of __toString Method
2346
+     * Routes call to Model. If the model implements a __toString() method this
2347
+     * method will be called and the result will be returned. In case of an
2348
+     * echo-statement this result will be printed. If the model does not
2349
+     * implement a __toString method, this method will return a JSON
2350
+     * representation of the current bean.
2351
+     *
2352
+     * @return string
2353
+     */
2354
+    public function __toString()
2355
+    {
2356
+        $string = $this->__call( '__toString', array() );
2357
+
2358
+        if ( $string === NULL ) {
2359
+            return json_encode( $this->properties );
2360
+        } else {
2361
+            return $string;
2362
+        }
2363
+    }
2364
+
2365
+    /**
2366
+     * Implementation of Array Access Interface, you can access bean objects
2367
+     * like an array.
2368
+     * Call gets routed to __set.
2369
+     *
2370
+     * @param  mixed $offset offset string
2371
+     * @param  mixed $value  value
2372
+     *
2373
+     * @return void
2374
+     */
2375
+    public function offsetSet( $offset, $value )
2376
+    {
2377
+        $this->__set( $offset, $value );
2378
+    }
2379
+
2380
+    /**
2381
+     * Implementation of Array Access Interface, you can access bean objects
2382
+     * like an array.
2383
+     *
2384
+     * Array functions do not reveal x-own-lists and list-alias because
2385
+     * you dont want duplicate entries in foreach-loops.
2386
+     * Also offers a slight performance improvement for array access.
2387
+     *
2388
+     * @param  mixed $offset property
2389
+     *
2390
+     * @return boolean
2391
+     */
2392
+    public function offsetExists( $offset )
2393
+    {
2394
+        return $this->__isset( $offset );
2395
+    }
2396
+
2397
+    /**
2398
+     * Implementation of Array Access Interface, you can access bean objects
2399
+     * like an array.
2400
+     * Unsets a value from the array/bean.
2401
+     *
2402
+     * Array functions do not reveal x-own-lists and list-alias because
2403
+     * you dont want duplicate entries in foreach-loops.
2404
+     * Also offers a slight performance improvement for array access.
2405
+     *
2406
+     * @param  mixed $offset property
2407
+     *
2408
+     * @return void
2409
+     */
2410
+    public function offsetUnset( $offset )
2411
+    {
2412
+        $this->__unset( $offset );
2413
+    }
2414
+
2415
+    /**
2416
+     * Implementation of Array Access Interface, you can access bean objects
2417
+     * like an array.
2418
+     * Returns value of a property.
2419
+     *
2420
+     * Array functions do not reveal x-own-lists and list-alias because
2421
+     * you dont want duplicate entries in foreach-loops.
2422
+     * Also offers a slight performance improvement for array access.
2423
+     *
2424
+     * @param  mixed $offset property
2425
+     *
2426
+     * @return mixed
2427
+     */
2428
+    public function &offsetGet( $offset )
2429
+    {
2430
+        return $this->__get( $offset );
2431
+    }
2432
+
2433
+    /**
2434
+     * Chainable method to cast a certain ID to a bean; for instance:
2435
+     * $person = $club->fetchAs('person')->member;
2436
+     * This will load a bean of type person using member_id as ID.
2437
+     *
2438
+     * @param  string $type preferred fetch type
2439
+     *
2440
+     * @return OODBBean
2441
+     */
2442
+    public function fetchAs( $type )
2443
+    {
2444
+        $this->fetchType = $type;
2445
+
2446
+        return $this;
2447
+    }
2448
+
2449
+    /**
2450
+     * For polymorphic bean relations.
2451
+     * Same as fetchAs but uses a column instead of a direct value.
2452
+     *
2453
+     * @param string $column
2454
+     *
2455
+     * @return OODBBean
2456
+     */
2457
+    public function poly( $field )
2458
+    {
2459
+        return $this->fetchAs( $this->$field );
2460
+    }
2461
+
2462
+    /**
2463
+     * Traverses a bean property with the specified function.
2464
+     * Recursively iterates through the property invoking the
2465
+     * function for each bean along the way passing the bean to it.
2466
+     *
2467
+     * Can be used together with with, withCondition, alias and fetchAs.
2468
+     *
2469
+     * @param string  $property property
2470
+     * @param closure $function function
2471
+     *
2472
+     * @return OODBBean
2473
+     */
2474
+    public function traverse( $property, $function, $maxDepth = NULL )
2475
+    {
2476
+        $this->via = NULL;
2477
+        if ( strpos( $property, 'shared' ) !== FALSE ) {
2478
+            throw new RedException( 'Traverse only works with (x)own-lists.' );
2479
+        }
2480
+
2481
+        if ( !is_null( $maxDepth ) ) {
2482
+            if ( !$maxDepth-- ) return $this;
2483
+        }
2484
+
2485
+        $oldFetchType = $this->fetchType;
2486
+        $oldAliasName = $this->aliasName;
2487
+        $oldWith      = $this->withSql;
2488
+        $oldBindings  = $this->withParams;
2489
+
2490
+        $beans = $this->$property;
2491
+
2492
+        if ( $beans === NULL ) return $this;
2493
+
2494
+        if ( !is_array( $beans ) ) $beans = array( $beans );
2495
+
2496
+        foreach( $beans as $bean ) {
2497
+
2498
+            $function( $bean );
2499
+
2500
+            $bean->fetchType  = $oldFetchType;
2501
+            $bean->aliasName  = $oldAliasName;
2502
+            $bean->withSql    = $oldWith;
2503
+            $bean->withParams = $oldBindings;
2504
+
2505
+            $bean->traverse( $property, $function, $maxDepth );
2506
+        }
2507
+
2508
+        return $this;
2509
+    }
2510
+
2511
+    /**
2512
+     * Implementation of\Countable interface. Makes it possible to use
2513
+     * count() function on a bean.
2514
+     *
2515
+     * @return integer
2516
+     */
2517
+    public function count()
2518
+    {
2519
+        return count( $this->properties );
2520
+    }
2521
+
2522
+    /**
2523
+     * Checks whether a bean is empty or not.
2524
+     * A bean is empty if it has no other properties than the id field OR
2525
+     * if all the other property are empty().
2526
+     *
2527
+     * @return boolean
2528
+     */
2529
+    public function isEmpty()
2530
+    {
2531
+        $empty = TRUE;
2532
+        foreach ( $this->properties as $key => $value ) {
2533
+            if ( $key == 'id' ) {
2534
+                continue;
2535
+            }
2536
+            if ( !empty( $value ) ) {
2537
+                $empty = FALSE;
2538
+            }
2539
+        }
2540
+
2541
+        return $empty;
2542
+    }
2543
+
2544
+    /**
2545
+     * Chainable setter.
2546
+     *
2547
+     * @param string $property the property of the bean
2548
+     * @param mixed  $value    the value you want to set
2549
+     *
2550
+     * @return OODBBean
2551
+     */
2552
+    public function setAttr( $property, $value )
2553
+    {
2554
+        $this->$property = $value;
2555
+
2556
+        return $this;
2557
+    }
2558
+
2559
+    /**
2560
+     * Comfort method.
2561
+     * Unsets all properties in array.
2562
+     *
2563
+     * @param array $properties properties you want to unset.
2564
+     *
2565
+     * @return OODBBean
2566
+     */
2567
+    public function unsetAll( $properties )
2568
+    {
2569
+        foreach ( $properties as $prop ) {
2570
+            if ( isset( $this->properties[$prop] ) ) {
2571
+                unset( $this->properties[$prop] );
2572
+            }
2573
+        }
2574
+
2575
+        return $this;
2576
+    }
2577
+
2578
+    /**
2579
+     * Returns original (old) value of a property.
2580
+     * You can use this method to see what has changed in a
2581
+     * bean.
2582
+     *
2583
+     * @param string $property name of the property you want the old value of
2584
+     *
2585
+     * @return mixed
2586
+     */
2587
+    public function old( $property )
2588
+    {
2589
+        $old = $this->getMeta( 'sys.orig', array() );
2590
+
2591
+        if ( array_key_exists( $property, $old ) ) {
2592
+            return $old[$property];
2593
+        }
2594
+
2595
+        return NULL;
2596
+    }
2597
+
2598
+    /**
2599
+     * Convenience method.
2600
+     * Returns TRUE if the bean has been changed, or FALSE otherwise.
2601
+     * Same as $bean->getMeta('tainted');
2602
+     * Note that a bean becomes tainted as soon as you retrieve a list from
2603
+     * the bean. This is because the bean lists are arrays and the bean cannot
2604
+     * determine whether you have made modifications to a list so RedBeanPHP
2605
+     * will mark the whole bean as tainted.
2606
+     *
2607
+     * @return boolean
2608
+     */
2609
+    public function isTainted()
2610
+    {
2611
+        return $this->getMeta( 'tainted' );
2612
+    }
2613
+
2614
+    /**
2615
+     * Returns TRUE if the value of a certain property of the bean has been changed and
2616
+     * FALSE otherwise.
2617
+     *
2618
+     * Note that this method will return TRUE if applied to a loaded list.
2619
+     * Also note that this method keeps track of the bean's history regardless whether
2620
+     * it has been stored or not. Storing a bean does not undo it's history,
2621
+     * to clean the history of a bean use: clearHistory().
2622
+     *
2623
+     * @param string  $property  name of the property you want the change-status of
2624
+     *
2625
+     * @return boolean
2626
+     */
2627
+    public function hasChanged( $property )
2628
+    {
2629
+        return ( array_key_exists( $property, $this->properties ) ) ?
2630
+            $this->old( $property ) != $this->properties[$property] : FALSE;
2631
+    }
2632
+
2633
+    /**
2634
+     * Returns TRUE if the specified list exists, has been loaded and has been changed:
2635
+     * beans have been added or deleted. This method will not tell you anything about
2636
+     * the state of the beans in the list.
2637
+     *
2638
+     * @param string $property name of the list to check
2639
+     *
2640
+     * @return boolean
2641
+     */
2642
+    public function hasListChanged( $property )
2643
+    {
2644
+        if ( !array_key_exists( $property, $this->properties ) ) return FALSE;
2645
+        $diffAdded = array_diff_assoc( $this->properties[$property], $this->__info['sys.shadow.'.$property] );
2646
+        if ( count( $diffAdded ) ) return TRUE;
2647
+        $diffMissing = array_diff_assoc( $this->__info['sys.shadow.'.$property], $this->properties[$property] );
2648
+        if ( count( $diffMissing ) ) return TRUE;
2649
+        return FALSE;
2650
+    }
2651
+
2652
+    /**
2653
+     * Clears (syncs) the history of the bean.
2654
+     * Resets all shadow values of the bean to their current value.
2655
+     *
2656
+     * @return self
2657
+     */
2658
+    public function clearHistory()
2659
+    {
2660
+        $this->__info['sys.orig'] = array();
2661
+        foreach( $this->properties as $key => $value ) {
2662
+            if ( is_scalar($value) ) {
2663
+                $this->__info['sys.orig'][$key] = $value;
2664
+            } else {
2665
+                $this->__info['sys.shadow.'.$key] = $value;
2666
+            }
2667
+        }
2668
+        return $this;
2669
+    }
2670
+
2671
+    /**
2672
+     * Creates a N-M relation by linking an intermediate bean.
2673
+     * This method can be used to quickly connect beans using indirect
2674
+     * relations. For instance, given an album and a song you can connect the two
2675
+     * using a track with a number like this:
2676
+     *
2677
+     * Usage:
2678
+     *
2679
+     * $album->link('track', array('number'=>1))->song = $song;
2680
+     *
2681
+     * or:
2682
+     *
2683
+     * $album->link($trackBean)->song = $song;
2684
+     *
2685
+     * What this method does is adding the link bean to the own-list, in this case
2686
+     * ownTrack. If the first argument is a string and the second is an array or
2687
+     * a JSON string then the linking bean gets dispensed on-the-fly as seen in
2688
+     * example #1. After preparing the linking bean, the bean is returned thus
2689
+     * allowing the chained setter: ->song = $song.
2690
+     *
2691
+     * @param string|OODBBean $type          type of bean to dispense or the full bean
2692
+     * @param string|array            $qualification JSON string or array (optional)
2693
+     *
2694
+     * @return OODBBean
2695
+     */
2696
+    public function link( $typeOrBean, $qualification = array() )
2697
+    {
2698
+        if ( is_string( $typeOrBean ) ) {
2699
+
2700
+            $typeOrBean = AQueryWriter::camelsSnake( $typeOrBean );
2701
+
2702
+            $bean = $this->beanHelper->getToolBox()->getRedBean()->dispense( $typeOrBean );
2703
+
2704
+            if ( is_string( $qualification ) ) {
2705
+                $data = json_decode( $qualification, TRUE );
2706
+            } else {
2707
+                $data = $qualification;
2708
+            }
2709
+
2710
+            foreach ( $data as $key => $value ) {
2711
+                $bean->$key = $value;
2712
+            }
2713
+        } else {
2714
+            $bean = $typeOrBean;
2715
+        }
2716
+
2717
+        $list = 'own' . ucfirst( $bean->getMeta( 'type' ) );
2718
+
2719
+        array_push( $this->$list, $bean );
2720
+
2721
+        return $bean;
2722
+    }
2723
+
2724
+    /**
2725
+     * Returns the same bean freshly loaded from the database.
2726
+     *
2727
+     * @return OODBBean
2728
+     */
2729
+    public function fresh()
2730
+    {
2731
+        return $this->beanHelper->getToolbox()->getRedBean()->load( $this->getMeta( 'type' ), $this->properties['id'] );
2732
+    }
2733
+
2734
+    /**
2735
+     * Registers a association renaming globally.
2736
+     *
2737
+     * @param string $via type you wish to use for shared lists
2738
+     *
2739
+     * @return OODBBean
2740
+     */
2741
+    public function via( $via )
2742
+    {
2743
+        $this->via = AQueryWriter::camelsSnake( $via );
2744
+
2745
+        return $this;
2746
+    }
2747
+
2748
+    /**
2749
+     * Counts all own beans of type $type.
2750
+     * Also works with alias(), with() and withCondition().
2751
+     *
2752
+     * @param string $type the type of bean you want to count
2753
+     *
2754
+     * @return integer
2755
+     */
2756
+    public function countOwn( $type )
2757
+    {
2758
+        $type = $this->beau( $type );
2759
+
2760
+        if ( $this->aliasName ) {
2761
+            $myFieldLink     = $this->aliasName . '_id';
2762
+
2763
+            $this->aliasName = NULL;
2764
+        } else {
2765
+            $myFieldLink = $this->__info['type'] . '_id';
2766
+        }
2767
+
2768
+        $count = 0;
2769
+
2770
+        if ( $this->getID() ) {
2771
+
2772
+            $firstKey = NULL;
2773
+            if ( count( $this->withParams ) > 0 ) {
2774
+                reset( $this->withParams );
2775
+                $firstKey = key( $this->withParams );
2776
+            }
2777
+
2778
+            $joinSql = $this->parseJoin( $type );
2779
+
2780
+            if ( !is_numeric( $firstKey ) || $firstKey === NULL ) {
2781
+                    $bindings           = $this->withParams;
2782
+                    $bindings[':slot0'] = $this->getID();
2783
+                    $count              = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings );
2784
+            } else {
2785
+                    $bindings = array_merge( array( $this->getID() ), $this->withParams );
2786
+                    $count    = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings );
2787
+            }
2788
+
2789
+        }
2790
+
2791
+        $this->clearModifiers();
2792
+        return (int) $count;
2793
+    }
2794
+
2795
+    /**
2796
+     * Counts all shared beans of type $type.
2797
+     * Also works with via(), with() and withCondition().
2798
+     *
2799
+     * @param string $type type of bean you wish to count
2800
+     *
2801
+     * @return integer
2802
+     */
2803
+    public function countShared( $type )
2804
+    {
2805
+        $toolbox = $this->beanHelper->getToolbox();
2806
+        $redbean = $toolbox->getRedBean();
2807
+        $writer  = $toolbox->getWriter();
2808
+
2809
+        if ( $this->via ) {
2810
+            $oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
2811
+
2812
+            if ( $oldName !== $this->via ) {
2813
+                //set the new renaming rule
2814
+                $writer->renameAssocTable( $oldName, $this->via );
2815
+                $this->via = NULL;
2816
+            }
2817
+        }
2818
+
2819
+        $type  = $this->beau( $type );
2820
+        $count = 0;
2821
+
2822
+        if ( $this->getID() ) {
2823
+            $count = $redbean->getAssociationManager()->relatedCount( $this, $type, $this->withSql, $this->withParams, TRUE );
2824
+        }
2825
+
2826
+        $this->clearModifiers();
2827
+        return (integer) $count;
2828
+    }
2829
+
2830
+    /**
2831
+     * Iterates through the specified own-list and
2832
+     * fetches all properties (with their type) and
2833
+     * returns the references.
2834
+     * Use this method to quickly load indirectly related
2835
+     * beans in an own-list. Whenever you cannot use a
2836
+     * shared-list this method offers the same convenience
2837
+     * by aggregating the parent beans of all children in
2838
+     * the specified own-list.
2839
+     *
2840
+     * Example:
2841
+     *
2842
+     * $quest->aggr( 'xownQuestTarget', 'target', 'quest' );
2843
+     *
2844
+     * Loads (in batch) and returns references to all
2845
+     * quest beans residing in the $questTarget->target properties
2846
+     * of each element in the xownQuestTargetList.
2847
+     *
2848
+     * @param string $list     the list you wish to process
2849
+     * @param string $property the property to load
2850
+     * @param string $type     the type of bean residing in this property (optional)
2851
+     *
2852
+     * @return array
2853
+     */
2854
+    public function &aggr( $list, $property, $type = NULL )
2855
+    {
2856
+        $this->via = NULL;
2857
+        $ids = $beanIndex = $references = array();
2858
+
2859
+        if ( strlen( $list ) < 4 ) throw new RedException('Invalid own-list.');
2860
+        if ( strpos( $list, 'own') !== 0 ) throw new RedException('Only own-lists can be aggregated.');
2861
+        if ( !ctype_upper( substr( $list, 3, 1 ) ) ) throw new RedException('Invalid own-list.');
2862
+
2863
+        if ( is_null( $type ) ) $type = $property;
2864
+
2865
+        foreach( $this->$list as $bean ) {
2866
+            $field = $property . '_id';
2867
+            if ( isset( $bean->$field)  ) {
2868
+                $ids[] = $bean->$field;
2869
+                $beanIndex[$bean->$field] = $bean;
2870
+            }
2871
+        }
2872
+
2873
+        $beans = $this->beanHelper->getToolBox()->getRedBean()->batch( $type, $ids );
2874
+
2875
+        //now preload the beans as well
2876
+        foreach( $beans as $bean ) {
2877
+            $beanIndex[$bean->id]->setProperty( $property, $bean );
2878
+        }
2879
+
2880
+        foreach( $beanIndex as $indexedBean ) {
2881
+            $references[] = $indexedBean->$property;
2882
+        }
2883
+
2884
+        return $references;
2885
+    }
2886
+
2887
+    /**
2888
+     * Tests whether the database identities of two beans are equal.
2889
+     *
2890
+     * @param OODBBean $bean other bean
2891
+     *
2892
+     * @return boolean
2893
+     */
2894
+    public function equals(OODBBean $bean)
2895
+    {
2896
+        return (bool) (
2897
+                ( (string) $this->properties['id'] === (string) $bean->properties['id'] )
2898
+            && ( (string) $this->__info['type']   === (string) $bean->__info['type']   )
2899
+        );
2900
+    }
2901 2901
 }
2902 2902
 } 
2903 2903
 
@@ -2920,59 +2920,59 @@  discard block
 block discarded – undo
2920 2920
  */
2921 2921
 abstract class Observable { //bracket must be here - otherwise coverage software does not understand.
2922 2922
 
2923
-	/**
2924
-	 * @var array
2925
-	 */
2926
-	private $observers = array();
2927
-
2928
-	/**
2929
-	 * Implementation of the Observer Pattern.
2930
-	 * Adds an event listener to the observable object.
2931
-	 * First argument should be the name of the event you wish to listen for.
2932
-	 * Second argument should be the object that wants to be notified in case
2933
-	 * the event occurs.
2934
-	 *
2935
-	 * @param string           $eventname event identifier
2936
-	 * @param Observer $observer  observer instance
2937
-	 *
2938
-	 * @return void
2939
-	 */
2940
-	public function addEventListener( $eventname, Observer $observer )
2941
-	{
2942
-		if ( !isset( $this->observers[$eventname] ) ) {
2943
-			$this->observers[$eventname] = array();
2944
-		}
2945
-
2946
-		foreach ( $this->observers[$eventname] as $o ) {
2947
-			if ( $o == $observer ) {
2948
-				return;
2949
-			}
2950
-		}
2951
-
2952
-		$this->observers[$eventname][] = $observer;
2953
-	}
2954
-
2955
-	/**
2956
-	 * Notifies listeners.
2957
-	 * Sends the signal $eventname, the event identifier and a message object
2958
-	 * to all observers that have been registered to receive notification for
2959
-	 * this event. Part of the observer pattern implementation in RedBeanPHP.
2960
-	 *
2961
-	 * @param string $eventname event you want signal
2962
-	 * @param mixed  $info      message object to send along
2963
-	 *
2964
-	 * @return void
2965
-	 */
2966
-	public function signal( $eventname, $info )
2967
-	{
2968
-		if ( !isset( $this->observers[$eventname] ) ) {
2969
-			$this->observers[$eventname] = array();
2970
-		}
2971
-
2972
-		foreach ( $this->observers[$eventname] as $observer ) {
2973
-			$observer->onEvent( $eventname, $info );
2974
-		}
2975
-	}
2923
+    /**
2924
+     * @var array
2925
+     */
2926
+    private $observers = array();
2927
+
2928
+    /**
2929
+     * Implementation of the Observer Pattern.
2930
+     * Adds an event listener to the observable object.
2931
+     * First argument should be the name of the event you wish to listen for.
2932
+     * Second argument should be the object that wants to be notified in case
2933
+     * the event occurs.
2934
+     *
2935
+     * @param string           $eventname event identifier
2936
+     * @param Observer $observer  observer instance
2937
+     *
2938
+     * @return void
2939
+     */
2940
+    public function addEventListener( $eventname, Observer $observer )
2941
+    {
2942
+        if ( !isset( $this->observers[$eventname] ) ) {
2943
+            $this->observers[$eventname] = array();
2944
+        }
2945
+
2946
+        foreach ( $this->observers[$eventname] as $o ) {
2947
+            if ( $o == $observer ) {
2948
+                return;
2949
+            }
2950
+        }
2951
+
2952
+        $this->observers[$eventname][] = $observer;
2953
+    }
2954
+
2955
+    /**
2956
+     * Notifies listeners.
2957
+     * Sends the signal $eventname, the event identifier and a message object
2958
+     * to all observers that have been registered to receive notification for
2959
+     * this event. Part of the observer pattern implementation in RedBeanPHP.
2960
+     *
2961
+     * @param string $eventname event you want signal
2962
+     * @param mixed  $info      message object to send along
2963
+     *
2964
+     * @return void
2965
+     */
2966
+    public function signal( $eventname, $info )
2967
+    {
2968
+        if ( !isset( $this->observers[$eventname] ) ) {
2969
+            $this->observers[$eventname] = array();
2970
+        }
2971
+
2972
+        foreach ( $this->observers[$eventname] as $observer ) {
2973
+            $observer->onEvent( $eventname, $info );
2974
+        }
2975
+    }
2976 2976
 }
2977 2977
 } 
2978 2978
 
@@ -2996,18 +2996,18 @@  discard block
 block discarded – undo
2996 2996
  */
2997 2997
 interface Observer
2998 2998
 {
2999
-	/**
3000
-	 * An observer object needs to be capable of receiving
3001
-	 * notifications. Therefore the observer needs to implement the
3002
-	 * onEvent method with two parameters: the event identifier specifying the
3003
-	 * current event and a message object (in RedBeanPHP this can also be a bean).
3004
-	 * 
3005
-	 * @param string $eventname event identifier
3006
-	 * @param mixed  $bean      a message sent along with the notification
3007
-	 *
3008
-	 * @return void
3009
-	 */
3010
-	public function onEvent( $eventname, $bean );
2999
+    /**
3000
+     * An observer object needs to be capable of receiving
3001
+     * notifications. Therefore the observer needs to implement the
3002
+     * onEvent method with two parameters: the event identifier specifying the
3003
+     * current event and a message object (in RedBeanPHP this can also be a bean).
3004
+     * 
3005
+     * @param string $eventname event identifier
3006
+     * @param mixed  $bean      a message sent along with the notification
3007
+     *
3008
+     * @return void
3009
+     */
3010
+    public function onEvent( $eventname, $bean );
3011 3011
 }
3012 3012
 } 
3013 3013
 
@@ -3030,171 +3030,171 @@  discard block
 block discarded – undo
3030 3030
  */
3031 3031
 interface Adapter
3032 3032
 {
3033
-	/**
3034
-	 * Returns the latest SQL statement
3035
-	 *
3036
-	 * @return string
3037
-	 */
3038
-	public function getSQL();
3039
-
3040
-	/**
3041
-	 * Executes an SQL Statement using an array of values to bind
3042
-	 * If $noevent is TRUE then this function will not signal its
3043
-	 * observers to notify about the SQL execution; this to prevent
3044
-	 * infinite recursion when using observers.
3045
-	 *
3046
-	 * @param string  $sql      SQL
3047
-	 * @param array   $bindings values
3048
-	 * @param boolean $noevent  no event firing
3049
-	 */
3050
-	public function exec( $sql, $bindings = array(), $noevent = FALSE );
3051
-
3052
-	/**
3053
-	 * Executes an SQL Query and returns a resultset.
3054
-	 * This method returns a multi dimensional resultset similar to getAll
3055
-	 * The values array can be used to bind values to the place holders in the
3056
-	 * SQL query.
3057
-	 *
3058
-	 * @param string $sql      SQL
3059
-	 * @param array  $bindings values
3060
-	 *
3061
-	 * @return array
3062
-	 */
3063
-	public function get( $sql, $bindings = array() );
3064
-
3065
-	/**
3066
-	 * Executes an SQL Query and returns a resultset.
3067
-	 * This method returns a single row (one array) resultset.
3068
-	 * The values array can be used to bind values to the place holders in the
3069
-	 * SQL query.
3070
-	 *
3071
-	 * @param string $sql      SQL
3072
-	 * @param array  $bindings values to bind
3073
-	 *
3074
-	 * @return array
3075
-	 */
3076
-	public function getRow( $sql, $bindings = array() );
3077
-
3078
-	/**
3079
-	 * Executes an SQL Query and returns a resultset.
3080
-	 * This method returns a single column (one array) resultset.
3081
-	 * The values array can be used to bind values to the place holders in the
3082
-	 * SQL query.
3083
-	 *
3084
-	 * @param string $sql      SQL
3085
-	 * @param array  $bindings values to bind
3086
-	 *
3087
-	 * @return array
3088
-	 */
3089
-	public function getCol( $sql, $bindings = array() );
3090
-
3091
-	/**
3092
-	 * Executes an SQL Query and returns a resultset.
3093
-	 * This method returns a single cell, a scalar value as the resultset.
3094
-	 * The values array can be used to bind values to the place holders in the
3095
-	 * SQL query.
3096
-	 *
3097
-	 * @param string $sql      SQL
3098
-	 * @param array  $bindings values to bind
3099
-	 *
3100
-	 * @return string
3101
-	 */
3102
-	public function getCell( $sql, $bindings = array() );
3103
-
3104
-	/**
3105
-	 * Executes the SQL query specified in $sql and takes
3106
-	 * the first two columns of the resultset. This function transforms the
3107
-	 * resultset into an associative array. Values from the the first column will
3108
-	 * serve as keys while the values of the second column will be used as values.
3109
-	 * The values array can be used to bind values to the place holders in the
3110
-	 * SQL query.
3111
-	 *
3112
-	 * @param string $sql      SQL
3113
-	 * @param array  $bindings values to bind
3114
-	 *
3115
-	 * @return array
3116
-	 */
3117
-	public function getAssoc( $sql, $bindings = array() );
3033
+    /**
3034
+     * Returns the latest SQL statement
3035
+     *
3036
+     * @return string
3037
+     */
3038
+    public function getSQL();
3039
+
3040
+    /**
3041
+     * Executes an SQL Statement using an array of values to bind
3042
+     * If $noevent is TRUE then this function will not signal its
3043
+     * observers to notify about the SQL execution; this to prevent
3044
+     * infinite recursion when using observers.
3045
+     *
3046
+     * @param string  $sql      SQL
3047
+     * @param array   $bindings values
3048
+     * @param boolean $noevent  no event firing
3049
+     */
3050
+    public function exec( $sql, $bindings = array(), $noevent = FALSE );
3051
+
3052
+    /**
3053
+     * Executes an SQL Query and returns a resultset.
3054
+     * This method returns a multi dimensional resultset similar to getAll
3055
+     * The values array can be used to bind values to the place holders in the
3056
+     * SQL query.
3057
+     *
3058
+     * @param string $sql      SQL
3059
+     * @param array  $bindings values
3060
+     *
3061
+     * @return array
3062
+     */
3063
+    public function get( $sql, $bindings = array() );
3064
+
3065
+    /**
3066
+     * Executes an SQL Query and returns a resultset.
3067
+     * This method returns a single row (one array) resultset.
3068
+     * The values array can be used to bind values to the place holders in the
3069
+     * SQL query.
3070
+     *
3071
+     * @param string $sql      SQL
3072
+     * @param array  $bindings values to bind
3073
+     *
3074
+     * @return array
3075
+     */
3076
+    public function getRow( $sql, $bindings = array() );
3077
+
3078
+    /**
3079
+     * Executes an SQL Query and returns a resultset.
3080
+     * This method returns a single column (one array) resultset.
3081
+     * The values array can be used to bind values to the place holders in the
3082
+     * SQL query.
3083
+     *
3084
+     * @param string $sql      SQL
3085
+     * @param array  $bindings values to bind
3086
+     *
3087
+     * @return array
3088
+     */
3089
+    public function getCol( $sql, $bindings = array() );
3090
+
3091
+    /**
3092
+     * Executes an SQL Query and returns a resultset.
3093
+     * This method returns a single cell, a scalar value as the resultset.
3094
+     * The values array can be used to bind values to the place holders in the
3095
+     * SQL query.
3096
+     *
3097
+     * @param string $sql      SQL
3098
+     * @param array  $bindings values to bind
3099
+     *
3100
+     * @return string
3101
+     */
3102
+    public function getCell( $sql, $bindings = array() );
3103
+
3104
+    /**
3105
+     * Executes the SQL query specified in $sql and takes
3106
+     * the first two columns of the resultset. This function transforms the
3107
+     * resultset into an associative array. Values from the the first column will
3108
+     * serve as keys while the values of the second column will be used as values.
3109
+     * The values array can be used to bind values to the place holders in the
3110
+     * SQL query.
3111
+     *
3112
+     * @param string $sql      SQL
3113
+     * @param array  $bindings values to bind
3114
+     *
3115
+     * @return array
3116
+     */
3117
+    public function getAssoc( $sql, $bindings = array() );
3118 3118
 	
3119
-	/**
3120
-	 * Executes the SQL query specified in $sql and indexes
3121
-	 * the row by the first column.
3122
-	 * 
3123
-	 * @param string $sql      SQL
3124
-	 * @param array  $bindings values to bind
3125
-	 *
3126
-	 * @return array
3127
-	 */
3128
-	public function getAssocRow( $sql, $bindings = array() );
3129
-
3130
-	/**
3131
-	 * Returns the latest insert ID.
3132
-	 *
3133
-	 * @return integer
3134
-	 */
3135
-	public function getInsertID();
3136
-
3137
-	/**
3138
-	 * Returns the number of rows that have been
3139
-	 * affected by the last update statement.
3140
-	 *
3141
-	 * @return integer
3142
-	 */
3143
-	public function getAffectedRows();
3144
-
3145
-	/**
3146
-	 * Returns a database agnostic Cursor object.
3147
-	 *
3148
-	 * @param string $sql      SQL
3149
-	 * @param array  $bindings bindings
3150
-	 *
3151
-	 * @return Cursor
3152
-	 */
3153
-	public function getCursor( $sql, $bindings = array() );
3154
-
3155
-	/**
3156
-	 * Returns the original database resource. This is useful if you want to
3157
-	 * perform operations on the driver directly instead of working with the
3158
-	 * adapter. RedBean will only access the adapter and never to talk
3159
-	 * directly to the driver though.
3160
-	 *
3161
-	 * @return object
3162
-	 */
3163
-	public function getDatabase();
3164
-
3165
-	/**
3166
-	 * This method is part of the RedBean Transaction Management
3167
-	 * mechanisms.
3168
-	 * Starts a transaction.
3169
-	 *
3170
-	 * @return void
3171
-	 */
3172
-	public function startTransaction();
3173
-
3174
-	/**
3175
-	 * This method is part of the RedBean Transaction Management
3176
-	 * mechanisms.
3177
-	 * Commits the transaction.
3178
-	 *
3179
-	 * @return void
3180
-	 */
3181
-	public function commit();
3182
-
3183
-	/**
3184
-	 * This method is part of the RedBean Transaction Management
3185
-	 * mechanisms.
3186
-	 * Rolls back the transaction.
3187
-	 *
3188
-	 * @return void
3189
-	 */
3190
-	public function rollback();
3191
-
3192
-	/**
3193
-	 * Closes database connection.
3194
-	 *
3195
-	 * @return void
3196
-	 */
3197
-	public function close();
3119
+    /**
3120
+     * Executes the SQL query specified in $sql and indexes
3121
+     * the row by the first column.
3122
+     * 
3123
+     * @param string $sql      SQL
3124
+     * @param array  $bindings values to bind
3125
+     *
3126
+     * @return array
3127
+     */
3128
+    public function getAssocRow( $sql, $bindings = array() );
3129
+
3130
+    /**
3131
+     * Returns the latest insert ID.
3132
+     *
3133
+     * @return integer
3134
+     */
3135
+    public function getInsertID();
3136
+
3137
+    /**
3138
+     * Returns the number of rows that have been
3139
+     * affected by the last update statement.
3140
+     *
3141
+     * @return integer
3142
+     */
3143
+    public function getAffectedRows();
3144
+
3145
+    /**
3146
+     * Returns a database agnostic Cursor object.
3147
+     *
3148
+     * @param string $sql      SQL
3149
+     * @param array  $bindings bindings
3150
+     *
3151
+     * @return Cursor
3152
+     */
3153
+    public function getCursor( $sql, $bindings = array() );
3154
+
3155
+    /**
3156
+     * Returns the original database resource. This is useful if you want to
3157
+     * perform operations on the driver directly instead of working with the
3158
+     * adapter. RedBean will only access the adapter and never to talk
3159
+     * directly to the driver though.
3160
+     *
3161
+     * @return object
3162
+     */
3163
+    public function getDatabase();
3164
+
3165
+    /**
3166
+     * This method is part of the RedBean Transaction Management
3167
+     * mechanisms.
3168
+     * Starts a transaction.
3169
+     *
3170
+     * @return void
3171
+     */
3172
+    public function startTransaction();
3173
+
3174
+    /**
3175
+     * This method is part of the RedBean Transaction Management
3176
+     * mechanisms.
3177
+     * Commits the transaction.
3178
+     *
3179
+     * @return void
3180
+     */
3181
+    public function commit();
3182
+
3183
+    /**
3184
+     * This method is part of the RedBean Transaction Management
3185
+     * mechanisms.
3186
+     * Rolls back the transaction.
3187
+     *
3188
+     * @return void
3189
+     */
3190
+    public function rollback();
3191
+
3192
+    /**
3193
+     * Closes database connection.
3194
+     *
3195
+     * @return void
3196
+     */
3197
+    public function close();
3198 3198
 }
3199 3199
 }
3200 3200
 
@@ -3225,206 +3225,206 @@  discard block
 block discarded – undo
3225 3225
  */
3226 3226
 class DBAdapter extends Observable implements Adapter
3227 3227
 {
3228
-	/**
3229
-	 * @var Driver
3230
-	 */
3231
-	private $db = NULL;
3232
-
3233
-	/**
3234
-	 * @var string
3235
-	 */
3236
-	private $sql = '';
3237
-
3238
-	/**
3239
-	 * Constructor.
3240
-	 *
3241
-	 * Creates an instance of the RedBean Adapter Class.
3242
-	 * This class provides an interface for RedBean to work
3243
-	 * with ADO compatible DB instances.
3244
-	 *
3245
-	 * @param Driver $database ADO Compatible DB Instance
3246
-	 */
3247
-	public function __construct( $database )
3248
-	{
3249
-		$this->db = $database;
3250
-	}
3251
-
3252
-	/**
3253
-	 * @see Adapter::getSQL
3254
-	 */
3255
-	public function getSQL()
3256
-	{
3257
-		return $this->sql;
3258
-	}
3259
-
3260
-	/**
3261
-	 * @see Adapter::exec
3262
-	 */
3263
-	public function exec( $sql, $bindings = array(), $noevent = FALSE )
3264
-	{
3265
-		if ( !$noevent ) {
3266
-			$this->sql = $sql;
3267
-			$this->signal( 'sql_exec', $this );
3268
-		}
3269
-
3270
-		return $this->db->Execute( $sql, $bindings );
3271
-	}
3272
-
3273
-	/**
3274
-	 * @see Adapter::get
3275
-	 */
3276
-	public function get( $sql, $bindings = array() )
3277
-	{
3278
-		$this->sql = $sql;
3279
-		$this->signal( 'sql_exec', $this );
3280
-
3281
-		return $this->db->GetAll( $sql, $bindings );
3282
-	}
3283
-
3284
-	/**
3285
-	 * @see Adapter::getRow
3286
-	 */
3287
-	public function getRow( $sql, $bindings = array() )
3288
-	{
3289
-		$this->sql = $sql;
3290
-		$this->signal( 'sql_exec', $this );
3291
-
3292
-		return $this->db->GetRow( $sql, $bindings );
3293
-	}
3294
-
3295
-	/**
3296
-	 * @see Adapter::getCol
3297
-	 */
3298
-	public function getCol( $sql, $bindings = array() )
3299
-	{
3300
-		$this->sql = $sql;
3301
-		$this->signal( 'sql_exec', $this );
3302
-
3303
-		return $this->db->GetCol( $sql, $bindings );
3304
-	}
3305
-
3306
-	/**
3307
-	 * @see Adapter::getAssoc
3308
-	 */
3309
-	public function getAssoc( $sql, $bindings = array() )
3310
-	{
3311
-		$this->sql = $sql;
3312
-
3313
-		$this->signal( 'sql_exec', $this );
3314
-
3315
-		$rows  = $this->db->GetAll( $sql, $bindings );
3316
-
3317
-		$assoc = array();
3318
-		if ( !$rows ) {
3319
-			return $assoc;
3320
-		}
3321
-
3322
-		foreach ( $rows as $row ) {
3323
-			if ( empty( $row ) ) continue;
3324
-
3325
-			if ( count( $row ) > 2 ) {
3228
+    /**
3229
+     * @var Driver
3230
+     */
3231
+    private $db = NULL;
3232
+
3233
+    /**
3234
+     * @var string
3235
+     */
3236
+    private $sql = '';
3237
+
3238
+    /**
3239
+     * Constructor.
3240
+     *
3241
+     * Creates an instance of the RedBean Adapter Class.
3242
+     * This class provides an interface for RedBean to work
3243
+     * with ADO compatible DB instances.
3244
+     *
3245
+     * @param Driver $database ADO Compatible DB Instance
3246
+     */
3247
+    public function __construct( $database )
3248
+    {
3249
+        $this->db = $database;
3250
+    }
3251
+
3252
+    /**
3253
+     * @see Adapter::getSQL
3254
+     */
3255
+    public function getSQL()
3256
+    {
3257
+        return $this->sql;
3258
+    }
3259
+
3260
+    /**
3261
+     * @see Adapter::exec
3262
+     */
3263
+    public function exec( $sql, $bindings = array(), $noevent = FALSE )
3264
+    {
3265
+        if ( !$noevent ) {
3266
+            $this->sql = $sql;
3267
+            $this->signal( 'sql_exec', $this );
3268
+        }
3269
+
3270
+        return $this->db->Execute( $sql, $bindings );
3271
+    }
3272
+
3273
+    /**
3274
+     * @see Adapter::get
3275
+     */
3276
+    public function get( $sql, $bindings = array() )
3277
+    {
3278
+        $this->sql = $sql;
3279
+        $this->signal( 'sql_exec', $this );
3280
+
3281
+        return $this->db->GetAll( $sql, $bindings );
3282
+    }
3283
+
3284
+    /**
3285
+     * @see Adapter::getRow
3286
+     */
3287
+    public function getRow( $sql, $bindings = array() )
3288
+    {
3289
+        $this->sql = $sql;
3290
+        $this->signal( 'sql_exec', $this );
3291
+
3292
+        return $this->db->GetRow( $sql, $bindings );
3293
+    }
3294
+
3295
+    /**
3296
+     * @see Adapter::getCol
3297
+     */
3298
+    public function getCol( $sql, $bindings = array() )
3299
+    {
3300
+        $this->sql = $sql;
3301
+        $this->signal( 'sql_exec', $this );
3302
+
3303
+        return $this->db->GetCol( $sql, $bindings );
3304
+    }
3305
+
3306
+    /**
3307
+     * @see Adapter::getAssoc
3308
+     */
3309
+    public function getAssoc( $sql, $bindings = array() )
3310
+    {
3311
+        $this->sql = $sql;
3312
+
3313
+        $this->signal( 'sql_exec', $this );
3314
+
3315
+        $rows  = $this->db->GetAll( $sql, $bindings );
3316
+
3317
+        $assoc = array();
3318
+        if ( !$rows ) {
3319
+            return $assoc;
3320
+        }
3321
+
3322
+        foreach ( $rows as $row ) {
3323
+            if ( empty( $row ) ) continue;
3324
+
3325
+            if ( count( $row ) > 2 ) {
3326 3326
             $key   = array_shift( $row );
3327 3327
             $value = $row;
3328 3328
         } elseif ( count( $row ) > 1 ) {
3329
-				$key   = array_shift( $row );
3330
-				$value = array_shift( $row );
3331
-			} else {
3332
-				$key   = array_shift( $row );
3333
-				$value = $key;
3334
-			}
3335
-
3336
-			$assoc[$key] = $value;
3337
-		}
3338
-
3339
-		return $assoc;
3340
-	}
3329
+                $key   = array_shift( $row );
3330
+                $value = array_shift( $row );
3331
+            } else {
3332
+                $key   = array_shift( $row );
3333
+                $value = $key;
3334
+            }
3335
+
3336
+            $assoc[$key] = $value;
3337
+        }
3338
+
3339
+        return $assoc;
3340
+    }
3341 3341
 	
3342
-	/**
3343
-	 * @see Adapter::getAssocRow
3344
-	 */
3345
-	public function getAssocRow($sql, $bindings = array())
3346
-	{
3347
-		$this->sql = $sql;
3348
-		$this->signal( 'sql_exec', $this );
3349
-
3350
-		return $this->db->GetAssocRow( $sql, $bindings );
3351
-	}
3352
-
3353
-	/**
3354
-	 * @see Adapter::getCell
3355
-	 */
3356
-	public function getCell( $sql, $bindings = array(), $noSignal = NULL )
3357
-	{
3358
-		$this->sql = $sql;
3359
-
3360
-		if ( !$noSignal ) $this->signal( 'sql_exec', $this );
3361
-
3362
-		return $this->db->GetOne( $sql, $bindings );
3363
-	}
3364
-
3365
-	/**
3366
-	 * @see Adapter::getCursor
3367
-	 */
3368
-	public function getCursor( $sql, $bindings = array() )
3369
-	{
3370
-		return $this->db->GetCursor( $sql, $bindings );
3371
-	}
3372
-
3373
-	/**
3374
-	 * @see Adapter::getInsertID
3375
-	 */
3376
-	public function getInsertID()
3377
-	{
3378
-		return $this->db->getInsertID();
3379
-	}
3380
-
3381
-	/**
3382
-	 * @see Adapter::getAffectedRows
3383
-	 */
3384
-	public function getAffectedRows()
3385
-	{
3386
-		return $this->db->Affected_Rows();
3387
-	}
3388
-
3389
-	/**
3390
-	 * @see Adapter::getDatabase
3391
-	 */
3392
-	public function getDatabase()
3393
-	{
3394
-		return $this->db;
3395
-	}
3396
-
3397
-	/**
3398
-	 * @see Adapter::startTransaction
3399
-	 */
3400
-	public function startTransaction()
3401
-	{
3402
-		$this->db->StartTrans();
3403
-	}
3404
-
3405
-	/**
3406
-	 * @see Adapter::commit
3407
-	 */
3408
-	public function commit()
3409
-	{
3410
-		$this->db->CommitTrans();
3411
-	}
3412
-
3413
-	/**
3414
-	 * @see Adapter::rollback
3415
-	 */
3416
-	public function rollback()
3417
-	{
3418
-		$this->db->FailTrans();
3419
-	}
3420
-
3421
-	/**
3422
-	 * @see Adapter::close.
3423
-	 */
3424
-	public function close()
3425
-	{
3426
-		$this->db->close();
3427
-	}
3342
+    /**
3343
+     * @see Adapter::getAssocRow
3344
+     */
3345
+    public function getAssocRow($sql, $bindings = array())
3346
+    {
3347
+        $this->sql = $sql;
3348
+        $this->signal( 'sql_exec', $this );
3349
+
3350
+        return $this->db->GetAssocRow( $sql, $bindings );
3351
+    }
3352
+
3353
+    /**
3354
+     * @see Adapter::getCell
3355
+     */
3356
+    public function getCell( $sql, $bindings = array(), $noSignal = NULL )
3357
+    {
3358
+        $this->sql = $sql;
3359
+
3360
+        if ( !$noSignal ) $this->signal( 'sql_exec', $this );
3361
+
3362
+        return $this->db->GetOne( $sql, $bindings );
3363
+    }
3364
+
3365
+    /**
3366
+     * @see Adapter::getCursor
3367
+     */
3368
+    public function getCursor( $sql, $bindings = array() )
3369
+    {
3370
+        return $this->db->GetCursor( $sql, $bindings );
3371
+    }
3372
+
3373
+    /**
3374
+     * @see Adapter::getInsertID
3375
+     */
3376
+    public function getInsertID()
3377
+    {
3378
+        return $this->db->getInsertID();
3379
+    }
3380
+
3381
+    /**
3382
+     * @see Adapter::getAffectedRows
3383
+     */
3384
+    public function getAffectedRows()
3385
+    {
3386
+        return $this->db->Affected_Rows();
3387
+    }
3388
+
3389
+    /**
3390
+     * @see Adapter::getDatabase
3391
+     */
3392
+    public function getDatabase()
3393
+    {
3394
+        return $this->db;
3395
+    }
3396
+
3397
+    /**
3398
+     * @see Adapter::startTransaction
3399
+     */
3400
+    public function startTransaction()
3401
+    {
3402
+        $this->db->StartTrans();
3403
+    }
3404
+
3405
+    /**
3406
+     * @see Adapter::commit
3407
+     */
3408
+    public function commit()
3409
+    {
3410
+        $this->db->CommitTrans();
3411
+    }
3412
+
3413
+    /**
3414
+     * @see Adapter::rollback
3415
+     */
3416
+    public function rollback()
3417
+    {
3418
+        $this->db->FailTrans();
3419
+    }
3420
+
3421
+    /**
3422
+     * @see Adapter::close.
3423
+     */
3424
+    public function close()
3425
+    {
3426
+        $this->db->close();
3427
+    }
3428 3428
 }
3429 3429
 }
3430 3430
 
@@ -3446,21 +3446,21 @@  discard block
 block discarded – undo
3446 3446
  */
3447 3447
 interface Cursor
3448 3448
 {
3449
-	/**
3450
-	 * Retrieves the next row from the result set.
3451
-	 *
3452
-	 * @return array
3453
-	 */
3454
-	public function getNextItem();
3455
-
3456
-	/**
3457
-	 * Closes the database cursor.
3458
-	 * Some databases require a cursor to be closed before executing
3459
-	 * another statement/opening a new cursor.
3460
-	 *
3461
-	 * @return void
3462
-	 */
3463
-	public function close();
3449
+    /**
3450
+     * Retrieves the next row from the result set.
3451
+     *
3452
+     * @return array
3453
+     */
3454
+    public function getNextItem();
3455
+
3456
+    /**
3457
+     * Closes the database cursor.
3458
+     * Some databases require a cursor to be closed before executing
3459
+     * another statement/opening a new cursor.
3460
+     *
3461
+     * @return void
3462
+     */
3463
+    public function close();
3464 3464
 }
3465 3465
 }
3466 3466
 
@@ -3484,45 +3484,45 @@  discard block
 block discarded – undo
3484 3484
  */
3485 3485
 class PDOCursor implements Cursor
3486 3486
 {
3487
-	/**
3488
-	 * @var PDOStatement
3489
-	 */
3490
-	protected $res;
3491
-
3492
-	/**
3493
-	 * @var string
3494
-	 */
3495
-	protected $fetchStyle;
3496
-
3497
-	/**
3498
-	 * Constructor, creates a new instance of a PDO Database Cursor.
3499
-	 *
3500
-	 * @param PDOStatement  $res        the PDO statement
3501
-	 * @param string        $fetchStyle fetch style constant to use
3502
-	 *
3503
-	 * @return void
3504
-	 */
3505
-	public function __construct( \PDOStatement $res, $fetchStyle )
3506
-	{
3507
-		$this->res = $res;
3508
-		$this->fetchStyle = $fetchStyle;
3509
-	}
3510
-
3511
-	/**
3512
-	 * @see Cursor::getNextItem
3513
-	 */
3514
-	public function getNextItem()
3515
-	{
3516
-		return $this->res->fetch();
3517
-	}
3518
-
3519
-	/**
3520
-	 * @see Cursor::close
3521
-	 */
3522
-	public function close()
3523
-	{
3524
-		$this->res->closeCursor();
3525
-	}
3487
+    /**
3488
+     * @var PDOStatement
3489
+     */
3490
+    protected $res;
3491
+
3492
+    /**
3493
+     * @var string
3494
+     */
3495
+    protected $fetchStyle;
3496
+
3497
+    /**
3498
+     * Constructor, creates a new instance of a PDO Database Cursor.
3499
+     *
3500
+     * @param PDOStatement  $res        the PDO statement
3501
+     * @param string        $fetchStyle fetch style constant to use
3502
+     *
3503
+     * @return void
3504
+     */
3505
+    public function __construct( \PDOStatement $res, $fetchStyle )
3506
+    {
3507
+        $this->res = $res;
3508
+        $this->fetchStyle = $fetchStyle;
3509
+    }
3510
+
3511
+    /**
3512
+     * @see Cursor::getNextItem
3513
+     */
3514
+    public function getNextItem()
3515
+    {
3516
+        return $this->res->fetch();
3517
+    }
3518
+
3519
+    /**
3520
+     * @see Cursor::close
3521
+     */
3522
+    public function close()
3523
+    {
3524
+        $this->res->closeCursor();
3525
+    }
3526 3526
 }
3527 3527
 }
3528 3528
 
@@ -3546,21 +3546,21 @@  discard block
 block discarded – undo
3546 3546
  */
3547 3547
 class NullCursor implements Cursor
3548 3548
 {
3549
-	/**
3550
-	 * @see Cursor::getNextItem
3551
-	 */
3552
-	public function getNextItem()
3553
-	{
3554
-		return NULL;
3555
-	}
3556
-
3557
-	/**
3558
-	 * @see Cursor::close
3559
-	 */
3560
-	public function close()
3561
-	{
3562
-		return NULL;
3563
-	}
3549
+    /**
3550
+     * @see Cursor::getNextItem
3551
+     */
3552
+    public function getNextItem()
3553
+    {
3554
+        return NULL;
3555
+    }
3556
+
3557
+    /**
3558
+     * @see Cursor::close
3559
+     */
3560
+    public function close()
3561
+    {
3562
+        return NULL;
3563
+    }
3564 3564
 }
3565 3565
 }
3566 3566
 
@@ -3594,65 +3594,65 @@  discard block
 block discarded – undo
3594 3594
  */
3595 3595
 class BeanCollection
3596 3596
 {
3597
-	/**
3598
-	 * @var Cursor
3599
-	 */
3600
-	protected $cursor = NULL;
3601
-
3602
-	/**
3603
-	 * @var Repository
3604
-	 */
3605
-	protected $repository = NULL;
3606
-
3607
-	/**
3608
-	 * @var string
3609
-	 */
3610
-	protected $type = NULL;
3597
+    /**
3598
+     * @var Cursor
3599
+     */
3600
+    protected $cursor = NULL;
3601
+
3602
+    /**
3603
+     * @var Repository
3604
+     */
3605
+    protected $repository = NULL;
3606
+
3607
+    /**
3608
+     * @var string
3609
+     */
3610
+    protected $type = NULL;
3611 3611
 	
3612
-	/**
3613
-	 * Constructor, creates a new instance of the BeanCollection.
3614
-	 * 
3615
-	 * @param string     $type       type of beans in this collection
3616
-	 * @param Repository $repository repository to use to generate bean objects
3617
-	 * @param Cursor     $cursor     cursor object to use
3618
-	 *
3619
-	 * @return void
3620
-	 */
3621
-	public function __construct( $type, Repository $repository, Cursor $cursor )
3622
-	{
3623
-		$this->type = $type;
3624
-		$this->cursor = $cursor;
3625
-		$this->repository = $repository;
3626
-	}
3627
-
3628
-	/**
3629
-	 * Returns the next bean in the collection.
3630
-	 * If called the first time, this will return the first bean in the collection.
3631
-	 * If there are no more beans left in the collection, this method
3632
-	 * will return NULL.
3633
-	 *
3634
-	 * @return OODBBean|NULL
3635
-	 */
3636
-	public function next()
3637
-	{
3638
-		$row = $this->cursor->getNextItem();
3639
-		if ( $row ) {
3640
-			$beans = $this->repository->convertToBeans( $this->type, array( $row ) );
3641
-			$bean = array_shift( $beans );
3642
-			return $bean;
3643
-		}
3644
-		return NULL;
3645
-	}
3646
-
3647
-	/**
3648
-	 * Closes the underlying cursor (needed for some databases).
3649
-	 *
3650
-	 * @return void
3651
-	 */
3652
-	public function close()
3653
-	{
3654
-		$this->cursor->close();
3655
-	}
3612
+    /**
3613
+     * Constructor, creates a new instance of the BeanCollection.
3614
+     * 
3615
+     * @param string     $type       type of beans in this collection
3616
+     * @param Repository $repository repository to use to generate bean objects
3617
+     * @param Cursor     $cursor     cursor object to use
3618
+     *
3619
+     * @return void
3620
+     */
3621
+    public function __construct( $type, Repository $repository, Cursor $cursor )
3622
+    {
3623
+        $this->type = $type;
3624
+        $this->cursor = $cursor;
3625
+        $this->repository = $repository;
3626
+    }
3627
+
3628
+    /**
3629
+     * Returns the next bean in the collection.
3630
+     * If called the first time, this will return the first bean in the collection.
3631
+     * If there are no more beans left in the collection, this method
3632
+     * will return NULL.
3633
+     *
3634
+     * @return OODBBean|NULL
3635
+     */
3636
+    public function next()
3637
+    {
3638
+        $row = $this->cursor->getNextItem();
3639
+        if ( $row ) {
3640
+            $beans = $this->repository->convertToBeans( $this->type, array( $row ) );
3641
+            $bean = array_shift( $beans );
3642
+            return $bean;
3643
+        }
3644
+        return NULL;
3645
+    }
3646
+
3647
+    /**
3648
+     * Closes the underlying cursor (needed for some databases).
3649
+     *
3650
+     * @return void
3651
+     */
3652
+    public function close()
3653
+    {
3654
+        $this->cursor->close();
3655
+    }
3656 3656
 }
3657 3657
 }
3658 3658
 
@@ -3685,464 +3685,464 @@  discard block
 block discarded – undo
3685 3685
  */
3686 3686
 interface QueryWriter
3687 3687
 {
3688
-	/**
3689
-	 * SQL filter constants
3690
-	 */
3691
-	const C_SQLFILTER_READ = 'r';
3692
-	const C_SQLFILTER_WRITE = 'w';
3693
-
3694
-	/**
3695
-	 * Query Writer constants.
3696
-	 */
3697
-	const C_SQLSTATE_NO_SUCH_TABLE                  = 1;
3698
-	const C_SQLSTATE_NO_SUCH_COLUMN                 = 2;
3699
-	const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3;
3700
-
3701
-	/**
3702
-	 * Define data type regions
3703
-	 *
3704
-	 * 00 - 80: normal data types
3705
-	 * 80 - 99: special data types, only scan/code if requested
3706
-	 * 99     : specified by user, don't change
3707
-	 */
3708
-	const C_DATATYPE_RANGE_SPECIAL   = 80;
3709
-	const C_DATATYPE_RANGE_SPECIFIED = 99;
3710
-
3711
-	/**
3712
-	 * Define GLUE types for use with glueSQLCondition methods.
3713
-	 * Determines how to prefix a snippet of SQL before appending it
3714
-	 * to other SQL (or integrating it, mixing it otherwise).
3715
-	 *
3716
-	 * WHERE - glue as WHERE condition
3717
-	 * AND   - glue as AND condition
3718
-	 */
3719
-	const C_GLUE_WHERE = 1;
3720
-	const C_GLUE_AND   = 2;
3721
-
3722
-	/**
3723
-	 * Writes an SQL Snippet for a JOIN, returns the
3724
-	 * SQL snippet string.
3725
-	 *
3726
-	 * @note A default implementation is available in AQueryWriter
3727
-	 * unless a database uses very different SQL this should suffice.
3728
-	 *
3729
-	 * @param string $type       source type
3730
-	 * @param string $targetType target type (type to join)
3731
-	 * @param string $leftRight  type of join (possible: 'LEFT', 'RIGHT' or 'INNER').
3732
-	 *
3733
-	 * @return string $joinSQLSnippet
3734
-	 */
3735
-	public function writeJoin( $type, $targetType, $joinType );
3736
-
3737
-	/**
3738
-	 * Glues an SQL snippet to the beginning of a WHERE clause.
3739
-	 * This ensures users don't have to add WHERE to their query snippets.
3740
-	 *
3741
-	 * The snippet gets prefixed with WHERE or AND
3742
-	 * if it starts with a condition.
3743
-	 *
3744
-	 * If the snippet does NOT start with a condition (or this function thinks so)
3745
-	 * the snippet is returned as-is.
3746
-	 *
3747
-	 * The GLUE type determines the prefix:
3748
-	 *
3749
-	 * - NONE  prefixes with WHERE
3750
-	 * - WHERE prefixes with WHERE and replaces AND if snippets starts with AND
3751
-	 * - AND   prefixes with AND
3752
-	 *
3753
-	 * This method will never replace WHERE with AND since a snippet should never
3754
-	 * begin with WHERE in the first place. OR is not supported.
3755
-	 *
3756
-	 * Only a limited set of clauses will be recognized as non-conditions.
3757
-	 * For instance beginning a snippet with complex statements like JOIN or UNION
3758
-	 * will not work. This is too complex for use in a snippet.
3759
-	 *
3760
-	 * @note A default implementation is available in AQueryWriter
3761
-	 * unless a database uses very different SQL this should suffice.
3762
-	 *
3763
-	 * @param string  $sql   SQL Snippet
3764
-	 * @param integer $glue  the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND)
3765
-	 *
3766
-	 * @return string
3767
-	 */
3768
-	public function glueSQLCondition( $sql, $glue = NULL );
3769
-
3770
-	/**
3771
-	 * Determines if there is a LIMIT 1 clause in the SQL.
3772
-	 * If not, it will add a LIMIT 1. (used for findOne).
3773
-	 *
3774
-	 * @note A default implementation is available in AQueryWriter
3775
-	 * unless a database uses very different SQL this should suffice.
3776
-	 *
3777
-	 * @param string $sql query to scan and adjust
3778
-	 *
3779
-	 * @return string
3780
-	 */
3781
-	public function glueLimitOne( $sql );
3782
-
3783
-	/**
3784
-	 * Returns the tables that are in the database.
3785
-	 *
3786
-	 * @return array
3787
-	 */
3788
-	public function getTables();
3789
-
3790
-	/**
3791
-	 * This method will create a table for the bean.
3792
-	 * This methods accepts a type and infers the corresponding table name.
3793
-	 *
3794
-	 * @param string $type type of bean you want to create a table for
3795
-	 *
3796
-	 * @return void
3797
-	 */
3798
-	public function createTable( $type );
3799
-
3800
-	/**
3801
-	 * Returns an array containing all the columns of the specified type.
3802
-	 * The format of the return array looks like this:
3803
-	 * $field => $type where $field is the name of the column and $type
3804
-	 * is a database specific description of the datatype.
3805
-	 *
3806
-	 * This methods accepts a type and infers the corresponding table name.
3807
-	 *
3808
-	 * @param string $type type of bean you want to obtain a column list of
3809
-	 *
3810
-	 * @return array
3811
-	 */
3812
-	public function getColumns( $type );
3813
-
3814
-	/**
3815
-	 * Returns the Column Type Code (integer) that corresponds
3816
-	 * to the given value type. This method is used to determine the minimum
3817
-	 * column type required to represent the given value.
3818
-	 *
3819
-	 * @param string $value value
3820
-	 *
3821
-	 * @return integer
3822
-	 */
3823
-	public function scanType( $value, $alsoScanSpecialForTypes = FALSE );
3824
-
3825
-	/**
3826
-	 * This method will add a column to a table.
3827
-	 * This methods accepts a type and infers the corresponding table name.
3828
-	 *
3829
-	 * @param string  $type   name of the table
3830
-	 * @param string  $column name of the column
3831
-	 * @param integer $field  data type for field
3832
-	 *
3833
-	 * @return void
3834
-	 */
3835
-	public function addColumn( $type, $column, $field );
3836
-
3837
-	/**
3838
-	 * Returns the Type Code for a Column Description.
3839
-	 * Given an SQL column description this method will return the corresponding
3840
-	 * code for the writer. If the include specials flag is set it will also
3841
-	 * return codes for special columns. Otherwise special columns will be identified
3842
-	 * as specified columns.
3843
-	 *
3844
-	 * @param string  $typedescription description
3845
-	 * @param boolean $includeSpecials whether you want to get codes for special columns as well
3846
-	 *
3847
-	 * @return integer
3848
-	 */
3849
-	public function code( $typedescription, $includeSpecials = FALSE );
3850
-
3851
-	/**
3852
-	 * This method will widen the column to the specified data type.
3853
-	 * This methods accepts a type and infers the corresponding table name.
3854
-	 *
3855
-	 * @param string  $type       type / table that needs to be adjusted
3856
-	 * @param string  $column     column that needs to be altered
3857
-	 * @param integer $datatype   target data type
3858
-	 *
3859
-	 * @return void
3860
-	 */
3861
-	public function widenColumn( $type, $column, $datatype );
3862
-
3863
-	/**
3864
-	 * Selects records from the database.
3865
-	 * This methods selects the records from the database that match the specified
3866
-	 * type, conditions (optional) and additional SQL snippet (optional).
3867
-	 *
3868
-	 * @param string $type       name of the table you want to query
3869
-	 * @param array  $conditions criteria ( $column => array( $values ) )
3870
-	 * @param string $addSQL     additional SQL snippet
3871
-	 * @param array  $bindings   bindings for SQL snippet
3872
-	 *
3873
-	 * @return array
3874
-	 */
3875
-	public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
3876
-
3877
-	/**
3878
-	 * Selects records from the database and returns a cursor.
3879
-	 * This methods selects the records from the database that match the specified
3880
-	 * type, conditions (optional) and additional SQL snippet (optional).
3881
-	 *
3882
-	 * @param string $type       name of the table you want to query
3883
-	 * @param array  $conditions criteria ( $column => array( $values ) )
3884
-	 * @param string $addSQL     additional SQL snippet
3885
-	 * @param array  $bindings   bindings for SQL snippet
3886
-	 *
3887
-	 * @return Cursor
3888
-	 */
3889
-	public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() );
3890
-
3891
-	/**
3892
-	 * Returns records through an intermediate type. This method is used to obtain records using a link table and
3893
-	 * allows the SQL snippets to reference columns in the link table for additional filtering or ordering.
3894
-	 *
3895
-	 * @param string $sourceType source type, the reference type you want to use to fetch related items on the other side
3896
-	 * @param string $destType   destination type, the target type you want to get beans of
3897
-	 * @param mixed  $linkID     ID to use for the link table
3898
-	 * @param string $addSql     Additional SQL snippet
3899
-	 * @param array  $bindings   Bindings for SQL snippet
3900
-	 *
3901
-	 * @return array
3902
-	 */
3903
-	public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() );
3904
-
3905
-	/**
3906
-	 * Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation.
3907
-	 *
3908
-	 * @param string $sourceType source type, the first part of the link you're looking for
3909
-	 * @param string $destType   destination type, the second part of the link you're looking for
3910
-	 * @param string $sourceID   ID for the source
3911
-	 * @param string $destID     ID for the destination
3912
-	 *
3913
-	 * @return array|null
3914
-	 */
3915
-	public function queryRecordLink( $sourceType, $destType, $sourceID, $destID );
3916
-
3917
-	/**
3918
-	 * Counts the number of records in the database that match the
3919
-	 * conditions and additional SQL.
3920
-	 *
3921
-	 * @param string $type       name of the table you want to query
3922
-	 * @param array  $conditions criteria ( $column => array( $values ) )
3923
-	 * @param string $addSQL     additional SQL snippet
3924
-	 * @param array  $bindings   bindings for SQL snippet
3925
-	 *
3926
-	 * @return integer
3927
-	 */
3928
-	public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
3929
-
3930
-	/**
3931
-	 * Returns the number of records linked through $linkType and satisfying the SQL in $addSQL/$bindings.
3932
-	 *
3933
-	 * @param string $sourceType source type
3934
-	 * @param string $targetType the thing you want to count
3935
-	 * @param mixed  $linkID     the of the source type
3936
-	 * @param string $addSQL     additional SQL snippet
3937
-	 * @param array  $bindings   bindings for SQL snippet
3938
-	 *
3939
-	 * @return integer
3940
-	 */
3941
-	public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() );
3942
-
3943
-	/**
3944
-	 * Returns all rows of specified type that have been tagged with one of the
3945
-	 * strings in the specified tag list array.
3946
-	 *
3947
-	 * Note that the additional SQL snippet can only be used for pagination,
3948
-	 * the SQL snippet will be appended to the end of the query.
3949
-	 *
3950
-	 * @param string  $type     the bean type you want to query
3951
-	 * @param array   $tagList  an array of strings, each string containing a tag title
3952
-	 * @param boolean $all      if TRUE only return records that have been associated with ALL the tags in the list
3953
-	 * @param string  $addSql   addition SQL snippet, for pagination
3954
-	 * @param array   $bindings parameter bindings for additional SQL snippet
3955
-	 *
3956
-	 * @return array
3957
-	 */
3958
-	public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() );
3959
-
3960
-	/**
3961
-	 * This method should update (or insert a record), it takes
3962
-	 * a table name, a list of update values ( $field => $value ) and an
3963
-	 * primary key ID (optional). If no primary key ID is provided, an
3964
-	 * INSERT will take place.
3965
-	 * Returns the new ID.
3966
-	 * This methods accepts a type and infers the corresponding table name.
3967
-	 *
3968
-	 * @param string  $type            name of the table to update
3969
-	 * @param array   $updatevalues    list of update values
3970
-	 * @param integer $id              optional primary key ID value
3971
-	 *
3972
-	 * @return integer
3973
-	 */
3974
-	public function updateRecord( $type, $updatevalues, $id = NULL );
3975
-
3976
-	/**
3977
-	 * Deletes records from the database.
3978
-	 * @note $addSql is always prefixed with ' WHERE ' or ' AND .'
3979
-	 *
3980
-	 * @param string $type       name of the table you want to query
3981
-	 * @param array  $conditions criteria ( $column => array( $values ) )
3982
-	 * @param string $sql        additional SQL
3983
-	 * @param array  $bindings   bindings
3984
-	 *
3985
-	 * @return void
3986
-	 */
3987
-	public function deleteRecord( $type, $conditions = array(), $addSql = '', $bindings = array() );
3988
-
3989
-	/**
3990
-	 * Deletes all links between $sourceType and $destType in an N-M relation.
3991
-	 *
3992
-	 * @param string $sourceType source type
3993
-	 * @param string $destType   destination type
3994
-	 * @param string $sourceID   source ID
3995
-	 *
3996
-	 * @return void
3997
-	 */
3998
-	public function deleteRelations( $sourceType, $destType, $sourceID );
3999
-
4000
-	/**
4001
-	 * @see QueryWriter::addUniqueConstaint
4002
-	 */
4003
-	public function addUniqueIndex( $type, $columns );
4004
-
4005
-	/**
4006
-	 * This method will add a UNIQUE constraint index to a table on columns $columns.
4007
-	 * This methods accepts a type and infers the corresponding table name.
4008
-	 *
4009
-	 * @param string $type               type
4010
-	 * @param array  $columnsPartOfIndex columns to include in index
4011
-	 *
4012
-	 * @return void
4013
-	 */
4014
-	public function addUniqueConstraint( $type, $columns );
4015
-
4016
-	/**
4017
-	 * This method will check whether the SQL state is in the list of specified states
4018
-	 * and returns TRUE if it does appear in this list or FALSE if it
4019
-	 * does not. The purpose of this method is to translate the database specific state to
4020
-	 * a one of the constants defined in this class and then check whether it is in the list
4021
-	 * of standard states provided.
4022
-	 *
4023
-	 * @param string $state sql state
4024
-	 * @param array  $list  list
4025
-	 *
4026
-	 * @return boolean
4027
-	 */
4028
-	public function sqlStateIn( $state, $list );
4029
-
4030
-	/**
4031
-	 * This method will remove all beans of a certain type.
4032
-	 * This methods accepts a type and infers the corresponding table name.
4033
-	 *
4034
-	 * @param  string $type bean type
4035
-	 *
4036
-	 * @return void
4037
-	 */
4038
-	public function wipe( $type );
4039
-
4040
-	/**
4041
-	 * This method will add a foreign key from type and field to
4042
-	 * target type and target field.
4043
-	 * The foreign key is created without an action. On delete/update
4044
-	 * no action will be triggered. The FK is only used to allow database
4045
-	 * tools to generate pretty diagrams and to make it easy to add actions
4046
-	 * later on.
4047
-	 * This methods accepts a type and infers the corresponding table name.
4048
-	 *
4049
-	 *
4050
-	 * @param  string $type           type that will have a foreign key field
4051
-	 * @param  string $targetType     points to this type
4052
-	 * @param  string $property       field that contains the foreign key value
4053
-	 * @param  string $targetProperty field where the fk points to
4054
-	 * @param  string $isDep          whether target is dependent and should cascade on update/delete
4055
-	 *
4056
-	 * @return void
4057
-	 */
4058
-	public function addFK( $type, $targetType, $property, $targetProperty, $isDep = false );
4059
-
4060
-	/**
4061
-	 * This method will add an index to a type and field with name
4062
-	 * $name.
4063
-	 * This methods accepts a type and infers the corresponding table name.
4064
-	 *
4065
-	 * @param string $type     type to add index to
4066
-	 * @param string $name     name of the new index
4067
-	 * @param string $property field to index
4068
-	 *
4069
-	 * @return void
4070
-	 */
4071
-	public function addIndex( $type, $name, $property );
4072
-
4073
-	/**
4074
-	 * Checks and filters a database structure element like a table of column
4075
-	 * for safe use in a query. A database structure has to conform to the
4076
-	 * RedBeanPHP DB security policy which basically means only alphanumeric
4077
-	 * symbols are allowed. This security policy is more strict than conventional
4078
-	 * SQL policies and does therefore not require database specific escaping rules.
4079
-	 *
4080
-	 * @param string  $databaseStructure name of the column/table to check
4081
-	 * @param boolean $noQuotes          TRUE to NOT put backticks or quotes around the string
4082
-	 *
4083
-	 * @return string
4084
-	 */
4085
-	public function esc( $databaseStructure, $dontQuote = FALSE );
4086
-
4087
-	/**
4088
-	 * Removes all tables and views from the database.
4089
-	 *
4090
-	 * @return void
4091
-	 */
4092
-	public function wipeAll();
4093
-
4094
-	/**
4095
-	 * Renames an association. For instance if you would like to refer to
4096
-	 * album_song as: track you can specify this by calling this method like:
4097
-	 *
4098
-	 * renameAssociation('album_song','track')
4099
-	 *
4100
-	 * This allows:
4101
-	 *
4102
-	 * $album->sharedSong
4103
-	 *
4104
-	 * to add/retrieve beans from track instead of album_song.
4105
-	 * Also works for exportAll().
4106
-	 *
4107
-	 * This method also accepts a single associative array as
4108
-	 * its first argument.
4109
-	 *
4110
-	 * @param string|array $fromType
4111
-	 * @param string       $toType (optional)
4112
-	 *
4113
-	 * @return void
4114
-	 */
4115
-	public function renameAssocTable( $fromType, $toType = NULL );
4116
-
4117
-	/**
4118
-	 * Returns the format for link tables.
4119
-	 * Given an array containing two type names this method returns the
4120
-	 * name of the link table to be used to store and retrieve
4121
-	 * association records. For instance, given two types: person and
4122
-	 * project, the corresponding link table might be: 'person_project'.
4123
-	 *
4124
-	 * @param  array $types two types array($type1, $type2)
4125
-	 *
4126
-	 * @return string
4127
-	 */
4128
-	public function getAssocTable( $types );
4129
-
4130
-	/**
4131
-	 * Given a bean type and a property, this method
4132
-	 * tries to infer the fetch type using the foreign key
4133
-	 * definitions in the database. 
4134
-	 * For instance: project, student -> person.
4135
-	 * If no fetchType can be inferred, this method will return NULL.
4136
-	 *
4137
-	 * @note QueryWriters do not have to implement this method,
4138
-	 * it's optional. A default version is available in AQueryWriter.
4139
-	 *
4140
-	 * @param $type     the source type to fetch a target type for
4141
-	 * @param $property the property to fetch the type of
4142
-	 *
4143
-	 * @return string|NULL
4144
-	 */
4145
-	public function inferFetchType( $type, $property );
3688
+    /**
3689
+     * SQL filter constants
3690
+     */
3691
+    const C_SQLFILTER_READ = 'r';
3692
+    const C_SQLFILTER_WRITE = 'w';
3693
+
3694
+    /**
3695
+     * Query Writer constants.
3696
+     */
3697
+    const C_SQLSTATE_NO_SUCH_TABLE                  = 1;
3698
+    const C_SQLSTATE_NO_SUCH_COLUMN                 = 2;
3699
+    const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3;
3700
+
3701
+    /**
3702
+     * Define data type regions
3703
+     *
3704
+     * 00 - 80: normal data types
3705
+     * 80 - 99: special data types, only scan/code if requested
3706
+     * 99     : specified by user, don't change
3707
+     */
3708
+    const C_DATATYPE_RANGE_SPECIAL   = 80;
3709
+    const C_DATATYPE_RANGE_SPECIFIED = 99;
3710
+
3711
+    /**
3712
+     * Define GLUE types for use with glueSQLCondition methods.
3713
+     * Determines how to prefix a snippet of SQL before appending it
3714
+     * to other SQL (or integrating it, mixing it otherwise).
3715
+     *
3716
+     * WHERE - glue as WHERE condition
3717
+     * AND   - glue as AND condition
3718
+     */
3719
+    const C_GLUE_WHERE = 1;
3720
+    const C_GLUE_AND   = 2;
3721
+
3722
+    /**
3723
+     * Writes an SQL Snippet for a JOIN, returns the
3724
+     * SQL snippet string.
3725
+     *
3726
+     * @note A default implementation is available in AQueryWriter
3727
+     * unless a database uses very different SQL this should suffice.
3728
+     *
3729
+     * @param string $type       source type
3730
+     * @param string $targetType target type (type to join)
3731
+     * @param string $leftRight  type of join (possible: 'LEFT', 'RIGHT' or 'INNER').
3732
+     *
3733
+     * @return string $joinSQLSnippet
3734
+     */
3735
+    public function writeJoin( $type, $targetType, $joinType );
3736
+
3737
+    /**
3738
+     * Glues an SQL snippet to the beginning of a WHERE clause.
3739
+     * This ensures users don't have to add WHERE to their query snippets.
3740
+     *
3741
+     * The snippet gets prefixed with WHERE or AND
3742
+     * if it starts with a condition.
3743
+     *
3744
+     * If the snippet does NOT start with a condition (or this function thinks so)
3745
+     * the snippet is returned as-is.
3746
+     *
3747
+     * The GLUE type determines the prefix:
3748
+     *
3749
+     * - NONE  prefixes with WHERE
3750
+     * - WHERE prefixes with WHERE and replaces AND if snippets starts with AND
3751
+     * - AND   prefixes with AND
3752
+     *
3753
+     * This method will never replace WHERE with AND since a snippet should never
3754
+     * begin with WHERE in the first place. OR is not supported.
3755
+     *
3756
+     * Only a limited set of clauses will be recognized as non-conditions.
3757
+     * For instance beginning a snippet with complex statements like JOIN or UNION
3758
+     * will not work. This is too complex for use in a snippet.
3759
+     *
3760
+     * @note A default implementation is available in AQueryWriter
3761
+     * unless a database uses very different SQL this should suffice.
3762
+     *
3763
+     * @param string  $sql   SQL Snippet
3764
+     * @param integer $glue  the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND)
3765
+     *
3766
+     * @return string
3767
+     */
3768
+    public function glueSQLCondition( $sql, $glue = NULL );
3769
+
3770
+    /**
3771
+     * Determines if there is a LIMIT 1 clause in the SQL.
3772
+     * If not, it will add a LIMIT 1. (used for findOne).
3773
+     *
3774
+     * @note A default implementation is available in AQueryWriter
3775
+     * unless a database uses very different SQL this should suffice.
3776
+     *
3777
+     * @param string $sql query to scan and adjust
3778
+     *
3779
+     * @return string
3780
+     */
3781
+    public function glueLimitOne( $sql );
3782
+
3783
+    /**
3784
+     * Returns the tables that are in the database.
3785
+     *
3786
+     * @return array
3787
+     */
3788
+    public function getTables();
3789
+
3790
+    /**
3791
+     * This method will create a table for the bean.
3792
+     * This methods accepts a type and infers the corresponding table name.
3793
+     *
3794
+     * @param string $type type of bean you want to create a table for
3795
+     *
3796
+     * @return void
3797
+     */
3798
+    public function createTable( $type );
3799
+
3800
+    /**
3801
+     * Returns an array containing all the columns of the specified type.
3802
+     * The format of the return array looks like this:
3803
+     * $field => $type where $field is the name of the column and $type
3804
+     * is a database specific description of the datatype.
3805
+     *
3806
+     * This methods accepts a type and infers the corresponding table name.
3807
+     *
3808
+     * @param string $type type of bean you want to obtain a column list of
3809
+     *
3810
+     * @return array
3811
+     */
3812
+    public function getColumns( $type );
3813
+
3814
+    /**
3815
+     * Returns the Column Type Code (integer) that corresponds
3816
+     * to the given value type. This method is used to determine the minimum
3817
+     * column type required to represent the given value.
3818
+     *
3819
+     * @param string $value value
3820
+     *
3821
+     * @return integer
3822
+     */
3823
+    public function scanType( $value, $alsoScanSpecialForTypes = FALSE );
3824
+
3825
+    /**
3826
+     * This method will add a column to a table.
3827
+     * This methods accepts a type and infers the corresponding table name.
3828
+     *
3829
+     * @param string  $type   name of the table
3830
+     * @param string  $column name of the column
3831
+     * @param integer $field  data type for field
3832
+     *
3833
+     * @return void
3834
+     */
3835
+    public function addColumn( $type, $column, $field );
3836
+
3837
+    /**
3838
+     * Returns the Type Code for a Column Description.
3839
+     * Given an SQL column description this method will return the corresponding
3840
+     * code for the writer. If the include specials flag is set it will also
3841
+     * return codes for special columns. Otherwise special columns will be identified
3842
+     * as specified columns.
3843
+     *
3844
+     * @param string  $typedescription description
3845
+     * @param boolean $includeSpecials whether you want to get codes for special columns as well
3846
+     *
3847
+     * @return integer
3848
+     */
3849
+    public function code( $typedescription, $includeSpecials = FALSE );
3850
+
3851
+    /**
3852
+     * This method will widen the column to the specified data type.
3853
+     * This methods accepts a type and infers the corresponding table name.
3854
+     *
3855
+     * @param string  $type       type / table that needs to be adjusted
3856
+     * @param string  $column     column that needs to be altered
3857
+     * @param integer $datatype   target data type
3858
+     *
3859
+     * @return void
3860
+     */
3861
+    public function widenColumn( $type, $column, $datatype );
3862
+
3863
+    /**
3864
+     * Selects records from the database.
3865
+     * This methods selects the records from the database that match the specified
3866
+     * type, conditions (optional) and additional SQL snippet (optional).
3867
+     *
3868
+     * @param string $type       name of the table you want to query
3869
+     * @param array  $conditions criteria ( $column => array( $values ) )
3870
+     * @param string $addSQL     additional SQL snippet
3871
+     * @param array  $bindings   bindings for SQL snippet
3872
+     *
3873
+     * @return array
3874
+     */
3875
+    public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
3876
+
3877
+    /**
3878
+     * Selects records from the database and returns a cursor.
3879
+     * This methods selects the records from the database that match the specified
3880
+     * type, conditions (optional) and additional SQL snippet (optional).
3881
+     *
3882
+     * @param string $type       name of the table you want to query
3883
+     * @param array  $conditions criteria ( $column => array( $values ) )
3884
+     * @param string $addSQL     additional SQL snippet
3885
+     * @param array  $bindings   bindings for SQL snippet
3886
+     *
3887
+     * @return Cursor
3888
+     */
3889
+    public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() );
3890
+
3891
+    /**
3892
+     * Returns records through an intermediate type. This method is used to obtain records using a link table and
3893
+     * allows the SQL snippets to reference columns in the link table for additional filtering or ordering.
3894
+     *
3895
+     * @param string $sourceType source type, the reference type you want to use to fetch related items on the other side
3896
+     * @param string $destType   destination type, the target type you want to get beans of
3897
+     * @param mixed  $linkID     ID to use for the link table
3898
+     * @param string $addSql     Additional SQL snippet
3899
+     * @param array  $bindings   Bindings for SQL snippet
3900
+     *
3901
+     * @return array
3902
+     */
3903
+    public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() );
3904
+
3905
+    /**
3906
+     * Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation.
3907
+     *
3908
+     * @param string $sourceType source type, the first part of the link you're looking for
3909
+     * @param string $destType   destination type, the second part of the link you're looking for
3910
+     * @param string $sourceID   ID for the source
3911
+     * @param string $destID     ID for the destination
3912
+     *
3913
+     * @return array|null
3914
+     */
3915
+    public function queryRecordLink( $sourceType, $destType, $sourceID, $destID );
3916
+
3917
+    /**
3918
+     * Counts the number of records in the database that match the
3919
+     * conditions and additional SQL.
3920
+     *
3921
+     * @param string $type       name of the table you want to query
3922
+     * @param array  $conditions criteria ( $column => array( $values ) )
3923
+     * @param string $addSQL     additional SQL snippet
3924
+     * @param array  $bindings   bindings for SQL snippet
3925
+     *
3926
+     * @return integer
3927
+     */
3928
+    public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
3929
+
3930
+    /**
3931
+     * Returns the number of records linked through $linkType and satisfying the SQL in $addSQL/$bindings.
3932
+     *
3933
+     * @param string $sourceType source type
3934
+     * @param string $targetType the thing you want to count
3935
+     * @param mixed  $linkID     the of the source type
3936
+     * @param string $addSQL     additional SQL snippet
3937
+     * @param array  $bindings   bindings for SQL snippet
3938
+     *
3939
+     * @return integer
3940
+     */
3941
+    public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() );
3942
+
3943
+    /**
3944
+     * Returns all rows of specified type that have been tagged with one of the
3945
+     * strings in the specified tag list array.
3946
+     *
3947
+     * Note that the additional SQL snippet can only be used for pagination,
3948
+     * the SQL snippet will be appended to the end of the query.
3949
+     *
3950
+     * @param string  $type     the bean type you want to query
3951
+     * @param array   $tagList  an array of strings, each string containing a tag title
3952
+     * @param boolean $all      if TRUE only return records that have been associated with ALL the tags in the list
3953
+     * @param string  $addSql   addition SQL snippet, for pagination
3954
+     * @param array   $bindings parameter bindings for additional SQL snippet
3955
+     *
3956
+     * @return array
3957
+     */
3958
+    public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() );
3959
+
3960
+    /**
3961
+     * This method should update (or insert a record), it takes
3962
+     * a table name, a list of update values ( $field => $value ) and an
3963
+     * primary key ID (optional). If no primary key ID is provided, an
3964
+     * INSERT will take place.
3965
+     * Returns the new ID.
3966
+     * This methods accepts a type and infers the corresponding table name.
3967
+     *
3968
+     * @param string  $type            name of the table to update
3969
+     * @param array   $updatevalues    list of update values
3970
+     * @param integer $id              optional primary key ID value
3971
+     *
3972
+     * @return integer
3973
+     */
3974
+    public function updateRecord( $type, $updatevalues, $id = NULL );
3975
+
3976
+    /**
3977
+     * Deletes records from the database.
3978
+     * @note $addSql is always prefixed with ' WHERE ' or ' AND .'
3979
+     *
3980
+     * @param string $type       name of the table you want to query
3981
+     * @param array  $conditions criteria ( $column => array( $values ) )
3982
+     * @param string $sql        additional SQL
3983
+     * @param array  $bindings   bindings
3984
+     *
3985
+     * @return void
3986
+     */
3987
+    public function deleteRecord( $type, $conditions = array(), $addSql = '', $bindings = array() );
3988
+
3989
+    /**
3990
+     * Deletes all links between $sourceType and $destType in an N-M relation.
3991
+     *
3992
+     * @param string $sourceType source type
3993
+     * @param string $destType   destination type
3994
+     * @param string $sourceID   source ID
3995
+     *
3996
+     * @return void
3997
+     */
3998
+    public function deleteRelations( $sourceType, $destType, $sourceID );
3999
+
4000
+    /**
4001
+     * @see QueryWriter::addUniqueConstaint
4002
+     */
4003
+    public function addUniqueIndex( $type, $columns );
4004
+
4005
+    /**
4006
+     * This method will add a UNIQUE constraint index to a table on columns $columns.
4007
+     * This methods accepts a type and infers the corresponding table name.
4008
+     *
4009
+     * @param string $type               type
4010
+     * @param array  $columnsPartOfIndex columns to include in index
4011
+     *
4012
+     * @return void
4013
+     */
4014
+    public function addUniqueConstraint( $type, $columns );
4015
+
4016
+    /**
4017
+     * This method will check whether the SQL state is in the list of specified states
4018
+     * and returns TRUE if it does appear in this list or FALSE if it
4019
+     * does not. The purpose of this method is to translate the database specific state to
4020
+     * a one of the constants defined in this class and then check whether it is in the list
4021
+     * of standard states provided.
4022
+     *
4023
+     * @param string $state sql state
4024
+     * @param array  $list  list
4025
+     *
4026
+     * @return boolean
4027
+     */
4028
+    public function sqlStateIn( $state, $list );
4029
+
4030
+    /**
4031
+     * This method will remove all beans of a certain type.
4032
+     * This methods accepts a type and infers the corresponding table name.
4033
+     *
4034
+     * @param  string $type bean type
4035
+     *
4036
+     * @return void
4037
+     */
4038
+    public function wipe( $type );
4039
+
4040
+    /**
4041
+     * This method will add a foreign key from type and field to
4042
+     * target type and target field.
4043
+     * The foreign key is created without an action. On delete/update
4044
+     * no action will be triggered. The FK is only used to allow database
4045
+     * tools to generate pretty diagrams and to make it easy to add actions
4046
+     * later on.
4047
+     * This methods accepts a type and infers the corresponding table name.
4048
+     *
4049
+     *
4050
+     * @param  string $type           type that will have a foreign key field
4051
+     * @param  string $targetType     points to this type
4052
+     * @param  string $property       field that contains the foreign key value
4053
+     * @param  string $targetProperty field where the fk points to
4054
+     * @param  string $isDep          whether target is dependent and should cascade on update/delete
4055
+     *
4056
+     * @return void
4057
+     */
4058
+    public function addFK( $type, $targetType, $property, $targetProperty, $isDep = false );
4059
+
4060
+    /**
4061
+     * This method will add an index to a type and field with name
4062
+     * $name.
4063
+     * This methods accepts a type and infers the corresponding table name.
4064
+     *
4065
+     * @param string $type     type to add index to
4066
+     * @param string $name     name of the new index
4067
+     * @param string $property field to index
4068
+     *
4069
+     * @return void
4070
+     */
4071
+    public function addIndex( $type, $name, $property );
4072
+
4073
+    /**
4074
+     * Checks and filters a database structure element like a table of column
4075
+     * for safe use in a query. A database structure has to conform to the
4076
+     * RedBeanPHP DB security policy which basically means only alphanumeric
4077
+     * symbols are allowed. This security policy is more strict than conventional
4078
+     * SQL policies and does therefore not require database specific escaping rules.
4079
+     *
4080
+     * @param string  $databaseStructure name of the column/table to check
4081
+     * @param boolean $noQuotes          TRUE to NOT put backticks or quotes around the string
4082
+     *
4083
+     * @return string
4084
+     */
4085
+    public function esc( $databaseStructure, $dontQuote = FALSE );
4086
+
4087
+    /**
4088
+     * Removes all tables and views from the database.
4089
+     *
4090
+     * @return void
4091
+     */
4092
+    public function wipeAll();
4093
+
4094
+    /**
4095
+     * Renames an association. For instance if you would like to refer to
4096
+     * album_song as: track you can specify this by calling this method like:
4097
+     *
4098
+     * renameAssociation('album_song','track')
4099
+     *
4100
+     * This allows:
4101
+     *
4102
+     * $album->sharedSong
4103
+     *
4104
+     * to add/retrieve beans from track instead of album_song.
4105
+     * Also works for exportAll().
4106
+     *
4107
+     * This method also accepts a single associative array as
4108
+     * its first argument.
4109
+     *
4110
+     * @param string|array $fromType
4111
+     * @param string       $toType (optional)
4112
+     *
4113
+     * @return void
4114
+     */
4115
+    public function renameAssocTable( $fromType, $toType = NULL );
4116
+
4117
+    /**
4118
+     * Returns the format for link tables.
4119
+     * Given an array containing two type names this method returns the
4120
+     * name of the link table to be used to store and retrieve
4121
+     * association records. For instance, given two types: person and
4122
+     * project, the corresponding link table might be: 'person_project'.
4123
+     *
4124
+     * @param  array $types two types array($type1, $type2)
4125
+     *
4126
+     * @return string
4127
+     */
4128
+    public function getAssocTable( $types );
4129
+
4130
+    /**
4131
+     * Given a bean type and a property, this method
4132
+     * tries to infer the fetch type using the foreign key
4133
+     * definitions in the database. 
4134
+     * For instance: project, student -> person.
4135
+     * If no fetchType can be inferred, this method will return NULL.
4136
+     *
4137
+     * @note QueryWriters do not have to implement this method,
4138
+     * it's optional. A default version is available in AQueryWriter.
4139
+     *
4140
+     * @param $type     the source type to fetch a target type for
4141
+     * @param $property the property to fetch the type of
4142
+     *
4143
+     * @return string|NULL
4144
+     */
4145
+    public function inferFetchType( $type, $property );
4146 4146
 }
4147 4147
 }
4148 4148
 
@@ -4172,856 +4172,856 @@  discard block
 block discarded – undo
4172 4172
  */
4173 4173
 abstract class AQueryWriter
4174 4174
 {
4175
-	/**
4176
-	 * @var array
4177
-	 */
4178
-	private static $sqlFilters = array();
4179
-
4180
-	/**
4181
-	 * @var boolean
4182
-	 */
4183
-	private static $flagSQLFilterSafeMode = false;
4184
-
4185
-	/**
4186
-	 * @var boolean
4187
-	 */
4188
-	private static $flagNarrowFieldMode = true;
4189
-
4190
-	/**
4191
-	 * @var array
4192
-	 */
4193
-	public static $renames = array();
4194
-
4195
-	/**
4196
-	 * @var DBAdapter
4197
-	 */
4198
-	protected $adapter;
4199
-
4200
-	/**
4201
-	 * @var string
4202
-	 */
4203
-	protected $defaultValue = 'NULL';
4204
-
4205
-	/**
4206
-	 * @var string
4207
-	 */
4208
-	protected $quoteCharacter = '';
4209
-
4210
-	/**
4211
-	 * @var boolean
4212
-	 */
4213
-	protected $flagUseCache = TRUE;
4214
-
4215
-	/**
4216
-	 * @var array
4217
-	 */
4218
-	protected $cache = array();
4219
-
4220
-	/**
4221
-	 * @var integer
4222
-	 */
4223
-	protected $maxCacheSizePerType = 20;
4224
-
4225
-	/**
4226
-	 * @var array
4227
-	 */
4228
-	public $typeno_sqltype = array();
4229
-
4230
-	/**
4231
-	 * Checks whether a number can be treated like an int.
4232
-	 *
4233
-	 * @param  string $value string representation of a certain value
4234
-	 *
4235
-	 * @return boolean
4236
-	 */
4237
-	public static function canBeTreatedAsInt( $value )
4238
-	{
4239
-		return (bool) ( strval( $value ) === strval( intval( $value ) ) );
4240
-	}
4241
-
4242
-	/**
4243
-	 * @see QueryWriter::getAssocTableFormat
4244
-	 */
4245
-	public static function getAssocTableFormat( $types )
4246
-	{
4247
-		sort( $types );
4248
-
4249
-		$assoc = implode( '_', $types );
4250
-
4251
-		return ( isset( self::$renames[$assoc] ) ) ? self::$renames[$assoc] : $assoc;
4252
-	}
4253
-
4254
-	/**
4255
-	 * @see QueryWriter::renameAssociation
4256
-	 */
4257
-	public static function renameAssociation( $from, $to = NULL )
4258
-	{
4259
-		if ( is_array( $from ) ) {
4260
-			foreach ( $from as $key => $value ) self::$renames[$key] = $value;
4261
-
4262
-			return;
4263
-		}
4264
-
4265
-		self::$renames[$from] = $to;
4266
-	}
4267
-
4268
-	/**
4269
-	 * Globally available service method for RedBeanPHP.
4270
-	 * Converts a camel cased string to a snake cased string.
4271
-	 *
4272
-	 * @param string $camel a camelCased string
4273
-	 *
4274
-	 * @return string
4275
-	 */
4276
-	public static function camelsSnake( $camel )
4277
-	{
4278
-		return strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $camel ) );
4279
-	}
4280
-
4281
-	/**
4282
-	 * Clears renames.
4283
-	 *
4284
-	 * @return void
4285
-	 */
4286
-	public static function clearRenames()
4287
-	{
4288
-		self::$renames = array();
4289
-	}
4290
-
4291
-	/**
4292
-	 * Toggles 'Narrow Field Mode'.
4293
-	 * In Narrow Field mode the queryRecord method will
4294
-	 * narrow its selection field to
4295
-	 *
4296
-	 * SELECT {table}.*
4297
-	 *
4298
-	 * instead of
4299
-	 *
4300
-	 * SELECT *
4301
-	 *
4302
-	 * This is a better way of querying because it allows
4303
-	 * more flexibility (for instance joins). However if you need
4304
-	 * the wide selector for backward compatibility; use this method
4305
-	 * to turn OFF Narrow Field Mode by passing FALSE.
4306
-	 *
4307
-	 * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field
4308
-	 *
4309
-	 * @return void
4310
-	 */
4311
-	public static function setNarrowFieldMode( $narrowField )
4312
-	{
4313
-		self::$flagNarrowFieldMode = (boolean) $narrowField;
4314
-	}
4315
-
4316
-	/**
4317
-	 * Sets SQL filters.
4318
-	 * This is a lowlevel method to set the SQL filter array.
4319
-	 * The format of this array is:
4320
-	 *
4321
-	 * array(
4322
-	 * 		'<MODE, i.e. 'r' for read, 'w' for write>' => array(
4323
-	 * 			'<TABLE NAME>' => array(
4324
-	 * 				'<COLUMN NAME>' => '<SQL>'
4325
-	 * 			)
4326
-	 * 		)
4327
-	 * )
4328
-	 *
4329
-	 * Example:
4330
-	 *
4331
-	 * array(
4332
-	 * QueryWriter::C_SQLFILTER_READ => array(
4333
-	 * 	'book' => array(
4334
-	 * 		'title' => ' LOWER(book.title) '
4335
-	 * 	)
4336
-	 * )
4337
-	 *
4338
-	 * Note that you can use constants instead of magical chars
4339
-	 * as keys for the uppermost array.
4340
-	 * This is a lowlevel method. For a more friendly method
4341
-	 * please take a look at the facade: R::bindFunc().
4342
-	 *
4343
-	 * @param array
4344
-	 */
4345
-	public static function setSQLFilters( $sqlFilters, $safeMode = false )
4346
-	{
4347
-		self::$flagSQLFilterSafeMode = (boolean) $safeMode;
4348
-		self::$sqlFilters = $sqlFilters;
4349
-	}
4350
-
4351
-	/**
4352
-	 * Returns current SQL Filters.
4353
-	 * This method returns the raw SQL filter array.
4354
-	 * This is a lowlevel method. For a more friendly method
4355
-	 * please take a look at the facade: R::bindFunc().
4356
-	 *
4357
-	 * @return array
4358
-	 */
4359
-	public static function getSQLFilters()
4360
-	{
4361
-		return self::$sqlFilters;
4362
-	}
4363
-
4364
-	/**
4365
-	 * Returns a cache key for the cache values passed.
4366
-	 * This method returns a fingerprint string to be used as a key to store
4367
-	 * data in the writer cache.
4368
-	 *
4369
-	 * @param array $keyValues key-value to generate key for
4370
-	 *
4371
-	 * @return string
4372
-	 */
4373
-	private function getCacheKey( $keyValues )
4374
-	{
4375
-		return json_encode( $keyValues );
4376
-	}
4377
-
4378
-	/**
4379
-	 * Returns the values associated with the provided cache tag and key.
4380
-	 *
4381
-	 * @param string $cacheTag cache tag to use for lookup
4382
-	 * @param string $key      key to use for lookup
4383
-	 *
4384
-	 * @return mixed
4385
-	 */
4386
-	private function getCached( $cacheTag, $key )
4387
-	{
4388
-		$sql = $this->adapter->getSQL();
4389
-
4390
-		if ($this->updateCache()) {
4391
-			if ( isset( $this->cache[$cacheTag][$key] ) ) {
4392
-				return $this->cache[$cacheTag][$key];
4393
-			}
4394
-		}
4395
-
4396
-		return NULL;
4397
-	}
4398
-
4399
-	/**
4400
-	 * Checks if the previous query had a keep-cache tag.
4401
-	 * If so, the cache will persist, otherwise the cache will be flushed.
4402
-	 *
4403
-	 * Returns TRUE if the cache will remain and FALSE if a flush has
4404
-	 * been performed.
4405
-	 *
4406
-	 * @return boolean
4407
-	 */
4408
-	private function updateCache()
4409
-	{
4410
-		$sql = $this->adapter->getSQL();
4411
-		if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) {
4412
-			// If SQL has been taken place outside of this method then something else then
4413
-			// a select query might have happened! (or instruct to keep cache)
4414
-			$this->cache = array();
4415
-			return FALSE;
4416
-		}
4417
-		return TRUE;
4418
-	}
4419
-
4420
-	/**
4421
-	 * Stores data from the writer in the cache under a specific key and cache tag.
4422
-	 * A cache tag is used to make sure the cache remains consistent. In most cases the cache tag
4423
-	 * will be the bean type, this makes sure queries associated with a certain reference type will
4424
-	 * never contain conflicting data.
4425
-	 * Why not use the cache tag as a key? Well
4426
-	 * we need to make sure the cache contents fits the key (and key is based on the cache values).
4427
-	 * Otherwise it would be possible to store two different result sets under the same key (the cache tag).
4428
-	 *
4429
-	 * In previous versions you could only store one key-entry, I have changed this to
4430
-	 * improve caching efficiency (issue #400).
4431
-	 *
4432
-	 * @param string $cacheTag cache tag (secondary key)
4433
-	 * @param string $key      key
4434
-	 * @param array  $values   content to be stored
4435
-	 *
4436
-	 * @return void
4437
-	 */
4438
-	private function putResultInCache( $cacheTag, $key, $values )
4439
-	{
4440
-		if ( isset( $this->cache[$cacheTag] ) ) {
4441
-			if ( count( $this->cache[$cacheTag] ) > $this->maxCacheSizePerType ) array_shift( $this->cache[$cacheTag] );
4442
-		} else {
4443
-			$this->cache[$cacheTag] = array();
4444
-		}
4445
-
4446
-		$this->cache[$cacheTag][$key] = $values;
4447
-	}
4448
-
4449
-	/**
4450
-	 * Creates an SQL snippet from a list of conditions of format:
4451
-	 *
4452
-	 * array(
4453
-	 *    key => array(
4454
-	 *           value1, value2, value3 ....
4455
-	 *        )
4456
-	 * )
4457
-	 *
4458
-	 * @param array  $conditions list of conditions
4459
-	 * @param array  $bindings   parameter bindings for SQL snippet
4460
-	 * @param string $addSql     SQL snippet
4461
-	 *
4462
-	 * @return string
4463
-	 */
4464
-	private function makeSQLFromConditions( $conditions, &$bindings, $addSql = '' )
4465
-	{
4466
-		reset( $bindings );
4467
-		$firstKey       = key( $bindings );
4468
-		$paramTypeIsNum = ( is_numeric( $firstKey ) );
4469
-		$counter        = 0;
4470
-
4471
-		$sqlConditions = array();
4472
-		foreach ( $conditions as $column => $values ) {
4473
-			if ( !count( $values ) ) continue;
4474
-
4475
-			$sql = $this->esc( $column );
4476
-			$sql .= ' IN ( ';
4477
-
4478
-			if ( !is_array( $values ) ) $values = array( $values );
4479
-
4480
-			// If it's safe to skip bindings, do so...
4481
-			if ( ctype_digit( implode( '', $values ) ) ) {
4482
-				$sql .= implode( ',', $values ) . ' ) ';
4483
-
4484
-				// only numeric, cant do much harm
4485
-				$sqlConditions[] = $sql;
4486
-			} else {
4487
-
4488
-				if ( $paramTypeIsNum ) {
4489
-					$sql .= implode( ',', array_fill( 0, count( $values ), '?' ) ) . ' ) ';
4490
-
4491
-					array_unshift($sqlConditions, $sql);
4492
-
4493
-					foreach ( $values as $k => $v ) {
4494
-						$values[$k] = strval( $v );
4495
-
4496
-						array_unshift( $bindings, $v );
4497
-					}
4498
-				} else {
4499
-
4500
-					$slots = array();
4501
-
4502
-					foreach( $values as $k => $v ) {
4503
-						$slot            = ':slot'.$counter++;
4504
-						$slots[]         = $slot;
4505
-						$bindings[$slot] = strval( $v );
4506
-					}
4507
-
4508
-					$sql .= implode( ',', $slots ).' ) ';
4509
-					$sqlConditions[] = $sql;
4510
-				}
4511
-			}
4512
-		}
4513
-
4514
-		$sql = '';
4515
-		if ( is_array( $sqlConditions ) && count( $sqlConditions ) > 0 ) {
4516
-			$sql = implode( ' AND ', $sqlConditions );
4517
-			$sql = " WHERE ( $sql ) ";
4518
-
4519
-			if ( $addSql ) $sql .= $addSql;
4520
-		} elseif ( $addSql ) {
4521
-			$sql = $addSql;
4522
-		}
4523
-
4524
-		return $sql;
4525
-	}
4526
-
4527
-	/**
4528
-	 * Returns the table names and column names for a relational query.
4529
-	 *
4530
-	 * @param string  $sourceType type of the source bean
4531
-	 * @param string  $destType   type of the bean you want to obtain using the relation
4532
-	 * @param boolean $noQuote    TRUE if you want to omit quotes
4533
-	 *
4534
-	 * @return array
4535
-	 */
4536
-	private function getRelationalTablesAndColumns( $sourceType, $destType, $noQuote = FALSE )
4537
-	{
4538
-		$linkTable   = $this->esc( $this->getAssocTable( array( $sourceType, $destType ) ), $noQuote );
4539
-		$sourceCol   = $this->esc( $sourceType . '_id', $noQuote );
4540
-
4541
-		if ( $sourceType === $destType ) {
4542
-			$destCol = $this->esc( $destType . '2_id', $noQuote );
4543
-		} else {
4544
-			$destCol = $this->esc( $destType . '_id', $noQuote );
4545
-		}
4546
-
4547
-		$sourceTable = $this->esc( $sourceType, $noQuote );
4548
-		$destTable   = $this->esc( $destType, $noQuote );
4549
-
4550
-		return array( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol );
4551
-	}
4552
-
4553
-	/**
4554
-	 * Given a type and a property name this method
4555
-	 * returns the foreign key map section associated with this pair.
4556
-	 *
4557
-	 * @param string $type     name of the type
4558
-	 * @param string $property name of the property
4559
-	 *
4560
-	 * @return array|NULL
4561
-	 */
4562
-	protected function getForeignKeyForTypeProperty( $type, $property )
4563
-	{
4564
-		$property = $this->esc( $property, TRUE );
4565
-
4566
-		try {
4567
-			$map = $this->getKeyMapForType( $type );
4568
-		} catch ( SQLException $e ) {
4569
-			return NULL;
4570
-		}
4571
-
4572
-		foreach( $map as $key ) {
4573
-			if ( $key['from'] === $property ) return $key;
4574
-		}
4575
-		return NULL;
4576
-	}
4577
-
4578
-	/**
4579
-	 * Returns the foreign key map (FKM) for a type.
4580
-	 * A foreign key map describes the foreign keys in a table.
4581
-	 * A FKM always has the same structure:
4582
-	 *
4583
-	 * array(
4584
-	 * 	'name'      => <name of the foreign key>
4585
-	 *    'from'      => <name of the column on the source table>
4586
-	 *    'table'     => <name of the target table>
4587
-	 *    'to'        => <name of the target column> (most of the time 'id')
4588
-	 *    'on_update' => <update rule: 'SET NULL','CASCADE' or 'RESTRICT'>
4589
-	 *    'on_delete' => <delete rule: 'SET NULL','CASCADE' or 'RESTRICT'>
4590
-	 * )
4591
-	 *
4592
-	 * @note the keys in the result array are FKDLs, i.e. descriptive unique
4593
-	 * keys per source table. Also see: AQueryWriter::makeFKLabel for details.
4594
-	 *
4595
-	 * @param string $type the bean type you wish to obtain a key map of
4596
-	 *
4597
-	 * @return array
4598
-	 */
4599
-	protected function getKeyMapForType( $type )
4600
-	{
4601
-		return array();
4602
-	}
4603
-
4604
-	/**
4605
-	 * This method makes a key for a foreign key description array.
4606
-	 * This key is a readable string unique for every source table.
4607
-	 * This uniform key is called the FKDL Foreign Key Description Label.
4608
-	 * Note that the source table is not part of the FKDL because
4609
-	 * this key is supposed to be 'per source table'. If you wish to
4610
-	 * include a source table, prefix the key with 'on_table_<SOURCE>_'.
4611
-	 *
4612
-	 * @param string $from  the column of the key in the source table
4613
-	 * @param string $type  the type (table) where the key points to
4614
-	 * @param string $to    the target column of the foreign key (mostly just 'id')
4615
-	 *
4616
-	 * @return string
4617
-	 */
4618
-	protected function makeFKLabel($from, $type, $to)
4619
-	{
4620
-		return "from_{$from}_to_table_{$type}_col_{$to}";
4621
-	}
4622
-
4623
-	/**
4624
-	 * Returns an SQL Filter snippet for reading.
4625
-	 *
4626
-	 * @param string $type type of bean
4627
-	 *
4628
-	 * @return string
4629
-	 */
4630
-	protected function getSQLFilterSnippet( $type )
4631
-	{
4632
-		$existingCols = array();
4633
-		if (self::$flagSQLFilterSafeMode) {
4634
-			$existingCols = $this->getColumns( $type );
4635
-		}
4636
-
4637
-		$sqlFilters = array();
4638
-		if ( isset( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] ) ) {
4639
-			foreach( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] as $property => $sqlFilter ) {
4640
-				if ( !self::$flagSQLFilterSafeMode || isset( $existingCols[$property] ) ) {
4641
-					$sqlFilters[] = $sqlFilter.' AS '.$property.' ';
4642
-				}
4643
-			}
4644
-		}
4645
-		$sqlFilterStr = ( count($sqlFilters) ) ? ( ','.implode( ',', $sqlFilters ) ) : '';
4646
-		return $sqlFilterStr;
4647
-	}
4648
-
4649
-	/**
4650
-	 * Generates a list of parameters (slots) for an SQL snippet.
4651
-	 * This method calculates the correct number of slots to insert in the
4652
-	 * SQL snippet and determines the correct type of slot. If the bindings
4653
-	 * array contains named parameters this method will return named ones and
4654
-	 * update the keys in the value list accordingly (that's why we use the &).
4655
-	 *
4656
-	 * If you pass an offset the bindings will be re-added to the value list.
4657
-	 * Some databases cant handle duplicate parameter names in queries.
4658
-	 *
4659
-	 * @param array   &$valueList     list of values to generate slots for (gets modified if needed)
4660
-	 * @param array   $otherBindings  list of additional bindings
4661
-	 * @param integer $offset         start counter at...
4662
-	 *
4663
-	 * @return string
4664
-	 */
4665
-	protected function getParametersForInClause( &$valueList, $otherBindings, $offset = 0 )
4666
-	{
4667
-		if ( is_array( $otherBindings ) && count( $otherBindings ) > 0 ) {
4668
-			reset( $otherBindings );
4669
-
4670
-			$key = key( $otherBindings );
4671
-
4672
-			if ( !is_numeric($key) ) {
4673
-				$filler  = array();
4674
-				$newList = (!$offset) ? array() : $valueList;
4675
-				$counter = $offset;
4676
-
4677
-				foreach( $valueList as $value ) {
4678
-					$slot           = ':slot' . ( $counter++ );
4679
-					$filler[]       = $slot;
4680
-					$newList[$slot] = $value;
4681
-				}
4682
-
4683
-				// Change the keys!
4684
-				$valueList = $newList;
4685
-
4686
-				return implode( ',', $filler );
4687
-			}
4688
-		}
4689
-
4690
-		return implode( ',', array_fill( 0, count( $valueList ), '?' ) );
4691
-	}
4692
-
4693
-	/**
4694
-	 * Adds a data type to the list of data types.
4695
-	 * Use this method to add a new column type definition to the writer.
4696
-	 * Used for UUID support.
4697
-	 *
4698
-	 * @param integer $dataTypeID    magic number constant assigned to this data type
4699
-	 * @param string  $SQLDefinition SQL column definition (i.e. INT(11))
4700
-	 *
4701
-	 * @return self
4702
-	 */
4703
-	protected function addDataType( $dataTypeID, $SQLDefinition )
4704
-	{
4705
-		$this->typeno_sqltype[ $dataTypeID ] = $SQLDefinition;
4706
-		$this->sqltype_typeno[ $SQLDefinition ] = $dataTypeID;
4707
-		return $this;
4708
-	}
4709
-
4710
-	/**
4711
-	 * Returns the sql that should follow an insert statement.
4712
-	 *
4713
-	 * @param string $table name
4714
-	 *
4715
-	 * @return string
4716
-	 */
4717
-	protected function getInsertSuffix( $table )
4718
-	{
4719
-		return '';
4720
-	}
4721
-
4722
-	/**
4723
-	 * Checks whether a value starts with zeros. In this case
4724
-	 * the value should probably be stored using a text datatype instead of a
4725
-	 * numerical type in order to preserve the zeros.
4726
-	 *
4727
-	 * @param string $value value to be checked.
4728
-	 *
4729
-	 * @return boolean
4730
-	 */
4731
-	protected function startsWithZeros( $value )
4732
-	{
4733
-		$value = strval( $value );
4734
-
4735
-		if ( strlen( $value ) > 1 && strpos( $value, '0' ) === 0 && strpos( $value, '0.' ) !== 0 ) {
4736
-			return TRUE;
4737
-		} else {
4738
-			return FALSE;
4739
-		}
4740
-	}
4741
-
4742
-	/**
4743
-	 * Inserts a record into the database using a series of insert columns
4744
-	 * and corresponding insertvalues. Returns the insert id.
4745
-	 *
4746
-	 * @param string $table         table to perform query on
4747
-	 * @param array  $insertcolumns columns to be inserted
4748
-	 * @param array  $insertvalues  values to be inserted
4749
-	 *
4750
-	 * @return integer
4751
-	 */
4752
-	protected function insertRecord( $type, $insertcolumns, $insertvalues )
4753
-	{
4754
-		$default = $this->defaultValue;
4755
-		$suffix  = $this->getInsertSuffix( $type );
4756
-		$table   = $this->esc( $type );
4757
-
4758
-		if ( count( $insertvalues ) > 0 && is_array( $insertvalues[0] ) && count( $insertvalues[0] ) > 0 ) {
4759
-
4760
-			$insertSlots = array();
4761
-			foreach ( $insertcolumns as $k => $v ) {
4762
-				$insertcolumns[$k] = $this->esc( $v );
4763
-
4764
-				if (isset(self::$sqlFilters['w'][$type][$v])) {
4765
-					$insertSlots[] = self::$sqlFilters['w'][$type][$v];
4766
-				} else {
4767
-					$insertSlots[] = '?';
4768
-				}
4769
-			}
4770
-
4771
-			$insertSQL = "INSERT INTO $table ( id, " . implode( ',', $insertcolumns ) . " ) VALUES
4175
+    /**
4176
+     * @var array
4177
+     */
4178
+    private static $sqlFilters = array();
4179
+
4180
+    /**
4181
+     * @var boolean
4182
+     */
4183
+    private static $flagSQLFilterSafeMode = false;
4184
+
4185
+    /**
4186
+     * @var boolean
4187
+     */
4188
+    private static $flagNarrowFieldMode = true;
4189
+
4190
+    /**
4191
+     * @var array
4192
+     */
4193
+    public static $renames = array();
4194
+
4195
+    /**
4196
+     * @var DBAdapter
4197
+     */
4198
+    protected $adapter;
4199
+
4200
+    /**
4201
+     * @var string
4202
+     */
4203
+    protected $defaultValue = 'NULL';
4204
+
4205
+    /**
4206
+     * @var string
4207
+     */
4208
+    protected $quoteCharacter = '';
4209
+
4210
+    /**
4211
+     * @var boolean
4212
+     */
4213
+    protected $flagUseCache = TRUE;
4214
+
4215
+    /**
4216
+     * @var array
4217
+     */
4218
+    protected $cache = array();
4219
+
4220
+    /**
4221
+     * @var integer
4222
+     */
4223
+    protected $maxCacheSizePerType = 20;
4224
+
4225
+    /**
4226
+     * @var array
4227
+     */
4228
+    public $typeno_sqltype = array();
4229
+
4230
+    /**
4231
+     * Checks whether a number can be treated like an int.
4232
+     *
4233
+     * @param  string $value string representation of a certain value
4234
+     *
4235
+     * @return boolean
4236
+     */
4237
+    public static function canBeTreatedAsInt( $value )
4238
+    {
4239
+        return (bool) ( strval( $value ) === strval( intval( $value ) ) );
4240
+    }
4241
+
4242
+    /**
4243
+     * @see QueryWriter::getAssocTableFormat
4244
+     */
4245
+    public static function getAssocTableFormat( $types )
4246
+    {
4247
+        sort( $types );
4248
+
4249
+        $assoc = implode( '_', $types );
4250
+
4251
+        return ( isset( self::$renames[$assoc] ) ) ? self::$renames[$assoc] : $assoc;
4252
+    }
4253
+
4254
+    /**
4255
+     * @see QueryWriter::renameAssociation
4256
+     */
4257
+    public static function renameAssociation( $from, $to = NULL )
4258
+    {
4259
+        if ( is_array( $from ) ) {
4260
+            foreach ( $from as $key => $value ) self::$renames[$key] = $value;
4261
+
4262
+            return;
4263
+        }
4264
+
4265
+        self::$renames[$from] = $to;
4266
+    }
4267
+
4268
+    /**
4269
+     * Globally available service method for RedBeanPHP.
4270
+     * Converts a camel cased string to a snake cased string.
4271
+     *
4272
+     * @param string $camel a camelCased string
4273
+     *
4274
+     * @return string
4275
+     */
4276
+    public static function camelsSnake( $camel )
4277
+    {
4278
+        return strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $camel ) );
4279
+    }
4280
+
4281
+    /**
4282
+     * Clears renames.
4283
+     *
4284
+     * @return void
4285
+     */
4286
+    public static function clearRenames()
4287
+    {
4288
+        self::$renames = array();
4289
+    }
4290
+
4291
+    /**
4292
+     * Toggles 'Narrow Field Mode'.
4293
+     * In Narrow Field mode the queryRecord method will
4294
+     * narrow its selection field to
4295
+     *
4296
+     * SELECT {table}.*
4297
+     *
4298
+     * instead of
4299
+     *
4300
+     * SELECT *
4301
+     *
4302
+     * This is a better way of querying because it allows
4303
+     * more flexibility (for instance joins). However if you need
4304
+     * the wide selector for backward compatibility; use this method
4305
+     * to turn OFF Narrow Field Mode by passing FALSE.
4306
+     *
4307
+     * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field
4308
+     *
4309
+     * @return void
4310
+     */
4311
+    public static function setNarrowFieldMode( $narrowField )
4312
+    {
4313
+        self::$flagNarrowFieldMode = (boolean) $narrowField;
4314
+    }
4315
+
4316
+    /**
4317
+     * Sets SQL filters.
4318
+     * This is a lowlevel method to set the SQL filter array.
4319
+     * The format of this array is:
4320
+     *
4321
+     * array(
4322
+     * 		'<MODE, i.e. 'r' for read, 'w' for write>' => array(
4323
+     * 			'<TABLE NAME>' => array(
4324
+     * 				'<COLUMN NAME>' => '<SQL>'
4325
+     * 			)
4326
+     * 		)
4327
+     * )
4328
+     *
4329
+     * Example:
4330
+     *
4331
+     * array(
4332
+     * QueryWriter::C_SQLFILTER_READ => array(
4333
+     * 	'book' => array(
4334
+     * 		'title' => ' LOWER(book.title) '
4335
+     * 	)
4336
+     * )
4337
+     *
4338
+     * Note that you can use constants instead of magical chars
4339
+     * as keys for the uppermost array.
4340
+     * This is a lowlevel method. For a more friendly method
4341
+     * please take a look at the facade: R::bindFunc().
4342
+     *
4343
+     * @param array
4344
+     */
4345
+    public static function setSQLFilters( $sqlFilters, $safeMode = false )
4346
+    {
4347
+        self::$flagSQLFilterSafeMode = (boolean) $safeMode;
4348
+        self::$sqlFilters = $sqlFilters;
4349
+    }
4350
+
4351
+    /**
4352
+     * Returns current SQL Filters.
4353
+     * This method returns the raw SQL filter array.
4354
+     * This is a lowlevel method. For a more friendly method
4355
+     * please take a look at the facade: R::bindFunc().
4356
+     *
4357
+     * @return array
4358
+     */
4359
+    public static function getSQLFilters()
4360
+    {
4361
+        return self::$sqlFilters;
4362
+    }
4363
+
4364
+    /**
4365
+     * Returns a cache key for the cache values passed.
4366
+     * This method returns a fingerprint string to be used as a key to store
4367
+     * data in the writer cache.
4368
+     *
4369
+     * @param array $keyValues key-value to generate key for
4370
+     *
4371
+     * @return string
4372
+     */
4373
+    private function getCacheKey( $keyValues )
4374
+    {
4375
+        return json_encode( $keyValues );
4376
+    }
4377
+
4378
+    /**
4379
+     * Returns the values associated with the provided cache tag and key.
4380
+     *
4381
+     * @param string $cacheTag cache tag to use for lookup
4382
+     * @param string $key      key to use for lookup
4383
+     *
4384
+     * @return mixed
4385
+     */
4386
+    private function getCached( $cacheTag, $key )
4387
+    {
4388
+        $sql = $this->adapter->getSQL();
4389
+
4390
+        if ($this->updateCache()) {
4391
+            if ( isset( $this->cache[$cacheTag][$key] ) ) {
4392
+                return $this->cache[$cacheTag][$key];
4393
+            }
4394
+        }
4395
+
4396
+        return NULL;
4397
+    }
4398
+
4399
+    /**
4400
+     * Checks if the previous query had a keep-cache tag.
4401
+     * If so, the cache will persist, otherwise the cache will be flushed.
4402
+     *
4403
+     * Returns TRUE if the cache will remain and FALSE if a flush has
4404
+     * been performed.
4405
+     *
4406
+     * @return boolean
4407
+     */
4408
+    private function updateCache()
4409
+    {
4410
+        $sql = $this->adapter->getSQL();
4411
+        if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) {
4412
+            // If SQL has been taken place outside of this method then something else then
4413
+            // a select query might have happened! (or instruct to keep cache)
4414
+            $this->cache = array();
4415
+            return FALSE;
4416
+        }
4417
+        return TRUE;
4418
+    }
4419
+
4420
+    /**
4421
+     * Stores data from the writer in the cache under a specific key and cache tag.
4422
+     * A cache tag is used to make sure the cache remains consistent. In most cases the cache tag
4423
+     * will be the bean type, this makes sure queries associated with a certain reference type will
4424
+     * never contain conflicting data.
4425
+     * Why not use the cache tag as a key? Well
4426
+     * we need to make sure the cache contents fits the key (and key is based on the cache values).
4427
+     * Otherwise it would be possible to store two different result sets under the same key (the cache tag).
4428
+     *
4429
+     * In previous versions you could only store one key-entry, I have changed this to
4430
+     * improve caching efficiency (issue #400).
4431
+     *
4432
+     * @param string $cacheTag cache tag (secondary key)
4433
+     * @param string $key      key
4434
+     * @param array  $values   content to be stored
4435
+     *
4436
+     * @return void
4437
+     */
4438
+    private function putResultInCache( $cacheTag, $key, $values )
4439
+    {
4440
+        if ( isset( $this->cache[$cacheTag] ) ) {
4441
+            if ( count( $this->cache[$cacheTag] ) > $this->maxCacheSizePerType ) array_shift( $this->cache[$cacheTag] );
4442
+        } else {
4443
+            $this->cache[$cacheTag] = array();
4444
+        }
4445
+
4446
+        $this->cache[$cacheTag][$key] = $values;
4447
+    }
4448
+
4449
+    /**
4450
+     * Creates an SQL snippet from a list of conditions of format:
4451
+     *
4452
+     * array(
4453
+     *    key => array(
4454
+     *           value1, value2, value3 ....
4455
+     *        )
4456
+     * )
4457
+     *
4458
+     * @param array  $conditions list of conditions
4459
+     * @param array  $bindings   parameter bindings for SQL snippet
4460
+     * @param string $addSql     SQL snippet
4461
+     *
4462
+     * @return string
4463
+     */
4464
+    private function makeSQLFromConditions( $conditions, &$bindings, $addSql = '' )
4465
+    {
4466
+        reset( $bindings );
4467
+        $firstKey       = key( $bindings );
4468
+        $paramTypeIsNum = ( is_numeric( $firstKey ) );
4469
+        $counter        = 0;
4470
+
4471
+        $sqlConditions = array();
4472
+        foreach ( $conditions as $column => $values ) {
4473
+            if ( !count( $values ) ) continue;
4474
+
4475
+            $sql = $this->esc( $column );
4476
+            $sql .= ' IN ( ';
4477
+
4478
+            if ( !is_array( $values ) ) $values = array( $values );
4479
+
4480
+            // If it's safe to skip bindings, do so...
4481
+            if ( ctype_digit( implode( '', $values ) ) ) {
4482
+                $sql .= implode( ',', $values ) . ' ) ';
4483
+
4484
+                // only numeric, cant do much harm
4485
+                $sqlConditions[] = $sql;
4486
+            } else {
4487
+
4488
+                if ( $paramTypeIsNum ) {
4489
+                    $sql .= implode( ',', array_fill( 0, count( $values ), '?' ) ) . ' ) ';
4490
+
4491
+                    array_unshift($sqlConditions, $sql);
4492
+
4493
+                    foreach ( $values as $k => $v ) {
4494
+                        $values[$k] = strval( $v );
4495
+
4496
+                        array_unshift( $bindings, $v );
4497
+                    }
4498
+                } else {
4499
+
4500
+                    $slots = array();
4501
+
4502
+                    foreach( $values as $k => $v ) {
4503
+                        $slot            = ':slot'.$counter++;
4504
+                        $slots[]         = $slot;
4505
+                        $bindings[$slot] = strval( $v );
4506
+                    }
4507
+
4508
+                    $sql .= implode( ',', $slots ).' ) ';
4509
+                    $sqlConditions[] = $sql;
4510
+                }
4511
+            }
4512
+        }
4513
+
4514
+        $sql = '';
4515
+        if ( is_array( $sqlConditions ) && count( $sqlConditions ) > 0 ) {
4516
+            $sql = implode( ' AND ', $sqlConditions );
4517
+            $sql = " WHERE ( $sql ) ";
4518
+
4519
+            if ( $addSql ) $sql .= $addSql;
4520
+        } elseif ( $addSql ) {
4521
+            $sql = $addSql;
4522
+        }
4523
+
4524
+        return $sql;
4525
+    }
4526
+
4527
+    /**
4528
+     * Returns the table names and column names for a relational query.
4529
+     *
4530
+     * @param string  $sourceType type of the source bean
4531
+     * @param string  $destType   type of the bean you want to obtain using the relation
4532
+     * @param boolean $noQuote    TRUE if you want to omit quotes
4533
+     *
4534
+     * @return array
4535
+     */
4536
+    private function getRelationalTablesAndColumns( $sourceType, $destType, $noQuote = FALSE )
4537
+    {
4538
+        $linkTable   = $this->esc( $this->getAssocTable( array( $sourceType, $destType ) ), $noQuote );
4539
+        $sourceCol   = $this->esc( $sourceType . '_id', $noQuote );
4540
+
4541
+        if ( $sourceType === $destType ) {
4542
+            $destCol = $this->esc( $destType . '2_id', $noQuote );
4543
+        } else {
4544
+            $destCol = $this->esc( $destType . '_id', $noQuote );
4545
+        }
4546
+
4547
+        $sourceTable = $this->esc( $sourceType, $noQuote );
4548
+        $destTable   = $this->esc( $destType, $noQuote );
4549
+
4550
+        return array( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol );
4551
+    }
4552
+
4553
+    /**
4554
+     * Given a type and a property name this method
4555
+     * returns the foreign key map section associated with this pair.
4556
+     *
4557
+     * @param string $type     name of the type
4558
+     * @param string $property name of the property
4559
+     *
4560
+     * @return array|NULL
4561
+     */
4562
+    protected function getForeignKeyForTypeProperty( $type, $property )
4563
+    {
4564
+        $property = $this->esc( $property, TRUE );
4565
+
4566
+        try {
4567
+            $map = $this->getKeyMapForType( $type );
4568
+        } catch ( SQLException $e ) {
4569
+            return NULL;
4570
+        }
4571
+
4572
+        foreach( $map as $key ) {
4573
+            if ( $key['from'] === $property ) return $key;
4574
+        }
4575
+        return NULL;
4576
+    }
4577
+
4578
+    /**
4579
+     * Returns the foreign key map (FKM) for a type.
4580
+     * A foreign key map describes the foreign keys in a table.
4581
+     * A FKM always has the same structure:
4582
+     *
4583
+     * array(
4584
+     * 	'name'      => <name of the foreign key>
4585
+     *    'from'      => <name of the column on the source table>
4586
+     *    'table'     => <name of the target table>
4587
+     *    'to'        => <name of the target column> (most of the time 'id')
4588
+     *    'on_update' => <update rule: 'SET NULL','CASCADE' or 'RESTRICT'>
4589
+     *    'on_delete' => <delete rule: 'SET NULL','CASCADE' or 'RESTRICT'>
4590
+     * )
4591
+     *
4592
+     * @note the keys in the result array are FKDLs, i.e. descriptive unique
4593
+     * keys per source table. Also see: AQueryWriter::makeFKLabel for details.
4594
+     *
4595
+     * @param string $type the bean type you wish to obtain a key map of
4596
+     *
4597
+     * @return array
4598
+     */
4599
+    protected function getKeyMapForType( $type )
4600
+    {
4601
+        return array();
4602
+    }
4603
+
4604
+    /**
4605
+     * This method makes a key for a foreign key description array.
4606
+     * This key is a readable string unique for every source table.
4607
+     * This uniform key is called the FKDL Foreign Key Description Label.
4608
+     * Note that the source table is not part of the FKDL because
4609
+     * this key is supposed to be 'per source table'. If you wish to
4610
+     * include a source table, prefix the key with 'on_table_<SOURCE>_'.
4611
+     *
4612
+     * @param string $from  the column of the key in the source table
4613
+     * @param string $type  the type (table) where the key points to
4614
+     * @param string $to    the target column of the foreign key (mostly just 'id')
4615
+     *
4616
+     * @return string
4617
+     */
4618
+    protected function makeFKLabel($from, $type, $to)
4619
+    {
4620
+        return "from_{$from}_to_table_{$type}_col_{$to}";
4621
+    }
4622
+
4623
+    /**
4624
+     * Returns an SQL Filter snippet for reading.
4625
+     *
4626
+     * @param string $type type of bean
4627
+     *
4628
+     * @return string
4629
+     */
4630
+    protected function getSQLFilterSnippet( $type )
4631
+    {
4632
+        $existingCols = array();
4633
+        if (self::$flagSQLFilterSafeMode) {
4634
+            $existingCols = $this->getColumns( $type );
4635
+        }
4636
+
4637
+        $sqlFilters = array();
4638
+        if ( isset( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] ) ) {
4639
+            foreach( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] as $property => $sqlFilter ) {
4640
+                if ( !self::$flagSQLFilterSafeMode || isset( $existingCols[$property] ) ) {
4641
+                    $sqlFilters[] = $sqlFilter.' AS '.$property.' ';
4642
+                }
4643
+            }
4644
+        }
4645
+        $sqlFilterStr = ( count($sqlFilters) ) ? ( ','.implode( ',', $sqlFilters ) ) : '';
4646
+        return $sqlFilterStr;
4647
+    }
4648
+
4649
+    /**
4650
+     * Generates a list of parameters (slots) for an SQL snippet.
4651
+     * This method calculates the correct number of slots to insert in the
4652
+     * SQL snippet and determines the correct type of slot. If the bindings
4653
+     * array contains named parameters this method will return named ones and
4654
+     * update the keys in the value list accordingly (that's why we use the &).
4655
+     *
4656
+     * If you pass an offset the bindings will be re-added to the value list.
4657
+     * Some databases cant handle duplicate parameter names in queries.
4658
+     *
4659
+     * @param array   &$valueList     list of values to generate slots for (gets modified if needed)
4660
+     * @param array   $otherBindings  list of additional bindings
4661
+     * @param integer $offset         start counter at...
4662
+     *
4663
+     * @return string
4664
+     */
4665
+    protected function getParametersForInClause( &$valueList, $otherBindings, $offset = 0 )
4666
+    {
4667
+        if ( is_array( $otherBindings ) && count( $otherBindings ) > 0 ) {
4668
+            reset( $otherBindings );
4669
+
4670
+            $key = key( $otherBindings );
4671
+
4672
+            if ( !is_numeric($key) ) {
4673
+                $filler  = array();
4674
+                $newList = (!$offset) ? array() : $valueList;
4675
+                $counter = $offset;
4676
+
4677
+                foreach( $valueList as $value ) {
4678
+                    $slot           = ':slot' . ( $counter++ );
4679
+                    $filler[]       = $slot;
4680
+                    $newList[$slot] = $value;
4681
+                }
4682
+
4683
+                // Change the keys!
4684
+                $valueList = $newList;
4685
+
4686
+                return implode( ',', $filler );
4687
+            }
4688
+        }
4689
+
4690
+        return implode( ',', array_fill( 0, count( $valueList ), '?' ) );
4691
+    }
4692
+
4693
+    /**
4694
+     * Adds a data type to the list of data types.
4695
+     * Use this method to add a new column type definition to the writer.
4696
+     * Used for UUID support.
4697
+     *
4698
+     * @param integer $dataTypeID    magic number constant assigned to this data type
4699
+     * @param string  $SQLDefinition SQL column definition (i.e. INT(11))
4700
+     *
4701
+     * @return self
4702
+     */
4703
+    protected function addDataType( $dataTypeID, $SQLDefinition )
4704
+    {
4705
+        $this->typeno_sqltype[ $dataTypeID ] = $SQLDefinition;
4706
+        $this->sqltype_typeno[ $SQLDefinition ] = $dataTypeID;
4707
+        return $this;
4708
+    }
4709
+
4710
+    /**
4711
+     * Returns the sql that should follow an insert statement.
4712
+     *
4713
+     * @param string $table name
4714
+     *
4715
+     * @return string
4716
+     */
4717
+    protected function getInsertSuffix( $table )
4718
+    {
4719
+        return '';
4720
+    }
4721
+
4722
+    /**
4723
+     * Checks whether a value starts with zeros. In this case
4724
+     * the value should probably be stored using a text datatype instead of a
4725
+     * numerical type in order to preserve the zeros.
4726
+     *
4727
+     * @param string $value value to be checked.
4728
+     *
4729
+     * @return boolean
4730
+     */
4731
+    protected function startsWithZeros( $value )
4732
+    {
4733
+        $value = strval( $value );
4734
+
4735
+        if ( strlen( $value ) > 1 && strpos( $value, '0' ) === 0 && strpos( $value, '0.' ) !== 0 ) {
4736
+            return TRUE;
4737
+        } else {
4738
+            return FALSE;
4739
+        }
4740
+    }
4741
+
4742
+    /**
4743
+     * Inserts a record into the database using a series of insert columns
4744
+     * and corresponding insertvalues. Returns the insert id.
4745
+     *
4746
+     * @param string $table         table to perform query on
4747
+     * @param array  $insertcolumns columns to be inserted
4748
+     * @param array  $insertvalues  values to be inserted
4749
+     *
4750
+     * @return integer
4751
+     */
4752
+    protected function insertRecord( $type, $insertcolumns, $insertvalues )
4753
+    {
4754
+        $default = $this->defaultValue;
4755
+        $suffix  = $this->getInsertSuffix( $type );
4756
+        $table   = $this->esc( $type );
4757
+
4758
+        if ( count( $insertvalues ) > 0 && is_array( $insertvalues[0] ) && count( $insertvalues[0] ) > 0 ) {
4759
+
4760
+            $insertSlots = array();
4761
+            foreach ( $insertcolumns as $k => $v ) {
4762
+                $insertcolumns[$k] = $this->esc( $v );
4763
+
4764
+                if (isset(self::$sqlFilters['w'][$type][$v])) {
4765
+                    $insertSlots[] = self::$sqlFilters['w'][$type][$v];
4766
+                } else {
4767
+                    $insertSlots[] = '?';
4768
+                }
4769
+            }
4770
+
4771
+            $insertSQL = "INSERT INTO $table ( id, " . implode( ',', $insertcolumns ) . " ) VALUES
4772 4772
 			( $default, " . implode( ',', $insertSlots ) . " ) $suffix";
4773 4773
 
4774
-			$ids = array();
4775
-			foreach ( $insertvalues as $i => $insertvalue ) {
4776
-				$ids[] = $this->adapter->getCell( $insertSQL, $insertvalue, $i );
4777
-			}
4778
-
4779
-			$result = count( $ids ) === 1 ? array_pop( $ids ) : $ids;
4780
-		} else {
4781
-			$result = $this->adapter->getCell( "INSERT INTO $table (id) VALUES($default) $suffix" );
4782
-		}
4774
+            $ids = array();
4775
+            foreach ( $insertvalues as $i => $insertvalue ) {
4776
+                $ids[] = $this->adapter->getCell( $insertSQL, $insertvalue, $i );
4777
+            }
4778
+
4779
+            $result = count( $ids ) === 1 ? array_pop( $ids ) : $ids;
4780
+        } else {
4781
+            $result = $this->adapter->getCell( "INSERT INTO $table (id) VALUES($default) $suffix" );
4782
+        }
4783
+
4784
+        if ( $suffix ) return $result;
4785
+
4786
+        $last_id = $this->adapter->getInsertID();
4787
+
4788
+        return $last_id;
4789
+    }
4790
+
4791
+    /**
4792
+     * Checks table name or column name.
4793
+     *
4794
+     * @param string $table table string
4795
+     *
4796
+     * @return string
4797
+     *
4798
+     * @throws Security
4799
+     */
4800
+    protected function check( $struct )
4801
+    {
4802
+        if ( !is_string( $struct ) || !preg_match( '/^[a-zA-Z0-9_]+$/', $struct ) ) {
4803
+            throw new RedException( 'Identifier does not conform to RedBeanPHP security policies.' );
4804
+        }
4805
+
4806
+        return $struct;
4807
+    }
4808
+
4809
+    /**
4810
+     * Checks whether the specified type (i.e. table) already exists in the database.
4811
+     * Not part of the Object Database interface!
4812
+     *
4813
+     * @param string $table table name
4814
+     *
4815
+     * @return boolean
4816
+     */
4817
+    public function tableExists( $table )
4818
+    {
4819
+        $tables = $this->getTables();
4820
+
4821
+        return in_array( $table, $tables );
4822
+    }
4823
+
4824
+    /**
4825
+     * @see QueryWriter::glueSQLCondition
4826
+     */
4827
+    public function glueSQLCondition( $sql, $glue = NULL )
4828
+    {
4829
+        static $snippetCache = array();
4830
+
4831
+        if ( trim( $sql ) === '' ) {
4832
+            return $sql;
4833
+        }
4834
+
4835
+        $key = $glue . '|' . $sql;
4836
+
4837
+        if ( isset( $snippetCache[$key] ) ) {
4838
+            return $snippetCache[$key];
4839
+        }
4840
+
4841
+        $lsql = ltrim( $sql );
4842
+
4843
+        if ( preg_match( '/^(INNER|LEFT|RIGHT|JOIN|AND|OR|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) {
4844
+            if ( $glue === QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) {
4845
+                $snippetCache[$key] = ' WHERE ' . substr( $lsql, 3 );
4846
+            } else {
4847
+                $snippetCache[$key] = $sql;
4848
+            }
4849
+        } else {
4850
+            $snippetCache[$key] = ( ( $glue === QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql;
4851
+        }
4852
+
4853
+        return $snippetCache[$key];
4854
+    }
4855
+
4856
+    /**
4857
+     * @see QueryWriter::glueLimitOne
4858
+     */
4859
+    public function glueLimitOne( $sql = '')
4860
+    {
4861
+        return ( strpos( $sql, 'LIMIT' ) === FALSE ) ? ( $sql . ' LIMIT 1 ' ) : $sql;
4862
+    }
4863
+
4864
+    /**
4865
+     * @see QueryWriter::esc
4866
+     */
4867
+    public function esc( $dbStructure, $dontQuote = FALSE )
4868
+    {
4869
+        $this->check( $dbStructure );
4870
+
4871
+        return ( $dontQuote ) ? $dbStructure : $this->quoteCharacter . $dbStructure . $this->quoteCharacter;
4872
+    }
4873
+
4874
+    /**
4875
+     * @see QueryWriter::addColumn
4876
+     */
4877
+    public function addColumn( $type, $column, $field )
4878
+    {
4879
+        $table  = $type;
4880
+        $type   = $field;
4881
+        $table  = $this->esc( $table );
4882
+        $column = $this->esc( $column );
4883
+
4884
+        $type = ( isset( $this->typeno_sqltype[$type] ) ) ? $this->typeno_sqltype[$type] : '';
4885
+
4886
+        $this->adapter->exec( "ALTER TABLE $table ADD $column $type " );
4887
+    }
4888
+
4889
+    /**
4890
+     * @see QueryWriter::updateRecord
4891
+     */
4892
+    public function updateRecord( $type, $updatevalues, $id = NULL )
4893
+    {
4894
+        $table = $type;
4895
+
4896
+        if ( !$id ) {
4897
+            $insertcolumns = $insertvalues = array();
4898
+
4899
+            foreach ( $updatevalues as $pair ) {
4900
+                $insertcolumns[] = $pair['property'];
4901
+                $insertvalues[]  = $pair['value'];
4902
+            }
4903
+
4904
+            //Otherwise psql returns string while MySQL/SQLite return numeric causing problems with additions (array_diff)
4905
+            return (string) $this->insertRecord( $table, $insertcolumns, array( $insertvalues ) );
4906
+        }
4907
+
4908
+        if ( $id && !count( $updatevalues ) ) {
4909
+            return $id;
4910
+        }
4911
+
4912
+        $table = $this->esc( $table );
4913
+        $sql   = "UPDATE $table SET ";
4914
+
4915
+        $p = $v = array();
4916
+
4917
+        foreach ( $updatevalues as $uv ) {
4918
+
4919
+            if ( isset( self::$sqlFilters['w'][$type][$uv['property']] ) ) {
4920
+                $p[] = " {$this->esc( $uv["property"] )} = ". self::$sqlFilters['w'][$type][$uv['property']];
4921
+            } else {
4922
+                $p[] = " {$this->esc( $uv["property"] )} = ? ";
4923
+            }
4924
+
4925
+            $v[] = $uv['value'];
4926
+        }
4927
+
4928
+        $sql .= implode( ',', $p ) . ' WHERE id = ? ';
4929
+
4930
+        $v[] = $id;
4931
+
4932
+        $this->adapter->exec( $sql, $v );
4933
+
4934
+        return $id;
4935
+    }
4936
+
4937
+    /**
4938
+     * @see QueryWriter::writeJoin
4939
+     */
4940
+    public function writeJoin( $type, $targetType, $leftRight = 'LEFT' )
4941
+    {
4942
+        if ( $leftRight !== 'LEFT' && $leftRight !== 'RIGHT' && $leftRight !== 'INNER' )
4943
+            throw new RedException( 'Invalid JOIN.' );
4944
+
4945
+        $table = $this->esc( $type );
4946
+        $targetTable = $this->esc( $targetType );
4947
+        $field = $this->esc( $targetType, TRUE );
4948
+        return " {$leftRight} JOIN {$targetTable} ON {$targetTable}.id = {$table}.{$field}_id ";
4949
+    }
4950
+
4951
+    /**
4952
+     * @see QueryWriter::queryRecord
4953
+     */
4954
+    public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
4955
+    {
4956
+        $addSql = $this->glueSQLCondition( $addSql, ( count($conditions) > 0) ? QueryWriter::C_GLUE_AND : NULL );
4957
+
4958
+        $key = NULL;
4959
+        if ( $this->flagUseCache ) {
4960
+            $key = $this->getCacheKey( array( $conditions, $addSql, $bindings, 'select' ) );
4961
+
4962
+            if ( $cached = $this->getCached( $type, $key ) ) {
4963
+                return $cached;
4964
+            }
4965
+        }
4783 4966
 
4784
-		if ( $suffix ) return $result;
4967
+        $table = $this->esc( $type );
4785 4968
 
4786
-		$last_id = $this->adapter->getInsertID();
4969
+        $sqlFilterStr = '';
4970
+        if ( count( self::$sqlFilters ) ) {
4971
+            $sqlFilterStr = $this->getSQLFilterSnippet( $type );
4972
+        }
4787 4973
 
4788
-		return $last_id;
4789
-	}
4974
+        $sql   = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
4790 4975
 
4791
-	/**
4792
-	 * Checks table name or column name.
4793
-	 *
4794
-	 * @param string $table table string
4795
-	 *
4796
-	 * @return string
4797
-	 *
4798
-	 * @throws Security
4799
-	 */
4800
-	protected function check( $struct )
4801
-	{
4802
-		if ( !is_string( $struct ) || !preg_match( '/^[a-zA-Z0-9_]+$/', $struct ) ) {
4803
-			throw new RedException( 'Identifier does not conform to RedBeanPHP security policies.' );
4804
-		}
4805
-
4806
-		return $struct;
4807
-	}
4808
-
4809
-	/**
4810
-	 * Checks whether the specified type (i.e. table) already exists in the database.
4811
-	 * Not part of the Object Database interface!
4812
-	 *
4813
-	 * @param string $table table name
4814
-	 *
4815
-	 * @return boolean
4816
-	 */
4817
-	public function tableExists( $table )
4818
-	{
4819
-		$tables = $this->getTables();
4820
-
4821
-		return in_array( $table, $tables );
4822
-	}
4823
-
4824
-	/**
4825
-	 * @see QueryWriter::glueSQLCondition
4826
-	 */
4827
-	public function glueSQLCondition( $sql, $glue = NULL )
4828
-	{
4829
-		static $snippetCache = array();
4830
-
4831
-		if ( trim( $sql ) === '' ) {
4832
-			return $sql;
4833
-		}
4976
+        $fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*';
4977
+        $sql   = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} -- keep-cache";
4834 4978
 
4835
-		$key = $glue . '|' . $sql;
4979
+        $rows  = $this->adapter->get( $sql, $bindings );
4836 4980
 
4837
-		if ( isset( $snippetCache[$key] ) ) {
4838
-			return $snippetCache[$key];
4839
-		}
4840 4981
 
4841
-		$lsql = ltrim( $sql );
4982
+        if ( $this->flagUseCache && $key ) {
4983
+            $this->putResultInCache( $type, $key, $rows );
4984
+        }
4842 4985
 
4843
-		if ( preg_match( '/^(INNER|LEFT|RIGHT|JOIN|AND|OR|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) {
4844
-			if ( $glue === QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) {
4845
-				$snippetCache[$key] = ' WHERE ' . substr( $lsql, 3 );
4846
-			} else {
4847
-				$snippetCache[$key] = $sql;
4848
-			}
4849
-		} else {
4850
-			$snippetCache[$key] = ( ( $glue === QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql;
4851
-		}
4986
+        return $rows;
4987
+    }
4852 4988
 
4853
-		return $snippetCache[$key];
4854
-	}
4989
+    /**
4990
+     * @see QueryWriter::queryRecordWithCursor
4991
+     */
4992
+    public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() )
4993
+    {
4994
+        $sql = $this->glueSQLCondition( $addSql, NULL );
4995
+        $table = $this->esc( $type );
4996
+        $sql   = "SELECT {$table}.* FROM {$table} {$sql}";
4997
+        return $this->adapter->getCursor( $sql, $bindings );
4998
+    }
4855 4999
 
4856
-	/**
4857
-	 * @see QueryWriter::glueLimitOne
4858
-	 */
4859
-	public function glueLimitOne( $sql = '')
4860
-	{
4861
-		return ( strpos( $sql, 'LIMIT' ) === FALSE ) ? ( $sql . ' LIMIT 1 ' ) : $sql;
4862
-	}
5000
+    /**
5001
+     * @see QueryWriter::queryRecordRelated
5002
+     */
5003
+    public function queryRecordRelated( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() )
5004
+    {
5005
+        $addSql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE );
4863 5006
 
4864
-	/**
4865
-	 * @see QueryWriter::esc
4866
-	 */
4867
-	public function esc( $dbStructure, $dontQuote = FALSE )
4868
-	{
4869
-		$this->check( $dbStructure );
4870
-
4871
-		return ( $dontQuote ) ? $dbStructure : $this->quoteCharacter . $dbStructure . $this->quoteCharacter;
4872
-	}
4873
-
4874
-	/**
4875
-	 * @see QueryWriter::addColumn
4876
-	 */
4877
-	public function addColumn( $type, $column, $field )
4878
-	{
4879
-		$table  = $type;
4880
-		$type   = $field;
4881
-		$table  = $this->esc( $table );
4882
-		$column = $this->esc( $column );
4883
-
4884
-		$type = ( isset( $this->typeno_sqltype[$type] ) ) ? $this->typeno_sqltype[$type] : '';
4885
-
4886
-		$this->adapter->exec( "ALTER TABLE $table ADD $column $type " );
4887
-	}
4888
-
4889
-	/**
4890
-	 * @see QueryWriter::updateRecord
4891
-	 */
4892
-	public function updateRecord( $type, $updatevalues, $id = NULL )
4893
-	{
4894
-		$table = $type;
5007
+        list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
4895 5008
 
4896
-		if ( !$id ) {
4897
-			$insertcolumns = $insertvalues = array();
5009
+        $key = $this->getCacheKey( array( $sourceType, $destType, implode( ',', $linkIDs ), $addSql, $bindings ) );
4898 5010
 
4899
-			foreach ( $updatevalues as $pair ) {
4900
-				$insertcolumns[] = $pair['property'];
4901
-				$insertvalues[]  = $pair['value'];
4902
-			}
5011
+        if ( $this->flagUseCache && $cached = $this->getCached( $destType, $key ) ) {
5012
+            return $cached;
5013
+        }
4903 5014
 
4904
-			//Otherwise psql returns string while MySQL/SQLite return numeric causing problems with additions (array_diff)
4905
-			return (string) $this->insertRecord( $table, $insertcolumns, array( $insertvalues ) );
4906
-		}
5015
+        $inClause = $this->getParametersForInClause( $linkIDs, $bindings );
4907 5016
 
4908
-		if ( $id && !count( $updatevalues ) ) {
4909
-			return $id;
4910
-		}
5017
+        $sqlFilterStr = '';
5018
+        if ( count( self::$sqlFilters ) ) {
5019
+            $sqlFilterStr = $this->getSQLFilterSnippet( $destType );
5020
+        }
4911 5021
 
4912
-		$table = $this->esc( $table );
4913
-		$sql   = "UPDATE $table SET ";
4914
-
4915
-		$p = $v = array();
4916
-
4917
-		foreach ( $updatevalues as $uv ) {
4918
-
4919
-			if ( isset( self::$sqlFilters['w'][$type][$uv['property']] ) ) {
4920
-				$p[] = " {$this->esc( $uv["property"] )} = ". self::$sqlFilters['w'][$type][$uv['property']];
4921
-			} else {
4922
-				$p[] = " {$this->esc( $uv["property"] )} = ? ";
4923
-			}
4924
-
4925
-			$v[] = $uv['value'];
4926
-		}
4927
-
4928
-		$sql .= implode( ',', $p ) . ' WHERE id = ? ';
4929
-
4930
-		$v[] = $id;
4931
-
4932
-		$this->adapter->exec( $sql, $v );
4933
-
4934
-		return $id;
4935
-	}
4936
-
4937
-	/**
4938
-	 * @see QueryWriter::writeJoin
4939
-	 */
4940
-	public function writeJoin( $type, $targetType, $leftRight = 'LEFT' )
4941
-	{
4942
-		if ( $leftRight !== 'LEFT' && $leftRight !== 'RIGHT' && $leftRight !== 'INNER' )
4943
-			throw new RedException( 'Invalid JOIN.' );
4944
-
4945
-		$table = $this->esc( $type );
4946
-		$targetTable = $this->esc( $targetType );
4947
-		$field = $this->esc( $targetType, TRUE );
4948
-		return " {$leftRight} JOIN {$targetTable} ON {$targetTable}.id = {$table}.{$field}_id ";
4949
-	}
4950
-
4951
-	/**
4952
-	 * @see QueryWriter::queryRecord
4953
-	 */
4954
-	public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
4955
-	{
4956
-		$addSql = $this->glueSQLCondition( $addSql, ( count($conditions) > 0) ? QueryWriter::C_GLUE_AND : NULL );
4957
-
4958
-		$key = NULL;
4959
-		if ( $this->flagUseCache ) {
4960
-			$key = $this->getCacheKey( array( $conditions, $addSql, $bindings, 'select' ) );
4961
-
4962
-			if ( $cached = $this->getCached( $type, $key ) ) {
4963
-				return $cached;
4964
-			}
4965
-		}
4966
-
4967
-		$table = $this->esc( $type );
4968
-
4969
-		$sqlFilterStr = '';
4970
-		if ( count( self::$sqlFilters ) ) {
4971
-			$sqlFilterStr = $this->getSQLFilterSnippet( $type );
4972
-		}
4973
-
4974
-		$sql   = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
4975
-
4976
-		$fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*';
4977
-		$sql   = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} -- keep-cache";
4978
-
4979
-		$rows  = $this->adapter->get( $sql, $bindings );
4980
-
4981
-
4982
-		if ( $this->flagUseCache && $key ) {
4983
-			$this->putResultInCache( $type, $key, $rows );
4984
-		}
4985
-
4986
-		return $rows;
4987
-	}
4988
-
4989
-	/**
4990
-	 * @see QueryWriter::queryRecordWithCursor
4991
-	 */
4992
-	public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() )
4993
-	{
4994
-		$sql = $this->glueSQLCondition( $addSql, NULL );
4995
-		$table = $this->esc( $type );
4996
-		$sql   = "SELECT {$table}.* FROM {$table} {$sql}";
4997
-		return $this->adapter->getCursor( $sql, $bindings );
4998
-	}
4999
-
5000
-	/**
5001
-	 * @see QueryWriter::queryRecordRelated
5002
-	 */
5003
-	public function queryRecordRelated( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() )
5004
-	{
5005
-		$addSql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE );
5006
-
5007
-		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
5008
-
5009
-		$key = $this->getCacheKey( array( $sourceType, $destType, implode( ',', $linkIDs ), $addSql, $bindings ) );
5010
-
5011
-		if ( $this->flagUseCache && $cached = $this->getCached( $destType, $key ) ) {
5012
-			return $cached;
5013
-		}
5014
-
5015
-		$inClause = $this->getParametersForInClause( $linkIDs, $bindings );
5016
-
5017
-		$sqlFilterStr = '';
5018
-		if ( count( self::$sqlFilters ) ) {
5019
-			$sqlFilterStr = $this->getSQLFilterSnippet( $destType );
5020
-		}
5021
-
5022
-		if ( $sourceType === $destType ) {
5023
-			$inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases
5024
-			$sql = "
5022
+        if ( $sourceType === $destType ) {
5023
+            $inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases
5024
+            $sql = "
5025 5025
 			SELECT
5026 5026
 				{$destTable}.* {$sqlFilterStr} ,
5027 5027
 				COALESCE(
@@ -5034,9 +5034,9 @@  discard block
 block discarded – undo
5034 5034
 			{$addSql}
5035 5035
 			-- keep-cache";
5036 5036
 
5037
-			$linkIDs = array_merge( $linkIDs, $linkIDs );
5038
-		} else {
5039
-			$sql = "
5037
+            $linkIDs = array_merge( $linkIDs, $linkIDs );
5038
+        } else {
5039
+            $sql = "
5040 5040
 			SELECT
5041 5041
 				{$destTable}.* {$sqlFilterStr},
5042 5042
 				{$linkTable}.{$sourceCol} AS linked_by
@@ -5045,64 +5045,64 @@  discard block
 block discarded – undo
5045 5045
 			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) )
5046 5046
 			{$addSql}
5047 5047
 			-- keep-cache";
5048
-		}
5048
+        }
5049 5049
 
5050
-		$bindings = array_merge( $linkIDs, $bindings );
5050
+        $bindings = array_merge( $linkIDs, $bindings );
5051 5051
 
5052
-		$rows = $this->adapter->get( $sql, $bindings );
5052
+        $rows = $this->adapter->get( $sql, $bindings );
5053 5053
 
5054
-		$this->putResultInCache( $destType, $key, $rows );
5054
+        $this->putResultInCache( $destType, $key, $rows );
5055 5055
 
5056
-		return $rows;
5057
-	}
5056
+        return $rows;
5057
+    }
5058 5058
 
5059
-	/**
5060
-	 * @see QueryWriter::queryRecordLink
5061
-	 */
5062
-	public function queryRecordLink( $sourceType, $destType, $sourceID, $destID )
5063
-	{
5064
-		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
5059
+    /**
5060
+     * @see QueryWriter::queryRecordLink
5061
+     */
5062
+    public function queryRecordLink( $sourceType, $destType, $sourceID, $destID )
5063
+    {
5064
+        list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
5065 5065
 
5066
-		$key = $this->getCacheKey( array( $sourceType, $destType, $sourceID, $destID ) );
5066
+        $key = $this->getCacheKey( array( $sourceType, $destType, $sourceID, $destID ) );
5067 5067
 
5068
-		if ( $this->flagUseCache && $cached = $this->getCached( $linkTable, $key ) ) {
5069
-			return $cached;
5070
-		}
5068
+        if ( $this->flagUseCache && $cached = $this->getCached( $linkTable, $key ) ) {
5069
+            return $cached;
5070
+        }
5071 5071
 
5072
-		$sqlFilterStr = '';
5073
-		if ( count( self::$sqlFilters ) ) {
5074
-			$sqlFilterStr = $this->getSQLFilterSnippet( $destType );
5075
-		}
5072
+        $sqlFilterStr = '';
5073
+        if ( count( self::$sqlFilters ) ) {
5074
+            $sqlFilterStr = $this->getSQLFilterSnippet( $destType );
5075
+        }
5076 5076
 
5077
-		if ( $sourceTable === $destTable ) {
5078
-			$sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
5077
+        if ( $sourceTable === $destTable ) {
5078
+            $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
5079 5079
 				WHERE ( {$sourceCol} = ? AND {$destCol} = ? ) OR
5080 5080
 				 ( {$destCol} = ? AND {$sourceCol} = ? ) -- keep-cache";
5081
-			$row = $this->adapter->getRow( $sql, array( $sourceID, $destID, $sourceID, $destID ) );
5082
-		} else {
5083
-			$sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
5081
+            $row = $this->adapter->getRow( $sql, array( $sourceID, $destID, $sourceID, $destID ) );
5082
+        } else {
5083
+            $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable}
5084 5084
 				WHERE {$sourceCol} = ? AND {$destCol} = ? -- keep-cache";
5085
-			$row = $this->adapter->getRow( $sql, array( $sourceID, $destID ) );
5086
-		}
5087
-
5088
-		$this->putResultInCache( $linkTable, $key, $row );
5089
-
5090
-		return $row;
5091
-	}
5092
-
5093
-	/**
5094
-	 * @see QueryWriter::queryTagged
5095
-	 */
5096
-	public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() )
5097
-	{
5098
-		$assocType = $this->getAssocTable( array( $type, 'tag' ) );
5099
-		$assocTable = $this->esc( $assocType );
5100
-		$assocField = $type . '_id';
5101
-		$table = $this->esc( $type );
5102
-		$slots = implode( ',', array_fill( 0, count( $tagList ), '?' ) );
5103
-		$score = ( $all ) ? count( $tagList ) : 1;
5104
-
5105
-		$sql = "
5085
+            $row = $this->adapter->getRow( $sql, array( $sourceID, $destID ) );
5086
+        }
5087
+
5088
+        $this->putResultInCache( $linkTable, $key, $row );
5089
+
5090
+        return $row;
5091
+    }
5092
+
5093
+    /**
5094
+     * @see QueryWriter::queryTagged
5095
+     */
5096
+    public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() )
5097
+    {
5098
+        $assocType = $this->getAssocTable( array( $type, 'tag' ) );
5099
+        $assocTable = $this->esc( $assocType );
5100
+        $assocField = $type . '_id';
5101
+        $table = $this->esc( $type );
5102
+        $slots = implode( ',', array_fill( 0, count( $tagList ), '?' ) );
5103
+        $score = ( $all ) ? count( $tagList ) : 1;
5104
+
5105
+        $sql = "
5106 5106
 			SELECT {$table}.*, count({$table}.id) FROM {$table}
5107 5107
 			INNER JOIN {$assocTable} ON {$assocField} = {$table}.id
5108 5108
 			INNER JOIN tag ON {$assocTable}.tag_id = tag.id
@@ -5112,222 +5112,222 @@  discard block
 block discarded – undo
5112 5112
 			{$addSql}
5113 5113
 		";
5114 5114
 
5115
-		$bindings = array_merge( $tagList, array( $score ), $bindings );
5116
-		$rows = $this->adapter->get( $sql, $bindings );
5117
-		return $rows;
5118
-	}
5115
+        $bindings = array_merge( $tagList, array( $score ), $bindings );
5116
+        $rows = $this->adapter->get( $sql, $bindings );
5117
+        return $rows;
5118
+    }
5119 5119
 
5120
-	/**
5121
-	 * @see QueryWriter::queryRecordCount
5122
-	 */
5123
-	public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
5124
-	{
5125
-		$addSql = $this->glueSQLCondition( $addSql );
5120
+    /**
5121
+     * @see QueryWriter::queryRecordCount
5122
+     */
5123
+    public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
5124
+    {
5125
+        $addSql = $this->glueSQLCondition( $addSql );
5126 5126
 
5127
-		$table  = $this->esc( $type );
5127
+        $table  = $this->esc( $type );
5128 5128
 
5129
-		$this->updateCache(); //check if cache chain has been broken
5129
+        $this->updateCache(); //check if cache chain has been broken
5130 5130
 
5131
-		$sql    = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
5132
-		$sql    = "SELECT COUNT(*) FROM {$table} {$sql} -- keep-cache";
5131
+        $sql    = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
5132
+        $sql    = "SELECT COUNT(*) FROM {$table} {$sql} -- keep-cache";
5133 5133
 
5134
-		return (int) $this->adapter->getCell( $sql, $bindings );
5135
-	}
5134
+        return (int) $this->adapter->getCell( $sql, $bindings );
5135
+    }
5136 5136
 
5137
-	/**
5138
-	 * @see QueryWriter::queryRecordCountRelated
5139
-	 */
5140
-	public function queryRecordCountRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() )
5141
-	{
5142
-		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
5137
+    /**
5138
+     * @see QueryWriter::queryRecordCountRelated
5139
+     */
5140
+    public function queryRecordCountRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() )
5141
+    {
5142
+        list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
5143 5143
 
5144
-		$this->updateCache(); //check if cache chain has been broken
5144
+        $this->updateCache(); //check if cache chain has been broken
5145 5145
 
5146
-		if ( $sourceType === $destType ) {
5147
-			$sql = "
5146
+        if ( $sourceType === $destType ) {
5147
+            $sql = "
5148 5148
 			SELECT COUNT(*) FROM {$linkTable}
5149 5149
 			INNER JOIN {$destTable} ON
5150 5150
 			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) OR
5151 5151
 			( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} = ? )
5152
-			{$addSql}
5153
-			-- keep-cache";
5154
-
5155
-			$bindings = array_merge( array( $linkID, $linkID ), $bindings );
5156
-		} else {
5157
-			$sql = "
5158
-			SELECT COUNT(*) FROM {$linkTable}
5159
-			INNER JOIN {$destTable} ON
5160
-			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? )
5161
-			{$addSql}
5162
-			-- keep-cache";
5163
-
5164
-			$bindings = array_merge( array( $linkID ), $bindings );
5165
-		}
5166
-
5167
-		return (int) $this->adapter->getCell( $sql, $bindings );
5168
-	}
5169
-
5170
-	/**
5171
-	 * @see QueryWriter::deleteRecord
5172
-	 */
5173
-	public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
5174
-	{
5175
-		$addSql = $this->glueSQLCondition( $addSql );
5176
-
5177
-		$table  = $this->esc( $type );
5178
-
5179
-		$sql    = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
5180
-		$sql    = "DELETE FROM {$table} {$sql}";
5181
-
5182
-		$this->adapter->exec( $sql, $bindings );
5183
-	}
5184
-
5185
-	/**
5186
-	 * @see QueryWriter::deleteRelations
5187
-	 */
5188
-	public function deleteRelations( $sourceType, $destType, $sourceID )
5189
-	{
5190
-		list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
5191
-
5192
-		if ( $sourceTable === $destTable ) {
5193
-			$sql = "DELETE FROM {$linkTable}
5194
-				WHERE ( {$sourceCol} = ? ) OR
5195
-				( {$destCol} = ?  )
5196
-			";
5197
-
5198
-			$this->adapter->exec( $sql, array( $sourceID, $sourceID ) );
5199
-		} else {
5200
-			$sql = "DELETE FROM {$linkTable}
5201
-				WHERE {$sourceCol} = ? ";
5152
+			{$addSql}
5153
+			-- keep-cache";
5202 5154
 
5203
-			$this->adapter->exec( $sql, array( $sourceID ) );
5204
-		}
5205
-	}
5155
+            $bindings = array_merge( array( $linkID, $linkID ), $bindings );
5156
+        } else {
5157
+            $sql = "
5158
+			SELECT COUNT(*) FROM {$linkTable}
5159
+			INNER JOIN {$destTable} ON
5160
+			( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? )
5161
+			{$addSql}
5162
+			-- keep-cache";
5206 5163
 
5207
-	/**
5208
-	 * @see QueryWriter::widenColumn
5209
-	 */
5210
-	public function widenColumn( $type, $property, $dataType )
5211
-	{
5212
-		if ( !isset($this->typeno_sqltype[$dataType]) ) return FALSE;
5164
+            $bindings = array_merge( array( $linkID ), $bindings );
5165
+        }
5213 5166
 
5214
-		$table   = $this->esc( $type );
5215
-		$column  = $this->esc( $property );
5167
+        return (int) $this->adapter->getCell( $sql, $bindings );
5168
+    }
5216 5169
 
5217
-		$newType = $this->typeno_sqltype[$dataType];
5170
+    /**
5171
+     * @see QueryWriter::deleteRecord
5172
+     */
5173
+    public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
5174
+    {
5175
+        $addSql = $this->glueSQLCondition( $addSql );
5218 5176
 
5219
-		$this->adapter->exec( "ALTER TABLE $table CHANGE $column $column $newType " );
5177
+        $table  = $this->esc( $type );
5220 5178
 
5221
-		return TRUE;
5222
-	}
5179
+        $sql    = $this->makeSQLFromConditions( $conditions, $bindings, $addSql );
5180
+        $sql    = "DELETE FROM {$table} {$sql}";
5223 5181
 
5224
-	/**
5225
-	 * @see QueryWriter::wipe
5226
-	 */
5227
-	public function wipe( $type )
5228
-	{
5229
-		$table = $this->esc( $type );
5182
+        $this->adapter->exec( $sql, $bindings );
5183
+    }
5230 5184
 
5231
-		$this->adapter->exec( "TRUNCATE $table " );
5232
-	}
5185
+    /**
5186
+     * @see QueryWriter::deleteRelations
5187
+     */
5188
+    public function deleteRelations( $sourceType, $destType, $sourceID )
5189
+    {
5190
+        list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType );
5233 5191
 
5234
-	/**
5235
-	 * @see QueryWriter::renameAssocTable
5236
-	 */
5237
-	public function renameAssocTable( $from, $to = NULL )
5238
-	{
5239
-		self::renameAssociation( $from, $to );
5240
-	}
5192
+        if ( $sourceTable === $destTable ) {
5193
+            $sql = "DELETE FROM {$linkTable}
5194
+				WHERE ( {$sourceCol} = ? ) OR
5195
+				( {$destCol} = ?  )
5196
+			";
5241 5197
 
5242
-	/**
5243
-	 * @see QueryWriter::getAssocTable
5244
-	 */
5245
-	public function getAssocTable( $types )
5246
-	{
5247
-		return self::getAssocTableFormat( $types );
5248
-	}
5249
-
5250
-	/**
5251
-	 * Turns caching on or off. Default: off.
5252
-	 * If caching is turned on retrieval queries fired after eachother will
5253
-	 * use a result row cache.
5254
-	 *
5255
-	 * @param boolean
5256
-	 */
5257
-	public function setUseCache( $yesNo )
5258
-	{
5259
-		$this->flushCache();
5260
-
5261
-		$this->flagUseCache = (bool) $yesNo;
5262
-	}
5263
-
5264
-	/**
5265
-	 * Flushes the Query Writer Cache.
5266
-	 * Clears the internal query cache array and returns its overall
5267
-	 * size.
5268
-	 *
5269
-	 * @return integer
5270
-	 */
5271
-	public function flushCache( $newMaxCacheSizePerType = NULL )
5272
-	{
5273
-		if ( !is_null( $newMaxCacheSizePerType ) && $newMaxCacheSizePerType > 0 ) {
5274
-			$this->maxCacheSizePerType = $newMaxCacheSizePerType;
5275
-		}
5276
-		$count = count( $this->cache, COUNT_RECURSIVE );
5277
-		$this->cache = array();
5278
-		return $count;
5279
-	}
5280
-
5281
-	/**
5282
-	 * @deprecated Use esc() instead.
5283
-	 *
5284
-	 * @param string  $column   column to be escaped
5285
-	 * @param boolean $noQuotes omit quotes
5286
-	 *
5287
-	 * @return string
5288
-	 */
5289
-	public function safeColumn( $column, $noQuotes = FALSE )
5290
-	{
5291
-		return $this->esc( $column, $noQuotes );
5292
-	}
5293
-
5294
-	/**
5295
-	 * @deprecated Use esc() instead.
5296
-	 *
5297
-	 * @param string  $table    table to be escaped
5298
-	 * @param boolean $noQuotes omit quotes
5299
-	 *
5300
-	 * @return string
5301
-	 */
5302
-	public function safeTable( $table, $noQuotes = FALSE )
5303
-	{
5304
-		return $this->esc( $table, $noQuotes );
5305
-	}
5198
+            $this->adapter->exec( $sql, array( $sourceID, $sourceID ) );
5199
+        } else {
5200
+            $sql = "DELETE FROM {$linkTable}
5201
+				WHERE {$sourceCol} = ? ";
5306 5202
 
5307
-	/**
5308
-	 * @see QueryWriter::inferFetchType
5309
-	 */
5310
-	public function inferFetchType( $type, $property )
5311
-	{
5312
-		$type = $this->esc( $type, TRUE );
5313
-		$field = $this->esc( $property, TRUE ) . '_id';
5314
-		$keys = $this->getKeyMapForType( $type );
5315
-
5316
-		foreach( $keys as $key ) {
5317
-			if (
5318
-				$key['from'] === $field
5319
-			) return $key['table'];
5320
-		}
5321
-		return NULL;
5322
-	}
5323
-
5324
-	/**
5325
-	 * @see QueryWriter::addUniqueConstraint
5326
-	 */
5327
-	public function addUniqueIndex( $type, $properties )
5328
-	{
5329
-		return $this->addUniqueConstraint( $type, $properties );
5330
-	}
5203
+            $this->adapter->exec( $sql, array( $sourceID ) );
5204
+        }
5205
+    }
5206
+
5207
+    /**
5208
+     * @see QueryWriter::widenColumn
5209
+     */
5210
+    public function widenColumn( $type, $property, $dataType )
5211
+    {
5212
+        if ( !isset($this->typeno_sqltype[$dataType]) ) return FALSE;
5213
+
5214
+        $table   = $this->esc( $type );
5215
+        $column  = $this->esc( $property );
5216
+
5217
+        $newType = $this->typeno_sqltype[$dataType];
5218
+
5219
+        $this->adapter->exec( "ALTER TABLE $table CHANGE $column $column $newType " );
5220
+
5221
+        return TRUE;
5222
+    }
5223
+
5224
+    /**
5225
+     * @see QueryWriter::wipe
5226
+     */
5227
+    public function wipe( $type )
5228
+    {
5229
+        $table = $this->esc( $type );
5230
+
5231
+        $this->adapter->exec( "TRUNCATE $table " );
5232
+    }
5233
+
5234
+    /**
5235
+     * @see QueryWriter::renameAssocTable
5236
+     */
5237
+    public function renameAssocTable( $from, $to = NULL )
5238
+    {
5239
+        self::renameAssociation( $from, $to );
5240
+    }
5241
+
5242
+    /**
5243
+     * @see QueryWriter::getAssocTable
5244
+     */
5245
+    public function getAssocTable( $types )
5246
+    {
5247
+        return self::getAssocTableFormat( $types );
5248
+    }
5249
+
5250
+    /**
5251
+     * Turns caching on or off. Default: off.
5252
+     * If caching is turned on retrieval queries fired after eachother will
5253
+     * use a result row cache.
5254
+     *
5255
+     * @param boolean
5256
+     */
5257
+    public function setUseCache( $yesNo )
5258
+    {
5259
+        $this->flushCache();
5260
+
5261
+        $this->flagUseCache = (bool) $yesNo;
5262
+    }
5263
+
5264
+    /**
5265
+     * Flushes the Query Writer Cache.
5266
+     * Clears the internal query cache array and returns its overall
5267
+     * size.
5268
+     *
5269
+     * @return integer
5270
+     */
5271
+    public function flushCache( $newMaxCacheSizePerType = NULL )
5272
+    {
5273
+        if ( !is_null( $newMaxCacheSizePerType ) && $newMaxCacheSizePerType > 0 ) {
5274
+            $this->maxCacheSizePerType = $newMaxCacheSizePerType;
5275
+        }
5276
+        $count = count( $this->cache, COUNT_RECURSIVE );
5277
+        $this->cache = array();
5278
+        return $count;
5279
+    }
5280
+
5281
+    /**
5282
+     * @deprecated Use esc() instead.
5283
+     *
5284
+     * @param string  $column   column to be escaped
5285
+     * @param boolean $noQuotes omit quotes
5286
+     *
5287
+     * @return string
5288
+     */
5289
+    public function safeColumn( $column, $noQuotes = FALSE )
5290
+    {
5291
+        return $this->esc( $column, $noQuotes );
5292
+    }
5293
+
5294
+    /**
5295
+     * @deprecated Use esc() instead.
5296
+     *
5297
+     * @param string  $table    table to be escaped
5298
+     * @param boolean $noQuotes omit quotes
5299
+     *
5300
+     * @return string
5301
+     */
5302
+    public function safeTable( $table, $noQuotes = FALSE )
5303
+    {
5304
+        return $this->esc( $table, $noQuotes );
5305
+    }
5306
+
5307
+    /**
5308
+     * @see QueryWriter::inferFetchType
5309
+     */
5310
+    public function inferFetchType( $type, $property )
5311
+    {
5312
+        $type = $this->esc( $type, TRUE );
5313
+        $field = $this->esc( $property, TRUE ) . '_id';
5314
+        $keys = $this->getKeyMapForType( $type );
5315
+
5316
+        foreach( $keys as $key ) {
5317
+            if (
5318
+                $key['from'] === $field
5319
+            ) return $key['table'];
5320
+        }
5321
+        return NULL;
5322
+    }
5323
+
5324
+    /**
5325
+     * @see QueryWriter::addUniqueConstraint
5326
+     */
5327
+    public function addUniqueIndex( $type, $properties )
5328
+    {
5329
+        return $this->addUniqueConstraint( $type, $properties );
5330
+    }
5331 5331
 }
5332 5332
 }
5333 5333
 
@@ -5355,41 +5355,41 @@  discard block
 block discarded – undo
5355 5355
  */
5356 5356
 class MySQL extends AQueryWriter implements QueryWriter
5357 5357
 {
5358
-	/**
5359
-	 * Data types
5360
-	 */
5361
-	const C_DATATYPE_BOOL             = 0;
5362
-	const C_DATATYPE_UINT32           = 2;
5363
-	const C_DATATYPE_DOUBLE           = 3;
5364
-	const C_DATATYPE_TEXT7            = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible
5365
-	const C_DATATYPE_TEXT8            = 5;
5366
-	const C_DATATYPE_TEXT16           = 6;
5367
-	const C_DATATYPE_TEXT32           = 7;
5368
-	const C_DATATYPE_SPECIAL_DATE     = 80;
5369
-	const C_DATATYPE_SPECIAL_DATETIME = 81;
5370
-	const C_DATATYPE_SPECIAL_POINT    = 90;
5371
-	const C_DATATYPE_SPECIAL_LINESTRING = 91;
5372
-	const C_DATATYPE_SPECIAL_POLYGON    = 92;
5373
-
5374
-	const C_DATATYPE_SPECIFIED        = 99;
5375
-
5376
-	/**
5377
-	 * @var DBAdapter
5378
-	 */
5379
-	protected $adapter;
5380
-
5381
-	/**
5382
-	 * @var string
5383
-	 */
5384
-	protected $quoteCharacter = '`';
5385
-
5386
-	/**
5387
-	 * @see AQueryWriter::getKeyMapForType
5388
-	 */
5389
-	protected function getKeyMapForType( $type )
5390
-	{
5391
-		$table = $this->esc( $type, TRUE );
5392
-		$keys = $this->adapter->get('
5358
+    /**
5359
+     * Data types
5360
+     */
5361
+    const C_DATATYPE_BOOL             = 0;
5362
+    const C_DATATYPE_UINT32           = 2;
5363
+    const C_DATATYPE_DOUBLE           = 3;
5364
+    const C_DATATYPE_TEXT7            = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible
5365
+    const C_DATATYPE_TEXT8            = 5;
5366
+    const C_DATATYPE_TEXT16           = 6;
5367
+    const C_DATATYPE_TEXT32           = 7;
5368
+    const C_DATATYPE_SPECIAL_DATE     = 80;
5369
+    const C_DATATYPE_SPECIAL_DATETIME = 81;
5370
+    const C_DATATYPE_SPECIAL_POINT    = 90;
5371
+    const C_DATATYPE_SPECIAL_LINESTRING = 91;
5372
+    const C_DATATYPE_SPECIAL_POLYGON    = 92;
5373
+
5374
+    const C_DATATYPE_SPECIFIED        = 99;
5375
+
5376
+    /**
5377
+     * @var DBAdapter
5378
+     */
5379
+    protected $adapter;
5380
+
5381
+    /**
5382
+     * @var string
5383
+     */
5384
+    protected $quoteCharacter = '`';
5385
+
5386
+    /**
5387
+     * @see AQueryWriter::getKeyMapForType
5388
+     */
5389
+    protected function getKeyMapForType( $type )
5390
+    {
5391
+        $table = $this->esc( $type, TRUE );
5392
+        $keys = $this->adapter->get('
5393 5393
 			SELECT
5394 5394
 				information_schema.key_column_usage.constraint_name AS `name`,
5395 5395
 				information_schema.key_column_usage.referenced_table_name AS `table`,
@@ -5410,293 +5410,293 @@  discard block
 block discarded – undo
5410 5410
 				AND information_schema.key_column_usage.constraint_name != \'PRIMARY\'
5411 5411
 				AND information_schema.key_column_usage.referenced_table_name IS NOT NULL
5412 5412
 		', array($table));
5413
-		$keyInfoList = array();
5414
-		foreach ( $keys as $k ) {
5415
-			$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
5416
-			$keyInfoList[$label] = array(
5417
-				'name'          => $k['name'],
5418
-				'from'          => $k['from'],
5419
-				'table'         => $k['table'],
5420
-				'to'            => $k['to'],
5421
-				'on_update'     => $k['on_update'],
5422
-				'on_delete'     => $k['on_delete']
5423
-			);
5424
-		}
5425
-		return $keyInfoList;
5426
-	}
5427
-
5428
-	/**
5429
-	 * Constructor
5430
-	 *
5431
-	 * @param Adapter $adapter Database Adapter
5432
-	 */
5433
-	public function __construct( Adapter $adapter )
5434
-	{
5435
-		$this->typeno_sqltype = array(
5436
-			MySQL::C_DATATYPE_BOOL             => ' TINYINT(1) UNSIGNED ',
5437
-			MySQL::C_DATATYPE_UINT32           => ' INT(11) UNSIGNED ',
5438
-			MySQL::C_DATATYPE_DOUBLE           => ' DOUBLE ',
5439
-			MySQL::C_DATATYPE_TEXT7            => ' VARCHAR(191) ',
5440
-			MYSQL::C_DATATYPE_TEXT8	           => ' VARCHAR(255) ',
5441
-			MySQL::C_DATATYPE_TEXT16           => ' TEXT ',
5442
-			MySQL::C_DATATYPE_TEXT32           => ' LONGTEXT ',
5443
-			MySQL::C_DATATYPE_SPECIAL_DATE     => ' DATE ',
5444
-			MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
5445
-			MySQL::C_DATATYPE_SPECIAL_POINT    => ' POINT ',
5446
-			MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ',
5447
-			MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ',
5448
-		);
5449
-
5450
-		$this->sqltype_typeno = array();
5451
-
5452
-		foreach ( $this->typeno_sqltype as $k => $v ) {
5453
-			$this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
5454
-		}
5455
-
5456
-		$this->adapter = $adapter;
5457
-
5458
-		$this->encoding = $this->adapter->getDatabase()->getMysqlEncoding();
5459
-	}
5460
-
5461
-	/**
5462
-	 * This method returns the datatype to be used for primary key IDS and
5463
-	 * foreign keys. Returns one if the data type constants.
5464
-	 *
5465
-	 * @return integer $const data type to be used for IDS.
5466
-	 */
5467
-	public function getTypeForID()
5468
-	{
5469
-		return self::C_DATATYPE_UINT32;
5470
-	}
5471
-
5472
-	/**
5473
-	 * @see QueryWriter::getTables
5474
-	 */
5475
-	public function getTables()
5476
-	{
5477
-		return $this->adapter->getCol( 'show tables' );
5478
-	}
5479
-
5480
-	/**
5481
-	 * @see QueryWriter::createTable
5482
-	 */
5483
-	public function createTable( $table )
5484
-	{
5485
-		$table = $this->esc( $table );
5486
-
5487
-		$encoding = $this->adapter->getDatabase()->getMysqlEncoding();
5488
-		$sql   = "CREATE TABLE $table (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET={$encoding} COLLATE={$encoding}_unicode_ci ";
5489
-
5490
-		$this->adapter->exec( $sql );
5491
-	}
5492
-
5493
-	/**
5494
-	 * @see QueryWriter::getColumns
5495
-	 */
5496
-	public function getColumns( $table )
5497
-	{
5498
-		$columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) );
5499
-
5500
-		$columns = array();
5501
-		foreach ( $columnsRaw as $r ) {
5502
-			$columns[$r['Field']] = $r['Type'];
5503
-		}
5504
-
5505
-		return $columns;
5506
-	}
5507
-
5508
-	/**
5509
-	 * @see QueryWriter::scanType
5510
-	 */
5511
-	public function scanType( $value, $flagSpecial = FALSE )
5512
-	{
5513
-		$this->svalue = $value;
5514
-
5515
-		if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL;
5516
-		if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7;
5517
-
5518
-		if ( $flagSpecial ) {
5519
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
5520
-				return MySQL::C_DATATYPE_SPECIAL_DATE;
5521
-			}
5522
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
5523
-				return MySQL::C_DATATYPE_SPECIAL_DATETIME;
5524
-			}
5525
-			if ( preg_match( '/^POINT\(/', $value ) ) {
5526
-				return MySQL::C_DATATYPE_SPECIAL_POINT;
5527
-			}
5528
-			if ( preg_match( '/^LINESTRING\(/', $value ) ) {
5529
-				return MySQL::C_DATATYPE_SPECIAL_LINESTRING;
5530
-			}
5531
-			if ( preg_match( '/^POLYGON\(/', $value ) ) {
5532
-				return MySQL::C_DATATYPE_SPECIAL_POLYGON;
5533
-			}
5534
-		}
5535
-
5536
-		//setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?).
5537
-		if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) {
5538
-			return MySQL::C_DATATYPE_BOOL;
5539
-		}
5540
-
5541
-		if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
5542
-
5543
-		if ( !$this->startsWithZeros( $value ) ) {
5544
-
5545
-			if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) {
5546
-				return MySQL::C_DATATYPE_UINT32;
5547
-			}
5548
-
5549
-			if ( is_numeric( $value ) ) {
5550
-				return MySQL::C_DATATYPE_DOUBLE;
5551
-			}
5552
-		}
5553
-
5554
-		if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) {
5555
-			return MySQL::C_DATATYPE_TEXT7;
5556
-		}
5557
-
5558
-		if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) {
5559
-			return MySQL::C_DATATYPE_TEXT8;
5560
-		}
5561
-
5562
-		if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) {
5563
-			return MySQL::C_DATATYPE_TEXT16;
5564
-		}
5565
-
5566
-		return MySQL::C_DATATYPE_TEXT32;
5567
-	}
5568
-
5569
-	/**
5570
-	 * @see QueryWriter::code
5571
-	 */
5572
-	public function code( $typedescription, $includeSpecials = FALSE )
5573
-	{
5574
-		if ( isset( $this->sqltype_typeno[$typedescription] ) ) {
5575
-			$r = $this->sqltype_typeno[$typedescription];
5576
-		} else {
5577
-			$r = self::C_DATATYPE_SPECIFIED;
5578
-		}
5579
-
5580
-		if ( $includeSpecials ) {
5581
-			return $r;
5582
-		}
5583
-
5584
-		if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
5585
-			return self::C_DATATYPE_SPECIFIED;
5586
-		}
5587
-
5588
-		return $r;
5589
-	}
5590
-
5591
-	/**
5592
-	 * @see QueryWriter::addUniqueIndex
5593
-	 */
5594
-	public function addUniqueConstraint( $type, $properties )
5595
-	{
5596
-		$tableNoQ = $this->esc( $type, TRUE );
5597
-		$columns = array();
5598
-		foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
5599
-		$table = $this->esc( $type );
5600
-		sort( $columns ); // Else we get multiple indexes due to order-effects
5601
-		$name = 'UQ_' . sha1( implode( ',', $columns ) );
5602
-		try {
5603
-			$sql = "ALTER TABLE $table
5413
+        $keyInfoList = array();
5414
+        foreach ( $keys as $k ) {
5415
+            $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
5416
+            $keyInfoList[$label] = array(
5417
+                'name'          => $k['name'],
5418
+                'from'          => $k['from'],
5419
+                'table'         => $k['table'],
5420
+                'to'            => $k['to'],
5421
+                'on_update'     => $k['on_update'],
5422
+                'on_delete'     => $k['on_delete']
5423
+            );
5424
+        }
5425
+        return $keyInfoList;
5426
+    }
5427
+
5428
+    /**
5429
+     * Constructor
5430
+     *
5431
+     * @param Adapter $adapter Database Adapter
5432
+     */
5433
+    public function __construct( Adapter $adapter )
5434
+    {
5435
+        $this->typeno_sqltype = array(
5436
+            MySQL::C_DATATYPE_BOOL             => ' TINYINT(1) UNSIGNED ',
5437
+            MySQL::C_DATATYPE_UINT32           => ' INT(11) UNSIGNED ',
5438
+            MySQL::C_DATATYPE_DOUBLE           => ' DOUBLE ',
5439
+            MySQL::C_DATATYPE_TEXT7            => ' VARCHAR(191) ',
5440
+            MYSQL::C_DATATYPE_TEXT8	           => ' VARCHAR(255) ',
5441
+            MySQL::C_DATATYPE_TEXT16           => ' TEXT ',
5442
+            MySQL::C_DATATYPE_TEXT32           => ' LONGTEXT ',
5443
+            MySQL::C_DATATYPE_SPECIAL_DATE     => ' DATE ',
5444
+            MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
5445
+            MySQL::C_DATATYPE_SPECIAL_POINT    => ' POINT ',
5446
+            MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ',
5447
+            MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ',
5448
+        );
5449
+
5450
+        $this->sqltype_typeno = array();
5451
+
5452
+        foreach ( $this->typeno_sqltype as $k => $v ) {
5453
+            $this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
5454
+        }
5455
+
5456
+        $this->adapter = $adapter;
5457
+
5458
+        $this->encoding = $this->adapter->getDatabase()->getMysqlEncoding();
5459
+    }
5460
+
5461
+    /**
5462
+     * This method returns the datatype to be used for primary key IDS and
5463
+     * foreign keys. Returns one if the data type constants.
5464
+     *
5465
+     * @return integer $const data type to be used for IDS.
5466
+     */
5467
+    public function getTypeForID()
5468
+    {
5469
+        return self::C_DATATYPE_UINT32;
5470
+    }
5471
+
5472
+    /**
5473
+     * @see QueryWriter::getTables
5474
+     */
5475
+    public function getTables()
5476
+    {
5477
+        return $this->adapter->getCol( 'show tables' );
5478
+    }
5479
+
5480
+    /**
5481
+     * @see QueryWriter::createTable
5482
+     */
5483
+    public function createTable( $table )
5484
+    {
5485
+        $table = $this->esc( $table );
5486
+
5487
+        $encoding = $this->adapter->getDatabase()->getMysqlEncoding();
5488
+        $sql   = "CREATE TABLE $table (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET={$encoding} COLLATE={$encoding}_unicode_ci ";
5489
+
5490
+        $this->adapter->exec( $sql );
5491
+    }
5492
+
5493
+    /**
5494
+     * @see QueryWriter::getColumns
5495
+     */
5496
+    public function getColumns( $table )
5497
+    {
5498
+        $columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) );
5499
+
5500
+        $columns = array();
5501
+        foreach ( $columnsRaw as $r ) {
5502
+            $columns[$r['Field']] = $r['Type'];
5503
+        }
5504
+
5505
+        return $columns;
5506
+    }
5507
+
5508
+    /**
5509
+     * @see QueryWriter::scanType
5510
+     */
5511
+    public function scanType( $value, $flagSpecial = FALSE )
5512
+    {
5513
+        $this->svalue = $value;
5514
+
5515
+        if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL;
5516
+        if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7;
5517
+
5518
+        if ( $flagSpecial ) {
5519
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
5520
+                return MySQL::C_DATATYPE_SPECIAL_DATE;
5521
+            }
5522
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
5523
+                return MySQL::C_DATATYPE_SPECIAL_DATETIME;
5524
+            }
5525
+            if ( preg_match( '/^POINT\(/', $value ) ) {
5526
+                return MySQL::C_DATATYPE_SPECIAL_POINT;
5527
+            }
5528
+            if ( preg_match( '/^LINESTRING\(/', $value ) ) {
5529
+                return MySQL::C_DATATYPE_SPECIAL_LINESTRING;
5530
+            }
5531
+            if ( preg_match( '/^POLYGON\(/', $value ) ) {
5532
+                return MySQL::C_DATATYPE_SPECIAL_POLYGON;
5533
+            }
5534
+        }
5535
+
5536
+        //setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?).
5537
+        if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) {
5538
+            return MySQL::C_DATATYPE_BOOL;
5539
+        }
5540
+
5541
+        if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
5542
+
5543
+        if ( !$this->startsWithZeros( $value ) ) {
5544
+
5545
+            if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) {
5546
+                return MySQL::C_DATATYPE_UINT32;
5547
+            }
5548
+
5549
+            if ( is_numeric( $value ) ) {
5550
+                return MySQL::C_DATATYPE_DOUBLE;
5551
+            }
5552
+        }
5553
+
5554
+        if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) {
5555
+            return MySQL::C_DATATYPE_TEXT7;
5556
+        }
5557
+
5558
+        if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) {
5559
+            return MySQL::C_DATATYPE_TEXT8;
5560
+        }
5561
+
5562
+        if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) {
5563
+            return MySQL::C_DATATYPE_TEXT16;
5564
+        }
5565
+
5566
+        return MySQL::C_DATATYPE_TEXT32;
5567
+    }
5568
+
5569
+    /**
5570
+     * @see QueryWriter::code
5571
+     */
5572
+    public function code( $typedescription, $includeSpecials = FALSE )
5573
+    {
5574
+        if ( isset( $this->sqltype_typeno[$typedescription] ) ) {
5575
+            $r = $this->sqltype_typeno[$typedescription];
5576
+        } else {
5577
+            $r = self::C_DATATYPE_SPECIFIED;
5578
+        }
5579
+
5580
+        if ( $includeSpecials ) {
5581
+            return $r;
5582
+        }
5583
+
5584
+        if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
5585
+            return self::C_DATATYPE_SPECIFIED;
5586
+        }
5587
+
5588
+        return $r;
5589
+    }
5590
+
5591
+    /**
5592
+     * @see QueryWriter::addUniqueIndex
5593
+     */
5594
+    public function addUniqueConstraint( $type, $properties )
5595
+    {
5596
+        $tableNoQ = $this->esc( $type, TRUE );
5597
+        $columns = array();
5598
+        foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
5599
+        $table = $this->esc( $type );
5600
+        sort( $columns ); // Else we get multiple indexes due to order-effects
5601
+        $name = 'UQ_' . sha1( implode( ',', $columns ) );
5602
+        try {
5603
+            $sql = "ALTER TABLE $table
5604 5604
 						 ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")";
5605
-			$this->adapter->exec( $sql );
5606
-		} catch ( SQLException $e ) {
5607
-			//do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways!
5608
-			return FALSE;
5609
-		}
5610
-		return TRUE;
5611
-	}
5612
-
5613
-	/**
5614
-	 * @see QueryWriter::addIndex
5615
-	 */
5616
-	public function addIndex( $type, $name, $property )
5617
-	{
5618
-		try {
5619
-			$table  = $this->esc( $type );
5620
-			$name   = preg_replace( '/\W/', '', $name );
5621
-			$column = $this->esc( $property );
5622
-			$this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
5623
-			return TRUE;
5624
-		} catch ( SQLException $e ) {
5625
-			return FALSE;
5626
-		}
5627
-	}
5628
-
5629
-	/**
5630
-	 * @see QueryWriter::addFK
5631
-	 */
5632
-	public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
5633
-	{
5634
-		$table = $this->esc( $type );
5635
-		$targetTable = $this->esc( $targetType );
5636
-		$targetTableNoQ = $this->esc( $targetType, TRUE );
5637
-		$field = $this->esc( $property );
5638
-		$fieldNoQ = $this->esc( $property, TRUE );
5639
-		$targetField = $this->esc( $targetProperty );
5640
-		$targetFieldNoQ = $this->esc( $targetProperty, TRUE );
5641
-		$tableNoQ = $this->esc( $type, TRUE );
5642
-		$fieldNoQ = $this->esc( $property, TRUE );
5643
-		if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
5644
-
5645
-		//Widen the column if it's incapable of representing a foreign key (at least INT).
5646
-		$columns = $this->getColumns( $tableNoQ );
5647
-		$idType = $this->getTypeForID();
5648
-		if ( $this->code( $columns[$fieldNoQ] ) !==  $idType ) {
5649
-			$this->widenColumn( $type, $property, $idType );
5650
-		}
5651
-
5652
-		$fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ);
5653
-		$cName = 'c_'.$fkName;
5654
-		try {
5655
-			$this->adapter->exec( "
5605
+            $this->adapter->exec( $sql );
5606
+        } catch ( SQLException $e ) {
5607
+            //do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways!
5608
+            return FALSE;
5609
+        }
5610
+        return TRUE;
5611
+    }
5612
+
5613
+    /**
5614
+     * @see QueryWriter::addIndex
5615
+     */
5616
+    public function addIndex( $type, $name, $property )
5617
+    {
5618
+        try {
5619
+            $table  = $this->esc( $type );
5620
+            $name   = preg_replace( '/\W/', '', $name );
5621
+            $column = $this->esc( $property );
5622
+            $this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
5623
+            return TRUE;
5624
+        } catch ( SQLException $e ) {
5625
+            return FALSE;
5626
+        }
5627
+    }
5628
+
5629
+    /**
5630
+     * @see QueryWriter::addFK
5631
+     */
5632
+    public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
5633
+    {
5634
+        $table = $this->esc( $type );
5635
+        $targetTable = $this->esc( $targetType );
5636
+        $targetTableNoQ = $this->esc( $targetType, TRUE );
5637
+        $field = $this->esc( $property );
5638
+        $fieldNoQ = $this->esc( $property, TRUE );
5639
+        $targetField = $this->esc( $targetProperty );
5640
+        $targetFieldNoQ = $this->esc( $targetProperty, TRUE );
5641
+        $tableNoQ = $this->esc( $type, TRUE );
5642
+        $fieldNoQ = $this->esc( $property, TRUE );
5643
+        if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
5644
+
5645
+        //Widen the column if it's incapable of representing a foreign key (at least INT).
5646
+        $columns = $this->getColumns( $tableNoQ );
5647
+        $idType = $this->getTypeForID();
5648
+        if ( $this->code( $columns[$fieldNoQ] ) !==  $idType ) {
5649
+            $this->widenColumn( $type, $property, $idType );
5650
+        }
5651
+
5652
+        $fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ);
5653
+        $cName = 'c_'.$fkName;
5654
+        try {
5655
+            $this->adapter->exec( "
5656 5656
 				ALTER TABLE {$table}
5657 5657
 				ADD CONSTRAINT $cName
5658 5658
 				FOREIGN KEY $fkName ( {$fieldNoQ} ) REFERENCES {$targetTableNoQ}
5659 5659
 				({$targetFieldNoQ}) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';');
5660
-		} catch ( SQLException $e ) {
5661
-			// Failure of fk-constraints is not a problem
5662
-		}
5663
-	}
5664
-
5665
-	/**
5666
-	 * @see QueryWriter::sqlStateIn
5667
-	 */
5668
-	public function sqlStateIn( $state, $list )
5669
-	{
5670
-		$stateMap = array(
5671
-			'42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
5672
-			'42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
5673
-			'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
5674
-		);
5675
-
5676
-		return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
5677
-	}
5678
-
5679
-	/**
5680
-	 * @see QueryWriter::wipeAll
5681
-	 */
5682
-	public function wipeAll()
5683
-	{
5684
-		$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' );
5685
-
5686
-		foreach ( $this->getTables() as $t ) {
5687
-			try {
5688
-				$this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
5689
-			} catch ( SQLException $e ) {
5690
-			}
5691
-
5692
-			try {
5693
-				$this->adapter->exec( "DROP VIEW IF EXISTS `$t`" );
5694
-			} catch ( SQLException $e ) {
5695
-			}
5696
-		}
5697
-
5698
-		$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' );
5699
-	}
5660
+        } catch ( SQLException $e ) {
5661
+            // Failure of fk-constraints is not a problem
5662
+        }
5663
+    }
5664
+
5665
+    /**
5666
+     * @see QueryWriter::sqlStateIn
5667
+     */
5668
+    public function sqlStateIn( $state, $list )
5669
+    {
5670
+        $stateMap = array(
5671
+            '42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
5672
+            '42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
5673
+            '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
5674
+        );
5675
+
5676
+        return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
5677
+    }
5678
+
5679
+    /**
5680
+     * @see QueryWriter::wipeAll
5681
+     */
5682
+    public function wipeAll()
5683
+    {
5684
+        $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' );
5685
+
5686
+        foreach ( $this->getTables() as $t ) {
5687
+            try {
5688
+                $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
5689
+            } catch ( SQLException $e ) {
5690
+            }
5691
+
5692
+            try {
5693
+                $this->adapter->exec( "DROP VIEW IF EXISTS `$t`" );
5694
+            } catch ( SQLException $e ) {
5695
+            }
5696
+        }
5697
+
5698
+        $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' );
5699
+    }
5700 5700
 }
5701 5701
 } 
5702 5702
 
@@ -5724,415 +5724,415 @@  discard block
 block discarded – undo
5724 5724
  */
5725 5725
 class SQLiteT extends AQueryWriter implements QueryWriter
5726 5726
 {
5727
-	/**
5728
-	 * Data types
5729
-	 */
5730
-	const C_DATATYPE_INTEGER   = 0;
5731
-	const C_DATATYPE_NUMERIC   = 1;
5732
-	const C_DATATYPE_TEXT      = 2;
5733
-	const C_DATATYPE_SPECIFIED = 99;
5734
-
5735
-	/**
5736
-	 * @var DBAdapter
5737
-	 */
5738
-	protected $adapter;
5739
-
5740
-	/**
5741
-	 * @var string
5742
-	 */
5743
-	protected $quoteCharacter = '`';
5744
-
5745
-	/**
5746
-	 * Gets all information about a table (from a type).
5747
-	 *
5748
-	 * Format:
5749
-	 * array(
5750
-	 *    name => name of the table
5751
-	 *    columns => array( name => datatype )
5752
-	 *    indexes => array() raw index information rows from PRAGMA query
5753
-	 *    keys => array() raw key information rows from PRAGMA query
5754
-	 * )
5755
-	 *
5756
-	 * @param string $type type you want to get info of
5757
-	 *
5758
-	 * @return array $info
5759
-	 */
5760
-	protected function getTable( $type )
5761
-	{
5762
-		$tableName = $this->esc( $type, TRUE );
5763
-		$columns   = $this->getColumns( $type );
5764
-		$indexes   = $this->getIndexes( $type );
5765
-		$keys      = $this->getKeyMapForType( $type );
5766
-
5767
-		$table = array(
5768
-			'columns' => $columns,
5769
-			'indexes' => $indexes,
5770
-			'keys' => $keys,
5771
-			'name' => $tableName
5772
-		);
5773
-
5774
-		$this->tableArchive[$tableName] = $table;
5775
-
5776
-		return $table;
5777
-	}
5778
-
5779
-	/**
5780
-	 * Puts a table. Updates the table structure.
5781
-	 * In SQLite we can't change columns, drop columns, change or add foreign keys so we
5782
-	 * have a table-rebuild function. You simply load your table with getTable(), modify it and
5783
-	 * then store it with putTable()...
5784
-	 *
5785
-	 * @param array $tableMap information array
5786
-	 */
5787
-	protected function putTable( $tableMap )
5788
-	{
5789
-		$table = $tableMap['name'];
5790
-		$q     = array();
5791
-		$q[]   = "DROP TABLE IF EXISTS tmp_backup;";
5792
-
5793
-		$oldColumnNames = array_keys( $this->getColumns( $table ) );
5794
-
5795
-		foreach ( $oldColumnNames as $k => $v ) $oldColumnNames[$k] = "`$v`";
5796
-
5797
-		$q[] = "CREATE TEMPORARY TABLE tmp_backup(" . implode( ",", $oldColumnNames ) . ");";
5798
-		$q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
5799
-		$q[] = "PRAGMA foreign_keys = 0 ";
5800
-		$q[] = "DROP TABLE `$table`;";
5801
-
5802
-		$newTableDefStr = '';
5803
-		foreach ( $tableMap['columns'] as $column => $type ) {
5804
-			if ( $column != 'id' ) {
5805
-				$newTableDefStr .= ",`$column` $type";
5806
-			}
5807
-		}
5808
-
5809
-		$fkDef = '';
5810
-		foreach ( $tableMap['keys'] as $key ) {
5811
-			$fkDef .= ", FOREIGN KEY(`{$key['from']}`)
5727
+    /**
5728
+     * Data types
5729
+     */
5730
+    const C_DATATYPE_INTEGER   = 0;
5731
+    const C_DATATYPE_NUMERIC   = 1;
5732
+    const C_DATATYPE_TEXT      = 2;
5733
+    const C_DATATYPE_SPECIFIED = 99;
5734
+
5735
+    /**
5736
+     * @var DBAdapter
5737
+     */
5738
+    protected $adapter;
5739
+
5740
+    /**
5741
+     * @var string
5742
+     */
5743
+    protected $quoteCharacter = '`';
5744
+
5745
+    /**
5746
+     * Gets all information about a table (from a type).
5747
+     *
5748
+     * Format:
5749
+     * array(
5750
+     *    name => name of the table
5751
+     *    columns => array( name => datatype )
5752
+     *    indexes => array() raw index information rows from PRAGMA query
5753
+     *    keys => array() raw key information rows from PRAGMA query
5754
+     * )
5755
+     *
5756
+     * @param string $type type you want to get info of
5757
+     *
5758
+     * @return array $info
5759
+     */
5760
+    protected function getTable( $type )
5761
+    {
5762
+        $tableName = $this->esc( $type, TRUE );
5763
+        $columns   = $this->getColumns( $type );
5764
+        $indexes   = $this->getIndexes( $type );
5765
+        $keys      = $this->getKeyMapForType( $type );
5766
+
5767
+        $table = array(
5768
+            'columns' => $columns,
5769
+            'indexes' => $indexes,
5770
+            'keys' => $keys,
5771
+            'name' => $tableName
5772
+        );
5773
+
5774
+        $this->tableArchive[$tableName] = $table;
5775
+
5776
+        return $table;
5777
+    }
5778
+
5779
+    /**
5780
+     * Puts a table. Updates the table structure.
5781
+     * In SQLite we can't change columns, drop columns, change or add foreign keys so we
5782
+     * have a table-rebuild function. You simply load your table with getTable(), modify it and
5783
+     * then store it with putTable()...
5784
+     *
5785
+     * @param array $tableMap information array
5786
+     */
5787
+    protected function putTable( $tableMap )
5788
+    {
5789
+        $table = $tableMap['name'];
5790
+        $q     = array();
5791
+        $q[]   = "DROP TABLE IF EXISTS tmp_backup;";
5792
+
5793
+        $oldColumnNames = array_keys( $this->getColumns( $table ) );
5794
+
5795
+        foreach ( $oldColumnNames as $k => $v ) $oldColumnNames[$k] = "`$v`";
5796
+
5797
+        $q[] = "CREATE TEMPORARY TABLE tmp_backup(" . implode( ",", $oldColumnNames ) . ");";
5798
+        $q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
5799
+        $q[] = "PRAGMA foreign_keys = 0 ";
5800
+        $q[] = "DROP TABLE `$table`;";
5801
+
5802
+        $newTableDefStr = '';
5803
+        foreach ( $tableMap['columns'] as $column => $type ) {
5804
+            if ( $column != 'id' ) {
5805
+                $newTableDefStr .= ",`$column` $type";
5806
+            }
5807
+        }
5808
+
5809
+        $fkDef = '';
5810
+        foreach ( $tableMap['keys'] as $key ) {
5811
+            $fkDef .= ", FOREIGN KEY(`{$key['from']}`)
5812 5812
 						 REFERENCES `{$key['table']}`(`{$key['to']}`)
5813 5813
 						 ON DELETE {$key['on_delete']} ON UPDATE {$key['on_update']}";
5814
-		}
5815
-
5816
-		$q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT  $newTableDefStr  $fkDef );";
5817
-
5818
-		foreach ( $tableMap['indexes'] as $name => $index ) {
5819
-			if ( strpos( $name, 'UQ_' ) === 0 ) {
5820
-				$cols = explode( '__', substr( $name, strlen( 'UQ_' . $table ) ) );
5821
-				foreach ( $cols as $k => $v ) $cols[$k] = "`$v`";
5822
-				$q[] = "CREATE UNIQUE INDEX $name ON `$table` (" . implode( ',', $cols ) . ")";
5823
-			} else $q[] = "CREATE INDEX $name ON `$table` ({$index['name']}) ";
5824
-		}
5825
-
5826
-		$q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
5827
-		$q[] = "DROP TABLE tmp_backup;";
5828
-		$q[] = "PRAGMA foreign_keys = 1 ";
5829
-
5830
-		foreach ( $q as $sq ) $this->adapter->exec( $sq );
5831
-	}
5832
-
5833
-	/**
5834
-	 * Returns the indexes for type $type.
5835
-	 *
5836
-	 * @param string $type
5837
-	 *
5838
-	 * @return array $indexInfo index information
5839
-	 */
5840
-	protected function getIndexes( $type )
5841
-	{
5842
-		$table   = $this->esc( $type, TRUE );
5843
-		$indexes = $this->adapter->get( "PRAGMA index_list('$table')" );
5844
-
5845
-		$indexInfoList = array();
5846
-		foreach ( $indexes as $i ) {
5847
-			$indexInfoList[$i['name']] = $this->adapter->getRow( "PRAGMA index_info('{$i['name']}') " );
5848
-
5849
-			$indexInfoList[$i['name']]['unique'] = $i['unique'];
5850
-		}
5851
-
5852
-		return $indexInfoList;
5853
-	}
5854
-
5855
-	/**
5856
-	 * Adds a foreign key to a type
5857
-	 *
5858
-	 * @param  string  $type        type you want to modify table of
5859
-	 * @param  string  $targetType  target type
5860
-	 * @param  string  $field       field of the type that needs to get the fk
5861
-	 * @param  string  $targetField field where the fk needs to point to
5862
-	 * @param  integer $buildopt    0 = NO ACTION, 1 = ON DELETE CASCADE
5863
-	 *
5864
-	 * @return boolean $didIt
5865
-	 *
5866
-	 * @note: cant put this in try-catch because that can hide the fact
5867
-	 *      that database has been damaged.
5868
-	 */
5869
-	protected function buildFK( $type, $targetType, $property, $targetProperty, $constraint = FALSE )
5870
-	{
5871
-		$table           = $this->esc( $type, TRUE );
5872
-		$targetTable     = $this->esc( $targetType, TRUE );
5873
-		$column          = $this->esc( $property, TRUE );
5874
-		$targetColumn    = $this->esc( $targetProperty, TRUE );
5875
-
5876
-		$tables = $this->getTables();
5877
-		if ( !in_array( $targetTable, $tables ) ) return FALSE;
5878
-
5879
-		if ( !is_null( $this->getForeignKeyForTypeProperty( $table, $column ) ) ) return FALSE;
5880
-		$t = $this->getTable( $table );
5881
-		$consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' );
5882
-		$label   = 'from_' . $column . '_to_table_' . $targetTable . '_col_' . $targetColumn;
5883
-		$t['keys'][$label] = array(
5884
-			'table'     => $targetTable,
5885
-			'from'      => $column,
5886
-			'to'        => $targetColumn,
5887
-			'on_update' => $consSQL,
5888
-			'on_delete' => $consSQL
5889
-		);
5890
-		$this->putTable( $t );
5891
-		return TRUE;
5892
-	}
5893
-
5894
-	/**
5895
-	 * @see AQueryWriter::getKeyMapForType
5896
-	 */
5897
-	protected function getKeyMapForType( $type )
5898
-	{
5899
-		$table = $this->esc( $type, TRUE );
5900
-		$keys  = $this->adapter->get( "PRAGMA foreign_key_list('$table')" );
5901
-		$keyInfoList = array();
5902
-		foreach ( $keys as $k ) {
5903
-			$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
5904
-			$keyInfoList[$label] = array(
5905
-				'name'          => $label,
5906
-				'from'          => $k['from'],
5907
-				'table'         => $k['table'],
5908
-				'to'            => $k['to'],
5909
-				'on_update'     => $k['on_update'],
5910
-				'on_delete'     => $k['on_delete']
5911
-			);
5912
-		}
5913
-		return $keyInfoList;
5914
-	}
5915
-
5916
-	/**
5917
-	 * Constructor
5918
-	 *
5919
-	 * @param Adapter $adapter Database Adapter
5920
-	 */
5921
-	public function __construct( Adapter $adapter )
5922
-	{
5923
-		$this->typeno_sqltype = array(
5924
-			SQLiteT::C_DATATYPE_INTEGER => 'INTEGER',
5925
-			SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC',
5926
-			SQLiteT::C_DATATYPE_TEXT    => 'TEXT',
5927
-		);
5928
-
5929
-		$this->sqltype_typeno = array();
5930
-
5931
-		foreach ( $this->typeno_sqltype as $k => $v ) {
5932
-			$this->sqltype_typeno[$v] = $k;
5933
-		}
5934
-
5935
-		$this->adapter = $adapter;
5936
-	}
5937
-
5938
-	/**
5939
-	 * This method returns the datatype to be used for primary key IDS and
5940
-	 * foreign keys. Returns one if the data type constants.
5941
-	 *
5942
-	 * @return integer $const data type to be used for IDS.
5943
-	 */
5944
-	public function getTypeForID()
5945
-	{
5946
-		return self::C_DATATYPE_INTEGER;
5947
-	}
5948
-
5949
-	/**
5950
-	 * @see QueryWriter::scanType
5951
-	 */
5952
-	public function scanType( $value, $flagSpecial = FALSE )
5953
-	{
5954
-		$this->svalue = $value;
5955
-
5956
-		if ( $value === NULL ) return self::C_DATATYPE_INTEGER;
5957
-		if ( $value === INF ) return self::C_DATATYPE_TEXT;
5958
-
5959
-		if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
5960
-
5961
-		if ( $value === TRUE || $value === FALSE )  return self::C_DATATYPE_INTEGER;
5814
+        }
5815
+
5816
+        $q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT  $newTableDefStr  $fkDef );";
5817
+
5818
+        foreach ( $tableMap['indexes'] as $name => $index ) {
5819
+            if ( strpos( $name, 'UQ_' ) === 0 ) {
5820
+                $cols = explode( '__', substr( $name, strlen( 'UQ_' . $table ) ) );
5821
+                foreach ( $cols as $k => $v ) $cols[$k] = "`$v`";
5822
+                $q[] = "CREATE UNIQUE INDEX $name ON `$table` (" . implode( ',', $cols ) . ")";
5823
+            } else $q[] = "CREATE INDEX $name ON `$table` ({$index['name']}) ";
5824
+        }
5825
+
5826
+        $q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
5827
+        $q[] = "DROP TABLE tmp_backup;";
5828
+        $q[] = "PRAGMA foreign_keys = 1 ";
5829
+
5830
+        foreach ( $q as $sq ) $this->adapter->exec( $sq );
5831
+    }
5832
+
5833
+    /**
5834
+     * Returns the indexes for type $type.
5835
+     *
5836
+     * @param string $type
5837
+     *
5838
+     * @return array $indexInfo index information
5839
+     */
5840
+    protected function getIndexes( $type )
5841
+    {
5842
+        $table   = $this->esc( $type, TRUE );
5843
+        $indexes = $this->adapter->get( "PRAGMA index_list('$table')" );
5844
+
5845
+        $indexInfoList = array();
5846
+        foreach ( $indexes as $i ) {
5847
+            $indexInfoList[$i['name']] = $this->adapter->getRow( "PRAGMA index_info('{$i['name']}') " );
5848
+
5849
+            $indexInfoList[$i['name']]['unique'] = $i['unique'];
5850
+        }
5851
+
5852
+        return $indexInfoList;
5853
+    }
5854
+
5855
+    /**
5856
+     * Adds a foreign key to a type
5857
+     *
5858
+     * @param  string  $type        type you want to modify table of
5859
+     * @param  string  $targetType  target type
5860
+     * @param  string  $field       field of the type that needs to get the fk
5861
+     * @param  string  $targetField field where the fk needs to point to
5862
+     * @param  integer $buildopt    0 = NO ACTION, 1 = ON DELETE CASCADE
5863
+     *
5864
+     * @return boolean $didIt
5865
+     *
5866
+     * @note: cant put this in try-catch because that can hide the fact
5867
+     *      that database has been damaged.
5868
+     */
5869
+    protected function buildFK( $type, $targetType, $property, $targetProperty, $constraint = FALSE )
5870
+    {
5871
+        $table           = $this->esc( $type, TRUE );
5872
+        $targetTable     = $this->esc( $targetType, TRUE );
5873
+        $column          = $this->esc( $property, TRUE );
5874
+        $targetColumn    = $this->esc( $targetProperty, TRUE );
5875
+
5876
+        $tables = $this->getTables();
5877
+        if ( !in_array( $targetTable, $tables ) ) return FALSE;
5878
+
5879
+        if ( !is_null( $this->getForeignKeyForTypeProperty( $table, $column ) ) ) return FALSE;
5880
+        $t = $this->getTable( $table );
5881
+        $consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' );
5882
+        $label   = 'from_' . $column . '_to_table_' . $targetTable . '_col_' . $targetColumn;
5883
+        $t['keys'][$label] = array(
5884
+            'table'     => $targetTable,
5885
+            'from'      => $column,
5886
+            'to'        => $targetColumn,
5887
+            'on_update' => $consSQL,
5888
+            'on_delete' => $consSQL
5889
+        );
5890
+        $this->putTable( $t );
5891
+        return TRUE;
5892
+    }
5893
+
5894
+    /**
5895
+     * @see AQueryWriter::getKeyMapForType
5896
+     */
5897
+    protected function getKeyMapForType( $type )
5898
+    {
5899
+        $table = $this->esc( $type, TRUE );
5900
+        $keys  = $this->adapter->get( "PRAGMA foreign_key_list('$table')" );
5901
+        $keyInfoList = array();
5902
+        foreach ( $keys as $k ) {
5903
+            $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
5904
+            $keyInfoList[$label] = array(
5905
+                'name'          => $label,
5906
+                'from'          => $k['from'],
5907
+                'table'         => $k['table'],
5908
+                'to'            => $k['to'],
5909
+                'on_update'     => $k['on_update'],
5910
+                'on_delete'     => $k['on_delete']
5911
+            );
5912
+        }
5913
+        return $keyInfoList;
5914
+    }
5915
+
5916
+    /**
5917
+     * Constructor
5918
+     *
5919
+     * @param Adapter $adapter Database Adapter
5920
+     */
5921
+    public function __construct( Adapter $adapter )
5922
+    {
5923
+        $this->typeno_sqltype = array(
5924
+            SQLiteT::C_DATATYPE_INTEGER => 'INTEGER',
5925
+            SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC',
5926
+            SQLiteT::C_DATATYPE_TEXT    => 'TEXT',
5927
+        );
5928
+
5929
+        $this->sqltype_typeno = array();
5930
+
5931
+        foreach ( $this->typeno_sqltype as $k => $v ) {
5932
+            $this->sqltype_typeno[$v] = $k;
5933
+        }
5934
+
5935
+        $this->adapter = $adapter;
5936
+    }
5937
+
5938
+    /**
5939
+     * This method returns the datatype to be used for primary key IDS and
5940
+     * foreign keys. Returns one if the data type constants.
5941
+     *
5942
+     * @return integer $const data type to be used for IDS.
5943
+     */
5944
+    public function getTypeForID()
5945
+    {
5946
+        return self::C_DATATYPE_INTEGER;
5947
+    }
5948
+
5949
+    /**
5950
+     * @see QueryWriter::scanType
5951
+     */
5952
+    public function scanType( $value, $flagSpecial = FALSE )
5953
+    {
5954
+        $this->svalue = $value;
5955
+
5956
+        if ( $value === NULL ) return self::C_DATATYPE_INTEGER;
5957
+        if ( $value === INF ) return self::C_DATATYPE_TEXT;
5958
+
5959
+        if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
5960
+
5961
+        if ( $value === TRUE || $value === FALSE )  return self::C_DATATYPE_INTEGER;
5962 5962
 		
5963
-		if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER;
5964
-
5965
-		if ( ( is_numeric( $value ) && $value < 2147483648 && $value > -2147483648)
5966
-			|| preg_match( '/\d{4}\-\d\d\-\d\d/', $value )
5967
-			|| preg_match( '/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', $value )
5968
-		) {
5969
-			return self::C_DATATYPE_NUMERIC;
5970
-		}
5971
-
5972
-		return self::C_DATATYPE_TEXT;
5973
-	}
5974
-
5975
-	/**
5976
-	 * @see QueryWriter::addColumn
5977
-	 */
5978
-	public function addColumn( $table, $column, $type )
5979
-	{
5980
-		$column = $this->check( $column );
5981
-		$table  = $this->check( $table );
5982
-		$type   = $this->typeno_sqltype[$type];
5983
-
5984
-		$this->adapter->exec( "ALTER TABLE `$table` ADD `$column` $type " );
5985
-	}
5986
-
5987
-	/**
5988
-	 * @see QueryWriter::code
5989
-	 */
5990
-	public function code( $typedescription, $includeSpecials = FALSE )
5991
-	{
5992
-		$r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99 );
5963
+        if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER;
5964
+
5965
+        if ( ( is_numeric( $value ) && $value < 2147483648 && $value > -2147483648)
5966
+            || preg_match( '/\d{4}\-\d\d\-\d\d/', $value )
5967
+            || preg_match( '/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', $value )
5968
+        ) {
5969
+            return self::C_DATATYPE_NUMERIC;
5970
+        }
5971
+
5972
+        return self::C_DATATYPE_TEXT;
5973
+    }
5974
+
5975
+    /**
5976
+     * @see QueryWriter::addColumn
5977
+     */
5978
+    public function addColumn( $table, $column, $type )
5979
+    {
5980
+        $column = $this->check( $column );
5981
+        $table  = $this->check( $table );
5982
+        $type   = $this->typeno_sqltype[$type];
5983
+
5984
+        $this->adapter->exec( "ALTER TABLE `$table` ADD `$column` $type " );
5985
+    }
5986
+
5987
+    /**
5988
+     * @see QueryWriter::code
5989
+     */
5990
+    public function code( $typedescription, $includeSpecials = FALSE )
5991
+    {
5992
+        $r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99 );
5993 5993
 		
5994
-		return $r;
5995
-	}
5996
-
5997
-	/**
5998
-	 * @see QueryWriter::widenColumn
5999
-	 */
6000
-	public function widenColumn( $type, $column, $datatype )
6001
-	{
6002
-		$t = $this->getTable( $type );
6003
-
6004
-		$t['columns'][$column] = $this->typeno_sqltype[$datatype];
6005
-
6006
-		$this->putTable( $t );
6007
-	}
6008
-
6009
-	/**
6010
-	 * @see QueryWriter::getTables();
6011
-	 */
6012
-	public function getTables()
6013
-	{
6014
-		return $this->adapter->getCol( "SELECT name FROM sqlite_master
5994
+        return $r;
5995
+    }
5996
+
5997
+    /**
5998
+     * @see QueryWriter::widenColumn
5999
+     */
6000
+    public function widenColumn( $type, $column, $datatype )
6001
+    {
6002
+        $t = $this->getTable( $type );
6003
+
6004
+        $t['columns'][$column] = $this->typeno_sqltype[$datatype];
6005
+
6006
+        $this->putTable( $t );
6007
+    }
6008
+
6009
+    /**
6010
+     * @see QueryWriter::getTables();
6011
+     */
6012
+    public function getTables()
6013
+    {
6014
+        return $this->adapter->getCol( "SELECT name FROM sqlite_master
6015 6015
 			WHERE type='table' AND name!='sqlite_sequence';" );
6016
-	}
6017
-
6018
-	/**
6019
-	 * @see QueryWriter::createTable
6020
-	 */
6021
-	public function createTable( $table )
6022
-	{
6023
-		$table = $this->esc( $table );
6024
-
6025
-		$sql   = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) ";
6026
-
6027
-		$this->adapter->exec( $sql );
6028
-	}
6029
-
6030
-	/**
6031
-	 * @see QueryWriter::getColumns
6032
-	 */
6033
-	public function getColumns( $table )
6034
-	{
6035
-		$table      = $this->esc( $table, TRUE );
6036
-
6037
-		$columnsRaw = $this->adapter->get( "PRAGMA table_info('$table')" );
6038
-
6039
-		$columns    = array();
6040
-		foreach ( $columnsRaw as $r ) $columns[$r['name']] = $r['type'];
6041
-
6042
-		return $columns;
6043
-	}
6044
-
6045
-	/**
6046
-	 * @see QueryWriter::addUniqueIndex
6047
-	 */
6048
-	public function addUniqueConstraint( $type, $properties )
6049
-	{
6050
-		$tableNoQ = $this->esc( $type, TRUE );
6051
-		$name  = 'UQ_' . $this->esc( $type, TRUE ) . implode( '__', $properties );
6052
-		$t     = $this->getTable( $type );
6053
-		$t['indexes'][$name] = array( 'name' => $name );
6054
-		try {
6055
-			$this->putTable( $t );
6056
-		} catch( SQLException $e ) {
6057
-			return FALSE;
6058
-		}
6059
-		return TRUE;
6060
-	}
6061
-
6062
-	/**
6063
-	 * @see QueryWriter::sqlStateIn
6064
-	 */
6065
-	public function sqlStateIn( $state, $list )
6066
-	{
6067
-		$stateMap = array(
6068
-			'HY000' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
6069
-			'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
6070
-		);
6071
-
6072
-		return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
6073
-	}
6074
-
6075
-	/**
6076
-	 * @see QueryWriter::addIndex
6077
-	 */
6078
-	public function addIndex( $type, $name, $column )
6079
-	{
6080
-		$columns = $this->getColumns( $type );
6081
-		if ( !isset( $columns[$column] ) ) return FALSE;
6082
-
6083
-		$table  = $this->esc( $type );
6084
-		$name   = preg_replace( '/\W/', '', $name );
6085
-		$column = $this->esc( $column, TRUE );
6086
-
6087
-		try {
6088
-			$t = $this->getTable( $type );
6089
-			$t['indexes'][$name] = array( 'name' => $column );
6090
-			$this->putTable( $t );
6091
-			return TRUE;
6092
-		} catch( SQLException $exception ) {
6093
-			return FALSE;
6094
-		}
6095
-	}
6096
-
6097
-	/**
6098
-	 * @see QueryWriter::wipe
6099
-	 */
6100
-	public function wipe( $type )
6101
-	{
6102
-		$table = $this->esc( $type );
6016
+    }
6017
+
6018
+    /**
6019
+     * @see QueryWriter::createTable
6020
+     */
6021
+    public function createTable( $table )
6022
+    {
6023
+        $table = $this->esc( $table );
6024
+
6025
+        $sql   = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) ";
6026
+
6027
+        $this->adapter->exec( $sql );
6028
+    }
6029
+
6030
+    /**
6031
+     * @see QueryWriter::getColumns
6032
+     */
6033
+    public function getColumns( $table )
6034
+    {
6035
+        $table      = $this->esc( $table, TRUE );
6036
+
6037
+        $columnsRaw = $this->adapter->get( "PRAGMA table_info('$table')" );
6038
+
6039
+        $columns    = array();
6040
+        foreach ( $columnsRaw as $r ) $columns[$r['name']] = $r['type'];
6041
+
6042
+        return $columns;
6043
+    }
6044
+
6045
+    /**
6046
+     * @see QueryWriter::addUniqueIndex
6047
+     */
6048
+    public function addUniqueConstraint( $type, $properties )
6049
+    {
6050
+        $tableNoQ = $this->esc( $type, TRUE );
6051
+        $name  = 'UQ_' . $this->esc( $type, TRUE ) . implode( '__', $properties );
6052
+        $t     = $this->getTable( $type );
6053
+        $t['indexes'][$name] = array( 'name' => $name );
6054
+        try {
6055
+            $this->putTable( $t );
6056
+        } catch( SQLException $e ) {
6057
+            return FALSE;
6058
+        }
6059
+        return TRUE;
6060
+    }
6061
+
6062
+    /**
6063
+     * @see QueryWriter::sqlStateIn
6064
+     */
6065
+    public function sqlStateIn( $state, $list )
6066
+    {
6067
+        $stateMap = array(
6068
+            'HY000' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
6069
+            '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
6070
+        );
6071
+
6072
+        return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
6073
+    }
6074
+
6075
+    /**
6076
+     * @see QueryWriter::addIndex
6077
+     */
6078
+    public function addIndex( $type, $name, $column )
6079
+    {
6080
+        $columns = $this->getColumns( $type );
6081
+        if ( !isset( $columns[$column] ) ) return FALSE;
6082
+
6083
+        $table  = $this->esc( $type );
6084
+        $name   = preg_replace( '/\W/', '', $name );
6085
+        $column = $this->esc( $column, TRUE );
6086
+
6087
+        try {
6088
+            $t = $this->getTable( $type );
6089
+            $t['indexes'][$name] = array( 'name' => $column );
6090
+            $this->putTable( $t );
6091
+            return TRUE;
6092
+        } catch( SQLException $exception ) {
6093
+            return FALSE;
6094
+        }
6095
+    }
6096
+
6097
+    /**
6098
+     * @see QueryWriter::wipe
6099
+     */
6100
+    public function wipe( $type )
6101
+    {
6102
+        $table = $this->esc( $type );
6103 6103
 		
6104
-		$this->adapter->exec( "DELETE FROM $table " );
6105
-	}
6106
-
6107
-	/**
6108
-	 * @see QueryWriter::addFK
6109
-	 */
6110
-	public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
6111
-	{
6112
-		return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDep );
6113
-	}
6114
-
6115
-	/**
6116
-	 * @see QueryWriter::wipeAll
6117
-	 */
6118
-	public function wipeAll()
6119
-	{
6120
-		$this->adapter->exec( 'PRAGMA foreign_keys = 0 ' );
6121
-
6122
-		foreach ( $this->getTables() as $t ) {
6123
-			try {
6124
-				$this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
6125
-			} catch ( SQLException $e ) {
6126
-			}
6127
-
6128
-			try {
6129
-				$this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
6130
-			} catch ( SQLException $e ) {
6131
-			}
6132
-		}
6133
-
6134
-		$this->adapter->exec( 'PRAGMA foreign_keys = 1 ' );
6135
-	}
6104
+        $this->adapter->exec( "DELETE FROM $table " );
6105
+    }
6106
+
6107
+    /**
6108
+     * @see QueryWriter::addFK
6109
+     */
6110
+    public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
6111
+    {
6112
+        return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDep );
6113
+    }
6114
+
6115
+    /**
6116
+     * @see QueryWriter::wipeAll
6117
+     */
6118
+    public function wipeAll()
6119
+    {
6120
+        $this->adapter->exec( 'PRAGMA foreign_keys = 0 ' );
6121
+
6122
+        foreach ( $this->getTables() as $t ) {
6123
+            try {
6124
+                $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
6125
+            } catch ( SQLException $e ) {
6126
+            }
6127
+
6128
+            try {
6129
+                $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
6130
+            } catch ( SQLException $e ) {
6131
+            }
6132
+        }
6133
+
6134
+        $this->adapter->exec( 'PRAGMA foreign_keys = 1 ' );
6135
+    }
6136 6136
 }
6137 6137
 }
6138 6138
 
@@ -6160,55 +6160,55 @@  discard block
 block discarded – undo
6160 6160
  */
6161 6161
 class PostgreSQL extends AQueryWriter implements QueryWriter
6162 6162
 {
6163
-	/**
6164
-	 * Data types
6165
-	 */
6166
-	const C_DATATYPE_INTEGER          = 0;
6167
-	const C_DATATYPE_DOUBLE           = 1;
6168
-	const C_DATATYPE_TEXT             = 3;
6169
-	const C_DATATYPE_SPECIAL_DATE     = 80;
6170
-	const C_DATATYPE_SPECIAL_DATETIME = 81;
6171
-	const C_DATATYPE_SPECIAL_POINT    = 90;
6172
-	const C_DATATYPE_SPECIAL_LSEG     = 91;
6173
-	const C_DATATYPE_SPECIAL_CIRCLE   = 92;
6174
-	const C_DATATYPE_SPECIAL_MONEY    = 93;
6175
-	const C_DATATYPE_SPECIAL_POLYGON  = 94;
6176
-	const C_DATATYPE_SPECIFIED        = 99;
6177
-
6178
-	/**
6179
-	 * @var DBAdapter
6180
-	 */
6181
-	protected $adapter;
6182
-
6183
-	/**
6184
-	 * @var string
6185
-	 */
6186
-	protected $quoteCharacter = '"';
6187
-
6188
-	/**
6189
-	 * @var string
6190
-	 */
6191
-	protected $defaultValue = 'DEFAULT';
6192
-
6193
-	/**
6194
-	 * Returns the insert suffix SQL Snippet
6195
-	 *
6196
-	 * @param string $table table
6197
-	 *
6198
-	 * @return  string $sql SQL Snippet
6199
-	 */
6200
-	protected function getInsertSuffix( $table )
6201
-	{
6202
-		return 'RETURNING id ';
6203
-	}
6204
-
6205
-	/**
6206
-	 * @see AQueryWriter::getKeyMapForType
6207
-	 */
6208
-	protected function getKeyMapForType( $type )
6209
-	{
6210
-		$table = $this->esc( $type, TRUE );
6211
-		$keys = $this->adapter->get( '
6163
+    /**
6164
+     * Data types
6165
+     */
6166
+    const C_DATATYPE_INTEGER          = 0;
6167
+    const C_DATATYPE_DOUBLE           = 1;
6168
+    const C_DATATYPE_TEXT             = 3;
6169
+    const C_DATATYPE_SPECIAL_DATE     = 80;
6170
+    const C_DATATYPE_SPECIAL_DATETIME = 81;
6171
+    const C_DATATYPE_SPECIAL_POINT    = 90;
6172
+    const C_DATATYPE_SPECIAL_LSEG     = 91;
6173
+    const C_DATATYPE_SPECIAL_CIRCLE   = 92;
6174
+    const C_DATATYPE_SPECIAL_MONEY    = 93;
6175
+    const C_DATATYPE_SPECIAL_POLYGON  = 94;
6176
+    const C_DATATYPE_SPECIFIED        = 99;
6177
+
6178
+    /**
6179
+     * @var DBAdapter
6180
+     */
6181
+    protected $adapter;
6182
+
6183
+    /**
6184
+     * @var string
6185
+     */
6186
+    protected $quoteCharacter = '"';
6187
+
6188
+    /**
6189
+     * @var string
6190
+     */
6191
+    protected $defaultValue = 'DEFAULT';
6192
+
6193
+    /**
6194
+     * Returns the insert suffix SQL Snippet
6195
+     *
6196
+     * @param string $table table
6197
+     *
6198
+     * @return  string $sql SQL Snippet
6199
+     */
6200
+    protected function getInsertSuffix( $table )
6201
+    {
6202
+        return 'RETURNING id ';
6203
+    }
6204
+
6205
+    /**
6206
+     * @see AQueryWriter::getKeyMapForType
6207
+     */
6208
+    protected function getKeyMapForType( $type )
6209
+    {
6210
+        $table = $this->esc( $type, TRUE );
6211
+        $keys = $this->adapter->get( '
6212 6212
 			SELECT
6213 6213
 			information_schema.key_column_usage.constraint_name AS "name",
6214 6214
 			information_schema.key_column_usage.column_name AS "from",
@@ -6240,274 +6240,274 @@  discard block
 block discarded – undo
6240 6240
 				AND information_schema.key_column_usage.table_schema = ANY( current_schemas( FALSE ) )
6241 6241
 				AND information_schema.key_column_usage.table_name = ?
6242 6242
 		', array( $type ) );
6243
-		$keyInfoList = array();
6244
-		foreach ( $keys as $k ) {
6245
-			$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
6246
-			$keyInfoList[$label] = array(
6247
-				'name'          => $k['name'],
6248
-				'from'          => $k['from'],
6249
-				'table'         => $k['table'],
6250
-				'to'            => $k['to'],
6251
-				'on_update'     => $k['on_update'],
6252
-				'on_delete'     => $k['on_delete']
6253
-			);
6254
-		}
6255
-		return $keyInfoList;
6256
-	}
6257
-
6258
-	/**
6259
-	 * Constructor
6260
-	 *
6261
-	 * @param Adapter $adapter Database Adapter
6262
-	 */
6263
-	public function __construct( Adapter $adapter )
6264
-	{
6265
-		$this->typeno_sqltype = array(
6266
-			self::C_DATATYPE_INTEGER          => ' integer ',
6267
-			self::C_DATATYPE_DOUBLE           => ' double precision ',
6268
-			self::C_DATATYPE_TEXT             => ' text ',
6269
-			self::C_DATATYPE_SPECIAL_DATE     => ' date ',
6270
-			self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ',
6271
-			self::C_DATATYPE_SPECIAL_POINT    => ' point ',
6272
-			self::C_DATATYPE_SPECIAL_LSEG     => ' lseg ',
6273
-			self::C_DATATYPE_SPECIAL_CIRCLE   => ' circle ',
6274
-			self::C_DATATYPE_SPECIAL_MONEY    => ' money ',
6275
-			self::C_DATATYPE_SPECIAL_POLYGON  => ' polygon ',
6276
-		);
6277
-
6278
-		$this->sqltype_typeno = array();
6279
-
6280
-		foreach ( $this->typeno_sqltype as $k => $v ) {
6281
-			$this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
6282
-		}
6283
-
6284
-		$this->adapter = $adapter;
6285
-	}
6286
-
6287
-	/**
6288
-	 * This method returns the datatype to be used for primary key IDS and
6289
-	 * foreign keys. Returns one if the data type constants.
6290
-	 *
6291
-	 * @return integer $const data type to be used for IDS.
6292
-	 */
6293
-	public function getTypeForID()
6294
-	{
6295
-		return self::C_DATATYPE_INTEGER;
6296
-	}
6297
-
6298
-	/**
6299
-	 * @see QueryWriter::getTables
6300
-	 */
6301
-	public function getTables()
6302
-	{
6303
-		return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' );
6304
-	}
6305
-
6306
-	/**
6307
-	 * @see QueryWriter::createTable
6308
-	 */
6309
-	public function createTable( $table )
6310
-	{
6311
-		$table = $this->esc( $table );
6312
-
6313
-		$this->adapter->exec( " CREATE TABLE $table (id SERIAL PRIMARY KEY); " );
6314
-	}
6315
-
6316
-	/**
6317
-	 * @see QueryWriter::getColumns
6318
-	 */
6319
-	public function getColumns( $table )
6320
-	{
6321
-		$table      = $this->esc( $table, TRUE );
6322
-
6323
-		$columnsRaw = $this->adapter->get( "SELECT column_name, data_type FROM information_schema.columns WHERE table_name='$table'" );
6324
-
6325
-		$columns = array();
6326
-		foreach ( $columnsRaw as $r ) {
6327
-			$columns[$r['column_name']] = $r['data_type'];
6328
-		}
6329
-
6330
-		return $columns;
6331
-	}
6332
-
6333
-	/**
6334
-	 * @see QueryWriter::scanType
6335
-	 */
6336
-	public function scanType( $value, $flagSpecial = FALSE )
6337
-	{
6338
-		$this->svalue = $value;
6339
-
6340
-		if ( $value === INF ) return self::C_DATATYPE_TEXT;
6341
-
6342
-		if ( $flagSpecial && $value ) {
6343
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
6344
-				return PostgreSQL::C_DATATYPE_SPECIAL_DATE;
6345
-			}
6346
-
6347
-			if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) {
6348
-				return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME;
6349
-			}
6350
-
6351
-			if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) {
6352
-				return PostgreSQL::C_DATATYPE_SPECIAL_POINT;
6353
-			}
6354
-
6355
-			if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) {
6356
-				return PostgreSQL::C_DATATYPE_SPECIAL_LSEG;
6357
-			}
6358
-
6359
-			if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) {
6360
-				return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE;
6361
-			}
6362
-
6363
-			if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) {
6364
-				return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON;
6365
-			}
6366
-
6367
-			if ( preg_match( '/^\-?(\$|€|¥|£)[\d,\.]+$/', $value ) ) {
6368
-				return PostgreSQL::C_DATATYPE_SPECIAL_MONEY;
6369
-			}
6370
-		}
6371
-
6372
-		if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
6373
-
6374
-		if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
6243
+        $keyInfoList = array();
6244
+        foreach ( $keys as $k ) {
6245
+            $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
6246
+            $keyInfoList[$label] = array(
6247
+                'name'          => $k['name'],
6248
+                'from'          => $k['from'],
6249
+                'table'         => $k['table'],
6250
+                'to'            => $k['to'],
6251
+                'on_update'     => $k['on_update'],
6252
+                'on_delete'     => $k['on_delete']
6253
+            );
6254
+        }
6255
+        return $keyInfoList;
6256
+    }
6257
+
6258
+    /**
6259
+     * Constructor
6260
+     *
6261
+     * @param Adapter $adapter Database Adapter
6262
+     */
6263
+    public function __construct( Adapter $adapter )
6264
+    {
6265
+        $this->typeno_sqltype = array(
6266
+            self::C_DATATYPE_INTEGER          => ' integer ',
6267
+            self::C_DATATYPE_DOUBLE           => ' double precision ',
6268
+            self::C_DATATYPE_TEXT             => ' text ',
6269
+            self::C_DATATYPE_SPECIAL_DATE     => ' date ',
6270
+            self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ',
6271
+            self::C_DATATYPE_SPECIAL_POINT    => ' point ',
6272
+            self::C_DATATYPE_SPECIAL_LSEG     => ' lseg ',
6273
+            self::C_DATATYPE_SPECIAL_CIRCLE   => ' circle ',
6274
+            self::C_DATATYPE_SPECIAL_MONEY    => ' money ',
6275
+            self::C_DATATYPE_SPECIAL_POLYGON  => ' polygon ',
6276
+        );
6277
+
6278
+        $this->sqltype_typeno = array();
6279
+
6280
+        foreach ( $this->typeno_sqltype as $k => $v ) {
6281
+            $this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
6282
+        }
6283
+
6284
+        $this->adapter = $adapter;
6285
+    }
6286
+
6287
+    /**
6288
+     * This method returns the datatype to be used for primary key IDS and
6289
+     * foreign keys. Returns one if the data type constants.
6290
+     *
6291
+     * @return integer $const data type to be used for IDS.
6292
+     */
6293
+    public function getTypeForID()
6294
+    {
6295
+        return self::C_DATATYPE_INTEGER;
6296
+    }
6297
+
6298
+    /**
6299
+     * @see QueryWriter::getTables
6300
+     */
6301
+    public function getTables()
6302
+    {
6303
+        return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' );
6304
+    }
6305
+
6306
+    /**
6307
+     * @see QueryWriter::createTable
6308
+     */
6309
+    public function createTable( $table )
6310
+    {
6311
+        $table = $this->esc( $table );
6312
+
6313
+        $this->adapter->exec( " CREATE TABLE $table (id SERIAL PRIMARY KEY); " );
6314
+    }
6315
+
6316
+    /**
6317
+     * @see QueryWriter::getColumns
6318
+     */
6319
+    public function getColumns( $table )
6320
+    {
6321
+        $table      = $this->esc( $table, TRUE );
6322
+
6323
+        $columnsRaw = $this->adapter->get( "SELECT column_name, data_type FROM information_schema.columns WHERE table_name='$table'" );
6324
+
6325
+        $columns = array();
6326
+        foreach ( $columnsRaw as $r ) {
6327
+            $columns[$r['column_name']] = $r['data_type'];
6328
+        }
6329
+
6330
+        return $columns;
6331
+    }
6332
+
6333
+    /**
6334
+     * @see QueryWriter::scanType
6335
+     */
6336
+    public function scanType( $value, $flagSpecial = FALSE )
6337
+    {
6338
+        $this->svalue = $value;
6339
+
6340
+        if ( $value === INF ) return self::C_DATATYPE_TEXT;
6341
+
6342
+        if ( $flagSpecial && $value ) {
6343
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
6344
+                return PostgreSQL::C_DATATYPE_SPECIAL_DATE;
6345
+            }
6346
+
6347
+            if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) {
6348
+                return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME;
6349
+            }
6350
+
6351
+            if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) {
6352
+                return PostgreSQL::C_DATATYPE_SPECIAL_POINT;
6353
+            }
6354
+
6355
+            if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) {
6356
+                return PostgreSQL::C_DATATYPE_SPECIAL_LSEG;
6357
+            }
6358
+
6359
+            if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) {
6360
+                return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE;
6361
+            }
6362
+
6363
+            if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) {
6364
+                return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON;
6365
+            }
6366
+
6367
+            if ( preg_match( '/^\-?(\$|€|¥|£)[\d,\.]+$/', $value ) ) {
6368
+                return PostgreSQL::C_DATATYPE_SPECIAL_MONEY;
6369
+            }
6370
+        }
6371
+
6372
+        if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
6373
+
6374
+        if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
6375 6375
 		
6376
-		if ( $value === FALSE || $value === TRUE || $value === NULL || ( is_numeric( $value )
6377
-				&& AQueryWriter::canBeTreatedAsInt( $value )
6378
-				&& $value < 2147483648
6379
-				&& $value > -2147483648 )
6380
-		) {
6381
-			return self::C_DATATYPE_INTEGER;
6382
-		} elseif ( is_numeric( $value ) ) {
6383
-			return self::C_DATATYPE_DOUBLE;
6384
-		} else {
6385
-			return self::C_DATATYPE_TEXT;
6386
-		}
6387
-	}
6388
-
6389
-	/**
6390
-	 * @see QueryWriter::code
6391
-	 */
6392
-	public function code( $typedescription, $includeSpecials = FALSE )
6393
-	{
6394
-		$r = ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99;
6395
-
6396
-		if ( $includeSpecials ) return $r;
6397
-
6398
-		if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
6399
-			return self::C_DATATYPE_SPECIFIED;
6400
-		}
6401
-
6402
-		return $r;
6403
-	}
6404
-
6405
-	/**
6406
-	 * @see QueryWriter::widenColumn
6407
-	 */
6408
-	public function widenColumn( $type, $column, $datatype )
6409
-	{
6410
-		$table   = $type;
6411
-		$type    = $datatype;
6412
-
6413
-		$table   = $this->esc( $table );
6414
-		$column  = $this->esc( $column );
6415
-
6416
-		$newtype = $this->typeno_sqltype[$type];
6417
-
6418
-		$this->adapter->exec( "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype " );
6419
-	}
6420
-
6421
-	/**
6422
-	 * @see QueryWriter::addUniqueIndex
6423
-	 */
6424
-	public function addUniqueConstraint( $type, $properties )
6425
-	{
6426
-		$tableNoQ = $this->esc( $type, TRUE );
6427
-		$columns = array();
6428
-		foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
6429
-		$table = $this->esc( $type );
6430
-		sort( $columns ); //else we get multiple indexes due to order-effects
6431
-		$name = "UQ_" . sha1( $table . implode( ',', $columns ) );
6432
-		$sql = "ALTER TABLE {$table}
6376
+        if ( $value === FALSE || $value === TRUE || $value === NULL || ( is_numeric( $value )
6377
+                && AQueryWriter::canBeTreatedAsInt( $value )
6378
+                && $value < 2147483648
6379
+                && $value > -2147483648 )
6380
+        ) {
6381
+            return self::C_DATATYPE_INTEGER;
6382
+        } elseif ( is_numeric( $value ) ) {
6383
+            return self::C_DATATYPE_DOUBLE;
6384
+        } else {
6385
+            return self::C_DATATYPE_TEXT;
6386
+        }
6387
+    }
6388
+
6389
+    /**
6390
+     * @see QueryWriter::code
6391
+     */
6392
+    public function code( $typedescription, $includeSpecials = FALSE )
6393
+    {
6394
+        $r = ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99;
6395
+
6396
+        if ( $includeSpecials ) return $r;
6397
+
6398
+        if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
6399
+            return self::C_DATATYPE_SPECIFIED;
6400
+        }
6401
+
6402
+        return $r;
6403
+    }
6404
+
6405
+    /**
6406
+     * @see QueryWriter::widenColumn
6407
+     */
6408
+    public function widenColumn( $type, $column, $datatype )
6409
+    {
6410
+        $table   = $type;
6411
+        $type    = $datatype;
6412
+
6413
+        $table   = $this->esc( $table );
6414
+        $column  = $this->esc( $column );
6415
+
6416
+        $newtype = $this->typeno_sqltype[$type];
6417
+
6418
+        $this->adapter->exec( "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype " );
6419
+    }
6420
+
6421
+    /**
6422
+     * @see QueryWriter::addUniqueIndex
6423
+     */
6424
+    public function addUniqueConstraint( $type, $properties )
6425
+    {
6426
+        $tableNoQ = $this->esc( $type, TRUE );
6427
+        $columns = array();
6428
+        foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
6429
+        $table = $this->esc( $type );
6430
+        sort( $columns ); //else we get multiple indexes due to order-effects
6431
+        $name = "UQ_" . sha1( $table . implode( ',', $columns ) );
6432
+        $sql = "ALTER TABLE {$table}
6433 6433
                 ADD CONSTRAINT $name UNIQUE (" . implode( ',', $columns ) . ")";
6434
-		try {
6435
-			$this->adapter->exec( $sql );
6436
-		} catch( SQLException $e ) {
6437
-			return FALSE;
6438
-		}
6439
-		return TRUE;
6440
-	}
6441
-
6442
-	/**
6443
-	 * @see QueryWriter::sqlStateIn
6444
-	 */
6445
-	public function sqlStateIn( $state, $list )
6446
-	{
6447
-		$stateMap = array(
6448
-			'42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
6449
-			'42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
6450
-			'23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
6451
-		);
6452
-
6453
-		return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
6454
-	}
6455
-
6456
-	/**
6457
-	 * @see QueryWriter::addIndex
6458
-	 */
6459
-	public function addIndex( $type, $name, $property )
6460
-	{
6461
-		$table  = $this->esc( $type );
6462
-		$name   = preg_replace( '/\W/', '', $name );
6463
-		$column = $this->esc( $property );
6464
-
6465
-		try {
6466
-			$this->adapter->exec( "CREATE INDEX {$name} ON $table ({$column}) " );
6467
-			return TRUE;
6468
-		} catch ( SQLException $e ) {
6469
-			return FALSE;
6470
-		}
6471
-	}
6472
-
6473
-	/**
6474
-	 * @see QueryWriter::addFK
6475
-	 */
6476
-	public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
6477
-	{
6478
-		$table = $this->esc( $type );
6479
-		$targetTable = $this->esc( $targetType );
6480
-		$field = $this->esc( $property );
6481
-		$targetField = $this->esc( $targetProperty );
6482
-		$tableNoQ = $this->esc( $type, TRUE );
6483
-		$fieldNoQ = $this->esc( $property, TRUE );
6484
-		if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
6485
-		try{
6486
-			$delRule = ( $isDep ? 'CASCADE' : 'SET NULL' );
6487
-			$this->adapter->exec( "ALTER TABLE {$table}
6434
+        try {
6435
+            $this->adapter->exec( $sql );
6436
+        } catch( SQLException $e ) {
6437
+            return FALSE;
6438
+        }
6439
+        return TRUE;
6440
+    }
6441
+
6442
+    /**
6443
+     * @see QueryWriter::sqlStateIn
6444
+     */
6445
+    public function sqlStateIn( $state, $list )
6446
+    {
6447
+        $stateMap = array(
6448
+            '42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
6449
+            '42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
6450
+            '23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
6451
+        );
6452
+
6453
+        return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
6454
+    }
6455
+
6456
+    /**
6457
+     * @see QueryWriter::addIndex
6458
+     */
6459
+    public function addIndex( $type, $name, $property )
6460
+    {
6461
+        $table  = $this->esc( $type );
6462
+        $name   = preg_replace( '/\W/', '', $name );
6463
+        $column = $this->esc( $property );
6464
+
6465
+        try {
6466
+            $this->adapter->exec( "CREATE INDEX {$name} ON $table ({$column}) " );
6467
+            return TRUE;
6468
+        } catch ( SQLException $e ) {
6469
+            return FALSE;
6470
+        }
6471
+    }
6472
+
6473
+    /**
6474
+     * @see QueryWriter::addFK
6475
+     */
6476
+    public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
6477
+    {
6478
+        $table = $this->esc( $type );
6479
+        $targetTable = $this->esc( $targetType );
6480
+        $field = $this->esc( $property );
6481
+        $targetField = $this->esc( $targetProperty );
6482
+        $tableNoQ = $this->esc( $type, TRUE );
6483
+        $fieldNoQ = $this->esc( $property, TRUE );
6484
+        if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
6485
+        try{
6486
+            $delRule = ( $isDep ? 'CASCADE' : 'SET NULL' );
6487
+            $this->adapter->exec( "ALTER TABLE {$table}
6488 6488
 				ADD FOREIGN KEY ( {$field} ) REFERENCES  {$targetTable}
6489 6489
 				({$targetField}) ON DELETE {$delRule} ON UPDATE {$delRule} DEFERRABLE ;" );
6490
-			return TRUE;
6491
-		} catch ( SQLException $e ) {
6492
-			return FALSE;
6493
-		}
6494
-	}
6495
-
6496
-	/**
6497
-	 * @see QueryWriter::wipeAll
6498
-	 */
6499
-	public function wipeAll()
6500
-	{
6501
-		$this->adapter->exec( 'SET CONSTRAINTS ALL DEFERRED' );
6502
-
6503
-		foreach ( $this->getTables() as $t ) {
6504
-			$t = $this->esc( $t );
6505
-
6506
-			$this->adapter->exec( "DROP TABLE IF EXISTS $t CASCADE " );
6507
-		}
6508
-
6509
-		$this->adapter->exec( 'SET CONSTRAINTS ALL IMMEDIATE' );
6510
-	}
6490
+            return TRUE;
6491
+        } catch ( SQLException $e ) {
6492
+            return FALSE;
6493
+        }
6494
+    }
6495
+
6496
+    /**
6497
+     * @see QueryWriter::wipeAll
6498
+     */
6499
+    public function wipeAll()
6500
+    {
6501
+        $this->adapter->exec( 'SET CONSTRAINTS ALL DEFERRED' );
6502
+
6503
+        foreach ( $this->getTables() as $t ) {
6504
+            $t = $this->esc( $t );
6505
+
6506
+            $this->adapter->exec( "DROP TABLE IF EXISTS $t CASCADE " );
6507
+        }
6508
+
6509
+        $this->adapter->exec( 'SET CONSTRAINTS ALL IMMEDIATE' );
6510
+    }
6511 6511
 }
6512 6512
 } 
6513 6513
 
@@ -6550,44 +6550,44 @@  discard block
 block discarded – undo
6550 6550
  */
6551 6551
 class SQL extends RedException
6552 6552
 {
6553
-	/**
6554
-	 * @var string
6555
-	 */
6556
-	private $sqlState;
6557
-
6558
-	/**
6559
-	 * Returns an ANSI-92 compliant SQL state.
6560
-	 *
6561
-	 * @return string $state ANSI state code
6562
-	 */
6563
-	public function getSQLState()
6564
-	{
6565
-		return $this->sqlState;
6566
-	}
6567
-
6568
-	/**
6569
-	 * @todo parse state to verify valid ANSI92!
6570
-	 *       Stores ANSI-92 compliant SQL state.
6571
-	 *
6572
-	 * @param string $sqlState code
6573
-	 *
6574
-	 * @return void
6575
-	 */
6576
-	public function setSQLState( $sqlState )
6577
-	{
6578
-		$this->sqlState = $sqlState;
6579
-	}
6580
-
6581
-	/**
6582
-	 * To String prints both code and SQL state.
6583
-	 *
6584
-	 * @return string $message prints this exception instance as a string
6585
-	 */
6586
-	public function __toString()
6587
-	{
6588
-		return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n".
6589
-				'trace: ' . $this->getTraceAsString();
6590
-	}
6553
+    /**
6554
+     * @var string
6555
+     */
6556
+    private $sqlState;
6557
+
6558
+    /**
6559
+     * Returns an ANSI-92 compliant SQL state.
6560
+     *
6561
+     * @return string $state ANSI state code
6562
+     */
6563
+    public function getSQLState()
6564
+    {
6565
+        return $this->sqlState;
6566
+    }
6567
+
6568
+    /**
6569
+     * @todo parse state to verify valid ANSI92!
6570
+     *       Stores ANSI-92 compliant SQL state.
6571
+     *
6572
+     * @param string $sqlState code
6573
+     *
6574
+     * @return void
6575
+     */
6576
+    public function setSQLState( $sqlState )
6577
+    {
6578
+        $this->sqlState = $sqlState;
6579
+    }
6580
+
6581
+    /**
6582
+     * To String prints both code and SQL state.
6583
+     *
6584
+     * @return string $message prints this exception instance as a string
6585
+     */
6586
+    public function __toString()
6587
+    {
6588
+        return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n".
6589
+                'trace: ' . $this->getTraceAsString();
6590
+    }
6591 6591
 }
6592 6592
 }
6593 6593
 
@@ -6628,577 +6628,577 @@  discard block
 block discarded – undo
6628 6628
  */
6629 6629
 abstract class Repository
6630 6630
 {
6631
-	/**
6632
-	 * @var array
6633
-	 */
6634
-	protected $stash = NULL;
6631
+    /**
6632
+     * @var array
6633
+     */
6634
+    protected $stash = NULL;
6635 6635
 
6636
-	/*
6636
+    /*
6637 6637
 	 * @var integer
6638 6638
 	 */
6639
-	protected $nesting = 0;
6640
-
6641
-	/**
6642
-	 * @var DBAdapter
6643
-	 */
6644
-	protected $writer;
6645
-
6646
-	/**
6647
-	 * Stores a bean and its lists in one run.
6648
-	 *
6649
-	 * @param OODBBean $bean
6650
-	 *
6651
-	 * @return void
6652
-	 */
6653
-	protected function storeBeanWithLists( OODBBean $bean )
6654
-	{
6655
-		$sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups
6656
-		foreach ( $bean as $property => $value ) {
6657
-			$value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value;
6658
-			if ( $value instanceof OODBBean ) {
6659
-				$this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value );
6660
-				$bean->setMeta("sys.typeof.{$property}", $value->getMeta('type'));
6661
-			} elseif ( is_array( $value ) ) {
6662
-				$originals = $bean->moveMeta( 'sys.shadow.' . $property, array() );
6663
-				if ( strpos( $property, 'own' ) === 0 ) {
6664
-					list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue );
6665
-					$listName = lcfirst( substr( $property, 3 ) );
6666
-					if ($bean->moveMeta( 'sys.exclusive-'.  $listName ) ) {
6667
-						OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE );
6668
-						OODBBean::setMetaAll( $ownAdditions, 'sys.buildcommand.fkdependson', $bean->getMeta( 'type' ) );
6669
-					}
6670
-					unset( $bean->$property );
6671
-				} elseif ( strpos( $property, 'shared' ) === 0 ) {
6672
-					list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue );
6673
-					unset( $bean->$property );
6674
-				}
6675
-			}
6676
-		}
6677
-		$this->storeBean( $bean );
6678
-		$this->processTrashcan( $bean, $ownTrashcan );
6679
-		$this->processAdditions( $bean, $ownAdditions );
6680
-		$this->processResidue( $ownresidue );
6681
-		$this->processSharedTrashcan( $bean, $sharedTrashcan );
6682
-		$this->processSharedAdditions( $bean, $sharedAdditions );
6683
-		$this->processSharedResidue( $bean, $sharedresidue );
6684
-	}
6685
-
6686
-	/**
6687
-	 * Process groups. Internal function. Processes different kind of groups for
6688
-	 * storage function. Given a list of original beans and a list of current beans,
6689
-	 * this function calculates which beans remain in the list (residue), which
6690
-	 * have been deleted (are in the trashcan) and which beans have been added
6691
-	 * (additions).
6692
-	 *
6693
-	 * @param  array $originals originals
6694
-	 * @param  array $current   the current beans
6695
-	 * @param  array $additions beans that have been added
6696
-	 * @param  array $trashcan  beans that have been deleted
6697
-	 * @param  array $residue   beans that have been left untouched
6698
-	 *
6699
-	 * @return array
6700
-	 */
6701
-	protected function processGroups( $originals, $current, $additions, $trashcan, $residue )
6702
-	{
6703
-		return array(
6704
-			array_merge( $additions, array_diff( $current, $originals ) ),
6705
-			array_merge( $trashcan, array_diff( $originals, $current ) ),
6706
-			array_merge( $residue, array_intersect( $current, $originals ) )
6707
-		);
6708
-	}
6709
-
6710
-	/**
6711
-	 * Processes an embedded bean.
6712
-	 *
6713
-	 * @param OODBBean|SimpleModel $embeddedBean the bean or model
6714
-	 *
6715
-	 * @return integer
6716
-	 */
6717
-	protected function prepareEmbeddedBean( $embeddedBean )
6718
-	{
6719
-		if ( !$embeddedBean->id || $embeddedBean->getMeta( 'tainted' ) ) {
6720
-			$this->store( $embeddedBean );
6721
-		}
6722
-
6723
-		return $embeddedBean->id;
6724
-	}
6725
-
6726
-	/**
6727
-	 * Processes a list of beans from a bean. A bean may contain lists. This
6728
-	 * method handles shared addition lists; i.e. the $bean->sharedObject properties.
6729
-	 *
6730
-	 * @param OODBBean $bean             the bean
6731
-	 * @param array            $sharedAdditions  list with shared additions
6732
-	 *
6733
-	 * @return void
6734
-	 *
6735
-	 * @throws Security
6736
-	 */
6737
-	protected function processSharedAdditions( $bean, $sharedAdditions )
6738
-	{
6739
-		foreach ( $sharedAdditions as $addition ) {
6740
-			if ( $addition instanceof OODBBean ) {
6741
-				$this->oodb->getAssociationManager()->associate( $addition, $bean );
6742
-			} else {
6743
-				throw new RedException( 'Array may only contain OODBBeans' );
6744
-			}
6745
-		}
6746
-	}
6747
-
6748
-	/**
6749
-	 * Processes a list of beans from a bean. A bean may contain lists. This
6750
-	 * method handles own lists; i.e. the $bean->ownObject properties.
6751
-	 * A residue is a bean in an own-list that stays where it is. This method
6752
-	 * checks if there have been any modification to this bean, in that case
6753
-	 * the bean is stored once again, otherwise the bean will be left untouched.
6754
-	 *
6755
-	 * @param OODBBean $bean       the bean
6756
-	 * @param array            $ownresidue list
6757
-	 *
6758
-	 * @return void
6759
-	 */
6760
-	protected function processResidue( $ownresidue )
6761
-	{
6762
-		foreach ( $ownresidue as $residue ) {
6763
-			if ( $residue->getMeta( 'tainted' ) ) {
6764
-				$this->store( $residue );
6765
-			}
6766
-		}
6767
-	}
6768
-
6769
-	/**
6770
-	 * Processes a list of beans from a bean. A bean may contain lists. This
6771
-	 * method handles own lists; i.e. the $bean->ownObject properties.
6772
-	 * A trash can bean is a bean in an own-list that has been removed
6773
-	 * (when checked with the shadow). This method
6774
-	 * checks if the bean is also in the dependency list. If it is the bean will be removed.
6775
-	 * If not, the connection between the bean and the owner bean will be broken by
6776
-	 * setting the ID to NULL.
6777
-	 *
6778
-	 * @param OODBBean $bean        the bean
6779
-	 * @param array            $ownTrashcan list
6780
-	 *
6781
-	 * @return void
6782
-	 */
6783
-	protected function processTrashcan( $bean, $ownTrashcan )
6784
-	{
6785
-
6786
-		foreach ( $ownTrashcan as $trash ) {
6787
-
6788
-			$myFieldLink = $bean->getMeta( 'type' ) . '_id';
6789
-			$alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) );
6790
-			if ( $alias ) $myFieldLink = $alias . '_id';
6791
-
6792
-			if ( $trash->getMeta( 'sys.garbage' ) === true ) {
6793
-				$this->trash( $trash );
6794
-			} else {
6795
-				$trash->$myFieldLink = NULL;
6796
-				$this->store( $trash );
6797
-			}
6798
-		}
6799
-	}
6800
-
6801
-	/**
6802
-	 * Unassociates the list items in the trashcan.
6803
-	 *
6804
-	 * @param OODBBean $bean           bean
6805
-	 * @param array            $sharedTrashcan list
6806
-	 *
6807
-	 * @return void
6808
-	 */
6809
-	protected function processSharedTrashcan( $bean, $sharedTrashcan )
6810
-	{
6811
-		foreach ( $sharedTrashcan as $trash ) {
6812
-			$this->oodb->getAssociationManager()->unassociate( $trash, $bean );
6813
-		}
6814
-	}
6815
-
6816
-	/**
6817
-	 * Stores all the beans in the residue group.
6818
-	 *
6819
-	 * @param OODBBean $bean          bean
6820
-	 * @param array            $sharedresidue list
6821
-	 *
6822
-	 * @return void
6823
-	 */
6824
-	protected function processSharedResidue( $bean, $sharedresidue )
6825
-	{
6826
-		foreach ( $sharedresidue as $residue ) {
6827
-			$this->store( $residue );
6828
-		}
6829
-	}
6830
-
6831
-	/**
6832
-	 * Determines whether the bean has 'loaded lists' or
6833
-	 * 'loaded embedded beans' that need to be processed
6834
-	 * by the store() method.
6835
-	 *
6836
-	 * @param OODBBean $bean bean to be examined
6837
-	 *
6838
-	 * @return boolean
6839
-	 */
6840
-	protected function hasListsOrObjects( OODBBean $bean )
6841
-	{
6842
-		$processLists = FALSE;
6843
-		foreach ( $bean as $value ) {
6844
-			if ( is_array( $value ) || is_object( $value ) ) {
6845
-				$processLists = TRUE;
6846
-				break;
6847
-			}
6848
-		}
6849
-
6850
-		return $processLists;
6851
-	}
6852
-
6853
-
6854
-	/**
6855
-	 * Converts an embedded bean to an ID, removed the bean property and
6856
-	 * stores the bean in the embedded beans array.
6857
-	 *
6858
-	 * @param array            $embeddedBeans destination array for embedded bean
6859
-	 * @param OODBBean $bean          target bean
6860
-	 * @param string           $property      property that contains the embedded bean
6861
-	 * @param OODBBean $value         embedded bean itself
6862
-	 */
6863
-	protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value )
6864
-	{
6865
-		$linkField        = $property . '_id';
6866
-		$id = $this->prepareEmbeddedBean( $value );
6867
-		if ($bean->$linkField != $id) $bean->$linkField = $id;
6868
-		$bean->setMeta( 'cast.' . $linkField, 'id' );
6869
-		$embeddedBeans[$linkField] = $value;
6870
-		unset( $bean->$property );
6871
-	}
6872
-
6873
-
6874
-	/**
6875
-	 * Constructor, requires a query writer.
6876
-	 *
6877
-	 * @param QueryWriter $writer writer
6878
-	 */
6879
-	public function __construct( OODB $oodb, QueryWriter $writer )
6880
-	{
6881
-		$this->writer = $writer;
6882
-		$this->oodb = $oodb;
6883
-	}
6884
-
6885
-	/**
6886
-	 * Checks whether a OODBBean bean is valid.
6887
-	 * If the type is not valid or the ID is not valid it will
6888
-	 * throw an exception: Security.
6889
-	 *
6890
-	 * @param OODBBean $bean the bean that needs to be checked
6891
-	 *
6892
-	 * @return void
6893
-	 *
6894
-	 * @throws Security $exception
6895
-	 */
6896
-	public function check( OODBBean $bean )
6897
-	{
6898
-		//Is all meta information present?
6899
-		if ( !isset( $bean->id ) ) {
6900
-			throw new RedException( 'Bean has incomplete Meta Information id ' );
6901
-		}
6902
-		if ( !( $bean->getMeta( 'type' ) ) ) {
6903
-			throw new RedException( 'Bean has incomplete Meta Information II' );
6904
-		}
6905
-		//Pattern of allowed characters
6906
-		$pattern = '/[^a-z0-9_]/i';
6907
-		//Does the type contain invalid characters?
6908
-		if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) {
6909
-			throw new RedException( 'Bean Type is invalid' );
6910
-		}
6911
-		//Are the properties and values valid?
6912
-		foreach ( $bean as $prop => $value ) {
6913
-			if (
6914
-				is_array( $value )
6915
-				|| ( is_object( $value ) )
6916
-			) {
6917
-				throw new RedException( "Invalid Bean value: property $prop" );
6918
-			} else if (
6919
-				strlen( $prop ) < 1
6920
-				|| preg_match( $pattern, $prop )
6921
-			) {
6922
-				throw new RedException( "Invalid Bean property: property $prop" );
6923
-			}
6924
-		}
6925
-	}
6926
-
6927
-	/**
6928
-	 * Searches the database for a bean that matches conditions $conditions and sql $addSQL
6929
-	 * and returns an array containing all the beans that have been found.
6930
-	 *
6931
-	 * Conditions need to take form:
6932
-	 *
6933
-	 * array(
6934
-	 *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
6935
-	 *    'PROPERTY' => array( POSSIBLE VALUES... )
6936
-	 * );
6937
-	 *
6938
-	 * All conditions are glued together using the AND-operator, while all value lists
6939
-	 * are glued using IN-operators thus acting as OR-conditions.
6940
-	 *
6941
-	 * Note that you can use property names; the columns will be extracted using the
6942
-	 * appropriate bean formatter.
6943
-	 *
6944
-	 * @param string $type       type of beans you are looking for
6945
-	 * @param array  $conditions list of conditions
6946
-	 * @param string $addSQL     SQL to be used in query
6947
-	 * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
6948
-	 *
6949
-	 * @return array
6950
-	 *
6951
-	 */
6952
-	public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
6953
-	{
6954
-		//for backward compatibility, allow mismatch arguments:
6955
-		if ( is_array( $sql ) ) {
6956
-			if ( isset( $sql[1] ) ) {
6957
-				$bindings = $sql[1];
6958
-			}
6959
-			$sql = $sql[0];
6960
-		}
6961
-		try {
6962
-			$beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) );
6963
-
6964
-			return $beans;
6965
-		} catch ( SQLException $exception ) {
6966
-			$this->handleException( $exception );
6967
-		}
6968
-
6969
-		return array();
6970
-	}
6971
-
6972
-	/**
6973
-	 * Finds a BeanCollection.
6974
-	 *
6975
-	 * @param string $type     type of beans you are looking for
6976
-	 * @param string $sql      SQL to be used in query
6977
-	 * @param array  $bindings whether you prefer to use a WHERE clause or not (TRUE = not)
6978
-	 *
6979
-	 * @return BeanCollection
6980
-	 */
6981
-	public function findCollection( $type, $sql, $bindings = array() )
6982
-	{
6983
-		try {
6984
-			$cursor = $this->writer->queryRecordWithCursor( $type, $sql, $bindings );
6985
-			return new BeanCollection( $type, $this, $cursor );
6986
-		} catch ( SQLException $exception ) {
6987
-			$this->handleException( $exception );
6988
-		}
6989
-		return new BeanCollection( $type, $this, new NullCursor );
6990
-	}
6991
-
6992
-	/**
6993
-	 * Stores a bean in the database. This method takes a
6994
-	 * OODBBean Bean Object $bean and stores it
6995
-	 * in the database. If the database schema is not compatible
6996
-	 * with this bean and RedBean runs in fluid mode the schema
6997
-	 * will be altered to store the bean correctly.
6998
-	 * If the database schema is not compatible with this bean and
6999
-	 * RedBean runs in frozen mode it will throw an exception.
7000
-	 * This function returns the primary key ID of the inserted
7001
-	 * bean.
7002
-	 *
7003
-	 * The return value is an integer if possible. If it is not possible to
7004
-	 * represent the value as an integer a string will be returned. We use
7005
-	 * explicit casts instead of functions to preserve performance
7006
-	 * (0.13 vs 0.28 for 10000 iterations on Core i3).
7007
-	 *
7008
-	 * @param OODBBean|SimpleModel $bean bean to store
7009
-	 *
7010
-	 * @return integer|string
7011
-	 */
7012
-	public function store( $bean )
7013
-	{
7014
-		$processLists = $this->hasListsOrObjects( $bean );
7015
-		if ( !$processLists && !$bean->getMeta( 'tainted' ) ) {
7016
-			return $bean->getID(); //bail out!
7017
-		}
7018
-		$this->oodb->signal( 'update', $bean );
7019
-		$processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model!
7020
-		if ( $processLists ) {
7021
-			$this->storeBeanWithLists( $bean );
7022
-		} else {
7023
-			$this->storeBean( $bean );
7024
-		}
7025
-		$this->oodb->signal( 'after_update', $bean );
7026
-
7027
-		return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id;
7028
-	}
7029
-
7030
-	/**
7031
-	 * Returns an array of beans. Pass a type and a series of ids and
7032
-	 * this method will bring you the corresponding beans.
7033
-	 *
7034
-	 * important note: Because this method loads beans using the load()
7035
-	 * function (but faster) it will return empty beans with ID 0 for
7036
-	 * every bean that could not be located. The resulting beans will have the
7037
-	 * passed IDs as their keys.
7038
-	 *
7039
-	 * @param string $type type of beans
7040
-	 * @param array  $ids  ids to load
7041
-	 *
7042
-	 * @return array
7043
-	 */
7044
-	public function batch( $type, $ids )
7045
-	{
7046
-		if ( !$ids ) {
7047
-			return array();
7048
-		}
7049
-		$collection = array();
7050
-		try {
7051
-			$rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) );
7052
-		} catch ( SQLException $e ) {
7053
-			$this->handleException( $e );
7054
-			$rows = FALSE;
7055
-		}
7056
-		$this->stash[$this->nesting] = array();
7057
-		if ( !$rows ) {
7058
-			return array();
7059
-		}
7060
-		foreach ( $rows as $row ) {
7061
-			$this->stash[$this->nesting][$row['id']] = $row;
7062
-		}
7063
-		foreach ( $ids as $id ) {
7064
-			$collection[$id] = $this->load( $type, $id );
7065
-		}
7066
-		$this->stash[$this->nesting] = NULL;
7067
-
7068
-		return $collection;
7069
-	}
7070
-
7071
-	/**
7072
-	 * This is a convenience method; it converts database rows
7073
-	 * (arrays) into beans. Given a type and a set of rows this method
7074
-	 * will return an array of beans of the specified type loaded with
7075
-	 * the data fields provided by the result set from the database.
7076
-	 *
7077
-	 * @param string $type type of beans you would like to have
7078
-	 * @param array  $rows rows from the database result
7079
-	 *
7080
-	 * @return array
7081
-	 */
7082
-	public function convertToBeans( $type, $rows )
7083
-	{
7084
-		$collection                  = array();
7085
-		$this->stash[$this->nesting] = array();
7086
-		foreach ( $rows as $row ) {
7087
-			$id                               = $row['id'];
7088
-			$this->stash[$this->nesting][$id] = $row;
7089
-			$collection[$id]                  = $this->load( $type, $id );
7090
-		}
7091
-		$this->stash[$this->nesting] = NULL;
7092
-
7093
-		return $collection;
7094
-	}
7095
-
7096
-	/**
7097
-	 * Counts the number of beans of type $type.
7098
-	 * This method accepts a second argument to modify the count-query.
7099
-	 * A third argument can be used to provide bindings for the SQL snippet.
7100
-	 *
7101
-	 * @param string $type     type of bean we are looking for
7102
-	 * @param string $addSQL   additional SQL snippet
7103
-	 * @param array  $bindings parameters to bind to SQL
7104
-	 *
7105
-	 * @return integer
7106
-	 *
7107
-	 * @throws SQLException
7108
-	 */
7109
-	public function count( $type, $addSQL = '', $bindings = array() )
7110
-	{
7111
-		$type = AQueryWriter::camelsSnake( $type );
7112
-		if ( count( explode( '_', $type ) ) > 2 ) {
7113
-			throw new RedException( 'Invalid type for count.' );
7114
-		}
7115
-
7116
-		try {
7117
-			return (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings );
7118
-		} catch ( SQLException $exception ) {
7119
-			if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array(
7120
-				 QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
7121
-				 QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) ) {
7122
-				throw $exception;
7123
-			}
7124
-		}
7125
-
7126
-		return 0;
7127
-	}
7128
-
7129
-	/**
7130
-	 * Removes a bean from the database.
7131
-	 * This function will remove the specified OODBBean
7132
-	 * Bean Object from the database.
7133
-	 *
7134
-	 * @param OODBBean|SimpleModel $bean bean you want to remove from database
7135
-	 *
7136
-	 * @return void
7137
-	 *
7138
-	 * @throws SQLException
7139
-	 */
7140
-	public function trash( $bean )
7141
-	{
7142
-		$this->oodb->signal( 'delete', $bean );
7143
-		foreach ( $bean as $property => $value ) {
7144
-			if ( $value instanceof OODBBean ) {
7145
-				unset( $bean->$property );
7146
-			}
7147
-			if ( is_array( $value ) ) {
7148
-				if ( strpos( $property, 'own' ) === 0 ) {
7149
-					unset( $bean->$property );
7150
-				} elseif ( strpos( $property, 'shared' ) === 0 ) {
7151
-					unset( $bean->$property );
7152
-				}
7153
-			}
7154
-		}
7155
-		try {
7156
-			$this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL );
7157
-		} catch ( SQLException $exception ) {
7158
-			$this->handleException( $exception );
7159
-		}
7160
-		$bean->id = 0;
7161
-		$this->oodb->signal( 'after_delete', $bean );
7162
-	}
7163
-
7164
-	/**
7165
-	 * Checks whether the specified table already exists in the database.
7166
-	 * Not part of the Object Database interface!
7167
-	 *
7168
-	 * @deprecated Use AQueryWriter::typeExists() instead.
7169
-	 *
7170
-	 * @param string $table table name
7171
-	 *
7172
-	 * @return boolean
7173
-	 */
7174
-	public function tableExists( $table )
7175
-	{
7176
-		return $this->writer->tableExists( $table );
7177
-	}
7178
-
7179
-	/**
7180
-	 * Trash all beans of a given type. Wipes an entire type of bean.
7181
-	 *
7182
-	 * @param string $type type of bean you wish to delete all instances of
7183
-	 *
7184
-	 * @return boolean
7185
-	 *
7186
-	 * @throws SQLException
7187
-	 */
7188
-	public function wipe( $type )
7189
-	{
7190
-		try {
7191
-			$this->writer->wipe( $type );
7192
-
7193
-			return TRUE;
7194
-		} catch ( SQLException $exception ) {
7195
-			if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) {
7196
-				throw $exception;
7197
-			}
7198
-
7199
-			return FALSE;
7200
-		}
7201
-	}
6639
+    protected $nesting = 0;
6640
+
6641
+    /**
6642
+     * @var DBAdapter
6643
+     */
6644
+    protected $writer;
6645
+
6646
+    /**
6647
+     * Stores a bean and its lists in one run.
6648
+     *
6649
+     * @param OODBBean $bean
6650
+     *
6651
+     * @return void
6652
+     */
6653
+    protected function storeBeanWithLists( OODBBean $bean )
6654
+    {
6655
+        $sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups
6656
+        foreach ( $bean as $property => $value ) {
6657
+            $value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value;
6658
+            if ( $value instanceof OODBBean ) {
6659
+                $this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value );
6660
+                $bean->setMeta("sys.typeof.{$property}", $value->getMeta('type'));
6661
+            } elseif ( is_array( $value ) ) {
6662
+                $originals = $bean->moveMeta( 'sys.shadow.' . $property, array() );
6663
+                if ( strpos( $property, 'own' ) === 0 ) {
6664
+                    list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue );
6665
+                    $listName = lcfirst( substr( $property, 3 ) );
6666
+                    if ($bean->moveMeta( 'sys.exclusive-'.  $listName ) ) {
6667
+                        OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE );
6668
+                        OODBBean::setMetaAll( $ownAdditions, 'sys.buildcommand.fkdependson', $bean->getMeta( 'type' ) );
6669
+                    }
6670
+                    unset( $bean->$property );
6671
+                } elseif ( strpos( $property, 'shared' ) === 0 ) {
6672
+                    list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue );
6673
+                    unset( $bean->$property );
6674
+                }
6675
+            }
6676
+        }
6677
+        $this->storeBean( $bean );
6678
+        $this->processTrashcan( $bean, $ownTrashcan );
6679
+        $this->processAdditions( $bean, $ownAdditions );
6680
+        $this->processResidue( $ownresidue );
6681
+        $this->processSharedTrashcan( $bean, $sharedTrashcan );
6682
+        $this->processSharedAdditions( $bean, $sharedAdditions );
6683
+        $this->processSharedResidue( $bean, $sharedresidue );
6684
+    }
6685
+
6686
+    /**
6687
+     * Process groups. Internal function. Processes different kind of groups for
6688
+     * storage function. Given a list of original beans and a list of current beans,
6689
+     * this function calculates which beans remain in the list (residue), which
6690
+     * have been deleted (are in the trashcan) and which beans have been added
6691
+     * (additions).
6692
+     *
6693
+     * @param  array $originals originals
6694
+     * @param  array $current   the current beans
6695
+     * @param  array $additions beans that have been added
6696
+     * @param  array $trashcan  beans that have been deleted
6697
+     * @param  array $residue   beans that have been left untouched
6698
+     *
6699
+     * @return array
6700
+     */
6701
+    protected function processGroups( $originals, $current, $additions, $trashcan, $residue )
6702
+    {
6703
+        return array(
6704
+            array_merge( $additions, array_diff( $current, $originals ) ),
6705
+            array_merge( $trashcan, array_diff( $originals, $current ) ),
6706
+            array_merge( $residue, array_intersect( $current, $originals ) )
6707
+        );
6708
+    }
6709
+
6710
+    /**
6711
+     * Processes an embedded bean.
6712
+     *
6713
+     * @param OODBBean|SimpleModel $embeddedBean the bean or model
6714
+     *
6715
+     * @return integer
6716
+     */
6717
+    protected function prepareEmbeddedBean( $embeddedBean )
6718
+    {
6719
+        if ( !$embeddedBean->id || $embeddedBean->getMeta( 'tainted' ) ) {
6720
+            $this->store( $embeddedBean );
6721
+        }
6722
+
6723
+        return $embeddedBean->id;
6724
+    }
6725
+
6726
+    /**
6727
+     * Processes a list of beans from a bean. A bean may contain lists. This
6728
+     * method handles shared addition lists; i.e. the $bean->sharedObject properties.
6729
+     *
6730
+     * @param OODBBean $bean             the bean
6731
+     * @param array            $sharedAdditions  list with shared additions
6732
+     *
6733
+     * @return void
6734
+     *
6735
+     * @throws Security
6736
+     */
6737
+    protected function processSharedAdditions( $bean, $sharedAdditions )
6738
+    {
6739
+        foreach ( $sharedAdditions as $addition ) {
6740
+            if ( $addition instanceof OODBBean ) {
6741
+                $this->oodb->getAssociationManager()->associate( $addition, $bean );
6742
+            } else {
6743
+                throw new RedException( 'Array may only contain OODBBeans' );
6744
+            }
6745
+        }
6746
+    }
6747
+
6748
+    /**
6749
+     * Processes a list of beans from a bean. A bean may contain lists. This
6750
+     * method handles own lists; i.e. the $bean->ownObject properties.
6751
+     * A residue is a bean in an own-list that stays where it is. This method
6752
+     * checks if there have been any modification to this bean, in that case
6753
+     * the bean is stored once again, otherwise the bean will be left untouched.
6754
+     *
6755
+     * @param OODBBean $bean       the bean
6756
+     * @param array            $ownresidue list
6757
+     *
6758
+     * @return void
6759
+     */
6760
+    protected function processResidue( $ownresidue )
6761
+    {
6762
+        foreach ( $ownresidue as $residue ) {
6763
+            if ( $residue->getMeta( 'tainted' ) ) {
6764
+                $this->store( $residue );
6765
+            }
6766
+        }
6767
+    }
6768
+
6769
+    /**
6770
+     * Processes a list of beans from a bean. A bean may contain lists. This
6771
+     * method handles own lists; i.e. the $bean->ownObject properties.
6772
+     * A trash can bean is a bean in an own-list that has been removed
6773
+     * (when checked with the shadow). This method
6774
+     * checks if the bean is also in the dependency list. If it is the bean will be removed.
6775
+     * If not, the connection between the bean and the owner bean will be broken by
6776
+     * setting the ID to NULL.
6777
+     *
6778
+     * @param OODBBean $bean        the bean
6779
+     * @param array            $ownTrashcan list
6780
+     *
6781
+     * @return void
6782
+     */
6783
+    protected function processTrashcan( $bean, $ownTrashcan )
6784
+    {
6785
+
6786
+        foreach ( $ownTrashcan as $trash ) {
6787
+
6788
+            $myFieldLink = $bean->getMeta( 'type' ) . '_id';
6789
+            $alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) );
6790
+            if ( $alias ) $myFieldLink = $alias . '_id';
6791
+
6792
+            if ( $trash->getMeta( 'sys.garbage' ) === true ) {
6793
+                $this->trash( $trash );
6794
+            } else {
6795
+                $trash->$myFieldLink = NULL;
6796
+                $this->store( $trash );
6797
+            }
6798
+        }
6799
+    }
6800
+
6801
+    /**
6802
+     * Unassociates the list items in the trashcan.
6803
+     *
6804
+     * @param OODBBean $bean           bean
6805
+     * @param array            $sharedTrashcan list
6806
+     *
6807
+     * @return void
6808
+     */
6809
+    protected function processSharedTrashcan( $bean, $sharedTrashcan )
6810
+    {
6811
+        foreach ( $sharedTrashcan as $trash ) {
6812
+            $this->oodb->getAssociationManager()->unassociate( $trash, $bean );
6813
+        }
6814
+    }
6815
+
6816
+    /**
6817
+     * Stores all the beans in the residue group.
6818
+     *
6819
+     * @param OODBBean $bean          bean
6820
+     * @param array            $sharedresidue list
6821
+     *
6822
+     * @return void
6823
+     */
6824
+    protected function processSharedResidue( $bean, $sharedresidue )
6825
+    {
6826
+        foreach ( $sharedresidue as $residue ) {
6827
+            $this->store( $residue );
6828
+        }
6829
+    }
6830
+
6831
+    /**
6832
+     * Determines whether the bean has 'loaded lists' or
6833
+     * 'loaded embedded beans' that need to be processed
6834
+     * by the store() method.
6835
+     *
6836
+     * @param OODBBean $bean bean to be examined
6837
+     *
6838
+     * @return boolean
6839
+     */
6840
+    protected function hasListsOrObjects( OODBBean $bean )
6841
+    {
6842
+        $processLists = FALSE;
6843
+        foreach ( $bean as $value ) {
6844
+            if ( is_array( $value ) || is_object( $value ) ) {
6845
+                $processLists = TRUE;
6846
+                break;
6847
+            }
6848
+        }
6849
+
6850
+        return $processLists;
6851
+    }
6852
+
6853
+
6854
+    /**
6855
+     * Converts an embedded bean to an ID, removed the bean property and
6856
+     * stores the bean in the embedded beans array.
6857
+     *
6858
+     * @param array            $embeddedBeans destination array for embedded bean
6859
+     * @param OODBBean $bean          target bean
6860
+     * @param string           $property      property that contains the embedded bean
6861
+     * @param OODBBean $value         embedded bean itself
6862
+     */
6863
+    protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value )
6864
+    {
6865
+        $linkField        = $property . '_id';
6866
+        $id = $this->prepareEmbeddedBean( $value );
6867
+        if ($bean->$linkField != $id) $bean->$linkField = $id;
6868
+        $bean->setMeta( 'cast.' . $linkField, 'id' );
6869
+        $embeddedBeans[$linkField] = $value;
6870
+        unset( $bean->$property );
6871
+    }
6872
+
6873
+
6874
+    /**
6875
+     * Constructor, requires a query writer.
6876
+     *
6877
+     * @param QueryWriter $writer writer
6878
+     */
6879
+    public function __construct( OODB $oodb, QueryWriter $writer )
6880
+    {
6881
+        $this->writer = $writer;
6882
+        $this->oodb = $oodb;
6883
+    }
6884
+
6885
+    /**
6886
+     * Checks whether a OODBBean bean is valid.
6887
+     * If the type is not valid or the ID is not valid it will
6888
+     * throw an exception: Security.
6889
+     *
6890
+     * @param OODBBean $bean the bean that needs to be checked
6891
+     *
6892
+     * @return void
6893
+     *
6894
+     * @throws Security $exception
6895
+     */
6896
+    public function check( OODBBean $bean )
6897
+    {
6898
+        //Is all meta information present?
6899
+        if ( !isset( $bean->id ) ) {
6900
+            throw new RedException( 'Bean has incomplete Meta Information id ' );
6901
+        }
6902
+        if ( !( $bean->getMeta( 'type' ) ) ) {
6903
+            throw new RedException( 'Bean has incomplete Meta Information II' );
6904
+        }
6905
+        //Pattern of allowed characters
6906
+        $pattern = '/[^a-z0-9_]/i';
6907
+        //Does the type contain invalid characters?
6908
+        if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) {
6909
+            throw new RedException( 'Bean Type is invalid' );
6910
+        }
6911
+        //Are the properties and values valid?
6912
+        foreach ( $bean as $prop => $value ) {
6913
+            if (
6914
+                is_array( $value )
6915
+                || ( is_object( $value ) )
6916
+            ) {
6917
+                throw new RedException( "Invalid Bean value: property $prop" );
6918
+            } else if (
6919
+                strlen( $prop ) < 1
6920
+                || preg_match( $pattern, $prop )
6921
+            ) {
6922
+                throw new RedException( "Invalid Bean property: property $prop" );
6923
+            }
6924
+        }
6925
+    }
6926
+
6927
+    /**
6928
+     * Searches the database for a bean that matches conditions $conditions and sql $addSQL
6929
+     * and returns an array containing all the beans that have been found.
6930
+     *
6931
+     * Conditions need to take form:
6932
+     *
6933
+     * array(
6934
+     *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
6935
+     *    'PROPERTY' => array( POSSIBLE VALUES... )
6936
+     * );
6937
+     *
6938
+     * All conditions are glued together using the AND-operator, while all value lists
6939
+     * are glued using IN-operators thus acting as OR-conditions.
6940
+     *
6941
+     * Note that you can use property names; the columns will be extracted using the
6942
+     * appropriate bean formatter.
6943
+     *
6944
+     * @param string $type       type of beans you are looking for
6945
+     * @param array  $conditions list of conditions
6946
+     * @param string $addSQL     SQL to be used in query
6947
+     * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
6948
+     *
6949
+     * @return array
6950
+     *
6951
+     */
6952
+    public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
6953
+    {
6954
+        //for backward compatibility, allow mismatch arguments:
6955
+        if ( is_array( $sql ) ) {
6956
+            if ( isset( $sql[1] ) ) {
6957
+                $bindings = $sql[1];
6958
+            }
6959
+            $sql = $sql[0];
6960
+        }
6961
+        try {
6962
+            $beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) );
6963
+
6964
+            return $beans;
6965
+        } catch ( SQLException $exception ) {
6966
+            $this->handleException( $exception );
6967
+        }
6968
+
6969
+        return array();
6970
+    }
6971
+
6972
+    /**
6973
+     * Finds a BeanCollection.
6974
+     *
6975
+     * @param string $type     type of beans you are looking for
6976
+     * @param string $sql      SQL to be used in query
6977
+     * @param array  $bindings whether you prefer to use a WHERE clause or not (TRUE = not)
6978
+     *
6979
+     * @return BeanCollection
6980
+     */
6981
+    public function findCollection( $type, $sql, $bindings = array() )
6982
+    {
6983
+        try {
6984
+            $cursor = $this->writer->queryRecordWithCursor( $type, $sql, $bindings );
6985
+            return new BeanCollection( $type, $this, $cursor );
6986
+        } catch ( SQLException $exception ) {
6987
+            $this->handleException( $exception );
6988
+        }
6989
+        return new BeanCollection( $type, $this, new NullCursor );
6990
+    }
6991
+
6992
+    /**
6993
+     * Stores a bean in the database. This method takes a
6994
+     * OODBBean Bean Object $bean and stores it
6995
+     * in the database. If the database schema is not compatible
6996
+     * with this bean and RedBean runs in fluid mode the schema
6997
+     * will be altered to store the bean correctly.
6998
+     * If the database schema is not compatible with this bean and
6999
+     * RedBean runs in frozen mode it will throw an exception.
7000
+     * This function returns the primary key ID of the inserted
7001
+     * bean.
7002
+     *
7003
+     * The return value is an integer if possible. If it is not possible to
7004
+     * represent the value as an integer a string will be returned. We use
7005
+     * explicit casts instead of functions to preserve performance
7006
+     * (0.13 vs 0.28 for 10000 iterations on Core i3).
7007
+     *
7008
+     * @param OODBBean|SimpleModel $bean bean to store
7009
+     *
7010
+     * @return integer|string
7011
+     */
7012
+    public function store( $bean )
7013
+    {
7014
+        $processLists = $this->hasListsOrObjects( $bean );
7015
+        if ( !$processLists && !$bean->getMeta( 'tainted' ) ) {
7016
+            return $bean->getID(); //bail out!
7017
+        }
7018
+        $this->oodb->signal( 'update', $bean );
7019
+        $processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model!
7020
+        if ( $processLists ) {
7021
+            $this->storeBeanWithLists( $bean );
7022
+        } else {
7023
+            $this->storeBean( $bean );
7024
+        }
7025
+        $this->oodb->signal( 'after_update', $bean );
7026
+
7027
+        return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id;
7028
+    }
7029
+
7030
+    /**
7031
+     * Returns an array of beans. Pass a type and a series of ids and
7032
+     * this method will bring you the corresponding beans.
7033
+     *
7034
+     * important note: Because this method loads beans using the load()
7035
+     * function (but faster) it will return empty beans with ID 0 for
7036
+     * every bean that could not be located. The resulting beans will have the
7037
+     * passed IDs as their keys.
7038
+     *
7039
+     * @param string $type type of beans
7040
+     * @param array  $ids  ids to load
7041
+     *
7042
+     * @return array
7043
+     */
7044
+    public function batch( $type, $ids )
7045
+    {
7046
+        if ( !$ids ) {
7047
+            return array();
7048
+        }
7049
+        $collection = array();
7050
+        try {
7051
+            $rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) );
7052
+        } catch ( SQLException $e ) {
7053
+            $this->handleException( $e );
7054
+            $rows = FALSE;
7055
+        }
7056
+        $this->stash[$this->nesting] = array();
7057
+        if ( !$rows ) {
7058
+            return array();
7059
+        }
7060
+        foreach ( $rows as $row ) {
7061
+            $this->stash[$this->nesting][$row['id']] = $row;
7062
+        }
7063
+        foreach ( $ids as $id ) {
7064
+            $collection[$id] = $this->load( $type, $id );
7065
+        }
7066
+        $this->stash[$this->nesting] = NULL;
7067
+
7068
+        return $collection;
7069
+    }
7070
+
7071
+    /**
7072
+     * This is a convenience method; it converts database rows
7073
+     * (arrays) into beans. Given a type and a set of rows this method
7074
+     * will return an array of beans of the specified type loaded with
7075
+     * the data fields provided by the result set from the database.
7076
+     *
7077
+     * @param string $type type of beans you would like to have
7078
+     * @param array  $rows rows from the database result
7079
+     *
7080
+     * @return array
7081
+     */
7082
+    public function convertToBeans( $type, $rows )
7083
+    {
7084
+        $collection                  = array();
7085
+        $this->stash[$this->nesting] = array();
7086
+        foreach ( $rows as $row ) {
7087
+            $id                               = $row['id'];
7088
+            $this->stash[$this->nesting][$id] = $row;
7089
+            $collection[$id]                  = $this->load( $type, $id );
7090
+        }
7091
+        $this->stash[$this->nesting] = NULL;
7092
+
7093
+        return $collection;
7094
+    }
7095
+
7096
+    /**
7097
+     * Counts the number of beans of type $type.
7098
+     * This method accepts a second argument to modify the count-query.
7099
+     * A third argument can be used to provide bindings for the SQL snippet.
7100
+     *
7101
+     * @param string $type     type of bean we are looking for
7102
+     * @param string $addSQL   additional SQL snippet
7103
+     * @param array  $bindings parameters to bind to SQL
7104
+     *
7105
+     * @return integer
7106
+     *
7107
+     * @throws SQLException
7108
+     */
7109
+    public function count( $type, $addSQL = '', $bindings = array() )
7110
+    {
7111
+        $type = AQueryWriter::camelsSnake( $type );
7112
+        if ( count( explode( '_', $type ) ) > 2 ) {
7113
+            throw new RedException( 'Invalid type for count.' );
7114
+        }
7115
+
7116
+        try {
7117
+            return (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings );
7118
+        } catch ( SQLException $exception ) {
7119
+            if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array(
7120
+                    QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
7121
+                    QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) ) {
7122
+                throw $exception;
7123
+            }
7124
+        }
7125
+
7126
+        return 0;
7127
+    }
7128
+
7129
+    /**
7130
+     * Removes a bean from the database.
7131
+     * This function will remove the specified OODBBean
7132
+     * Bean Object from the database.
7133
+     *
7134
+     * @param OODBBean|SimpleModel $bean bean you want to remove from database
7135
+     *
7136
+     * @return void
7137
+     *
7138
+     * @throws SQLException
7139
+     */
7140
+    public function trash( $bean )
7141
+    {
7142
+        $this->oodb->signal( 'delete', $bean );
7143
+        foreach ( $bean as $property => $value ) {
7144
+            if ( $value instanceof OODBBean ) {
7145
+                unset( $bean->$property );
7146
+            }
7147
+            if ( is_array( $value ) ) {
7148
+                if ( strpos( $property, 'own' ) === 0 ) {
7149
+                    unset( $bean->$property );
7150
+                } elseif ( strpos( $property, 'shared' ) === 0 ) {
7151
+                    unset( $bean->$property );
7152
+                }
7153
+            }
7154
+        }
7155
+        try {
7156
+            $this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL );
7157
+        } catch ( SQLException $exception ) {
7158
+            $this->handleException( $exception );
7159
+        }
7160
+        $bean->id = 0;
7161
+        $this->oodb->signal( 'after_delete', $bean );
7162
+    }
7163
+
7164
+    /**
7165
+     * Checks whether the specified table already exists in the database.
7166
+     * Not part of the Object Database interface!
7167
+     *
7168
+     * @deprecated Use AQueryWriter::typeExists() instead.
7169
+     *
7170
+     * @param string $table table name
7171
+     *
7172
+     * @return boolean
7173
+     */
7174
+    public function tableExists( $table )
7175
+    {
7176
+        return $this->writer->tableExists( $table );
7177
+    }
7178
+
7179
+    /**
7180
+     * Trash all beans of a given type. Wipes an entire type of bean.
7181
+     *
7182
+     * @param string $type type of bean you wish to delete all instances of
7183
+     *
7184
+     * @return boolean
7185
+     *
7186
+     * @throws SQLException
7187
+     */
7188
+    public function wipe( $type )
7189
+    {
7190
+        try {
7191
+            $this->writer->wipe( $type );
7192
+
7193
+            return TRUE;
7194
+        } catch ( SQLException $exception ) {
7195
+            if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) {
7196
+                throw $exception;
7197
+            }
7198
+
7199
+            return FALSE;
7200
+        }
7201
+    }
7202 7202
 }
7203 7203
 }
7204 7204
 
@@ -7237,293 +7237,293 @@  discard block
 block discarded – undo
7237 7237
  */
7238 7238
 class Fluid extends Repository
7239 7239
 {
7240
-	/**
7241
-	 * Figures out the desired type given the cast string ID.
7242
-	 *
7243
-	 * @param string $cast cast identifier
7244
-	 *
7245
-	 * @return integer
7246
-	 *
7247
-	 * @throws Security
7248
-	 */
7249
-	private function getTypeFromCast( $cast )
7250
-	{
7251
-		if ( $cast == 'string' ) {
7252
-			$typeno = $this->writer->scanType( 'STRING' );
7253
-		} elseif ( $cast == 'id' ) {
7254
-			$typeno = $this->writer->getTypeForID();
7255
-		} elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) {
7256
-			$typeno = $this->writer->sqltype_typeno[$cast];
7257
-		} else {
7258
-			throw new RedException( 'Invalid Cast' );
7259
-		}
7260
-
7261
-		return $typeno;
7262
-	}
7263
-
7264
-	/**
7265
-	 * Orders the Query Writer to create a table if it does not exist already and
7266
-	 * adds a note in the build report about the creation.
7267
-	 *
7268
-	 * @param OODBBean $bean  bean to update report of
7269
-	 * @param string           $table table to check and create if not exists
7270
-	 *
7271
-	 * @return void
7272
-	 */
7273
-	private function createTableIfNotExists( OODBBean $bean, $table )
7274
-	{
7275
-		//Does table exist? If not, create
7276
-		if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) {
7277
-			$this->writer->createTable( $table );
7278
-			$bean->setMeta( 'buildreport.flags.created', TRUE );
7279
-		}
7280
-	}
7281
-
7282
-	/**
7283
-	 * Modifies the table to fit the bean data.
7284
-	 * Given a property and a value and the bean, this method will
7285
-	 * adjust the table structure to fit the requirements of the property and value.
7286
-	 * This may include adding a new column or widening an existing column to hold a larger
7287
-	 * or different kind of value. This method employs the writer to adjust the table
7288
-	 * structure in the database. Schema updates are recorded in meta properties of the bean.
7289
-	 *
7290
-	 * This method will also apply indexes, unique constraints and foreign keys.
7291
-	 *
7292
-	 * @param OODBBean $bean     bean to get cast data from and store meta in
7293
-	 * @param string   $property property to store
7294
-	 * @param mixed    $value    value to store
7295
-	 *
7296
-	 * @return void
7297
-	 */
7298
-	private function modifySchema( OODBBean $bean, $property, $value )
7299
-	{
7300
-		$doFKStuff = FALSE;
7301
-		$table   = $bean->getMeta( 'type' );
7302
-		$columns = $this->writer->getColumns( $table );
7303
-		$columnNoQ = $this->writer->esc( $property, TRUE );
7304
-		if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) {
7305
-			if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types
7306
-				$cast   = $bean->getMeta( "cast.$property" );
7307
-				$typeno = $this->getTypeFromCast( $cast );
7308
-			} else {
7309
-				$cast   = FALSE;
7310
-				$typeno = $this->writer->scanType( $value, TRUE );
7311
-			}
7312
-			if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ?
7313
-				if ( !$cast ) { //rescan without taking into account special types >80
7314
-					$typeno = $this->writer->scanType( $value, FALSE );
7315
-				}
7316
-				$sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] );
7317
-				if ( $typeno > $sqlt ) { //no, we have to widen the database column type
7318
-					$this->writer->widenColumn( $table, $property, $typeno );
7319
-					$bean->setMeta( 'buildreport.flags.widen', TRUE );
7320
-					$doFKStuff = TRUE;
7321
-				}
7322
-			} else {
7323
-				$this->writer->addColumn( $table, $property, $typeno );
7324
-				$bean->setMeta( 'buildreport.flags.addcolumn', TRUE );
7325
-				$doFKStuff = TRUE;
7326
-			}
7327
-			if ($doFKStuff) {
7328
-				if (strrpos($columnNoQ, '_id')===(strlen($columnNoQ)-3)) {
7329
-					$destinationColumnNoQ = substr($columnNoQ, 0, strlen($columnNoQ)-3);
7330
-					$indexName = "index_foreignkey_{$table}_{$destinationColumnNoQ}";
7331
-					$this->writer->addIndex($table, $indexName, $columnNoQ);
7332
-					$typeof = $bean->getMeta("sys.typeof.{$destinationColumnNoQ}", $destinationColumnNoQ);
7333
-					$isLink = $bean->getMeta( 'sys.buildcommand.unique', FALSE );
7334
-					//Make FK CASCADING if part of exclusive list (dependson=typeof) or if link bean
7335
-					$isDep = ( $bean->moveMeta( 'sys.buildcommand.fkdependson' ) === $typeof || is_array( $isLink ) );
7336
-					$result = $this->writer->addFK( $table, $typeof, $columnNoQ, 'id', $isDep );
7337
-					//If this is a link bean and all unique columns have been added already, then apply unique constraint
7338
-					if ( is_array( $isLink ) && !count( array_diff( $isLink, array_keys( $this->writer->getColumns( $table ) ) ) ) ) {
7339
-						$this->writer->addUniqueConstraint( $table, $bean->moveMeta('sys.buildcommand.unique') );
7340
-						$bean->setMeta("sys.typeof.{$destinationColumnNoQ}", NULL);
7341
-					}
7342
-				}
7343
-			}
7344
-		}
7345
-	}
7346
-
7347
-	/**
7348
-	 * Part of the store() functionality.
7349
-	 * Handles all new additions after the bean has been saved.
7350
-	 * Stores addition bean in own-list, extracts the id and
7351
-	 * adds a foreign key. Also adds a constraint in case the type is
7352
-	 * in the dependent list.
7353
-	 *
7354
-	 * @param OODBBean $bean         bean
7355
-	 * @param array            $ownAdditions list of addition beans in own-list
7356
-	 *
7357
-	 * @return void
7358
-	 *
7359
-	 * @throws Security
7360
-	 */
7361
-	protected function processAdditions( $bean, $ownAdditions )
7362
-	{
7363
-		$beanType = $bean->getMeta( 'type' );
7364
-
7365
-		foreach ( $ownAdditions as $addition ) {
7366
-			if ( $addition instanceof OODBBean ) {
7367
-
7368
-				$myFieldLink = $beanType . '_id';
7369
-				$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
7370
-				if ( $alias ) $myFieldLink = $alias . '_id';
7371
-
7372
-				$addition->$myFieldLink = $bean->id;
7373
-				$addition->setMeta( 'cast.' . $myFieldLink, 'id' );
7374
-
7375
-				if ($alias) {
7376
-					$addition->setMeta( "sys.typeof.{$alias}", $beanType );
7377
-				} else {
7378
-					$addition->setMeta( "sys.typeof.{$beanType}", $beanType );
7379
-				}
7380
-
7381
-				$this->store( $addition );
7382
-			} else {
7383
-				throw new RedException( 'Array may only contain OODBBeans' );
7384
-			}
7385
-		}
7386
-	}
7387
-
7388
-	/**
7389
-	 * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
7390
-	 * method. When all lists and embedded beans (parent objects) have been processed and
7391
-	 * removed from the original bean the bean is passed to this method to be stored
7392
-	 * in the database.
7393
-	 *
7394
-	 * @param OODBBean $bean the clean bean
7395
-	 *
7396
-	 * @return void
7397
-	 */
7398
-	protected function storeBean( OODBBean $bean )
7399
-	{
7400
-		if ( $bean->getMeta( 'changed' ) ) {
7401
-			$this->check( $bean );
7402
-			$table = $bean->getMeta( 'type' );
7403
-			$this->createTableIfNotExists( $bean, $table );
7404
-
7405
-			$updateValues = array();
7406
-			foreach ( $bean as $property => $value ) {
7407
-				if ( $property !== 'id' ) {
7408
-					$this->modifySchema( $bean, $property, $value );
7409
-				}
7410
-				if ( $property !== 'id' ) {
7411
-					$updateValues[] = array( 'property' => $property, 'value' => $value );
7412
-				}
7413
-			}
7414
-
7415
-			$bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id );
7416
-			$bean->setMeta( 'changed', FALSE );
7417
-		}
7418
-		$bean->setMeta( 'tainted', FALSE );
7419
-	}
7420
-
7421
-	/**
7422
-	 * Handles\Exceptions. Suppresses exceptions caused by missing structures.
7423
-	 *
7424
-	 * @param\Exception $exception exception
7425
-	 *
7426
-	 * @return void
7427
-	 *
7428
-	 * @throws\Exception
7429
-	 */
7430
-	protected function handleException( \Exception $exception )
7431
-	{
7432
-		if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
7433
-			array(
7434
-				QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
7435
-				QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) )
7436
-		) {
7437
-			throw $exception;
7438
-		}
7439
-	}
7440
-
7441
-	/**
7442
-	 * Dispenses a new bean (a OODBBean Bean Object)
7443
-	 * of the specified type. Always
7444
-	 * use this function to get an empty bean object. Never
7445
-	 * instantiate a OODBBean yourself because it needs
7446
-	 * to be configured before you can use it with RedBean. This
7447
-	 * function applies the appropriate initialization /
7448
-	 * configuration for you.
7449
-	 *
7450
-	 * @param string  $type              type of bean you want to dispense
7451
-	 * @param string  $number            number of beans you would like to get
7452
-	 * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
7453
-	 *
7454
-	 * @return OODBBean
7455
-	 */
7456
-	public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
7457
-	{
7458
-		$OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
7459
-		$beans = array();
7460
-		for ( $i = 0; $i < $number; $i++ ) {
7461
-			$bean = new $OODBBEAN;
7462
-			$bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
7463
-			$this->check( $bean );
7464
-			$this->oodb->signal( 'dispense', $bean );
7465
-			$beans[] = $bean;
7466
-		}
7467
-
7468
-		return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
7469
-	}
7470
-
7471
-	/**
7472
-	 * Loads a bean from the object database.
7473
-	 * It searches for a OODBBean Bean Object in the
7474
-	 * database. It does not matter how this bean has been stored.
7475
-	 * RedBean uses the primary key ID $id and the string $type
7476
-	 * to find the bean. The $type specifies what kind of bean you
7477
-	 * are looking for; this is the same type as used with the
7478
-	 * dispense() function. If RedBean finds the bean it will return
7479
-	 * the OODB Bean object; if it cannot find the bean
7480
-	 * RedBean will return a new bean of type $type and with
7481
-	 * primary key ID 0. In the latter case it acts basically the
7482
-	 * same as dispense().
7483
-	 *
7484
-	 * Important note:
7485
-	 * If the bean cannot be found in the database a new bean of
7486
-	 * the specified type will be generated and returned.
7487
-	 *
7488
-	 * @param string  $type type of bean you want to load
7489
-	 * @param integer $id   ID of the bean you want to load
7490
-	 *
7491
-	 * @throws SQL
7492
-	 *
7493
-	 * @return OODBBean
7494
-	 *
7495
-	 */
7496
-	public function load( $type, $id )
7497
-	{
7498
-		$bean = $this->dispense( $type );
7499
-		if ( isset( $this->stash[$this->nesting][$id] ) ) {
7500
-			$row = $this->stash[$this->nesting][$id];
7501
-		} else {
7502
-			try {
7503
-				$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
7504
-			} catch ( SQLException $exception ) {
7505
-				if ( $this->writer->sqlStateIn( $exception->getSQLState(),
7506
-					array(
7507
-						QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
7508
-						QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
7509
-				)
7510
-				) {
7511
-					$rows = 0;
7512
-
7513
-				}
7514
-			}
7515
-			if ( empty( $rows ) ) {
7516
-				return $bean;
7517
-			}
7518
-			$row = array_pop( $rows );
7519
-		}
7520
-		$bean->importRow( $row );
7521
-		$this->nesting++;
7522
-		$this->oodb->signal( 'open', $bean );
7523
-		$this->nesting--;
7524
-
7525
-		return $bean->setMeta( 'tainted', FALSE );
7526
-	}
7240
+    /**
7241
+     * Figures out the desired type given the cast string ID.
7242
+     *
7243
+     * @param string $cast cast identifier
7244
+     *
7245
+     * @return integer
7246
+     *
7247
+     * @throws Security
7248
+     */
7249
+    private function getTypeFromCast( $cast )
7250
+    {
7251
+        if ( $cast == 'string' ) {
7252
+            $typeno = $this->writer->scanType( 'STRING' );
7253
+        } elseif ( $cast == 'id' ) {
7254
+            $typeno = $this->writer->getTypeForID();
7255
+        } elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) {
7256
+            $typeno = $this->writer->sqltype_typeno[$cast];
7257
+        } else {
7258
+            throw new RedException( 'Invalid Cast' );
7259
+        }
7260
+
7261
+        return $typeno;
7262
+    }
7263
+
7264
+    /**
7265
+     * Orders the Query Writer to create a table if it does not exist already and
7266
+     * adds a note in the build report about the creation.
7267
+     *
7268
+     * @param OODBBean $bean  bean to update report of
7269
+     * @param string           $table table to check and create if not exists
7270
+     *
7271
+     * @return void
7272
+     */
7273
+    private function createTableIfNotExists( OODBBean $bean, $table )
7274
+    {
7275
+        //Does table exist? If not, create
7276
+        if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) {
7277
+            $this->writer->createTable( $table );
7278
+            $bean->setMeta( 'buildreport.flags.created', TRUE );
7279
+        }
7280
+    }
7281
+
7282
+    /**
7283
+     * Modifies the table to fit the bean data.
7284
+     * Given a property and a value and the bean, this method will
7285
+     * adjust the table structure to fit the requirements of the property and value.
7286
+     * This may include adding a new column or widening an existing column to hold a larger
7287
+     * or different kind of value. This method employs the writer to adjust the table
7288
+     * structure in the database. Schema updates are recorded in meta properties of the bean.
7289
+     *
7290
+     * This method will also apply indexes, unique constraints and foreign keys.
7291
+     *
7292
+     * @param OODBBean $bean     bean to get cast data from and store meta in
7293
+     * @param string   $property property to store
7294
+     * @param mixed    $value    value to store
7295
+     *
7296
+     * @return void
7297
+     */
7298
+    private function modifySchema( OODBBean $bean, $property, $value )
7299
+    {
7300
+        $doFKStuff = FALSE;
7301
+        $table   = $bean->getMeta( 'type' );
7302
+        $columns = $this->writer->getColumns( $table );
7303
+        $columnNoQ = $this->writer->esc( $property, TRUE );
7304
+        if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) {
7305
+            if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types
7306
+                $cast   = $bean->getMeta( "cast.$property" );
7307
+                $typeno = $this->getTypeFromCast( $cast );
7308
+            } else {
7309
+                $cast   = FALSE;
7310
+                $typeno = $this->writer->scanType( $value, TRUE );
7311
+            }
7312
+            if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ?
7313
+                if ( !$cast ) { //rescan without taking into account special types >80
7314
+                    $typeno = $this->writer->scanType( $value, FALSE );
7315
+                }
7316
+                $sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] );
7317
+                if ( $typeno > $sqlt ) { //no, we have to widen the database column type
7318
+                    $this->writer->widenColumn( $table, $property, $typeno );
7319
+                    $bean->setMeta( 'buildreport.flags.widen', TRUE );
7320
+                    $doFKStuff = TRUE;
7321
+                }
7322
+            } else {
7323
+                $this->writer->addColumn( $table, $property, $typeno );
7324
+                $bean->setMeta( 'buildreport.flags.addcolumn', TRUE );
7325
+                $doFKStuff = TRUE;
7326
+            }
7327
+            if ($doFKStuff) {
7328
+                if (strrpos($columnNoQ, '_id')===(strlen($columnNoQ)-3)) {
7329
+                    $destinationColumnNoQ = substr($columnNoQ, 0, strlen($columnNoQ)-3);
7330
+                    $indexName = "index_foreignkey_{$table}_{$destinationColumnNoQ}";
7331
+                    $this->writer->addIndex($table, $indexName, $columnNoQ);
7332
+                    $typeof = $bean->getMeta("sys.typeof.{$destinationColumnNoQ}", $destinationColumnNoQ);
7333
+                    $isLink = $bean->getMeta( 'sys.buildcommand.unique', FALSE );
7334
+                    //Make FK CASCADING if part of exclusive list (dependson=typeof) or if link bean
7335
+                    $isDep = ( $bean->moveMeta( 'sys.buildcommand.fkdependson' ) === $typeof || is_array( $isLink ) );
7336
+                    $result = $this->writer->addFK( $table, $typeof, $columnNoQ, 'id', $isDep );
7337
+                    //If this is a link bean and all unique columns have been added already, then apply unique constraint
7338
+                    if ( is_array( $isLink ) && !count( array_diff( $isLink, array_keys( $this->writer->getColumns( $table ) ) ) ) ) {
7339
+                        $this->writer->addUniqueConstraint( $table, $bean->moveMeta('sys.buildcommand.unique') );
7340
+                        $bean->setMeta("sys.typeof.{$destinationColumnNoQ}", NULL);
7341
+                    }
7342
+                }
7343
+            }
7344
+        }
7345
+    }
7346
+
7347
+    /**
7348
+     * Part of the store() functionality.
7349
+     * Handles all new additions after the bean has been saved.
7350
+     * Stores addition bean in own-list, extracts the id and
7351
+     * adds a foreign key. Also adds a constraint in case the type is
7352
+     * in the dependent list.
7353
+     *
7354
+     * @param OODBBean $bean         bean
7355
+     * @param array            $ownAdditions list of addition beans in own-list
7356
+     *
7357
+     * @return void
7358
+     *
7359
+     * @throws Security
7360
+     */
7361
+    protected function processAdditions( $bean, $ownAdditions )
7362
+    {
7363
+        $beanType = $bean->getMeta( 'type' );
7364
+
7365
+        foreach ( $ownAdditions as $addition ) {
7366
+            if ( $addition instanceof OODBBean ) {
7367
+
7368
+                $myFieldLink = $beanType . '_id';
7369
+                $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
7370
+                if ( $alias ) $myFieldLink = $alias . '_id';
7371
+
7372
+                $addition->$myFieldLink = $bean->id;
7373
+                $addition->setMeta( 'cast.' . $myFieldLink, 'id' );
7374
+
7375
+                if ($alias) {
7376
+                    $addition->setMeta( "sys.typeof.{$alias}", $beanType );
7377
+                } else {
7378
+                    $addition->setMeta( "sys.typeof.{$beanType}", $beanType );
7379
+                }
7380
+
7381
+                $this->store( $addition );
7382
+            } else {
7383
+                throw new RedException( 'Array may only contain OODBBeans' );
7384
+            }
7385
+        }
7386
+    }
7387
+
7388
+    /**
7389
+     * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
7390
+     * method. When all lists and embedded beans (parent objects) have been processed and
7391
+     * removed from the original bean the bean is passed to this method to be stored
7392
+     * in the database.
7393
+     *
7394
+     * @param OODBBean $bean the clean bean
7395
+     *
7396
+     * @return void
7397
+     */
7398
+    protected function storeBean( OODBBean $bean )
7399
+    {
7400
+        if ( $bean->getMeta( 'changed' ) ) {
7401
+            $this->check( $bean );
7402
+            $table = $bean->getMeta( 'type' );
7403
+            $this->createTableIfNotExists( $bean, $table );
7404
+
7405
+            $updateValues = array();
7406
+            foreach ( $bean as $property => $value ) {
7407
+                if ( $property !== 'id' ) {
7408
+                    $this->modifySchema( $bean, $property, $value );
7409
+                }
7410
+                if ( $property !== 'id' ) {
7411
+                    $updateValues[] = array( 'property' => $property, 'value' => $value );
7412
+                }
7413
+            }
7414
+
7415
+            $bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id );
7416
+            $bean->setMeta( 'changed', FALSE );
7417
+        }
7418
+        $bean->setMeta( 'tainted', FALSE );
7419
+    }
7420
+
7421
+    /**
7422
+     * Handles\Exceptions. Suppresses exceptions caused by missing structures.
7423
+     *
7424
+     * @param\Exception $exception exception
7425
+     *
7426
+     * @return void
7427
+     *
7428
+     * @throws\Exception
7429
+     */
7430
+    protected function handleException( \Exception $exception )
7431
+    {
7432
+        if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
7433
+            array(
7434
+                QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
7435
+                QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) )
7436
+        ) {
7437
+            throw $exception;
7438
+        }
7439
+    }
7440
+
7441
+    /**
7442
+     * Dispenses a new bean (a OODBBean Bean Object)
7443
+     * of the specified type. Always
7444
+     * use this function to get an empty bean object. Never
7445
+     * instantiate a OODBBean yourself because it needs
7446
+     * to be configured before you can use it with RedBean. This
7447
+     * function applies the appropriate initialization /
7448
+     * configuration for you.
7449
+     *
7450
+     * @param string  $type              type of bean you want to dispense
7451
+     * @param string  $number            number of beans you would like to get
7452
+     * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
7453
+     *
7454
+     * @return OODBBean
7455
+     */
7456
+    public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
7457
+    {
7458
+        $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
7459
+        $beans = array();
7460
+        for ( $i = 0; $i < $number; $i++ ) {
7461
+            $bean = new $OODBBEAN;
7462
+            $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
7463
+            $this->check( $bean );
7464
+            $this->oodb->signal( 'dispense', $bean );
7465
+            $beans[] = $bean;
7466
+        }
7467
+
7468
+        return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
7469
+    }
7470
+
7471
+    /**
7472
+     * Loads a bean from the object database.
7473
+     * It searches for a OODBBean Bean Object in the
7474
+     * database. It does not matter how this bean has been stored.
7475
+     * RedBean uses the primary key ID $id and the string $type
7476
+     * to find the bean. The $type specifies what kind of bean you
7477
+     * are looking for; this is the same type as used with the
7478
+     * dispense() function. If RedBean finds the bean it will return
7479
+     * the OODB Bean object; if it cannot find the bean
7480
+     * RedBean will return a new bean of type $type and with
7481
+     * primary key ID 0. In the latter case it acts basically the
7482
+     * same as dispense().
7483
+     *
7484
+     * Important note:
7485
+     * If the bean cannot be found in the database a new bean of
7486
+     * the specified type will be generated and returned.
7487
+     *
7488
+     * @param string  $type type of bean you want to load
7489
+     * @param integer $id   ID of the bean you want to load
7490
+     *
7491
+     * @throws SQL
7492
+     *
7493
+     * @return OODBBean
7494
+     *
7495
+     */
7496
+    public function load( $type, $id )
7497
+    {
7498
+        $bean = $this->dispense( $type );
7499
+        if ( isset( $this->stash[$this->nesting][$id] ) ) {
7500
+            $row = $this->stash[$this->nesting][$id];
7501
+        } else {
7502
+            try {
7503
+                $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
7504
+            } catch ( SQLException $exception ) {
7505
+                if ( $this->writer->sqlStateIn( $exception->getSQLState(),
7506
+                    array(
7507
+                        QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
7508
+                        QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
7509
+                )
7510
+                ) {
7511
+                    $rows = 0;
7512
+
7513
+                }
7514
+            }
7515
+            if ( empty( $rows ) ) {
7516
+                return $bean;
7517
+            }
7518
+            $row = array_pop( $rows );
7519
+        }
7520
+        $bean->importRow( $row );
7521
+        $this->nesting++;
7522
+        $this->oodb->signal( 'open', $bean );
7523
+        $this->nesting--;
7524
+
7525
+        return $bean->setMeta( 'tainted', FALSE );
7526
+    }
7527 7527
 }
7528 7528
 }
7529 7529
 
@@ -7562,169 +7562,169 @@  discard block
 block discarded – undo
7562 7562
  */
7563 7563
 class Frozen extends Repository
7564 7564
 {
7565
-	/**
7566
-	 * Handles\Exceptions. Suppresses exceptions caused by missing structures.
7567
-	 *
7568
-	 * @param \Exception $exception exception
7569
-	 *
7570
-	 * @return void
7571
-	 *
7572
-	 * @throws \Exception
7573
-	 */
7574
-	protected function handleException( \Exception $exception )
7575
-	{
7576
-		throw $exception;
7577
-	}
7578
-
7579
-	/**
7580
-	 * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
7581
-	 * method. When all lists and embedded beans (parent objects) have been processed and
7582
-	 * removed from the original bean the bean is passed to this method to be stored
7583
-	 * in the database.
7584
-	 *
7585
-	 * @param OODBBean $bean the clean bean
7586
-	 *
7587
-	 * @return void
7588
-	 */
7589
-	protected function storeBean( OODBBean $bean )
7590
-	{
7591
-		if ( $bean->getMeta( 'changed' ) ) {
7592
-
7593
-			list( $properties, $table ) = $bean->getPropertiesAndType();
7594
-			$id = $properties['id'];
7595
-			unset($properties['id']);
7596
-			$updateValues = array();
7597
-			$k1 = 'property';
7598
-			$k2 = 'value';
7599
-			foreach( $properties as $key => $value ) {
7600
-				$updateValues[] = array( $k1 => $key, $k2 => $value );
7601
-			}
7602
-			$bean->id = $this->writer->updateRecord( $table, $updateValues, $id );
7603
-			$bean->setMeta( 'changed', FALSE );
7604
-		}
7605
-		$bean->setMeta( 'tainted', FALSE );
7606
-	}
7607
-
7608
-	/**
7609
-	 * Part of the store() functionality.
7610
-	 * Handles all new additions after the bean has been saved.
7611
-	 * Stores addition bean in own-list, extracts the id and
7612
-	 * adds a foreign key. Also adds a constraint in case the type is
7613
-	 * in the dependent list.
7614
-	 *
7615
-	 * @param OODBBean $bean         bean
7616
-	 * @param array            $ownAdditions list of addition beans in own-list
7617
-	 *
7618
-	 * @return void
7619
-	 *
7620
-	 * @throws Security
7621
-	 */
7622
-	protected function processAdditions( $bean, $ownAdditions )
7623
-	{
7624
-		$beanType = $bean->getMeta( 'type' );
7625
-
7626
-		$cachedIndex = array();
7627
-		foreach ( $ownAdditions as $addition ) {
7628
-			if ( $addition instanceof OODBBean ) {
7629
-
7630
-				$myFieldLink = $beanType . '_id';
7631
-				$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
7632
-				if ( $alias ) $myFieldLink = $alias . '_id';
7633
-
7634
-				$addition->$myFieldLink = $bean->id;
7635
-				$addition->setMeta( 'cast.' . $myFieldLink, 'id' );
7636
-				$this->store( $addition );
7637
-
7638
-			} else {
7639
-				throw new RedException( 'Array may only contain OODBBeans' );
7640
-			}
7641
-		}
7642
-	}
7643
-
7644
-	/**
7645
-	 * Dispenses a new bean (a OODBBean Bean Object)
7646
-	 * of the specified type. Always
7647
-	 * use this function to get an empty bean object. Never
7648
-	 * instantiate a OODBBean yourself because it needs
7649
-	 * to be configured before you can use it with RedBean. This
7650
-	 * function applies the appropriate initialization /
7651
-	 * configuration for you.
7652
-	 *
7653
-	 * @param string  $type              type of bean you want to dispense
7654
-	 * @param string  $number            number of beans you would like to get
7655
-	 * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
7656
-	 *
7657
-	 * @return OODBBean
7658
-	 */
7659
-	public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
7660
-	{
7661
-		$OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
7662
-		$beans = array();
7663
-		for ( $i = 0; $i < $number; $i++ ) {
7664
-			$bean = new $OODBBEAN;
7665
-			$bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
7666
-			$this->oodb->signal( 'dispense', $bean );
7667
-			$beans[] = $bean;
7668
-		}
7669
-
7670
-		return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
7671
-	}
7672
-
7673
-	/**
7674
-	 * Loads a bean from the object database.
7675
-	 * It searches for a OODBBean Bean Object in the
7676
-	 * database. It does not matter how this bean has been stored.
7677
-	 * RedBean uses the primary key ID $id and the string $type
7678
-	 * to find the bean. The $type specifies what kind of bean you
7679
-	 * are looking for; this is the same type as used with the
7680
-	 * dispense() function. If RedBean finds the bean it will return
7681
-	 * the OODB Bean object; if it cannot find the bean
7682
-	 * RedBean will return a new bean of type $type and with
7683
-	 * primary key ID 0. In the latter case it acts basically the
7684
-	 * same as dispense().
7685
-	 *
7686
-	 * Important note:
7687
-	 * If the bean cannot be found in the database a new bean of
7688
-	 * the specified type will be generated and returned.
7689
-	 *
7690
-	 * @param string  $type type of bean you want to load
7691
-	 * @param integer $id   ID of the bean you want to load
7692
-	 *
7693
-	 * @throws SQL
7694
-	 *
7695
-	 * @return OODBBean
7696
-	 *
7697
-	 */
7698
-	public function load( $type, $id )
7699
-	{
7700
-		$bean = $this->dispense( $type );
7701
-		if ( isset( $this->stash[$this->nesting][$id] ) ) {
7702
-			$row = $this->stash[$this->nesting][$id];
7703
-		} else {
7704
-			try {
7705
-				$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
7706
-			} catch ( SQLException $exception ) {
7707
-				if ( $this->writer->sqlStateIn( $exception->getSQLState(),
7708
-					array(
7709
-						QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
7710
-						QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
7711
-				)
7712
-				) {
7713
-					throw $exception; //only throw if frozen
7714
-				}
7715
-			}
7716
-			if ( empty( $rows ) ) {
7717
-				return $bean;
7718
-			}
7719
-			$row = array_pop( $rows );
7720
-		}
7721
-		$bean->importRow( $row );
7722
-		$this->nesting++;
7723
-		$this->oodb->signal( 'open', $bean );
7724
-		$this->nesting--;
7725
-
7726
-		return $bean->setMeta( 'tainted', FALSE );
7727
-	}
7565
+    /**
7566
+     * Handles\Exceptions. Suppresses exceptions caused by missing structures.
7567
+     *
7568
+     * @param \Exception $exception exception
7569
+     *
7570
+     * @return void
7571
+     *
7572
+     * @throws \Exception
7573
+     */
7574
+    protected function handleException( \Exception $exception )
7575
+    {
7576
+        throw $exception;
7577
+    }
7578
+
7579
+    /**
7580
+     * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
7581
+     * method. When all lists and embedded beans (parent objects) have been processed and
7582
+     * removed from the original bean the bean is passed to this method to be stored
7583
+     * in the database.
7584
+     *
7585
+     * @param OODBBean $bean the clean bean
7586
+     *
7587
+     * @return void
7588
+     */
7589
+    protected function storeBean( OODBBean $bean )
7590
+    {
7591
+        if ( $bean->getMeta( 'changed' ) ) {
7592
+
7593
+            list( $properties, $table ) = $bean->getPropertiesAndType();
7594
+            $id = $properties['id'];
7595
+            unset($properties['id']);
7596
+            $updateValues = array();
7597
+            $k1 = 'property';
7598
+            $k2 = 'value';
7599
+            foreach( $properties as $key => $value ) {
7600
+                $updateValues[] = array( $k1 => $key, $k2 => $value );
7601
+            }
7602
+            $bean->id = $this->writer->updateRecord( $table, $updateValues, $id );
7603
+            $bean->setMeta( 'changed', FALSE );
7604
+        }
7605
+        $bean->setMeta( 'tainted', FALSE );
7606
+    }
7607
+
7608
+    /**
7609
+     * Part of the store() functionality.
7610
+     * Handles all new additions after the bean has been saved.
7611
+     * Stores addition bean in own-list, extracts the id and
7612
+     * adds a foreign key. Also adds a constraint in case the type is
7613
+     * in the dependent list.
7614
+     *
7615
+     * @param OODBBean $bean         bean
7616
+     * @param array            $ownAdditions list of addition beans in own-list
7617
+     *
7618
+     * @return void
7619
+     *
7620
+     * @throws Security
7621
+     */
7622
+    protected function processAdditions( $bean, $ownAdditions )
7623
+    {
7624
+        $beanType = $bean->getMeta( 'type' );
7625
+
7626
+        $cachedIndex = array();
7627
+        foreach ( $ownAdditions as $addition ) {
7628
+            if ( $addition instanceof OODBBean ) {
7629
+
7630
+                $myFieldLink = $beanType . '_id';
7631
+                $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
7632
+                if ( $alias ) $myFieldLink = $alias . '_id';
7633
+
7634
+                $addition->$myFieldLink = $bean->id;
7635
+                $addition->setMeta( 'cast.' . $myFieldLink, 'id' );
7636
+                $this->store( $addition );
7637
+
7638
+            } else {
7639
+                throw new RedException( 'Array may only contain OODBBeans' );
7640
+            }
7641
+        }
7642
+    }
7643
+
7644
+    /**
7645
+     * Dispenses a new bean (a OODBBean Bean Object)
7646
+     * of the specified type. Always
7647
+     * use this function to get an empty bean object. Never
7648
+     * instantiate a OODBBean yourself because it needs
7649
+     * to be configured before you can use it with RedBean. This
7650
+     * function applies the appropriate initialization /
7651
+     * configuration for you.
7652
+     *
7653
+     * @param string  $type              type of bean you want to dispense
7654
+     * @param string  $number            number of beans you would like to get
7655
+     * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
7656
+     *
7657
+     * @return OODBBean
7658
+     */
7659
+    public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
7660
+    {
7661
+        $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
7662
+        $beans = array();
7663
+        for ( $i = 0; $i < $number; $i++ ) {
7664
+            $bean = new $OODBBEAN;
7665
+            $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
7666
+            $this->oodb->signal( 'dispense', $bean );
7667
+            $beans[] = $bean;
7668
+        }
7669
+
7670
+        return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
7671
+    }
7672
+
7673
+    /**
7674
+     * Loads a bean from the object database.
7675
+     * It searches for a OODBBean Bean Object in the
7676
+     * database. It does not matter how this bean has been stored.
7677
+     * RedBean uses the primary key ID $id and the string $type
7678
+     * to find the bean. The $type specifies what kind of bean you
7679
+     * are looking for; this is the same type as used with the
7680
+     * dispense() function. If RedBean finds the bean it will return
7681
+     * the OODB Bean object; if it cannot find the bean
7682
+     * RedBean will return a new bean of type $type and with
7683
+     * primary key ID 0. In the latter case it acts basically the
7684
+     * same as dispense().
7685
+     *
7686
+     * Important note:
7687
+     * If the bean cannot be found in the database a new bean of
7688
+     * the specified type will be generated and returned.
7689
+     *
7690
+     * @param string  $type type of bean you want to load
7691
+     * @param integer $id   ID of the bean you want to load
7692
+     *
7693
+     * @throws SQL
7694
+     *
7695
+     * @return OODBBean
7696
+     *
7697
+     */
7698
+    public function load( $type, $id )
7699
+    {
7700
+        $bean = $this->dispense( $type );
7701
+        if ( isset( $this->stash[$this->nesting][$id] ) ) {
7702
+            $row = $this->stash[$this->nesting][$id];
7703
+        } else {
7704
+            try {
7705
+                $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
7706
+            } catch ( SQLException $exception ) {
7707
+                if ( $this->writer->sqlStateIn( $exception->getSQLState(),
7708
+                    array(
7709
+                        QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
7710
+                        QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
7711
+                )
7712
+                ) {
7713
+                    throw $exception; //only throw if frozen
7714
+                }
7715
+            }
7716
+            if ( empty( $rows ) ) {
7717
+                return $bean;
7718
+            }
7719
+            $row = array_pop( $rows );
7720
+        }
7721
+        $bean->importRow( $row );
7722
+        $this->nesting++;
7723
+        $this->oodb->signal( 'open', $bean );
7724
+        $this->nesting--;
7725
+
7726
+        return $bean->setMeta( 'tainted', FALSE );
7727
+    }
7728 7728
 }
7729 7729
 }
7730 7730
 
@@ -7764,515 +7764,515 @@  discard block
 block discarded – undo
7764 7764
  */
7765 7765
 class OODB extends Observable
7766 7766
 {
7767
-	/**
7768
-	 * @var array
7769
-	 */
7770
-	private static $sqlFilters = array();
7767
+    /**
7768
+     * @var array
7769
+     */
7770
+    private static $sqlFilters = array();
7771 7771
 
7772
-	/**
7773
-	 * @var array
7774
-	 */
7775
-	protected $chillList = array();
7772
+    /**
7773
+     * @var array
7774
+     */
7775
+    protected $chillList = array();
7776 7776
 
7777 7777
 
7778
-	/**
7779
-	 * @var array
7780
-	 */
7781
-	protected $stash = NULL;
7778
+    /**
7779
+     * @var array
7780
+     */
7781
+    protected $stash = NULL;
7782 7782
 
7783
-	/*
7783
+    /*
7784 7784
 	 * @var integer
7785 7785
 	 */
7786
-	protected $nesting = 0;
7787
-
7788
-	/**
7789
-	 * @var DBAdapter
7790
-	 */
7791
-	protected $writer;
7792
-
7793
-	/**
7794
-	 * @var boolean
7795
-	 */
7796
-	protected $isFrozen = FALSE;
7797
-
7798
-	/**
7799
-	 * @var FacadeBeanHelper
7800
-	 */
7801
-	protected $beanhelper = NULL;
7802
-
7803
-	/**
7804
-	 * @var AssociationManager
7805
-	 */
7806
-	protected $assocManager = NULL;
7807
-
7808
-	/**
7809
-	 * @var Repository
7810
-	 */
7811
-	protected $repository = NULL;
7812
-
7813
-	/**
7814
-	 * @var FrozenRepo
7815
-	 */
7816
-	protected $frozenRepository = NULL;
7817
-
7818
-	/**
7819
-	 * @var FluidRepo
7820
-	 */
7821
-	protected $fluidRepository = NULL;
7822
-
7823
-	/**
7824
-	 * Unboxes a bean from a FUSE model if needed and checks whether the bean is
7825
-	 * an instance of OODBBean.
7826
-	 *
7827
-	 * @param OODBBean $bean bean you wish to unbox
7828
-	 *
7829
-	 * @return OODBBean
7830
-	 *
7831
-	 * @throws Security
7832
-	 */
7833
-	protected function unboxIfNeeded( $bean )
7834
-	{
7835
-		if ( $bean instanceof SimpleModel ) {
7836
-			$bean = $bean->unbox();
7837
-		}
7838
-		if ( !( $bean instanceof OODBBean ) ) {
7839
-			throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) );
7840
-		}
7841
-
7842
-		return $bean;
7843
-	}
7844
-
7845
-	/**
7846
-	 * Constructor, requires a query writer.
7847
-	 *
7848
-	 * @param QueryWriter   $writer writer
7849
-	 * @param array|boolean $frozen mode of operation: TRUE (frozen), FALSE (default, fluid) or ARRAY (chilled)
7850
-	 */
7851
-	public function __construct( QueryWriter $writer, $frozen = FALSE )
7852
-	{
7853
-		if ( $writer instanceof QueryWriter ) {
7854
-			$this->writer = $writer;
7855
-		}
7856
-
7857
-		$this->freeze( $frozen );
7858
-	}
7859
-
7860
-	/**
7861
-	 * Toggles fluid or frozen mode. In fluid mode the database
7862
-	 * structure is adjusted to accomodate your objects. In frozen mode
7863
-	 * this is not the case.
7864
-	 *
7865
-	 * You can also pass an array containing a selection of frozen types.
7866
-	 * Let's call this chilly mode, it's just like fluid mode except that
7867
-	 * certain types (i.e. tables) aren't touched.
7868
-	 *
7869
-	 * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode
7870
-	 *
7871
-	 * @return void
7872
-	 */
7873
-	public function freeze( $toggle )
7874
-	{
7875
-		if ( is_array( $toggle ) ) {
7876
-			$this->chillList = $toggle;
7877
-			$this->isFrozen  = FALSE;
7878
-		} else {
7879
-			$this->isFrozen = (boolean) $toggle;
7880
-		}
7881
-
7882
-		if ( $this->isFrozen ) {
7883
-			if ( !$this->frozenRepository ) {
7884
-				$this->frozenRepository = new FrozenRepo( $this, $this->writer );
7885
-			}
7886
-
7887
-			$this->repository = $this->frozenRepository;
7888
-
7889
-		} else {
7890
-			if ( !$this->fluidRepository ) {
7891
-				$this->fluidRepository = new FluidRepo( $this, $this->writer );
7892
-			}
7893
-
7894
-			$this->repository = $this->fluidRepository;
7895
-		}
7896
-
7897
-		if ( count( self::$sqlFilters ) ) {
7898
-			AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
7899
-		}
7900
-
7901
-	}
7902
-
7903
-	/**
7904
-	 * Returns the current mode of operation of RedBean.
7905
-	 * In fluid mode the database
7906
-	 * structure is adjusted to accomodate your objects.
7907
-	 * In frozen mode
7908
-	 * this is not the case.
7909
-	 *
7910
-	 * @return boolean
7911
-	 */
7912
-	public function isFrozen()
7913
-	{
7914
-		return (bool) $this->isFrozen;
7915
-	}
7916
-
7917
-	/**
7918
-	 * Determines whether a type is in the chill list.
7919
-	 * If a type is 'chilled' it's frozen, so its schema cannot be
7920
-	 * changed anymore. However other bean types may still be modified.
7921
-	 * This method is a convenience method for other objects to check if
7922
-	 * the schema of a certain type is locked for modification.
7923
-	 *
7924
-	 * @param string $type the type you wish to check
7925
-	 *
7926
-	 * @return boolean
7927
-	 */
7928
-	public function isChilled( $type )
7929
-	{
7930
-		return (boolean) ( in_array( $type, $this->chillList ) );
7931
-	}
7932
-
7933
-	/**
7934
-	 * Dispenses a new bean (a OODBBean Bean Object)
7935
-	 * of the specified type. Always
7936
-	 * use this function to get an empty bean object. Never
7937
-	 * instantiate a OODBBean yourself because it needs
7938
-	 * to be configured before you can use it with RedBean. This
7939
-	 * function applies the appropriate initialization /
7940
-	 * configuration for you.
7941
-	 *
7942
-	 * @param string  $type              type of bean you want to dispense
7943
-	 * @param string  $number            number of beans you would like to get
7944
-	 * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
7945
-	 *
7946
-	 * @return OODBBean
7947
-	 */
7948
-	public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
7949
-	{
7950
-		if ( $number < 1 ) {
7951
-			if ( $alwaysReturnArray ) return array();
7952
-			return NULL;
7953
-		}
7954
-
7955
-		return $this->repository->dispense( $type, $number, $alwaysReturnArray );
7956
-	}
7957
-
7958
-	/**
7959
-	 * Sets bean helper to be given to beans.
7960
-	 * Bean helpers assist beans in getting a reference to a toolbox.
7961
-	 *
7962
-	 * @param BeanHelper $beanhelper helper
7963
-	 *
7964
-	 * @return void
7965
-	 */
7966
-	public function setBeanHelper( BeanHelper $beanhelper )
7967
-	{
7968
-		$this->beanhelper = $beanhelper;
7969
-	}
7970
-
7971
-	/**
7972
-	 * Returns the current bean helper.
7973
-	 * Bean helpers assist beans in getting a reference to a toolbox.
7974
-	 *
7975
-	 * @return BeanHelper
7976
-	 */
7977
-	public function getBeanHelper()
7978
-	{
7979
-		return $this->beanhelper;
7980
-	}
7981
-
7982
-	/**
7983
-	 * Checks whether a OODBBean bean is valid.
7984
-	 * If the type is not valid or the ID is not valid it will
7985
-	 * throw an exception: Security.
7986
-	 *
7987
-	 * @param OODBBean $bean the bean that needs to be checked
7988
-	 *
7989
-	 * @return void
7990
-	 *
7991
-	 * @throws Security $exception
7992
-	 */
7993
-	public function check( OODBBean $bean )
7994
-	{
7995
-		$this->repository->check( $bean );
7996
-	}
7997
-
7998
-	/**
7999
-	 * Searches the database for a bean that matches conditions $conditions and sql $addSQL
8000
-	 * and returns an array containing all the beans that have been found.
8001
-	 *
8002
-	 * Conditions need to take form:
8003
-	 *
8004
-	 * array(
8005
-	 *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
8006
-	 *    'PROPERTY' => array( POSSIBLE VALUES... )
8007
-	 * );
8008
-	 *
8009
-	 * All conditions are glued together using the AND-operator, while all value lists
8010
-	 * are glued using IN-operators thus acting as OR-conditions.
8011
-	 *
8012
-	 * Note that you can use property names; the columns will be extracted using the
8013
-	 * appropriate bean formatter.
8014
-	 *
8015
-	 * @param string $type       type of beans you are looking for
8016
-	 * @param array  $conditions list of conditions
8017
-	 * @param string $addSQL     SQL to be used in query
8018
-	 * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
8019
-	 *
8020
-	 * @return array
8021
-	 *
8022
-	 * @throws SQL
8023
-	 */
8024
-	public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
8025
-	{
8026
-		return $this->repository->find( $type, $conditions, $sql, $bindings );
8027
-	}
8028
-
8029
-	/**
8030
-	 * Same as find() but returns a BeanCollection.
8031
-	 *
8032
-	 * @param string $type       type of beans you are looking for
8033
-	 * @param string $addSQL     SQL to be used in query
8034
-	 * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
8035
-	 *
8036
-	 * @return array
8037
-	 *
8038
-	 * @throws SQL
8039
-	 */
8040
-	public function findCollection(  $type, $sql = NULL, $bindings = array() )
8041
-	{
8042
-		return $this->repository->findCollection( $type, $sql, $bindings );
8043
-	}
8044
-
8045
-	/**
8046
-	 * Checks whether the specified table already exists in the database.
8047
-	 * Not part of the Object Database interface!
8048
-	 *
8049
-	 * @deprecated Use AQueryWriter::typeExists() instead.
8050
-	 *
8051
-	 * @param string $table table name
8052
-	 *
8053
-	 * @return boolean
8054
-	 */
8055
-	public function tableExists( $table )
8056
-	{
8057
-		return $this->repository->tableExists( $table );
8058
-	}
8059
-
8060
-	/**
8061
-	 * Stores a bean in the database. This method takes a
8062
-	 * OODBBean Bean Object $bean and stores it
8063
-	 * in the database. If the database schema is not compatible
8064
-	 * with this bean and RedBean runs in fluid mode the schema
8065
-	 * will be altered to store the bean correctly.
8066
-	 * If the database schema is not compatible with this bean and
8067
-	 * RedBean runs in frozen mode it will throw an exception.
8068
-	 * This function returns the primary key ID of the inserted
8069
-	 * bean.
8070
-	 *
8071
-	 * The return value is an integer if possible. If it is not possible to
8072
-	 * represent the value as an integer a string will be returned. We use
8073
-	 * explicit casts instead of functions to preserve performance
8074
-	 * (0.13 vs 0.28 for 10000 iterations on Core i3).
8075
-	 *
8076
-	 * @param OODBBean|SimpleModel $bean bean to store
8077
-	 *
8078
-	 * @return integer|string
8079
-	 *
8080
-	 * @throws Security
8081
-	 */
8082
-	public function store( $bean )
8083
-	{
8084
-		$bean = $this->unboxIfNeeded( $bean );
8085
-		return $this->repository->store( $bean );
8086
-	}
8087
-
8088
-	/**
8089
-	 * Loads a bean from the object database.
8090
-	 * It searches for a OODBBean Bean Object in the
8091
-	 * database. It does not matter how this bean has been stored.
8092
-	 * RedBean uses the primary key ID $id and the string $type
8093
-	 * to find the bean. The $type specifies what kind of bean you
8094
-	 * are looking for; this is the same type as used with the
8095
-	 * dispense() function. If RedBean finds the bean it will return
8096
-	 * the OODB Bean object; if it cannot find the bean
8097
-	 * RedBean will return a new bean of type $type and with
8098
-	 * primary key ID 0. In the latter case it acts basically the
8099
-	 * same as dispense().
8100
-	 *
8101
-	 * Important note:
8102
-	 * If the bean cannot be found in the database a new bean of
8103
-	 * the specified type will be generated and returned.
8104
-	 *
8105
-	 * @param string  $type type of bean you want to load
8106
-	 * @param integer $id   ID of the bean you want to load
8107
-	 *
8108
-	 * @throws SQL
8109
-	 *
8110
-	 * @return OODBBean
8111
-	 *
8112
-	 */
8113
-	public function load( $type, $id )
8114
-	{
8115
-		return $this->repository->load( $type, $id );
8116
-	}
8117
-
8118
-	/**
8119
-	 * Removes a bean from the database.
8120
-	 * This function will remove the specified OODBBean
8121
-	 * Bean Object from the database.
8122
-	 *
8123
-	 * @param OODBBean|SimpleModel $bean bean you want to remove from database
8124
-	 *
8125
-	 * @return void
8126
-	 *
8127
-	 * @throws Security
8128
-	 */
8129
-	public function trash( $bean )
8130
-	{
8131
-		$bean = $this->unboxIfNeeded( $bean );
8132
-		return $this->repository->trash( $bean );
8133
-	}
8134
-
8135
-	/**
8136
-	 * Returns an array of beans. Pass a type and a series of ids and
8137
-	 * this method will bring you the corresponding beans.
8138
-	 *
8139
-	 * important note: Because this method loads beans using the load()
8140
-	 * function (but faster) it will return empty beans with ID 0 for
8141
-	 * every bean that could not be located. The resulting beans will have the
8142
-	 * passed IDs as their keys.
8143
-	 *
8144
-	 * @param string $type type of beans
8145
-	 * @param array  $ids  ids to load
8146
-	 *
8147
-	 * @return array
8148
-	 */
8149
-	public function batch( $type, $ids )
8150
-	{
8151
-		return $this->repository->batch( $type, $ids );
8152
-	}
8153
-
8154
-	/**
8155
-	 * This is a convenience method; it converts database rows
8156
-	 * (arrays) into beans. Given a type and a set of rows this method
8157
-	 * will return an array of beans of the specified type loaded with
8158
-	 * the data fields provided by the result set from the database.
8159
-	 *
8160
-	 * @param string $type type of beans you would like to have
8161
-	 * @param array  $rows rows from the database result
8162
-	 *
8163
-	 * @return array
8164
-	 */
8165
-	public function convertToBeans( $type, $rows )
8166
-	{
8167
-		return $this->repository->convertToBeans( $type, $rows );
8168
-	}
8169
-
8170
-	/**
8171
-	 * Counts the number of beans of type $type.
8172
-	 * This method accepts a second argument to modify the count-query.
8173
-	 * A third argument can be used to provide bindings for the SQL snippet.
8174
-	 *
8175
-	 * @param string $type     type of bean we are looking for
8176
-	 * @param string $addSQL   additional SQL snippet
8177
-	 * @param array  $bindings parameters to bind to SQL
8178
-	 *
8179
-	 * @return integer
8180
-	 *
8181
-	 * @throws SQL
8182
-	 */
8183
-	public function count( $type, $addSQL = '', $bindings = array() )
8184
-	{
8185
-		return $this->repository->count( $type, $addSQL, $bindings );
8186
-	}
8187
-
8188
-	/**
8189
-	 * Trash all beans of a given type. Wipes an entire type of bean.
8190
-	 *
8191
-	 * @param string $type type of bean you wish to delete all instances of
8192
-	 *
8193
-	 * @return boolean
8194
-	 *
8195
-	 * @throws SQL
8196
-	 */
8197
-	public function wipe( $type )
8198
-	{
8199
-		return $this->repository->wipe( $type );
8200
-	}
8201
-
8202
-	/**
8203
-	 * Returns an Association Manager for use with OODB.
8204
-	 * A simple getter function to obtain a reference to the association manager used for
8205
-	 * storage and more.
8206
-	 *
8207
-	 * @return AssociationManager
8208
-	 *
8209
-	 * @throws Security
8210
-	 */
8211
-	public function getAssociationManager()
8212
-	{
8213
-		if ( !isset( $this->assocManager ) ) {
8214
-			throw new RedException( 'No association manager available.' );
8215
-		}
8216
-
8217
-		return $this->assocManager;
8218
-	}
8219
-
8220
-	/**
8221
-	 * Sets the association manager instance to be used by this OODB.
8222
-	 * A simple setter function to set the association manager to be used for storage and
8223
-	 * more.
8224
-	 *
8225
-	 * @param AssociationManager $assoc sets the association manager to be used
8226
-	 *
8227
-	 * @return void
8228
-	 */
8229
-	public function setAssociationManager( AssociationManager $assocManager )
8230
-	{
8231
-		$this->assocManager = $assocManager;
8232
-	}
8233
-
8234
-	/**
8235
-	 * Returns the currently used repository instance.
8236
-	 * For testing purposes only.
8237
-	 *
8238
-	 * @return Repository
8239
-	 */
8240
-	public function getCurrentRepository()
8241
-	{
8242
-		return $this->repository;
8243
-	}
8244
-
8245
-	/**
8246
-	 * Binds an SQL function to a column.
8247
-	 * This method can be used to setup a decode/encode scheme or
8248
-	 * perform UUID insertion. This method is especially useful for handling
8249
-	 * MySQL spatial columns, because they need to be processed first using
8250
-	 * the asText/GeomFromText functions.
8251
-	 *
8252
-	 * @param string $mode (read or write)
8253
-	 * @param string $field
8254
-	 * @param string $function
8255
-	 */
8256
-	public function bindFunc( $mode, $field, $function )
8257
-	{
8258
-		list( $type, $property ) = explode( '.', $field );
8259
-		$mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ;
8260
-
8261
-		if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array();
8262
-		if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array();
8263
-
8264
-		if ( is_null( $function ) ) {
8265
-			unset( self::$sqlFilters[$mode][$type][$property] );
8266
-		} else {
8267
-			if ($mode === QueryWriter::C_SQLFILTER_WRITE) {
8268
-				self::$sqlFilters[$mode][$type][$property] = $function.'(?)';
8269
-			} else {
8270
-				self::$sqlFilters[$mode][$type][$property] = $function."($field)";
8271
-			}
8272
-		}
8273
-
8274
-		AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
8275
-	}
7786
+    protected $nesting = 0;
7787
+
7788
+    /**
7789
+     * @var DBAdapter
7790
+     */
7791
+    protected $writer;
7792
+
7793
+    /**
7794
+     * @var boolean
7795
+     */
7796
+    protected $isFrozen = FALSE;
7797
+
7798
+    /**
7799
+     * @var FacadeBeanHelper
7800
+     */
7801
+    protected $beanhelper = NULL;
7802
+
7803
+    /**
7804
+     * @var AssociationManager
7805
+     */
7806
+    protected $assocManager = NULL;
7807
+
7808
+    /**
7809
+     * @var Repository
7810
+     */
7811
+    protected $repository = NULL;
7812
+
7813
+    /**
7814
+     * @var FrozenRepo
7815
+     */
7816
+    protected $frozenRepository = NULL;
7817
+
7818
+    /**
7819
+     * @var FluidRepo
7820
+     */
7821
+    protected $fluidRepository = NULL;
7822
+
7823
+    /**
7824
+     * Unboxes a bean from a FUSE model if needed and checks whether the bean is
7825
+     * an instance of OODBBean.
7826
+     *
7827
+     * @param OODBBean $bean bean you wish to unbox
7828
+     *
7829
+     * @return OODBBean
7830
+     *
7831
+     * @throws Security
7832
+     */
7833
+    protected function unboxIfNeeded( $bean )
7834
+    {
7835
+        if ( $bean instanceof SimpleModel ) {
7836
+            $bean = $bean->unbox();
7837
+        }
7838
+        if ( !( $bean instanceof OODBBean ) ) {
7839
+            throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) );
7840
+        }
7841
+
7842
+        return $bean;
7843
+    }
7844
+
7845
+    /**
7846
+     * Constructor, requires a query writer.
7847
+     *
7848
+     * @param QueryWriter   $writer writer
7849
+     * @param array|boolean $frozen mode of operation: TRUE (frozen), FALSE (default, fluid) or ARRAY (chilled)
7850
+     */
7851
+    public function __construct( QueryWriter $writer, $frozen = FALSE )
7852
+    {
7853
+        if ( $writer instanceof QueryWriter ) {
7854
+            $this->writer = $writer;
7855
+        }
7856
+
7857
+        $this->freeze( $frozen );
7858
+    }
7859
+
7860
+    /**
7861
+     * Toggles fluid or frozen mode. In fluid mode the database
7862
+     * structure is adjusted to accomodate your objects. In frozen mode
7863
+     * this is not the case.
7864
+     *
7865
+     * You can also pass an array containing a selection of frozen types.
7866
+     * Let's call this chilly mode, it's just like fluid mode except that
7867
+     * certain types (i.e. tables) aren't touched.
7868
+     *
7869
+     * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode
7870
+     *
7871
+     * @return void
7872
+     */
7873
+    public function freeze( $toggle )
7874
+    {
7875
+        if ( is_array( $toggle ) ) {
7876
+            $this->chillList = $toggle;
7877
+            $this->isFrozen  = FALSE;
7878
+        } else {
7879
+            $this->isFrozen = (boolean) $toggle;
7880
+        }
7881
+
7882
+        if ( $this->isFrozen ) {
7883
+            if ( !$this->frozenRepository ) {
7884
+                $this->frozenRepository = new FrozenRepo( $this, $this->writer );
7885
+            }
7886
+
7887
+            $this->repository = $this->frozenRepository;
7888
+
7889
+        } else {
7890
+            if ( !$this->fluidRepository ) {
7891
+                $this->fluidRepository = new FluidRepo( $this, $this->writer );
7892
+            }
7893
+
7894
+            $this->repository = $this->fluidRepository;
7895
+        }
7896
+
7897
+        if ( count( self::$sqlFilters ) ) {
7898
+            AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
7899
+        }
7900
+
7901
+    }
7902
+
7903
+    /**
7904
+     * Returns the current mode of operation of RedBean.
7905
+     * In fluid mode the database
7906
+     * structure is adjusted to accomodate your objects.
7907
+     * In frozen mode
7908
+     * this is not the case.
7909
+     *
7910
+     * @return boolean
7911
+     */
7912
+    public function isFrozen()
7913
+    {
7914
+        return (bool) $this->isFrozen;
7915
+    }
7916
+
7917
+    /**
7918
+     * Determines whether a type is in the chill list.
7919
+     * If a type is 'chilled' it's frozen, so its schema cannot be
7920
+     * changed anymore. However other bean types may still be modified.
7921
+     * This method is a convenience method for other objects to check if
7922
+     * the schema of a certain type is locked for modification.
7923
+     *
7924
+     * @param string $type the type you wish to check
7925
+     *
7926
+     * @return boolean
7927
+     */
7928
+    public function isChilled( $type )
7929
+    {
7930
+        return (boolean) ( in_array( $type, $this->chillList ) );
7931
+    }
7932
+
7933
+    /**
7934
+     * Dispenses a new bean (a OODBBean Bean Object)
7935
+     * of the specified type. Always
7936
+     * use this function to get an empty bean object. Never
7937
+     * instantiate a OODBBean yourself because it needs
7938
+     * to be configured before you can use it with RedBean. This
7939
+     * function applies the appropriate initialization /
7940
+     * configuration for you.
7941
+     *
7942
+     * @param string  $type              type of bean you want to dispense
7943
+     * @param string  $number            number of beans you would like to get
7944
+     * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
7945
+     *
7946
+     * @return OODBBean
7947
+     */
7948
+    public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
7949
+    {
7950
+        if ( $number < 1 ) {
7951
+            if ( $alwaysReturnArray ) return array();
7952
+            return NULL;
7953
+        }
7954
+
7955
+        return $this->repository->dispense( $type, $number, $alwaysReturnArray );
7956
+    }
7957
+
7958
+    /**
7959
+     * Sets bean helper to be given to beans.
7960
+     * Bean helpers assist beans in getting a reference to a toolbox.
7961
+     *
7962
+     * @param BeanHelper $beanhelper helper
7963
+     *
7964
+     * @return void
7965
+     */
7966
+    public function setBeanHelper( BeanHelper $beanhelper )
7967
+    {
7968
+        $this->beanhelper = $beanhelper;
7969
+    }
7970
+
7971
+    /**
7972
+     * Returns the current bean helper.
7973
+     * Bean helpers assist beans in getting a reference to a toolbox.
7974
+     *
7975
+     * @return BeanHelper
7976
+     */
7977
+    public function getBeanHelper()
7978
+    {
7979
+        return $this->beanhelper;
7980
+    }
7981
+
7982
+    /**
7983
+     * Checks whether a OODBBean bean is valid.
7984
+     * If the type is not valid or the ID is not valid it will
7985
+     * throw an exception: Security.
7986
+     *
7987
+     * @param OODBBean $bean the bean that needs to be checked
7988
+     *
7989
+     * @return void
7990
+     *
7991
+     * @throws Security $exception
7992
+     */
7993
+    public function check( OODBBean $bean )
7994
+    {
7995
+        $this->repository->check( $bean );
7996
+    }
7997
+
7998
+    /**
7999
+     * Searches the database for a bean that matches conditions $conditions and sql $addSQL
8000
+     * and returns an array containing all the beans that have been found.
8001
+     *
8002
+     * Conditions need to take form:
8003
+     *
8004
+     * array(
8005
+     *    'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
8006
+     *    'PROPERTY' => array( POSSIBLE VALUES... )
8007
+     * );
8008
+     *
8009
+     * All conditions are glued together using the AND-operator, while all value lists
8010
+     * are glued using IN-operators thus acting as OR-conditions.
8011
+     *
8012
+     * Note that you can use property names; the columns will be extracted using the
8013
+     * appropriate bean formatter.
8014
+     *
8015
+     * @param string $type       type of beans you are looking for
8016
+     * @param array  $conditions list of conditions
8017
+     * @param string $addSQL     SQL to be used in query
8018
+     * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
8019
+     *
8020
+     * @return array
8021
+     *
8022
+     * @throws SQL
8023
+     */
8024
+    public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
8025
+    {
8026
+        return $this->repository->find( $type, $conditions, $sql, $bindings );
8027
+    }
8028
+
8029
+    /**
8030
+     * Same as find() but returns a BeanCollection.
8031
+     *
8032
+     * @param string $type       type of beans you are looking for
8033
+     * @param string $addSQL     SQL to be used in query
8034
+     * @param array  $bindings   whether you prefer to use a WHERE clause or not (TRUE = not)
8035
+     *
8036
+     * @return array
8037
+     *
8038
+     * @throws SQL
8039
+     */
8040
+    public function findCollection(  $type, $sql = NULL, $bindings = array() )
8041
+    {
8042
+        return $this->repository->findCollection( $type, $sql, $bindings );
8043
+    }
8044
+
8045
+    /**
8046
+     * Checks whether the specified table already exists in the database.
8047
+     * Not part of the Object Database interface!
8048
+     *
8049
+     * @deprecated Use AQueryWriter::typeExists() instead.
8050
+     *
8051
+     * @param string $table table name
8052
+     *
8053
+     * @return boolean
8054
+     */
8055
+    public function tableExists( $table )
8056
+    {
8057
+        return $this->repository->tableExists( $table );
8058
+    }
8059
+
8060
+    /**
8061
+     * Stores a bean in the database. This method takes a
8062
+     * OODBBean Bean Object $bean and stores it
8063
+     * in the database. If the database schema is not compatible
8064
+     * with this bean and RedBean runs in fluid mode the schema
8065
+     * will be altered to store the bean correctly.
8066
+     * If the database schema is not compatible with this bean and
8067
+     * RedBean runs in frozen mode it will throw an exception.
8068
+     * This function returns the primary key ID of the inserted
8069
+     * bean.
8070
+     *
8071
+     * The return value is an integer if possible. If it is not possible to
8072
+     * represent the value as an integer a string will be returned. We use
8073
+     * explicit casts instead of functions to preserve performance
8074
+     * (0.13 vs 0.28 for 10000 iterations on Core i3).
8075
+     *
8076
+     * @param OODBBean|SimpleModel $bean bean to store
8077
+     *
8078
+     * @return integer|string
8079
+     *
8080
+     * @throws Security
8081
+     */
8082
+    public function store( $bean )
8083
+    {
8084
+        $bean = $this->unboxIfNeeded( $bean );
8085
+        return $this->repository->store( $bean );
8086
+    }
8087
+
8088
+    /**
8089
+     * Loads a bean from the object database.
8090
+     * It searches for a OODBBean Bean Object in the
8091
+     * database. It does not matter how this bean has been stored.
8092
+     * RedBean uses the primary key ID $id and the string $type
8093
+     * to find the bean. The $type specifies what kind of bean you
8094
+     * are looking for; this is the same type as used with the
8095
+     * dispense() function. If RedBean finds the bean it will return
8096
+     * the OODB Bean object; if it cannot find the bean
8097
+     * RedBean will return a new bean of type $type and with
8098
+     * primary key ID 0. In the latter case it acts basically the
8099
+     * same as dispense().
8100
+     *
8101
+     * Important note:
8102
+     * If the bean cannot be found in the database a new bean of
8103
+     * the specified type will be generated and returned.
8104
+     *
8105
+     * @param string  $type type of bean you want to load
8106
+     * @param integer $id   ID of the bean you want to load
8107
+     *
8108
+     * @throws SQL
8109
+     *
8110
+     * @return OODBBean
8111
+     *
8112
+     */
8113
+    public function load( $type, $id )
8114
+    {
8115
+        return $this->repository->load( $type, $id );
8116
+    }
8117
+
8118
+    /**
8119
+     * Removes a bean from the database.
8120
+     * This function will remove the specified OODBBean
8121
+     * Bean Object from the database.
8122
+     *
8123
+     * @param OODBBean|SimpleModel $bean bean you want to remove from database
8124
+     *
8125
+     * @return void
8126
+     *
8127
+     * @throws Security
8128
+     */
8129
+    public function trash( $bean )
8130
+    {
8131
+        $bean = $this->unboxIfNeeded( $bean );
8132
+        return $this->repository->trash( $bean );
8133
+    }
8134
+
8135
+    /**
8136
+     * Returns an array of beans. Pass a type and a series of ids and
8137
+     * this method will bring you the corresponding beans.
8138
+     *
8139
+     * important note: Because this method loads beans using the load()
8140
+     * function (but faster) it will return empty beans with ID 0 for
8141
+     * every bean that could not be located. The resulting beans will have the
8142
+     * passed IDs as their keys.
8143
+     *
8144
+     * @param string $type type of beans
8145
+     * @param array  $ids  ids to load
8146
+     *
8147
+     * @return array
8148
+     */
8149
+    public function batch( $type, $ids )
8150
+    {
8151
+        return $this->repository->batch( $type, $ids );
8152
+    }
8153
+
8154
+    /**
8155
+     * This is a convenience method; it converts database rows
8156
+     * (arrays) into beans. Given a type and a set of rows this method
8157
+     * will return an array of beans of the specified type loaded with
8158
+     * the data fields provided by the result set from the database.
8159
+     *
8160
+     * @param string $type type of beans you would like to have
8161
+     * @param array  $rows rows from the database result
8162
+     *
8163
+     * @return array
8164
+     */
8165
+    public function convertToBeans( $type, $rows )
8166
+    {
8167
+        return $this->repository->convertToBeans( $type, $rows );
8168
+    }
8169
+
8170
+    /**
8171
+     * Counts the number of beans of type $type.
8172
+     * This method accepts a second argument to modify the count-query.
8173
+     * A third argument can be used to provide bindings for the SQL snippet.
8174
+     *
8175
+     * @param string $type     type of bean we are looking for
8176
+     * @param string $addSQL   additional SQL snippet
8177
+     * @param array  $bindings parameters to bind to SQL
8178
+     *
8179
+     * @return integer
8180
+     *
8181
+     * @throws SQL
8182
+     */
8183
+    public function count( $type, $addSQL = '', $bindings = array() )
8184
+    {
8185
+        return $this->repository->count( $type, $addSQL, $bindings );
8186
+    }
8187
+
8188
+    /**
8189
+     * Trash all beans of a given type. Wipes an entire type of bean.
8190
+     *
8191
+     * @param string $type type of bean you wish to delete all instances of
8192
+     *
8193
+     * @return boolean
8194
+     *
8195
+     * @throws SQL
8196
+     */
8197
+    public function wipe( $type )
8198
+    {
8199
+        return $this->repository->wipe( $type );
8200
+    }
8201
+
8202
+    /**
8203
+     * Returns an Association Manager for use with OODB.
8204
+     * A simple getter function to obtain a reference to the association manager used for
8205
+     * storage and more.
8206
+     *
8207
+     * @return AssociationManager
8208
+     *
8209
+     * @throws Security
8210
+     */
8211
+    public function getAssociationManager()
8212
+    {
8213
+        if ( !isset( $this->assocManager ) ) {
8214
+            throw new RedException( 'No association manager available.' );
8215
+        }
8216
+
8217
+        return $this->assocManager;
8218
+    }
8219
+
8220
+    /**
8221
+     * Sets the association manager instance to be used by this OODB.
8222
+     * A simple setter function to set the association manager to be used for storage and
8223
+     * more.
8224
+     *
8225
+     * @param AssociationManager $assoc sets the association manager to be used
8226
+     *
8227
+     * @return void
8228
+     */
8229
+    public function setAssociationManager( AssociationManager $assocManager )
8230
+    {
8231
+        $this->assocManager = $assocManager;
8232
+    }
8233
+
8234
+    /**
8235
+     * Returns the currently used repository instance.
8236
+     * For testing purposes only.
8237
+     *
8238
+     * @return Repository
8239
+     */
8240
+    public function getCurrentRepository()
8241
+    {
8242
+        return $this->repository;
8243
+    }
8244
+
8245
+    /**
8246
+     * Binds an SQL function to a column.
8247
+     * This method can be used to setup a decode/encode scheme or
8248
+     * perform UUID insertion. This method is especially useful for handling
8249
+     * MySQL spatial columns, because they need to be processed first using
8250
+     * the asText/GeomFromText functions.
8251
+     *
8252
+     * @param string $mode (read or write)
8253
+     * @param string $field
8254
+     * @param string $function
8255
+     */
8256
+    public function bindFunc( $mode, $field, $function )
8257
+    {
8258
+        list( $type, $property ) = explode( '.', $field );
8259
+        $mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ;
8260
+
8261
+        if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array();
8262
+        if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array();
8263
+
8264
+        if ( is_null( $function ) ) {
8265
+            unset( self::$sqlFilters[$mode][$type][$property] );
8266
+        } else {
8267
+            if ($mode === QueryWriter::C_SQLFILTER_WRITE) {
8268
+                self::$sqlFilters[$mode][$type][$property] = $function.'(?)';
8269
+            } else {
8270
+                self::$sqlFilters[$mode][$type][$property] = $function."($field)";
8271
+            }
8272
+        }
8273
+
8274
+        AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
8275
+    }
8276 8276
 }
8277 8277
 } 
8278 8278
 
@@ -8304,80 +8304,80 @@  discard block
 block discarded – undo
8304 8304
 class ToolBox
8305 8305
 {
8306 8306
 
8307
-	/**
8308
-	 * @var OODB
8309
-	 */
8310
-	protected $oodb;
8311
-
8312
-	/**
8313
-	 * @var QueryWriter
8314
-	 */
8315
-	protected $writer;
8316
-
8317
-	/**
8318
-	 * @var DBAdapter
8319
-	 */
8320
-	protected $adapter;
8321
-
8322
-	/**
8323
-	 * Constructor.
8324
-	 * The toolbox is an integral part of RedBeanPHP providing the basic
8325
-	 * architectural building blocks to manager objects, helpers and additional tools
8326
-	 * like plugins. A toolbox contains the three core components of RedBeanPHP:
8327
-	 * the adapter, the query writer and the core functionality of RedBeanPHP in
8328
-	 * OODB.
8329
-	 *
8330
-	 * @param OODB              $oodb    Object Database
8331
-	 * @param DBAdapter $adapter Adapter
8332
-	 * @param QueryWriter       $writer  Writer
8333
-	 *
8334
-	 * @return ToolBox
8335
-	 */
8336
-	public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer )
8337
-	{
8338
-		$this->oodb    = $oodb;
8339
-		$this->adapter = $adapter;
8340
-		$this->writer  = $writer;
8341
-
8342
-		return $this;
8343
-	}
8344
-
8345
-	/**
8346
-	 * Returns the query writer in this toolbox.
8347
-	 * The Query Writer is responsible for building the queries for a
8348
-	 * specific database and executing them through the adapter.
8349
-	 *
8350
-	 * @return QueryWriter
8351
-	 */
8352
-	public function getWriter()
8353
-	{
8354
-		return $this->writer;
8355
-	}
8356
-
8357
-	/**
8358
-	 * Returns the OODB instance in this toolbox.
8359
-	 * OODB is responsible for creating, storing, retrieving and deleting 
8360
-	 * single beans. Other components rely
8361
-	 * on OODB for their basic functionality.
8362
-	 *
8363
-	 * @return OODB
8364
-	 */
8365
-	public function getRedBean()
8366
-	{
8367
-		return $this->oodb;
8368
-	}
8369
-
8370
-	/**
8371
-	 * Returns the database adapter in this toolbox.
8372
-	 * The adapter is responsible for executing the query and binding the values.
8373
-	 * The adapter also takes care of transaction handling.
8374
-	 * 
8375
-	 * @return DBAdapter
8376
-	 */
8377
-	public function getDatabaseAdapter()
8378
-	{
8379
-		return $this->adapter;
8380
-	}
8307
+    /**
8308
+     * @var OODB
8309
+     */
8310
+    protected $oodb;
8311
+
8312
+    /**
8313
+     * @var QueryWriter
8314
+     */
8315
+    protected $writer;
8316
+
8317
+    /**
8318
+     * @var DBAdapter
8319
+     */
8320
+    protected $adapter;
8321
+
8322
+    /**
8323
+     * Constructor.
8324
+     * The toolbox is an integral part of RedBeanPHP providing the basic
8325
+     * architectural building blocks to manager objects, helpers and additional tools
8326
+     * like plugins. A toolbox contains the three core components of RedBeanPHP:
8327
+     * the adapter, the query writer and the core functionality of RedBeanPHP in
8328
+     * OODB.
8329
+     *
8330
+     * @param OODB              $oodb    Object Database
8331
+     * @param DBAdapter $adapter Adapter
8332
+     * @param QueryWriter       $writer  Writer
8333
+     *
8334
+     * @return ToolBox
8335
+     */
8336
+    public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer )
8337
+    {
8338
+        $this->oodb    = $oodb;
8339
+        $this->adapter = $adapter;
8340
+        $this->writer  = $writer;
8341
+
8342
+        return $this;
8343
+    }
8344
+
8345
+    /**
8346
+     * Returns the query writer in this toolbox.
8347
+     * The Query Writer is responsible for building the queries for a
8348
+     * specific database and executing them through the adapter.
8349
+     *
8350
+     * @return QueryWriter
8351
+     */
8352
+    public function getWriter()
8353
+    {
8354
+        return $this->writer;
8355
+    }
8356
+
8357
+    /**
8358
+     * Returns the OODB instance in this toolbox.
8359
+     * OODB is responsible for creating, storing, retrieving and deleting 
8360
+     * single beans. Other components rely
8361
+     * on OODB for their basic functionality.
8362
+     *
8363
+     * @return OODB
8364
+     */
8365
+    public function getRedBean()
8366
+    {
8367
+        return $this->oodb;
8368
+    }
8369
+
8370
+    /**
8371
+     * Returns the database adapter in this toolbox.
8372
+     * The adapter is responsible for executing the query and binding the values.
8373
+     * The adapter also takes care of transaction handling.
8374
+     * 
8375
+     * @return DBAdapter
8376
+     */
8377
+    public function getDatabaseAdapter()
8378
+    {
8379
+        return $this->adapter;
8380
+    }
8381 8381
 }
8382 8382
 }
8383 8383
 
@@ -8405,359 +8405,359 @@  discard block
 block discarded – undo
8405 8405
  * with this source code in the file license.txt.
8406 8406
  */
8407 8407
 class Finder
8408
-{
8409
-	/**
8410
-	 * @var ToolBox
8411
-	 */
8412
-	protected $toolbox;
8413
-
8414
-	/**
8415
-	 * @var OODB
8416
-	 */
8417
-	protected $redbean;
8418
-
8419
-	/**
8420
-	 * Constructor.
8421
-	 * The Finder requires a toolbox.
8422
-	 *
8423
-	 * @param ToolBox $toolbox
8424
-	 */
8425
-	public function __construct( ToolBox $toolbox )
8426
-	{
8427
-		$this->toolbox = $toolbox;
8428
-		$this->redbean = $toolbox->getRedBean();
8429
-	}
8430
-
8431
-	/**
8432
-	 * Finds a bean using a type and a where clause (SQL).
8433
-	 * As with most Query tools in RedBean you can provide values to
8434
-	 * be inserted in the SQL statement by populating the value
8435
-	 * array parameter; you can either use the question mark notation
8436
-	 * or the slot-notation (:keyname).
8437
-	 *
8438
-	 * @param string $type     type   the type of bean you are looking for
8439
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
8440
-	 * @param array  $bindings values array of values to be bound to parameters in query
8441
-	 *
8442
-	 * @return array
8443
-	 *
8444
-	 * @throws Security
8445
-	 */
8446
-	public function find( $type, $sql = NULL, $bindings = array() )
8447
-	{
8448
-		if ( !is_array( $bindings ) ) {
8449
-			throw new RedException(
8450
-				'Expected array, ' . gettype( $bindings ) . ' given.'
8451
-			);
8452
-		}
8453
-
8454
-		return $this->redbean->find( $type, array(), $sql, $bindings );
8455
-	}
8456
-
8457
-	/**
8458
-	 * Like find() but also exports the beans as an array.
8459
-	 *
8460
-	 * @see Finder::findAndExport
8461
-	 *
8462
-	 * @param string $type     type   the type of bean you are looking for
8463
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
8464
-	 * @param array  $bindings values array of values to be bound to parameters in query
8465
-	 *
8466
-	 * @return array
8467
-	 */
8468
-	public function findAndExport( $type, $sql = NULL, $bindings = array() )
8469
-	{
8470
-		$arr = array();
8471
-		foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) {
8472
-			$arr[] = $item->export();
8473
-		}
8474
-
8475
-		return $arr;
8476
-	}
8477
-
8478
-	/**
8479
-	 * Like find() but returns just one bean instead of an array of beans.
8480
-	 * This method will return only the first bean of the array.
8481
-	 *
8482
-	 * @see Finder::find
8483
-	 *
8484
-	 * @param string $type     type   the type of bean you are looking for
8485
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
8486
-	 * @param array  $bindings values array of values to be bound to parameters in query
8487
-	 *
8488
-	 * @return OODBBean
8489
-	 */
8490
-	public function findOne( $type, $sql = NULL, $bindings = array() )
8491
-	{
8492
-		$sql = $this->toolbox->getWriter()->glueLimitOne( $sql );
8493
-		
8494
-		$items = $this->find( $type, $sql, $bindings );
8495
-
8496
-		if ( empty($items) ) {
8497
-			return NULL;
8498
-		}
8499
-
8500
-		return reset( $items );
8501
-	}
8502
-
8503
-	/**
8504
-	 * Like find() but returns the last bean of the result array.
8505
-	 * Opposite of Finder::findLast().
8506
-	 *
8507
-	 * @see Finder::find
8508
-	 *
8509
-	 * @param string $type     the type of bean you are looking for
8510
-	 * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
8511
-	 * @param array  $bindings values array of values to be bound to parameters in query
8512
-	 *
8513
-	 * @return OODBBean
8514
-	 */
8515
-	public function findLast( $type, $sql = NULL, $bindings = array() )
8516
-	{
8517
-		$items = $this->find( $type, $sql, $bindings );
8518
-
8519
-		if ( empty($items) ) {
8520
-			return NULL;
8521
-		}
8522
-
8523
-		return end( $items );
8524
-	}
8525
-
8526
-	/**
8527
-	 * Tries to find beans of a certain type,
8528
-	 * if no beans are found, it dispenses a bean of that type.
8529
-	 *
8530
-	 * @see Finder::find
8531
-	 *
8532
-	 * @param  string $type     the type of bean you are looking for
8533
-	 * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
8534
-	 * @param  array  $bindings values array of values to be bound to parameters in query
8535
-	 *
8536
-	 * @return array
8537
-	 */
8538
-	public function findOrDispense( $type, $sql = NULL, $bindings = array() )
8539
-	{
8540
-		$foundBeans = $this->find( $type, $sql, $bindings );
8541
-
8542
-		if ( empty( $foundBeans ) ) {
8543
-			return array( $this->redbean->dispense( $type ) );
8544
-		} else {
8545
-			return $foundBeans;
8546
-		}
8547
-	}
8548
-
8549
-	/**
8550
-	 * Finds a BeanCollection using the repository.
8551
-	 * A bean collection can be used to retrieve one bean at a time using
8552
-	 * cursors - this is useful for processing large datasets. A bean collection
8553
-	 * will not load all beans into memory all at once, just one at a time.
8554
-	 *
8555
-	 * @param  string $type     the type of bean you are looking for
8556
-	 * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
8557
-	 * @param  array  $bindings values array of values to be bound to parameters in query
8558
-	 *
8559
-	 * @return BeanCollection
8560
-	 */
8561
-	public function findCollection( $type, $sql, $bindings = array() )
8562
-	{
8563
-		return $this->redbean->findCollection( $type, $sql, $bindings );
8564
-	}
8565
-
8566
-	/**
8567
-	 * Finds or creates a bean.
8568
-	 * Tries to find a bean with certain properties specified in the second
8569
-	 * parameter ($like). If the bean is found, it will be returned.
8570
-	 * If multiple beans are found, only the first will be returned.
8571
-	 * If no beans match the criteria, a new bean will be dispensed,
8572
-	 * the criteria will be imported as properties and this new bean
8573
-	 * will be stored and returned.
8574
-	 *
8575
-	 * Format of criteria set: property => value
8576
-	 * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
8577
-	 *
8578
-	 * @param string $type type of bean to search for
8579
-	 * @param array  $like criteria set describing bean to search for
8580
-	 *
8581
-	 * @return OODBBean
8582
-	 */
8583
-	public function findOrCreate( $type, $like = array() )
8584
-	{
8585
-			$beans = $this->findLike( $type, $like );
8586
-			if ( count( $beans ) ) {
8587
-				$bean = reset( $beans );
8588
-				return $bean;
8589
-			}
8590
-
8591
-			$bean = $this->redbean->dispense( $type );
8592
-			$bean->import( $like );
8593
-			$this->redbean->store( $bean );
8594
-			return $bean;
8595
-	}
8596
-
8597
-	/**
8598
-	 * Finds beans by its type and a certain criteria set.
8599
-	 *
8600
-	 * Format of criteria set: property => value
8601
-	 * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
8602
-	 *
8603
-	 * If the additional SQL is a condition, this condition will be glued to the rest
8604
-	 * of the query using an AND operator. Note that this is as far as this method
8605
-	 * can go, there is no way to glue additional SQL using an OR-condition.
8606
-	 * This method provides access to an underlying mechanism in the RedBeanPHP architecture
8607
-	 * to find beans using criteria sets. However, please do not use this method
8608
-	 * for complex queries, use plain SQL instead ( the regular find method ) as it is
8609
-	 * more suitable for the job. This method is
8610
-	 * meant for basic search-by-example operations.
8611
-	 *
8612
-	 * @param string $type       type of bean to search for
8613
-	 * @param array  $conditions criteria set describing the bean to search for
8614
-	 * @param string $sql        additional SQL (for sorting)
8615
-	 *
8616
-	 * @return array
8617
-	 */
8618
-	public function findLike( $type, $conditions = array(), $sql = '' )
8619
-	{
8620
-		if ( count( $conditions ) > 0 ) {
8621
-			foreach( $conditions as $key => $condition ) {
8622
-				if ( !count( $condition ) ) unset( $conditions[$key] );
8623
-			}
8624
-		}
8625
-
8626
-		return $this->redbean->find( $type, $conditions, $sql );
8627
-	}
8628
-
8629
-	/**
8630
-	 * Returns a hashmap with bean arrays keyed by type using an SQL
8631
-	 * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review'
8632
-	 * this method will return movie and review beans.
8633
-	 *
8634
-	 * Example:
8635
-	 *
8636
-	 * $stuff = $finder->findMulti('movie,review', '
8637
-	 *          SELECT movie.*, review.* FROM movie
8638
-	 *          LEFT JOIN review ON review.movie_id = movie.id');
8639
-	 *
8640
-	 * After this operation, $stuff will contain an entry 'movie' containing all
8641
-	 * movies and an entry named 'review' containing all reviews (all beans).
8642
-	 * You can also pass bindings.
8643
-	 *
8644
-	 * If you want to re-map your beans, so you can use $movie->ownReviewList without
8645
-	 * having RedBeanPHP executing an SQL query you can use the fourth parameter to
8646
-	 * define a selection of remapping closures.
8647
-	 *
8648
-	 * The remapping argument (optional) should contain an array of arrays.
8649
-	 * Each array in the remapping array should contain the following entries:
8650
-	 *
8651
-	 * array(
8652
-	 * 	'a'       => TYPE A
8653
-	 *    'b'       => TYPE B
8654
-	 *    'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS
8655
-	 *    'do'      => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS
8656
-	 * )
8657
-	 *
8658
-	 * Using this mechanism you can build your own 'preloader' with tiny function
8659
-	 * snippets (and those can be re-used and shared online of course).
8660
-	 *
8661
-	 * Example:
8662
-	 *
8663
-	 * array(
8664
-	 * 	'a'       => 'movie'     //define A as movie
8665
-	 *    'b'       => 'review'    //define B as review
8666
-	 *    'matcher' => function( $a, $b ) {
8667
-	 *       return ( $b->movie_id == $a->id );  //Perform action if review.movie_id equals movie.id
8668
-	 *    }
8669
-	 *    'do'      => function( $a, $b ) {
8670
-	 *       $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie
8671
-	 *       $a->clearHistory();                 //optional, act 'as if these beans have been loaded through ownReviewList'.
8672
-	 *    }
8673
-	 * )
8674
-	 *
8675
-	 * The Query Template parameter is optional as well but can be used to
8676
-	 * set a different SQL template (sprintf-style) for processing the original query.
8677
-	 *
8678
-	 * @note the SQL query provided IS NOT THE ONE used internally by this function,
8679
-	 * this function will pre-process the query to get all the data required to find the beans.
8680
-	 *
8681
-	 * @note if you use the 'book.*' notation make SURE you're
8682
-	 * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because
8683
-	 * it's actually an SQL-like template SLOT, not real SQL.
8684
-	 *
8685
-	 * @note instead of an SQL query you can pass a result array as well.
8686
-	 *
8687
-	 * @param string|array $types         a list of types (either array or comma separated string)
8688
-	 * @param string|array $sqlOrArr      an SQL query or an array of prefetched records
8689
-	 * @param array        $bindings      optional, bindings for SQL query
8690
-	 * @param array        $remappings    optional, an array of remapping arrays
8691
-	 * @param string       $queryTemplate optional, query template
8692
-	 *
8693
-	 * @return array
8694
-	 */
8695
-	public function findMulti( $types, $sql, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s' )
8696
-	{
8697
-		if ( !is_array( $types ) ) $types = explode( ',', $types );
8698
-		if ( !is_array( $sql ) ) {
8699
-			$writer = $this->toolbox->getWriter();
8700
-			$adapter = $this->toolbox->getDatabaseAdapter();
8701
-
8702
-			//Repair the query, replace book.* with book.id AS book_id etc..
8703
-			foreach( $types as $type ) {
8704
-				$pattern = " {$type}.*";
8705
-				if ( strpos( $sql, $pattern ) !== FALSE ) {
8706
-					$newSelectorArray = array();
8707
-					$columns = $writer->getColumns( $type );
8708
-					foreach( $columns as $column => $definition ) {
8709
-						$newSelectorArray[] = sprintf( $queryTemplate, $type, $column, $type, $column );
8710
-					}
8711
-					$newSelector = implode( ',', $newSelectorArray );
8712
-					$sql = str_replace( $pattern, $newSelector, $sql );
8713
-				}
8714
-			}
8715
-
8716
-			$rows = $adapter->get( $sql, $bindings );
8717
-		} else {
8718
-			$rows = $sql;
8719
-		}
8720
-
8721
-		//Gather the bean data from the query results using the prefix
8722
-		$wannaBeans = array();
8723
-		foreach( $types as $type ) {
8724
-			$wannaBeans[$type] = array();
8725
-			$prefix            = "{$type}__";
8726
-			foreach( $rows as $rowkey=>$row ) {
8727
-				$wannaBean = array();
8728
-				foreach( $row as $cell => $value ) {
8729
-					if ( strpos( $cell, $prefix ) === 0 ) {
8730
-						$property = substr( $cell, strlen( $prefix ) );
8731
-						unset( $rows[$rowkey][$cell] );
8732
-						$wannaBean[$property] = $value;
8733
-					}
8734
-				}
8735
-				if ( !isset( $wannaBean['id'] ) ) continue;
8736
-				if ( is_null( $wannaBean['id'] ) ) continue;
8737
-				$wannaBeans[$type][$wannaBean['id']] = $wannaBean;
8738
-			}
8739
-		}
8740
-
8741
-		//Turn the rows into beans
8742
-		$beans = array();
8743
-		foreach( $wannaBeans as $type => $wannabees ) {
8744
-			$beans[$type] = $this->redbean->convertToBeans( $type, $wannabees );
8745
-		}
8746
-
8747
-		//Apply additional re-mappings
8748
-		foreach($remappings as $remapping) {
8749
-			$a       = $remapping['a'];
8750
-			$b       = $remapping['b'];
8751
-			$matcher = $remapping['matcher'];
8752
-			$do      = $remapping['do'];
8753
-			foreach( $beans[$a] as $bean ) {
8754
-				foreach( $beans[$b] as $putBean ) {
8755
-					if ( $matcher( $bean, $putBean, $beans ) ) $do( $bean, $putBean, $beans, $remapping );
8756
-				}
8757
-			}
8758
-		}
8759
-		return $beans;
8760
-	}
8408
+{
8409
+    /**
8410
+     * @var ToolBox
8411
+     */
8412
+    protected $toolbox;
8413
+
8414
+    /**
8415
+     * @var OODB
8416
+     */
8417
+    protected $redbean;
8418
+
8419
+    /**
8420
+     * Constructor.
8421
+     * The Finder requires a toolbox.
8422
+     *
8423
+     * @param ToolBox $toolbox
8424
+     */
8425
+    public function __construct( ToolBox $toolbox )
8426
+    {
8427
+        $this->toolbox = $toolbox;
8428
+        $this->redbean = $toolbox->getRedBean();
8429
+    }
8430
+
8431
+    /**
8432
+     * Finds a bean using a type and a where clause (SQL).
8433
+     * As with most Query tools in RedBean you can provide values to
8434
+     * be inserted in the SQL statement by populating the value
8435
+     * array parameter; you can either use the question mark notation
8436
+     * or the slot-notation (:keyname).
8437
+     *
8438
+     * @param string $type     type   the type of bean you are looking for
8439
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
8440
+     * @param array  $bindings values array of values to be bound to parameters in query
8441
+     *
8442
+     * @return array
8443
+     *
8444
+     * @throws Security
8445
+     */
8446
+    public function find( $type, $sql = NULL, $bindings = array() )
8447
+    {
8448
+        if ( !is_array( $bindings ) ) {
8449
+            throw new RedException(
8450
+                'Expected array, ' . gettype( $bindings ) . ' given.'
8451
+            );
8452
+        }
8453
+
8454
+        return $this->redbean->find( $type, array(), $sql, $bindings );
8455
+    }
8456
+
8457
+    /**
8458
+     * Like find() but also exports the beans as an array.
8459
+     *
8460
+     * @see Finder::findAndExport
8461
+     *
8462
+     * @param string $type     type   the type of bean you are looking for
8463
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
8464
+     * @param array  $bindings values array of values to be bound to parameters in query
8465
+     *
8466
+     * @return array
8467
+     */
8468
+    public function findAndExport( $type, $sql = NULL, $bindings = array() )
8469
+    {
8470
+        $arr = array();
8471
+        foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) {
8472
+            $arr[] = $item->export();
8473
+        }
8474
+
8475
+        return $arr;
8476
+    }
8477
+
8478
+    /**
8479
+     * Like find() but returns just one bean instead of an array of beans.
8480
+     * This method will return only the first bean of the array.
8481
+     *
8482
+     * @see Finder::find
8483
+     *
8484
+     * @param string $type     type   the type of bean you are looking for
8485
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
8486
+     * @param array  $bindings values array of values to be bound to parameters in query
8487
+     *
8488
+     * @return OODBBean
8489
+     */
8490
+    public function findOne( $type, $sql = NULL, $bindings = array() )
8491
+    {
8492
+        $sql = $this->toolbox->getWriter()->glueLimitOne( $sql );
8493
+		
8494
+        $items = $this->find( $type, $sql, $bindings );
8495
+
8496
+        if ( empty($items) ) {
8497
+            return NULL;
8498
+        }
8499
+
8500
+        return reset( $items );
8501
+    }
8502
+
8503
+    /**
8504
+     * Like find() but returns the last bean of the result array.
8505
+     * Opposite of Finder::findLast().
8506
+     *
8507
+     * @see Finder::find
8508
+     *
8509
+     * @param string $type     the type of bean you are looking for
8510
+     * @param string $sql      SQL query to find the desired bean, starting right after WHERE clause
8511
+     * @param array  $bindings values array of values to be bound to parameters in query
8512
+     *
8513
+     * @return OODBBean
8514
+     */
8515
+    public function findLast( $type, $sql = NULL, $bindings = array() )
8516
+    {
8517
+        $items = $this->find( $type, $sql, $bindings );
8518
+
8519
+        if ( empty($items) ) {
8520
+            return NULL;
8521
+        }
8522
+
8523
+        return end( $items );
8524
+    }
8525
+
8526
+    /**
8527
+     * Tries to find beans of a certain type,
8528
+     * if no beans are found, it dispenses a bean of that type.
8529
+     *
8530
+     * @see Finder::find
8531
+     *
8532
+     * @param  string $type     the type of bean you are looking for
8533
+     * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
8534
+     * @param  array  $bindings values array of values to be bound to parameters in query
8535
+     *
8536
+     * @return array
8537
+     */
8538
+    public function findOrDispense( $type, $sql = NULL, $bindings = array() )
8539
+    {
8540
+        $foundBeans = $this->find( $type, $sql, $bindings );
8541
+
8542
+        if ( empty( $foundBeans ) ) {
8543
+            return array( $this->redbean->dispense( $type ) );
8544
+        } else {
8545
+            return $foundBeans;
8546
+        }
8547
+    }
8548
+
8549
+    /**
8550
+     * Finds a BeanCollection using the repository.
8551
+     * A bean collection can be used to retrieve one bean at a time using
8552
+     * cursors - this is useful for processing large datasets. A bean collection
8553
+     * will not load all beans into memory all at once, just one at a time.
8554
+     *
8555
+     * @param  string $type     the type of bean you are looking for
8556
+     * @param  string $sql      SQL query to find the desired bean, starting right after WHERE clause
8557
+     * @param  array  $bindings values array of values to be bound to parameters in query
8558
+     *
8559
+     * @return BeanCollection
8560
+     */
8561
+    public function findCollection( $type, $sql, $bindings = array() )
8562
+    {
8563
+        return $this->redbean->findCollection( $type, $sql, $bindings );
8564
+    }
8565
+
8566
+    /**
8567
+     * Finds or creates a bean.
8568
+     * Tries to find a bean with certain properties specified in the second
8569
+     * parameter ($like). If the bean is found, it will be returned.
8570
+     * If multiple beans are found, only the first will be returned.
8571
+     * If no beans match the criteria, a new bean will be dispensed,
8572
+     * the criteria will be imported as properties and this new bean
8573
+     * will be stored and returned.
8574
+     *
8575
+     * Format of criteria set: property => value
8576
+     * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
8577
+     *
8578
+     * @param string $type type of bean to search for
8579
+     * @param array  $like criteria set describing bean to search for
8580
+     *
8581
+     * @return OODBBean
8582
+     */
8583
+    public function findOrCreate( $type, $like = array() )
8584
+    {
8585
+            $beans = $this->findLike( $type, $like );
8586
+            if ( count( $beans ) ) {
8587
+                $bean = reset( $beans );
8588
+                return $bean;
8589
+            }
8590
+
8591
+            $bean = $this->redbean->dispense( $type );
8592
+            $bean->import( $like );
8593
+            $this->redbean->store( $bean );
8594
+            return $bean;
8595
+    }
8596
+
8597
+    /**
8598
+     * Finds beans by its type and a certain criteria set.
8599
+     *
8600
+     * Format of criteria set: property => value
8601
+     * The criteria set also supports OR-conditions: property => array( value1, orValue2 )
8602
+     *
8603
+     * If the additional SQL is a condition, this condition will be glued to the rest
8604
+     * of the query using an AND operator. Note that this is as far as this method
8605
+     * can go, there is no way to glue additional SQL using an OR-condition.
8606
+     * This method provides access to an underlying mechanism in the RedBeanPHP architecture
8607
+     * to find beans using criteria sets. However, please do not use this method
8608
+     * for complex queries, use plain SQL instead ( the regular find method ) as it is
8609
+     * more suitable for the job. This method is
8610
+     * meant for basic search-by-example operations.
8611
+     *
8612
+     * @param string $type       type of bean to search for
8613
+     * @param array  $conditions criteria set describing the bean to search for
8614
+     * @param string $sql        additional SQL (for sorting)
8615
+     *
8616
+     * @return array
8617
+     */
8618
+    public function findLike( $type, $conditions = array(), $sql = '' )
8619
+    {
8620
+        if ( count( $conditions ) > 0 ) {
8621
+            foreach( $conditions as $key => $condition ) {
8622
+                if ( !count( $condition ) ) unset( $conditions[$key] );
8623
+            }
8624
+        }
8625
+
8626
+        return $this->redbean->find( $type, $conditions, $sql );
8627
+    }
8628
+
8629
+    /**
8630
+     * Returns a hashmap with bean arrays keyed by type using an SQL
8631
+     * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review'
8632
+     * this method will return movie and review beans.
8633
+     *
8634
+     * Example:
8635
+     *
8636
+     * $stuff = $finder->findMulti('movie,review', '
8637
+     *          SELECT movie.*, review.* FROM movie
8638
+     *          LEFT JOIN review ON review.movie_id = movie.id');
8639
+     *
8640
+     * After this operation, $stuff will contain an entry 'movie' containing all
8641
+     * movies and an entry named 'review' containing all reviews (all beans).
8642
+     * You can also pass bindings.
8643
+     *
8644
+     * If you want to re-map your beans, so you can use $movie->ownReviewList without
8645
+     * having RedBeanPHP executing an SQL query you can use the fourth parameter to
8646
+     * define a selection of remapping closures.
8647
+     *
8648
+     * The remapping argument (optional) should contain an array of arrays.
8649
+     * Each array in the remapping array should contain the following entries:
8650
+     *
8651
+     * array(
8652
+     * 	'a'       => TYPE A
8653
+     *    'b'       => TYPE B
8654
+     *    'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS
8655
+     *    'do'      => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS
8656
+     * )
8657
+     *
8658
+     * Using this mechanism you can build your own 'preloader' with tiny function
8659
+     * snippets (and those can be re-used and shared online of course).
8660
+     *
8661
+     * Example:
8662
+     *
8663
+     * array(
8664
+     * 	'a'       => 'movie'     //define A as movie
8665
+     *    'b'       => 'review'    //define B as review
8666
+     *    'matcher' => function( $a, $b ) {
8667
+     *       return ( $b->movie_id == $a->id );  //Perform action if review.movie_id equals movie.id
8668
+     *    }
8669
+     *    'do'      => function( $a, $b ) {
8670
+     *       $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie
8671
+     *       $a->clearHistory();                 //optional, act 'as if these beans have been loaded through ownReviewList'.
8672
+     *    }
8673
+     * )
8674
+     *
8675
+     * The Query Template parameter is optional as well but can be used to
8676
+     * set a different SQL template (sprintf-style) for processing the original query.
8677
+     *
8678
+     * @note the SQL query provided IS NOT THE ONE used internally by this function,
8679
+     * this function will pre-process the query to get all the data required to find the beans.
8680
+     *
8681
+     * @note if you use the 'book.*' notation make SURE you're
8682
+     * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because
8683
+     * it's actually an SQL-like template SLOT, not real SQL.
8684
+     *
8685
+     * @note instead of an SQL query you can pass a result array as well.
8686
+     *
8687
+     * @param string|array $types         a list of types (either array or comma separated string)
8688
+     * @param string|array $sqlOrArr      an SQL query or an array of prefetched records
8689
+     * @param array        $bindings      optional, bindings for SQL query
8690
+     * @param array        $remappings    optional, an array of remapping arrays
8691
+     * @param string       $queryTemplate optional, query template
8692
+     *
8693
+     * @return array
8694
+     */
8695
+    public function findMulti( $types, $sql, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s' )
8696
+    {
8697
+        if ( !is_array( $types ) ) $types = explode( ',', $types );
8698
+        if ( !is_array( $sql ) ) {
8699
+            $writer = $this->toolbox->getWriter();
8700
+            $adapter = $this->toolbox->getDatabaseAdapter();
8701
+
8702
+            //Repair the query, replace book.* with book.id AS book_id etc..
8703
+            foreach( $types as $type ) {
8704
+                $pattern = " {$type}.*";
8705
+                if ( strpos( $sql, $pattern ) !== FALSE ) {
8706
+                    $newSelectorArray = array();
8707
+                    $columns = $writer->getColumns( $type );
8708
+                    foreach( $columns as $column => $definition ) {
8709
+                        $newSelectorArray[] = sprintf( $queryTemplate, $type, $column, $type, $column );
8710
+                    }
8711
+                    $newSelector = implode( ',', $newSelectorArray );
8712
+                    $sql = str_replace( $pattern, $newSelector, $sql );
8713
+                }
8714
+            }
8715
+
8716
+            $rows = $adapter->get( $sql, $bindings );
8717
+        } else {
8718
+            $rows = $sql;
8719
+        }
8720
+
8721
+        //Gather the bean data from the query results using the prefix
8722
+        $wannaBeans = array();
8723
+        foreach( $types as $type ) {
8724
+            $wannaBeans[$type] = array();
8725
+            $prefix            = "{$type}__";
8726
+            foreach( $rows as $rowkey=>$row ) {
8727
+                $wannaBean = array();
8728
+                foreach( $row as $cell => $value ) {
8729
+                    if ( strpos( $cell, $prefix ) === 0 ) {
8730
+                        $property = substr( $cell, strlen( $prefix ) );
8731
+                        unset( $rows[$rowkey][$cell] );
8732
+                        $wannaBean[$property] = $value;
8733
+                    }
8734
+                }
8735
+                if ( !isset( $wannaBean['id'] ) ) continue;
8736
+                if ( is_null( $wannaBean['id'] ) ) continue;
8737
+                $wannaBeans[$type][$wannaBean['id']] = $wannaBean;
8738
+            }
8739
+        }
8740
+
8741
+        //Turn the rows into beans
8742
+        $beans = array();
8743
+        foreach( $wannaBeans as $type => $wannabees ) {
8744
+            $beans[$type] = $this->redbean->convertToBeans( $type, $wannabees );
8745
+        }
8746
+
8747
+        //Apply additional re-mappings
8748
+        foreach($remappings as $remapping) {
8749
+            $a       = $remapping['a'];
8750
+            $b       = $remapping['b'];
8751
+            $matcher = $remapping['matcher'];
8752
+            $do      = $remapping['do'];
8753
+            foreach( $beans[$a] as $bean ) {
8754
+                foreach( $beans[$b] as $putBean ) {
8755
+                    if ( $matcher( $bean, $putBean, $beans ) ) $do( $bean, $putBean, $beans, $remapping );
8756
+                }
8757
+            }
8758
+        }
8759
+        return $beans;
8760
+    }
8761 8761
 }
8762 8762
 }
8763 8763
 
@@ -8788,340 +8788,340 @@  discard block
 block discarded – undo
8788 8788
  */
8789 8789
 class AssociationManager extends Observable
8790 8790
 {
8791
-	/**
8792
-	 * @var OODB
8793
-	 */
8794
-	protected $oodb;
8795
-
8796
-	/**
8797
-	 * @var DBAdapter
8798
-	 */
8799
-	protected $adapter;
8800
-
8801
-	/**
8802
-	 * @var QueryWriter
8803
-	 */
8804
-	protected $writer;
8805
-
8806
-	/**
8807
-	 * Handles\Exceptions. Suppresses exceptions caused by missing structures.
8808
-	 *
8809
-	 * @param\Exception $exception
8810
-	 *
8811
-	 * @return void
8812
-	 *
8813
-	 * @throws\Exception
8814
-	 */
8815
-	private function handleException(\Exception $exception )
8816
-	{
8817
-		if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(),
8818
-			array(
8819
-				QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
8820
-				QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN )
8821
-			)
8822
-		) {
8823
-			throw $exception;
8824
-		}
8825
-	}
8826
-
8827
-	/**
8828
-	 * Internal method.
8829
-	 * Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and
8830
-	 * $bindings bindings. If $getLinks is TRUE, link rows are returned instead.
8831
-	 *
8832
-	 * @param OODBBean $bean     reference bean
8833
-	 * @param string           $type     target type
8834
-	 * @param string           $sql      additional SQL snippet
8835
-	 * @param array            $bindings bindings
8836
-	 *
8837
-	 * @return array
8838
-	 *
8839
-	 * @throws Security
8840
-	 * @throws SQL
8841
-	 */
8842
-	private function relatedRows( $bean, $type, $sql = '', $bindings = array() )
8843
-	{
8844
-		$ids = array( $bean->id );
8845
-		$sourceType = $bean->getMeta( 'type' );
8846
-		try {
8847
-			return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings );
8848
-		} catch ( SQLException $exception ) {
8849
-			$this->handleException( $exception );
8850
-			return array();
8851
-		}
8852
-	}
8853
-
8854
-	/**
8855
-	 * Associates a pair of beans. This method associates two beans, no matter
8856
-	 * what types. Accepts a base bean that contains data for the linking record.
8857
-	 * This method is used by associate. This method also accepts a base bean to be used
8858
-	 * as the template for the link record in the database.
8859
-	 *
8860
-	 * @param OODBBean $bean1 first bean
8861
-	 * @param OODBBean $bean2 second bean
8862
-	 * @param OODBBean $bean  base bean (association record)
8863
-	 *
8864
-	 * @throws\Exception|SQL
8865
-	 *
8866
-	 * @return mixed
8867
-	 */
8868
-	protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean )
8869
-	{
8870
-		$type      = $bean->getMeta( 'type' );
8871
-		$property1 = $bean1->getMeta( 'type' ) . '_id';
8872
-		$property2 = $bean2->getMeta( 'type' ) . '_id';
8873
-
8874
-		if ( $property1 == $property2 ) {
8875
-			$property2 = $bean2->getMeta( 'type' ) . '2_id';
8876
-		}
8877
-
8878
-		$this->oodb->store( $bean1 );
8879
-		$this->oodb->store( $bean2 );
8880
-
8881
-		$bean->setMeta( "cast.$property1", "id" );
8882
-		$bean->setMeta( "cast.$property2", "id" );
8883
-		$bean->setMeta( 'sys.buildcommand.unique', array( $property1, $property2 ) );
8884
-
8885
-		$bean->$property1 = $bean1->id;
8886
-		$bean->$property2 = $bean2->id;
8887
-
8888
-		$results   = array();
8889
-
8890
-		try {
8891
-			$id = $this->oodb->store( $bean );
8892
-			$results[] = $id;
8893
-		} catch ( SQLException $exception ) {
8894
-			if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
8895
-				array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ) )
8896
-			) {
8897
-				throw $exception;
8898
-			}
8899
-		}
8900
-
8901
-		return $results;
8902
-	}
8903
-
8904
-	/**
8905
-	 * Constructor
8906
-	 *
8907
-	 * @param ToolBox $tools toolbox
8908
-	 */
8909
-	public function __construct( ToolBox $tools )
8910
-	{
8911
-		$this->oodb    = $tools->getRedBean();
8912
-		$this->adapter = $tools->getDatabaseAdapter();
8913
-		$this->writer  = $tools->getWriter();
8914
-		$this->toolbox = $tools;
8915
-	}
8916
-
8917
-	/**
8918
-	 * Creates a table name based on a types array.
8919
-	 * Manages the get the correct name for the linking table for the
8920
-	 * types provided.
8921
-	 *
8922
-	 * @todo find a nice way to decouple this class from QueryWriter?
8923
-	 *
8924
-	 * @param array $types 2 types as strings
8925
-	 *
8926
-	 * @return string
8927
-	 */
8928
-	public function getTable( $types )
8929
-	{
8930
-		return $this->writer->getAssocTable( $types );
8931
-	}
8932
-
8933
-	/**
8934
-	 * Associates two beans in a many-to-many relation.
8935
-	 * This method will associate two beans and store the connection between the
8936
-	 * two in a link table. Instead of two single beans this method also accepts
8937
-	 * two sets of beans. Returns the ID or the IDs of the linking beans.
8938
-	 * 
8939
-	 * @param OODBBean|array $beans1 one or more beans to form the association
8940
-	 * @param OODBBean|array $beans2 one or more beans to form the association
8941
-	 *
8942
-	 * @return array
8943
-	 */
8944
-	public function associate( $beans1, $beans2 )
8945
-	{
8946
-		if ( !is_array( $beans1 ) ) {
8947
-			$beans1 = array( $beans1 );
8948
-		}
8949
-
8950
-		if ( !is_array( $beans2 ) ) {
8951
-			$beans2 = array( $beans2 );
8952
-		}
8953
-
8954
-		$results = array();
8955
-		foreach ( $beans1 as $bean1 ) {
8956
-			foreach ( $beans2 as $bean2 ) {
8957
-				$table     = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) );
8958
-				$bean      = $this->oodb->dispense( $table );
8959
-				$results[] = $this->associateBeans( $bean1, $bean2, $bean );
8960
-			}
8961
-		}
8962
-
8963
-		return ( count( $results ) > 1 ) ? $results : reset( $results );
8964
-	}
8965
-
8966
-	/**
8967
-	 * Counts the number of related beans in an N-M relation.
8968
-	 * This method returns the number of beans of type $type associated
8969
-	 * with reference bean(s) $bean. The query can be tuned using an
8970
-	 * SQL snippet for additional filtering.
8971
-	 *
8972
-	 * @param OODBBean|array $bean     a bean object or an array of beans
8973
-	 * @param string                 $type     type of bean you're interested in
8974
-	 * @param string                 $sql      SQL snippet (optional)
8975
-	 * @param array                  $bindings bindings for your SQL string
8976
-	 *
8977
-	 * @return integer
8978
-	 *
8979
-	 * @throws Security
8980
-	 */
8981
-	public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() )
8982
-	{
8983
-		if ( !( $bean instanceof OODBBean ) ) {
8984
-			throw new RedException(
8985
-				'Expected array or OODBBean but got:' . gettype( $bean )
8986
-			);
8987
-		}
8988
-
8989
-		if ( !$bean->id ) {
8990
-			return 0;
8991
-		}
8992
-
8993
-		$beanType = $bean->getMeta( 'type' );
8994
-
8995
-		try {
8996
-			return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings );
8997
-		} catch ( SQLException $exception ) {
8998
-			$this->handleException( $exception );
8999
-
9000
-			return 0;
9001
-		}
9002
-	}
9003
-
9004
-	/**
9005
-	 * Breaks the association between two beans. This method unassociates two beans. If the
9006
-	 * method succeeds the beans will no longer form an association. In the database
9007
-	 * this means that the association record will be removed. This method uses the
9008
-	 * OODB trash() method to remove the association links, thus giving FUSE models the
9009
-	 * opportunity to hook-in additional business logic. If the $fast parameter is
9010
-	 * set to boolean TRUE this method will remove the beans without their consent,
9011
-	 * bypassing FUSE. This can be used to improve performance.
9012
-	 *
9013
-	 * @param OODBBean $bean1 first bean
9014
-	 * @param OODBBean $bean2 second bean
9015
-	 * @param boolean          $fast  If TRUE, removes the entries by query without FUSE
9016
-	 *
9017
-	 * @return void
9018
-	 */
9019
-	public function unassociate( $beans1, $beans2, $fast = NULL )
9020
-	{
9021
-		$beans1 = ( !is_array( $beans1 ) ) ? array( $beans1 ) : $beans1;
9022
-		$beans2 = ( !is_array( $beans2 ) ) ? array( $beans2 ) : $beans2;
9023
-
9024
-		foreach ( $beans1 as $bean1 ) {
9025
-			foreach ( $beans2 as $bean2 ) {
9026
-				try {
9027
-					$this->oodb->store( $bean1 );
9028
-					$this->oodb->store( $bean2 );
9029
-
9030
-					$type1 = $bean1->getMeta( 'type' );
9031
-					$type2 = $bean2->getMeta( 'type' );
9032
-
9033
-					$row      = $this->writer->queryRecordLink( $type1, $type2, $bean1->id, $bean2->id );
9034
-					$linkType = $this->getTable( array( $type1, $type2 ) );
9035
-
9036
-					if ( $fast ) {
9037
-						$this->writer->deleteRecord( $linkType, array( 'id' => $row['id'] ) );
9038
-
9039
-						return;
9040
-					}
9041
-
9042
-					$beans = $this->oodb->convertToBeans( $linkType, array( $row ) );
9043
-
9044
-					if ( count( $beans ) > 0 ) {
9045
-						$bean = reset( $beans );
9046
-						$this->oodb->trash( $bean );
9047
-					}
9048
-				} catch ( SQLException $exception ) {
9049
-					$this->handleException( $exception );
9050
-				}
9051
-			}
9052
-		}
9053
-	}
9054
-
9055
-	/**
9056
-	 * Removes all relations for a bean. This method breaks every connection between
9057
-	 * a certain bean $bean and every other bean of type $type. Warning: this method
9058
-	 * is really fast because it uses a direct SQL query however it does not inform the
9059
-	 * models about this. If you want to notify FUSE models about deletion use a foreach-loop
9060
-	 * with unassociate() instead. (that might be slower though)
9061
-	 *
9062
-	 * @param OODBBean $bean reference bean
9063
-	 * @param string           $type type of beans that need to be unassociated
9064
-	 *
9065
-	 * @return void
9066
-	 */
9067
-	public function clearRelations( OODBBean $bean, $type )
9068
-	{
9069
-		$this->oodb->store( $bean );
9070
-		try {
9071
-			$this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id );
9072
-		} catch ( SQLException $exception ) {
9073
-			$this->handleException( $exception );
9074
-		}
9075
-	}
9076
-
9077
-	/**
9078
-	 * Returns all the beans associated with $bean.
9079
-	 * This method will return an array containing all the beans that have
9080
-	 * been associated once with the associate() function and are still
9081
-	 * associated with the bean specified. The type parameter indicates the
9082
-	 * type of beans you are looking for. You can also pass some extra SQL and
9083
-	 * values for that SQL to filter your results after fetching the
9084
-	 * related beans.
9085
-	 *
9086
-	 * Don't try to make use of subqueries, a subquery using IN() seems to
9087
-	 * be slower than two queries!
9088
-	 *
9089
-	 * Since 3.2, you can now also pass an array of beans instead just one
9090
-	 * bean as the first parameter.
9091
-	 *
9092
-	 * @param OODBBean|array $bean      the bean you have
9093
-	 * @param string                 $type      the type of beans you want
9094
-	 * @param string                 $sql       SQL snippet for extra filtering
9095
-	 * @param array                  $bindings  values to be inserted in SQL slots
9096
-	 * @param boolean                $glue      whether the SQL should be prefixed with WHERE
9097
-	 *
9098
-	 * @return array
9099
-	 */
9100
-	public function related( $bean, $type, $sql = '', $bindings = array() )
9101
-	{
9102
-		$sql   = $this->writer->glueSQLCondition( $sql );
9103
-
9104
-		$rows  = $this->relatedRows( $bean, $type, $sql, $bindings );
9105
-
9106
-		$links = array();
9107
-		foreach ( $rows as $key => $row ) {
9108
-			if ( !isset( $links[$row['id']] ) ) {
9109
-				$links[$row['id']] = array();
9110
-			}
9111
-
9112
-			$links[$row['id']][] = $row['linked_by'];
9113
-
9114
-			unset( $rows[$key]['linked_by'] );
9115
-		}
9116
-
9117
-		$beans = $this->oodb->convertToBeans( $type, $rows );
9118
-
9119
-		foreach ( $beans as $bean ) {
9120
-			$bean->setMeta( 'sys.belongs-to', $links[$bean->id] );
9121
-		}
9122
-
9123
-		return $beans;
9124
-	}
8791
+    /**
8792
+     * @var OODB
8793
+     */
8794
+    protected $oodb;
8795
+
8796
+    /**
8797
+     * @var DBAdapter
8798
+     */
8799
+    protected $adapter;
8800
+
8801
+    /**
8802
+     * @var QueryWriter
8803
+     */
8804
+    protected $writer;
8805
+
8806
+    /**
8807
+     * Handles\Exceptions. Suppresses exceptions caused by missing structures.
8808
+     *
8809
+     * @param\Exception $exception
8810
+     *
8811
+     * @return void
8812
+     *
8813
+     * @throws\Exception
8814
+     */
8815
+    private function handleException(\Exception $exception )
8816
+    {
8817
+        if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(),
8818
+            array(
8819
+                QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
8820
+                QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN )
8821
+            )
8822
+        ) {
8823
+            throw $exception;
8824
+        }
8825
+    }
8826
+
8827
+    /**
8828
+     * Internal method.
8829
+     * Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and
8830
+     * $bindings bindings. If $getLinks is TRUE, link rows are returned instead.
8831
+     *
8832
+     * @param OODBBean $bean     reference bean
8833
+     * @param string           $type     target type
8834
+     * @param string           $sql      additional SQL snippet
8835
+     * @param array            $bindings bindings
8836
+     *
8837
+     * @return array
8838
+     *
8839
+     * @throws Security
8840
+     * @throws SQL
8841
+     */
8842
+    private function relatedRows( $bean, $type, $sql = '', $bindings = array() )
8843
+    {
8844
+        $ids = array( $bean->id );
8845
+        $sourceType = $bean->getMeta( 'type' );
8846
+        try {
8847
+            return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings );
8848
+        } catch ( SQLException $exception ) {
8849
+            $this->handleException( $exception );
8850
+            return array();
8851
+        }
8852
+    }
8853
+
8854
+    /**
8855
+     * Associates a pair of beans. This method associates two beans, no matter
8856
+     * what types. Accepts a base bean that contains data for the linking record.
8857
+     * This method is used by associate. This method also accepts a base bean to be used
8858
+     * as the template for the link record in the database.
8859
+     *
8860
+     * @param OODBBean $bean1 first bean
8861
+     * @param OODBBean $bean2 second bean
8862
+     * @param OODBBean $bean  base bean (association record)
8863
+     *
8864
+     * @throws\Exception|SQL
8865
+     *
8866
+     * @return mixed
8867
+     */
8868
+    protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean )
8869
+    {
8870
+        $type      = $bean->getMeta( 'type' );
8871
+        $property1 = $bean1->getMeta( 'type' ) . '_id';
8872
+        $property2 = $bean2->getMeta( 'type' ) . '_id';
8873
+
8874
+        if ( $property1 == $property2 ) {
8875
+            $property2 = $bean2->getMeta( 'type' ) . '2_id';
8876
+        }
8877
+
8878
+        $this->oodb->store( $bean1 );
8879
+        $this->oodb->store( $bean2 );
8880
+
8881
+        $bean->setMeta( "cast.$property1", "id" );
8882
+        $bean->setMeta( "cast.$property2", "id" );
8883
+        $bean->setMeta( 'sys.buildcommand.unique', array( $property1, $property2 ) );
8884
+
8885
+        $bean->$property1 = $bean1->id;
8886
+        $bean->$property2 = $bean2->id;
8887
+
8888
+        $results   = array();
8889
+
8890
+        try {
8891
+            $id = $this->oodb->store( $bean );
8892
+            $results[] = $id;
8893
+        } catch ( SQLException $exception ) {
8894
+            if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
8895
+                array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ) )
8896
+            ) {
8897
+                throw $exception;
8898
+            }
8899
+        }
8900
+
8901
+        return $results;
8902
+    }
8903
+
8904
+    /**
8905
+     * Constructor
8906
+     *
8907
+     * @param ToolBox $tools toolbox
8908
+     */
8909
+    public function __construct( ToolBox $tools )
8910
+    {
8911
+        $this->oodb    = $tools->getRedBean();
8912
+        $this->adapter = $tools->getDatabaseAdapter();
8913
+        $this->writer  = $tools->getWriter();
8914
+        $this->toolbox = $tools;
8915
+    }
8916
+
8917
+    /**
8918
+     * Creates a table name based on a types array.
8919
+     * Manages the get the correct name for the linking table for the
8920
+     * types provided.
8921
+     *
8922
+     * @todo find a nice way to decouple this class from QueryWriter?
8923
+     *
8924
+     * @param array $types 2 types as strings
8925
+     *
8926
+     * @return string
8927
+     */
8928
+    public function getTable( $types )
8929
+    {
8930
+        return $this->writer->getAssocTable( $types );
8931
+    }
8932
+
8933
+    /**
8934
+     * Associates two beans in a many-to-many relation.
8935
+     * This method will associate two beans and store the connection between the
8936
+     * two in a link table. Instead of two single beans this method also accepts
8937
+     * two sets of beans. Returns the ID or the IDs of the linking beans.
8938
+     * 
8939
+     * @param OODBBean|array $beans1 one or more beans to form the association
8940
+     * @param OODBBean|array $beans2 one or more beans to form the association
8941
+     *
8942
+     * @return array
8943
+     */
8944
+    public function associate( $beans1, $beans2 )
8945
+    {
8946
+        if ( !is_array( $beans1 ) ) {
8947
+            $beans1 = array( $beans1 );
8948
+        }
8949
+
8950
+        if ( !is_array( $beans2 ) ) {
8951
+            $beans2 = array( $beans2 );
8952
+        }
8953
+
8954
+        $results = array();
8955
+        foreach ( $beans1 as $bean1 ) {
8956
+            foreach ( $beans2 as $bean2 ) {
8957
+                $table     = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) );
8958
+                $bean      = $this->oodb->dispense( $table );
8959
+                $results[] = $this->associateBeans( $bean1, $bean2, $bean );
8960
+            }
8961
+        }
8962
+
8963
+        return ( count( $results ) > 1 ) ? $results : reset( $results );
8964
+    }
8965
+
8966
+    /**
8967
+     * Counts the number of related beans in an N-M relation.
8968
+     * This method returns the number of beans of type $type associated
8969
+     * with reference bean(s) $bean. The query can be tuned using an
8970
+     * SQL snippet for additional filtering.
8971
+     *
8972
+     * @param OODBBean|array $bean     a bean object or an array of beans
8973
+     * @param string                 $type     type of bean you're interested in
8974
+     * @param string                 $sql      SQL snippet (optional)
8975
+     * @param array                  $bindings bindings for your SQL string
8976
+     *
8977
+     * @return integer
8978
+     *
8979
+     * @throws Security
8980
+     */
8981
+    public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() )
8982
+    {
8983
+        if ( !( $bean instanceof OODBBean ) ) {
8984
+            throw new RedException(
8985
+                'Expected array or OODBBean but got:' . gettype( $bean )
8986
+            );
8987
+        }
8988
+
8989
+        if ( !$bean->id ) {
8990
+            return 0;
8991
+        }
8992
+
8993
+        $beanType = $bean->getMeta( 'type' );
8994
+
8995
+        try {
8996
+            return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings );
8997
+        } catch ( SQLException $exception ) {
8998
+            $this->handleException( $exception );
8999
+
9000
+            return 0;
9001
+        }
9002
+    }
9003
+
9004
+    /**
9005
+     * Breaks the association between two beans. This method unassociates two beans. If the
9006
+     * method succeeds the beans will no longer form an association. In the database
9007
+     * this means that the association record will be removed. This method uses the
9008
+     * OODB trash() method to remove the association links, thus giving FUSE models the
9009
+     * opportunity to hook-in additional business logic. If the $fast parameter is
9010
+     * set to boolean TRUE this method will remove the beans without their consent,
9011
+     * bypassing FUSE. This can be used to improve performance.
9012
+     *
9013
+     * @param OODBBean $bean1 first bean
9014
+     * @param OODBBean $bean2 second bean
9015
+     * @param boolean          $fast  If TRUE, removes the entries by query without FUSE
9016
+     *
9017
+     * @return void
9018
+     */
9019
+    public function unassociate( $beans1, $beans2, $fast = NULL )
9020
+    {
9021
+        $beans1 = ( !is_array( $beans1 ) ) ? array( $beans1 ) : $beans1;
9022
+        $beans2 = ( !is_array( $beans2 ) ) ? array( $beans2 ) : $beans2;
9023
+
9024
+        foreach ( $beans1 as $bean1 ) {
9025
+            foreach ( $beans2 as $bean2 ) {
9026
+                try {
9027
+                    $this->oodb->store( $bean1 );
9028
+                    $this->oodb->store( $bean2 );
9029
+
9030
+                    $type1 = $bean1->getMeta( 'type' );
9031
+                    $type2 = $bean2->getMeta( 'type' );
9032
+
9033
+                    $row      = $this->writer->queryRecordLink( $type1, $type2, $bean1->id, $bean2->id );
9034
+                    $linkType = $this->getTable( array( $type1, $type2 ) );
9035
+
9036
+                    if ( $fast ) {
9037
+                        $this->writer->deleteRecord( $linkType, array( 'id' => $row['id'] ) );
9038
+
9039
+                        return;
9040
+                    }
9041
+
9042
+                    $beans = $this->oodb->convertToBeans( $linkType, array( $row ) );
9043
+
9044
+                    if ( count( $beans ) > 0 ) {
9045
+                        $bean = reset( $beans );
9046
+                        $this->oodb->trash( $bean );
9047
+                    }
9048
+                } catch ( SQLException $exception ) {
9049
+                    $this->handleException( $exception );
9050
+                }
9051
+            }
9052
+        }
9053
+    }
9054
+
9055
+    /**
9056
+     * Removes all relations for a bean. This method breaks every connection between
9057
+     * a certain bean $bean and every other bean of type $type. Warning: this method
9058
+     * is really fast because it uses a direct SQL query however it does not inform the
9059
+     * models about this. If you want to notify FUSE models about deletion use a foreach-loop
9060
+     * with unassociate() instead. (that might be slower though)
9061
+     *
9062
+     * @param OODBBean $bean reference bean
9063
+     * @param string           $type type of beans that need to be unassociated
9064
+     *
9065
+     * @return void
9066
+     */
9067
+    public function clearRelations( OODBBean $bean, $type )
9068
+    {
9069
+        $this->oodb->store( $bean );
9070
+        try {
9071
+            $this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id );
9072
+        } catch ( SQLException $exception ) {
9073
+            $this->handleException( $exception );
9074
+        }
9075
+    }
9076
+
9077
+    /**
9078
+     * Returns all the beans associated with $bean.
9079
+     * This method will return an array containing all the beans that have
9080
+     * been associated once with the associate() function and are still
9081
+     * associated with the bean specified. The type parameter indicates the
9082
+     * type of beans you are looking for. You can also pass some extra SQL and
9083
+     * values for that SQL to filter your results after fetching the
9084
+     * related beans.
9085
+     *
9086
+     * Don't try to make use of subqueries, a subquery using IN() seems to
9087
+     * be slower than two queries!
9088
+     *
9089
+     * Since 3.2, you can now also pass an array of beans instead just one
9090
+     * bean as the first parameter.
9091
+     *
9092
+     * @param OODBBean|array $bean      the bean you have
9093
+     * @param string                 $type      the type of beans you want
9094
+     * @param string                 $sql       SQL snippet for extra filtering
9095
+     * @param array                  $bindings  values to be inserted in SQL slots
9096
+     * @param boolean                $glue      whether the SQL should be prefixed with WHERE
9097
+     *
9098
+     * @return array
9099
+     */
9100
+    public function related( $bean, $type, $sql = '', $bindings = array() )
9101
+    {
9102
+        $sql   = $this->writer->glueSQLCondition( $sql );
9103
+
9104
+        $rows  = $this->relatedRows( $bean, $type, $sql, $bindings );
9105
+
9106
+        $links = array();
9107
+        foreach ( $rows as $key => $row ) {
9108
+            if ( !isset( $links[$row['id']] ) ) {
9109
+                $links[$row['id']] = array();
9110
+            }
9111
+
9112
+            $links[$row['id']][] = $row['linked_by'];
9113
+
9114
+            unset( $rows[$key]['linked_by'] );
9115
+        }
9116
+
9117
+        $beans = $this->oodb->convertToBeans( $type, $rows );
9118
+
9119
+        foreach ( $beans as $bean ) {
9120
+            $bean->setMeta( 'sys.belongs-to', $links[$bean->id] );
9121
+        }
9122
+
9123
+        return $beans;
9124
+    }
9125 9125
 }
9126 9126
 }
9127 9127
 
@@ -9147,35 +9147,35 @@  discard block
 block discarded – undo
9147 9147
  */
9148 9148
 interface BeanHelper
9149 9149
 {
9150
-	/**
9151
-	 * Returns a toolbox to empower the bean.
9152
-	 * This allows beans to perform OODB operations by themselves,
9153
-	 * as such the bean is a proxy for OODB. This allows beans to implement
9154
-	 * their magic getters and setters and return lists.
9155
-	 *
9156
-	 * @return ToolBox $toolbox toolbox
9157
-	 */
9158
-	public function getToolbox();
9159
-
9160
-	/**
9161
-	 * Does approximately the same as getToolbox but also extracts the
9162
-	 * toolbox for you.
9163
-	 * This method returns a list with all toolbox items in Toolbox Constructor order:
9164
-	 * OODB, adapter, writer and finally the toolbox itself!.
9165
-	 *
9166
-	 * @return array
9167
-	 */
9168
-	public function getExtractedToolbox();
9169
-
9170
-	/**
9171
-	 * Given a certain bean this method will
9172
-	 * return the corresponding model.
9173
-	 *
9174
-	 * @param OODBBean $bean
9175
-	 *
9176
-	 * @return string
9177
-	 */
9178
-	public function getModelForBean( OODBBean $bean );
9150
+    /**
9151
+     * Returns a toolbox to empower the bean.
9152
+     * This allows beans to perform OODB operations by themselves,
9153
+     * as such the bean is a proxy for OODB. This allows beans to implement
9154
+     * their magic getters and setters and return lists.
9155
+     *
9156
+     * @return ToolBox $toolbox toolbox
9157
+     */
9158
+    public function getToolbox();
9159
+
9160
+    /**
9161
+     * Does approximately the same as getToolbox but also extracts the
9162
+     * toolbox for you.
9163
+     * This method returns a list with all toolbox items in Toolbox Constructor order:
9164
+     * OODB, adapter, writer and finally the toolbox itself!.
9165
+     *
9166
+     * @return array
9167
+     */
9168
+    public function getExtractedToolbox();
9169
+
9170
+    /**
9171
+     * Given a certain bean this method will
9172
+     * return the corresponding model.
9173
+     *
9174
+     * @param OODBBean $bean
9175
+     *
9176
+     * @return string
9177
+     */
9178
+    public function getModelForBean( OODBBean $bean );
9179 9179
 }
9180 9180
 }
9181 9181
 
@@ -9204,93 +9204,93 @@  discard block
 block discarded – undo
9204 9204
  */
9205 9205
 class SimpleFacadeBeanHelper implements BeanHelper
9206 9206
 {
9207
-	/**
9208
-	 * Factory function to create instance of Simple Model, if any.
9209
-	 *
9210
-	 * @var closure
9211
-	 */
9212
-	private static $factory = null;
9213
-
9214
-	/**
9215
-	 * Factory method using a customizable factory function to create
9216
-	 * the instance of the Simple Model.
9217
-	 *
9218
-	 * @param string $modelClassName name of the class
9219
-	 *
9220
-	 * @return SimpleModel
9221
-	 */
9222
-	public static function factory( $modelClassName )
9223
-	{
9224
-		$factory = self::$factory;
9225
-		return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName();
9226
-	}
9227
-
9228
-	/**
9229
-	 * Sets the factory function to create the model when using FUSE
9230
-	 * to connect a bean to a model.
9231
-	 *
9232
-	 * @param closure $factory
9233
-	 *
9234
-	 * @return void
9235
-	 */
9236
-	public static function setFactoryFunction( $factory ) 
9237
-	{
9238
-		self::$factory = $factory;
9239
-	}
9240
-
9241
-	/**
9242
-	 * @see BeanHelper::getToolbox
9243
-	 */
9244
-	public function getToolbox()
9245
-	{
9246
-		return Facade::getToolBox();
9247
-	}
9248
-
9249
-	/**
9250
-	 * @see BeanHelper::getModelForBean
9251
-	 */
9252
-	public function getModelForBean( OODBBean $bean )
9253
-	{
9254
-		$model     = $bean->getMeta( 'type' );
9255
-		$prefix    = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_';
9256
-
9257
-		if ( strpos( $model, '_' ) !== FALSE ) {
9258
-			$modelParts = explode( '_', $model );
9259
-			$modelName = '';
9260
-			foreach( $modelParts as $part ) {
9261
-				$modelName .= ucfirst( $part );
9262
-			}
9263
-			$modelName = $prefix . $modelName;
9264
-
9265
-			if ( !class_exists( $modelName ) ) {
9266
-				//second try
9267
-				$modelName = $prefix . ucfirst( $model );
9207
+    /**
9208
+     * Factory function to create instance of Simple Model, if any.
9209
+     *
9210
+     * @var closure
9211
+     */
9212
+    private static $factory = null;
9213
+
9214
+    /**
9215
+     * Factory method using a customizable factory function to create
9216
+     * the instance of the Simple Model.
9217
+     *
9218
+     * @param string $modelClassName name of the class
9219
+     *
9220
+     * @return SimpleModel
9221
+     */
9222
+    public static function factory( $modelClassName )
9223
+    {
9224
+        $factory = self::$factory;
9225
+        return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName();
9226
+    }
9227
+
9228
+    /**
9229
+     * Sets the factory function to create the model when using FUSE
9230
+     * to connect a bean to a model.
9231
+     *
9232
+     * @param closure $factory
9233
+     *
9234
+     * @return void
9235
+     */
9236
+    public static function setFactoryFunction( $factory ) 
9237
+    {
9238
+        self::$factory = $factory;
9239
+    }
9240
+
9241
+    /**
9242
+     * @see BeanHelper::getToolbox
9243
+     */
9244
+    public function getToolbox()
9245
+    {
9246
+        return Facade::getToolBox();
9247
+    }
9248
+
9249
+    /**
9250
+     * @see BeanHelper::getModelForBean
9251
+     */
9252
+    public function getModelForBean( OODBBean $bean )
9253
+    {
9254
+        $model     = $bean->getMeta( 'type' );
9255
+        $prefix    = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_';
9256
+
9257
+        if ( strpos( $model, '_' ) !== FALSE ) {
9258
+            $modelParts = explode( '_', $model );
9259
+            $modelName = '';
9260
+            foreach( $modelParts as $part ) {
9261
+                $modelName .= ucfirst( $part );
9262
+            }
9263
+            $modelName = $prefix . $modelName;
9264
+
9265
+            if ( !class_exists( $modelName ) ) {
9266
+                //second try
9267
+                $modelName = $prefix . ucfirst( $model );
9268 9268
 				
9269
-				if ( !class_exists( $modelName ) ) {
9270
-					return NULL;
9271
-				}
9272
-			}
9273
-
9274
-		} else {
9275
-
9276
-			$modelName = $prefix . ucfirst( $model );
9277
-			if ( !class_exists( $modelName ) ) {
9278
-				return NULL;
9279
-			}
9280
-		}
9281
-		$obj = self::factory( $modelName );
9282
-		$obj->loadBean( $bean );
9283
-
9284
-		return $obj;
9285
-	}
9286
-
9287
-	/**
9288
-	 * @see BeanHelper::getExtractedToolbox
9289
-	 */
9290
-	public function getExtractedToolbox()
9291
-	{
9292
-		return Facade::getExtractedToolbox();
9293
-	}
9269
+                if ( !class_exists( $modelName ) ) {
9270
+                    return NULL;
9271
+                }
9272
+            }
9273
+
9274
+        } else {
9275
+
9276
+            $modelName = $prefix . ucfirst( $model );
9277
+            if ( !class_exists( $modelName ) ) {
9278
+                return NULL;
9279
+            }
9280
+        }
9281
+        $obj = self::factory( $modelName );
9282
+        $obj->loadBean( $bean );
9283
+
9284
+        return $obj;
9285
+    }
9286
+
9287
+    /**
9288
+     * @see BeanHelper::getExtractedToolbox
9289
+     */
9290
+    public function getExtractedToolbox()
9291
+    {
9292
+        return Facade::getExtractedToolbox();
9293
+    }
9294 9294
 }
9295 9295
 } 
9296 9296
 
@@ -9317,93 +9317,93 @@  discard block
 block discarded – undo
9317 9317
  */
9318 9318
 class SimpleModel
9319 9319
 {
9320
-	/**
9321
-	 * @var OODBBean
9322
-	 */
9323
-	protected $bean;
9324
-
9325
-	/**
9326
-	 * Used by FUSE: the ModelHelper class to connect a bean to a model.
9327
-	 * This method loads a bean in the model.
9328
-	 *
9329
-	 * @param OODBBean $bean bean
9330
-	 *
9331
-	 * @return void
9332
-	 */
9333
-	public function loadBean( OODBBean $bean )
9334
-	{
9335
-		$this->bean = $bean;
9336
-	}
9337
-
9338
-	/**
9339
-	 * Magic Getter to make the bean properties available from
9340
-	 * the $this-scope.
9341
-	 *
9342
-	 * @note this method returns a value, not a reference!
9343
-	 *       To obtain a reference unbox the bean first!
9344
-	 *
9345
-	 * @param string $prop property
9346
-	 *
9347
-	 * @return mixed
9348
-	 */
9349
-	public function __get( $prop )
9350
-	{
9351
-		return $this->bean->$prop;
9352
-	}
9353
-
9354
-	/**
9355
-	 * Magic Setter.
9356
-	 * Sets the value directly as a bean property.
9357
-	 *
9358
-	 * @param string $prop  property
9359
-	 * @param mixed  $value value
9360
-	 *
9361
-	 * @return void
9362
-	 */
9363
-	public function __set( $prop, $value )
9364
-	{
9365
-		$this->bean->$prop = $value;
9366
-	}
9367
-
9368
-	/**
9369
-	 * Isset implementation.
9370
-	 * Implements the isset function for array-like access.
9371
-	 *
9372
-	 * @param  string $key key to check
9373
-	 *
9374
-	 * @return boolean
9375
-	 */
9376
-	public function __isset( $key )
9377
-	{
9378
-		return isset( $this->bean->$key );
9379
-	}
9380
-
9381
-	/**
9382
-	 * Box the bean using the current model.
9383
-	 * This method wraps the current bean in this model.
9384
-	 * This method can be reached using FUSE through a simple
9385
-	 * OODBBean. The method returns a RedBeanPHP Simple Model.
9386
-	 * This is useful if you would like to rely on PHP type hinting.
9387
-	 * You can box your beans before passing them to functions or methods
9388
-	 * with typed parameters.
9389
-	 *
9390
-	 * @return SimpleModel
9391
-	 */
9392
-	public function box()
9393
-	{
9394
-		return $this;
9395
-	}
9396
-
9397
-	/**
9398
-	 * Unbox the bean from the model.
9399
-	 * This method returns the bean inside the model.
9400
-	 *
9401
-	 * @return OODBBean
9402
-	 */
9403
-	public function unbox()
9404
-	{
9405
-		return $this->bean;
9406
-	}
9320
+    /**
9321
+     * @var OODBBean
9322
+     */
9323
+    protected $bean;
9324
+
9325
+    /**
9326
+     * Used by FUSE: the ModelHelper class to connect a bean to a model.
9327
+     * This method loads a bean in the model.
9328
+     *
9329
+     * @param OODBBean $bean bean
9330
+     *
9331
+     * @return void
9332
+     */
9333
+    public function loadBean( OODBBean $bean )
9334
+    {
9335
+        $this->bean = $bean;
9336
+    }
9337
+
9338
+    /**
9339
+     * Magic Getter to make the bean properties available from
9340
+     * the $this-scope.
9341
+     *
9342
+     * @note this method returns a value, not a reference!
9343
+     *       To obtain a reference unbox the bean first!
9344
+     *
9345
+     * @param string $prop property
9346
+     *
9347
+     * @return mixed
9348
+     */
9349
+    public function __get( $prop )
9350
+    {
9351
+        return $this->bean->$prop;
9352
+    }
9353
+
9354
+    /**
9355
+     * Magic Setter.
9356
+     * Sets the value directly as a bean property.
9357
+     *
9358
+     * @param string $prop  property
9359
+     * @param mixed  $value value
9360
+     *
9361
+     * @return void
9362
+     */
9363
+    public function __set( $prop, $value )
9364
+    {
9365
+        $this->bean->$prop = $value;
9366
+    }
9367
+
9368
+    /**
9369
+     * Isset implementation.
9370
+     * Implements the isset function for array-like access.
9371
+     *
9372
+     * @param  string $key key to check
9373
+     *
9374
+     * @return boolean
9375
+     */
9376
+    public function __isset( $key )
9377
+    {
9378
+        return isset( $this->bean->$key );
9379
+    }
9380
+
9381
+    /**
9382
+     * Box the bean using the current model.
9383
+     * This method wraps the current bean in this model.
9384
+     * This method can be reached using FUSE through a simple
9385
+     * OODBBean. The method returns a RedBeanPHP Simple Model.
9386
+     * This is useful if you would like to rely on PHP type hinting.
9387
+     * You can box your beans before passing them to functions or methods
9388
+     * with typed parameters.
9389
+     *
9390
+     * @return SimpleModel
9391
+     */
9392
+    public function box()
9393
+    {
9394
+        return $this;
9395
+    }
9396
+
9397
+    /**
9398
+     * Unbox the bean from the model.
9399
+     * This method returns the bean inside the model.
9400
+     *
9401
+     * @return OODBBean
9402
+     */
9403
+    public function unbox()
9404
+    {
9405
+        return $this->bean;
9406
+    }
9407 9407
 }
9408 9408
 } 
9409 9409
 
@@ -9431,30 +9431,30 @@  discard block
 block discarded – undo
9431 9431
 class SimpleModelHelper implements Observer
9432 9432
 {
9433 9433
 
9434
-	/**
9435
-	 * @see Observer::onEvent
9436
-	 */
9437
-	public function onEvent( $eventName, $bean )
9438
-	{
9439
-		$bean->$eventName();
9440
-	}
9441
-
9442
-	/**
9443
-	 * Attaches the FUSE event listeners. Now the Model Helper will listen for
9444
-	 * CRUD events. If a CRUD event occurs it will send a signal to the model
9445
-	 * that belongs to the CRUD bean and this model will take over control from
9446
-	 * there.
9447
-	 *
9448
-	 * @param Observable $observable
9449
-	 *
9450
-	 * @return void
9451
-	 */
9452
-	public function attachEventListeners( Observable $observable )
9453
-	{
9454
-		foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $e ) {
9455
-			$observable->addEventListener( $e, $this );
9456
-		}
9457
-	}
9434
+    /**
9435
+     * @see Observer::onEvent
9436
+     */
9437
+    public function onEvent( $eventName, $bean )
9438
+    {
9439
+        $bean->$eventName();
9440
+    }
9441
+
9442
+    /**
9443
+     * Attaches the FUSE event listeners. Now the Model Helper will listen for
9444
+     * CRUD events. If a CRUD event occurs it will send a signal to the model
9445
+     * that belongs to the CRUD bean and this model will take over control from
9446
+     * there.
9447
+     *
9448
+     * @param Observable $observable
9449
+     *
9450
+     * @return void
9451
+     */
9452
+    public function attachEventListeners( Observable $observable )
9453
+    {
9454
+        foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $e ) {
9455
+            $observable->addEventListener( $e, $this );
9456
+        }
9457
+    }
9458 9458
 }
9459 9459
 } 
9460 9460
 
@@ -9484,242 +9484,242 @@  discard block
 block discarded – undo
9484 9484
  */
9485 9485
 class TagManager
9486 9486
 {
9487
-	/**
9488
-	 * @var ToolBox
9489
-	 */
9490
-	protected $toolbox;
9491
-
9492
-	/**
9493
-	 * @var AssociationManager
9494
-	 */
9495
-	protected $associationManager;
9496
-
9497
-	/**
9498
-	 * @var OODBBean
9499
-	 */
9500
-	protected $redbean;
9501
-
9502
-	/**
9503
-	 * Checks if the argument is a comma separated string, in this case
9504
-	 * it will split the string into words and return an array instead.
9505
-	 * In case of an array the argument will be returned 'as is'.
9506
-	 *
9507
-	 * @param array|string $tagList list of tags
9508
-	 *
9509
-	 * @return array
9510
-	 */
9511
-	private function extractTagsIfNeeded( $tagList )
9512
-	{
9513
-		if ( $tagList !== FALSE && !is_array( $tagList ) ) {
9514
-			$tags = explode( ',', (string) $tagList );
9515
-		} else {
9516
-			$tags = $tagList;
9517
-		}
9518
-
9519
-		return $tags;
9520
-	}
9521
-
9522
-	/**
9523
-	 * Finds a tag bean by it's title.
9524
-	 * Internal method.
9525
-	 *
9526
-	 * @param string $title title
9527
-	 *
9528
-	 * @return OODBBean
9529
-	 */
9530
-	protected function findTagByTitle( $title )
9531
-	{
9532
-		$beans = $this->redbean->find( 'tag', array( 'title' => array( $title ) ) );
9533
-
9534
-		if ( $beans ) {
9535
-			$bean = reset( $beans );
9536
-
9537
-			return $bean;
9538
-		}
9539
-
9540
-		return NULL;
9541
-	}
9542
-
9543
-	/**
9544
-	 * Constructor.
9545
-	 * The tag manager offers an easy way to quickly implement basic tagging
9546
-	 * functionality.
9547
-	 *
9548
-	 * @param ToolBox $toolbox
9549
-	 */
9550
-	public function __construct( ToolBox $toolbox )
9551
-	{
9552
-		$this->toolbox = $toolbox;
9553
-		$this->redbean = $toolbox->getRedBean();
9554
-
9555
-		$this->associationManager = $this->redbean->getAssociationManager();
9556
-	}
9557
-
9558
-	/**
9559
-	 * Tests whether a bean has been associated with one ore more
9560
-	 * of the listed tags. If the third parameter is TRUE this method
9561
-	 * will return TRUE only if all tags that have been specified are indeed
9562
-	 * associated with the given bean, otherwise FALSE.
9563
-	 * If the third parameter is FALSE this
9564
-	 * method will return TRUE if one of the tags matches, FALSE if none
9565
-	 * match.
9566
-	 * 
9567
-	 * Tag list can be either an array with tag names or a comma separated list
9568
-	 * of tag names.
9569
-	 *
9570
-	 * @param  OODBBean $bean bean to check for tags
9571
-	 * @param  array|string     $tags list of tags
9572
-	 * @param  boolean          $all  whether they must all match or just some
9573
-	 *
9574
-	 * @return boolean
9575
-	 */
9576
-	public function hasTag( $bean, $tags, $all = FALSE )
9577
-	{
9578
-		$foundtags = $this->tag( $bean );
9579
-
9580
-		$tags = $this->extractTagsIfNeeded( $tags );
9581
-		$same = array_intersect( $tags, $foundtags );
9582
-
9583
-		if ( $all ) {
9584
-			return ( implode( ',', $same ) === implode( ',', $tags ) );
9585
-		}
9586
-
9587
-		return (bool) ( count( $same ) > 0 );
9588
-	}
9589
-
9590
-	/**
9591
-	 * Removes all sepcified tags from the bean. The tags specified in
9592
-	 * the second parameter will no longer be associated with the bean.
9593
-	 * 
9594
-	 * Tag list can be either an array with tag names or a comma separated list
9595
-	 * of tag names.
9596
-	 *
9597
-	 * @param  OODBBean $bean    tagged bean
9598
-	 * @param  array|string     $tagList list of tags (names)
9599
-	 *
9600
-	 * @return void
9601
-	 */
9602
-	public function untag( $bean, $tagList )
9603
-	{
9604
-		$tags = $this->extractTagsIfNeeded( $tagList );
9605
-
9606
-		foreach ( $tags as $tag ) {
9607
-			if ( $t = $this->findTagByTitle( $tag ) ) {
9608
-				$this->associationManager->unassociate( $bean, $t );
9609
-			}
9610
-		}
9611
-	}
9612
-
9613
-	/**
9614
-	 * Tags a bean or returns tags associated with a bean.
9615
-	 * If $tagList is NULL or omitted this method will return a
9616
-	 * comma separated list of tags associated with the bean provided.
9617
-	 * If $tagList is a comma separated list (string) of tags all tags will
9618
-	 * be associated with the bean.
9619
-	 * You may also pass an array instead of a string.
9620
-	 * 
9621
-	 * Tag list can be either an array with tag names or a comma separated list
9622
-	 * of tag names.
9623
-	 *
9624
-	 * @param OODBBean $bean    bean to be tagged
9625
-	 * @param array|string     $tagList a list of tags
9626
-	 *
9627
-	 * @return array
9628
-	 */
9629
-	public function tag( OODBBean $bean, $tagList = NULL )
9630
-	{
9631
-		if ( is_null( $tagList ) ) {
9487
+    /**
9488
+     * @var ToolBox
9489
+     */
9490
+    protected $toolbox;
9491
+
9492
+    /**
9493
+     * @var AssociationManager
9494
+     */
9495
+    protected $associationManager;
9496
+
9497
+    /**
9498
+     * @var OODBBean
9499
+     */
9500
+    protected $redbean;
9501
+
9502
+    /**
9503
+     * Checks if the argument is a comma separated string, in this case
9504
+     * it will split the string into words and return an array instead.
9505
+     * In case of an array the argument will be returned 'as is'.
9506
+     *
9507
+     * @param array|string $tagList list of tags
9508
+     *
9509
+     * @return array
9510
+     */
9511
+    private function extractTagsIfNeeded( $tagList )
9512
+    {
9513
+        if ( $tagList !== FALSE && !is_array( $tagList ) ) {
9514
+            $tags = explode( ',', (string) $tagList );
9515
+        } else {
9516
+            $tags = $tagList;
9517
+        }
9518
+
9519
+        return $tags;
9520
+    }
9521
+
9522
+    /**
9523
+     * Finds a tag bean by it's title.
9524
+     * Internal method.
9525
+     *
9526
+     * @param string $title title
9527
+     *
9528
+     * @return OODBBean
9529
+     */
9530
+    protected function findTagByTitle( $title )
9531
+    {
9532
+        $beans = $this->redbean->find( 'tag', array( 'title' => array( $title ) ) );
9533
+
9534
+        if ( $beans ) {
9535
+            $bean = reset( $beans );
9536
+
9537
+            return $bean;
9538
+        }
9539
+
9540
+        return NULL;
9541
+    }
9542
+
9543
+    /**
9544
+     * Constructor.
9545
+     * The tag manager offers an easy way to quickly implement basic tagging
9546
+     * functionality.
9547
+     *
9548
+     * @param ToolBox $toolbox
9549
+     */
9550
+    public function __construct( ToolBox $toolbox )
9551
+    {
9552
+        $this->toolbox = $toolbox;
9553
+        $this->redbean = $toolbox->getRedBean();
9554
+
9555
+        $this->associationManager = $this->redbean->getAssociationManager();
9556
+    }
9557
+
9558
+    /**
9559
+     * Tests whether a bean has been associated with one ore more
9560
+     * of the listed tags. If the third parameter is TRUE this method
9561
+     * will return TRUE only if all tags that have been specified are indeed
9562
+     * associated with the given bean, otherwise FALSE.
9563
+     * If the third parameter is FALSE this
9564
+     * method will return TRUE if one of the tags matches, FALSE if none
9565
+     * match.
9566
+     * 
9567
+     * Tag list can be either an array with tag names or a comma separated list
9568
+     * of tag names.
9569
+     *
9570
+     * @param  OODBBean $bean bean to check for tags
9571
+     * @param  array|string     $tags list of tags
9572
+     * @param  boolean          $all  whether they must all match or just some
9573
+     *
9574
+     * @return boolean
9575
+     */
9576
+    public function hasTag( $bean, $tags, $all = FALSE )
9577
+    {
9578
+        $foundtags = $this->tag( $bean );
9579
+
9580
+        $tags = $this->extractTagsIfNeeded( $tags );
9581
+        $same = array_intersect( $tags, $foundtags );
9582
+
9583
+        if ( $all ) {
9584
+            return ( implode( ',', $same ) === implode( ',', $tags ) );
9585
+        }
9586
+
9587
+        return (bool) ( count( $same ) > 0 );
9588
+    }
9589
+
9590
+    /**
9591
+     * Removes all sepcified tags from the bean. The tags specified in
9592
+     * the second parameter will no longer be associated with the bean.
9593
+     * 
9594
+     * Tag list can be either an array with tag names or a comma separated list
9595
+     * of tag names.
9596
+     *
9597
+     * @param  OODBBean $bean    tagged bean
9598
+     * @param  array|string     $tagList list of tags (names)
9599
+     *
9600
+     * @return void
9601
+     */
9602
+    public function untag( $bean, $tagList )
9603
+    {
9604
+        $tags = $this->extractTagsIfNeeded( $tagList );
9605
+
9606
+        foreach ( $tags as $tag ) {
9607
+            if ( $t = $this->findTagByTitle( $tag ) ) {
9608
+                $this->associationManager->unassociate( $bean, $t );
9609
+            }
9610
+        }
9611
+    }
9612
+
9613
+    /**
9614
+     * Tags a bean or returns tags associated with a bean.
9615
+     * If $tagList is NULL or omitted this method will return a
9616
+     * comma separated list of tags associated with the bean provided.
9617
+     * If $tagList is a comma separated list (string) of tags all tags will
9618
+     * be associated with the bean.
9619
+     * You may also pass an array instead of a string.
9620
+     * 
9621
+     * Tag list can be either an array with tag names or a comma separated list
9622
+     * of tag names.
9623
+     *
9624
+     * @param OODBBean $bean    bean to be tagged
9625
+     * @param array|string     $tagList a list of tags
9626
+     *
9627
+     * @return array
9628
+     */
9629
+    public function tag( OODBBean $bean, $tagList = NULL )
9630
+    {
9631
+        if ( is_null( $tagList ) ) {
9632 9632
 			
9633
-			$tags = $bean->sharedTag;
9634
-			$foundTags = array();
9635
-
9636
-			foreach ( $tags as $tag ) {
9637
-				$foundTags[] = $tag->title;
9638
-			}
9639
-
9640
-			return $foundTags;
9641
-		}
9642
-
9643
-		$this->associationManager->clearRelations( $bean, 'tag' );
9644
-		$this->addTags( $bean, $tagList );
9645
-
9646
-		return $tagList;
9647
-	}
9648
-
9649
-	/**
9650
-	 * Adds tags to a bean.
9651
-	 * If $tagList is a comma separated list of tags all tags will
9652
-	 * be associated with the bean.
9653
-	 * You may also pass an array instead of a string.
9654
-	 * 
9655
-	 * Tag list can be either an array with tag names or a comma separated list
9656
-	 * of tag names.
9657
-	 *
9658
-	 * @param OODBBean $bean    bean to add tags to
9659
-	 * @param array|string     $tagList list of tags to add to bean
9660
-	 *
9661
-	 * @return void
9662
-	 */
9663
-	public function addTags( OODBBean $bean, $tagList )
9664
-	{
9665
-		$tags = $this->extractTagsIfNeeded( $tagList );
9666
-
9667
-		if ( $tagList === FALSE ) {
9668
-			return;
9669
-		}
9670
-
9671
-		foreach ( $tags as $tag ) {
9672
-			if ( !$t = $this->findTagByTitle( $tag ) ) {
9673
-				$t        = $this->redbean->dispense( 'tag' );
9674
-				$t->title = $tag;
9675
-
9676
-				$this->redbean->store( $t );
9677
-			}
9678
-
9679
-			$this->associationManager->associate( $bean, $t );
9680
-		}
9681
-	}
9682
-
9683
-	/**
9684
-	 * Returns all beans that have been tagged with one or more
9685
-	 * of the specified tags.
9686
-	 *
9687
-	 * Tag list can be either an array with tag names or a comma separated list
9688
-	 * of tag names.
9689
-	 *
9690
-	 * @param string       $beanType type of bean you are looking for
9691
-	 * @param array|string $tagList  list of tags to match
9692
-	 * @param string       $sql      additional SQL (use only for pagination)
9693
-	 * @param array        $bindings bindings
9694
-	 *
9695
-	 * @return array
9696
-	 */
9697
-	public function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
9698
-	{
9699
-		$tags       = $this->extractTagsIfNeeded( $tagList );
9700
-		$records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings );
9701
-
9702
-		return $this->redbean->convertToBeans( $beanType, $records );
9703
-	}
9704
-
9705
-	/**
9706
-	 * Returns all beans that have been tagged with ALL of the tags given.
9707
-	 *
9708
-	 * Tag list can be either an array with tag names or a comma separated list
9709
-	 * of tag names.
9710
-	 *
9711
-	 * @param string        $beanType type of bean you are looking for
9712
-	 * @param array|string  $tagList  list of tags to match
9713
-	 *
9714
-	 * @return array
9715
-	 */
9716
-	public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
9717
-	{
9718
-		$tags  = $this->extractTagsIfNeeded( $tagList );
9719
-		$records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings );
9720
-
9721
-		return $this->redbean->convertToBeans( $beanType, $records );
9722
-	}
9633
+            $tags = $bean->sharedTag;
9634
+            $foundTags = array();
9635
+
9636
+            foreach ( $tags as $tag ) {
9637
+                $foundTags[] = $tag->title;
9638
+            }
9639
+
9640
+            return $foundTags;
9641
+        }
9642
+
9643
+        $this->associationManager->clearRelations( $bean, 'tag' );
9644
+        $this->addTags( $bean, $tagList );
9645
+
9646
+        return $tagList;
9647
+    }
9648
+
9649
+    /**
9650
+     * Adds tags to a bean.
9651
+     * If $tagList is a comma separated list of tags all tags will
9652
+     * be associated with the bean.
9653
+     * You may also pass an array instead of a string.
9654
+     * 
9655
+     * Tag list can be either an array with tag names or a comma separated list
9656
+     * of tag names.
9657
+     *
9658
+     * @param OODBBean $bean    bean to add tags to
9659
+     * @param array|string     $tagList list of tags to add to bean
9660
+     *
9661
+     * @return void
9662
+     */
9663
+    public function addTags( OODBBean $bean, $tagList )
9664
+    {
9665
+        $tags = $this->extractTagsIfNeeded( $tagList );
9666
+
9667
+        if ( $tagList === FALSE ) {
9668
+            return;
9669
+        }
9670
+
9671
+        foreach ( $tags as $tag ) {
9672
+            if ( !$t = $this->findTagByTitle( $tag ) ) {
9673
+                $t        = $this->redbean->dispense( 'tag' );
9674
+                $t->title = $tag;
9675
+
9676
+                $this->redbean->store( $t );
9677
+            }
9678
+
9679
+            $this->associationManager->associate( $bean, $t );
9680
+        }
9681
+    }
9682
+
9683
+    /**
9684
+     * Returns all beans that have been tagged with one or more
9685
+     * of the specified tags.
9686
+     *
9687
+     * Tag list can be either an array with tag names or a comma separated list
9688
+     * of tag names.
9689
+     *
9690
+     * @param string       $beanType type of bean you are looking for
9691
+     * @param array|string $tagList  list of tags to match
9692
+     * @param string       $sql      additional SQL (use only for pagination)
9693
+     * @param array        $bindings bindings
9694
+     *
9695
+     * @return array
9696
+     */
9697
+    public function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
9698
+    {
9699
+        $tags       = $this->extractTagsIfNeeded( $tagList );
9700
+        $records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings );
9701
+
9702
+        return $this->redbean->convertToBeans( $beanType, $records );
9703
+    }
9704
+
9705
+    /**
9706
+     * Returns all beans that have been tagged with ALL of the tags given.
9707
+     *
9708
+     * Tag list can be either an array with tag names or a comma separated list
9709
+     * of tag names.
9710
+     *
9711
+     * @param string        $beanType type of bean you are looking for
9712
+     * @param array|string  $tagList  list of tags to match
9713
+     *
9714
+     * @return array
9715
+     */
9716
+    public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
9717
+    {
9718
+        $tags  = $this->extractTagsIfNeeded( $tagList );
9719
+        $records    = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings );
9720
+
9721
+        return $this->redbean->convertToBeans( $beanType, $records );
9722
+    }
9723 9723
 }
9724 9724
 }
9725 9725
 
@@ -9747,134 +9747,134 @@  discard block
 block discarded – undo
9747 9747
  */
9748 9748
 class LabelMaker
9749 9749
 {
9750
-	/**
9751
-	 * @var ToolBox
9752
-	 */
9753
-	protected $toolbox;
9754
-
9755
-	/**
9756
-	 * Constructor.
9757
-	 *
9758
-	 * @param ToolBox $toolbox
9759
-	 */
9760
-	public function __construct( ToolBox $toolbox )
9761
-	{
9762
-		$this->toolbox = $toolbox;
9763
-	}
9764
-
9765
-	/**
9766
-	 * A label is a bean with only an id, type and name property.
9767
-	 * This function will dispense beans for all entries in the array. The
9768
-	 * values of the array will be assigned to the name property of each
9769
-	 * individual bean.
9770
-	 *
9771
-	 * $people = R::dispenseLabels( 'person', [ 'Santa', 'Claus' ] );
9772
-	 *
9773
-	 * @param string $type   type of beans you would like to have
9774
-	 * @param array  $labels list of labels, names for each bean
9775
-	 *
9776
-	 * @return array
9777
-	 */
9778
-	public function dispenseLabels( $type, $labels )
9779
-	{
9780
-		$labelBeans = array();
9781
-		foreach ( $labels as $label ) {
9782
-			$labelBean       = $this->toolbox->getRedBean()->dispense( $type );
9783
-			$labelBean->name = $label;
9784
-			$labelBeans[]    = $labelBean;
9785
-		}
9786
-
9787
-		return $labelBeans;
9788
-	}
9789
-
9790
-	/**
9791
-	 * Gathers labels from beans. This function loops through the beans,
9792
-	 * collects the value of the name property for each individual bean
9793
-	 * and stores the names in a new array. The array then gets sorted using the
9794
-	 * default sort function of PHP (sort).
9795
-	 *
9796
-	 * Usage:
9797
-	 *
9798
-	 * $o1->name = 'hamburger';
9799
-	 * $o2->name = 'pizza';
9800
-	 * implode( ',', R::gatherLabels( [ $o1, $o2 ] ) ); //hamburger,pizza
9801
-	 *
9802
-	 * Note that the return value is an array of strings, not beans.
9803
-	 *
9804
-	 * @param array $beans list of beans to loop through
9805
-	 *
9806
-	 * @return array
9807
-	 */
9808
-	public function gatherLabels( $beans )
9809
-	{
9810
-		$labels = array();
9811
-
9812
-		foreach ( $beans as $bean ) {
9813
-			$labels[] = $bean->name;
9814
-		}
9815
-
9816
-		sort( $labels );
9817
-
9818
-		return $labels;
9819
-	}
9750
+    /**
9751
+     * @var ToolBox
9752
+     */
9753
+    protected $toolbox;
9754
+
9755
+    /**
9756
+     * Constructor.
9757
+     *
9758
+     * @param ToolBox $toolbox
9759
+     */
9760
+    public function __construct( ToolBox $toolbox )
9761
+    {
9762
+        $this->toolbox = $toolbox;
9763
+    }
9764
+
9765
+    /**
9766
+     * A label is a bean with only an id, type and name property.
9767
+     * This function will dispense beans for all entries in the array. The
9768
+     * values of the array will be assigned to the name property of each
9769
+     * individual bean.
9770
+     *
9771
+     * $people = R::dispenseLabels( 'person', [ 'Santa', 'Claus' ] );
9772
+     *
9773
+     * @param string $type   type of beans you would like to have
9774
+     * @param array  $labels list of labels, names for each bean
9775
+     *
9776
+     * @return array
9777
+     */
9778
+    public function dispenseLabels( $type, $labels )
9779
+    {
9780
+        $labelBeans = array();
9781
+        foreach ( $labels as $label ) {
9782
+            $labelBean       = $this->toolbox->getRedBean()->dispense( $type );
9783
+            $labelBean->name = $label;
9784
+            $labelBeans[]    = $labelBean;
9785
+        }
9786
+
9787
+        return $labelBeans;
9788
+    }
9789
+
9790
+    /**
9791
+     * Gathers labels from beans. This function loops through the beans,
9792
+     * collects the value of the name property for each individual bean
9793
+     * and stores the names in a new array. The array then gets sorted using the
9794
+     * default sort function of PHP (sort).
9795
+     *
9796
+     * Usage:
9797
+     *
9798
+     * $o1->name = 'hamburger';
9799
+     * $o2->name = 'pizza';
9800
+     * implode( ',', R::gatherLabels( [ $o1, $o2 ] ) ); //hamburger,pizza
9801
+     *
9802
+     * Note that the return value is an array of strings, not beans.
9803
+     *
9804
+     * @param array $beans list of beans to loop through
9805
+     *
9806
+     * @return array
9807
+     */
9808
+    public function gatherLabels( $beans )
9809
+    {
9810
+        $labels = array();
9811
+
9812
+        foreach ( $beans as $bean ) {
9813
+            $labels[] = $bean->name;
9814
+        }
9815
+
9816
+        sort( $labels );
9817
+
9818
+        return $labels;
9819
+    }
9820 9820
 	
9821
-	/**
9822
-	 * Fetches an ENUM from the database and creates it if necessary.
9823
-	 * An ENUM has the following format:
9824
-	 *
9825
-	 * ENUM:VALUE
9826
-	 *
9827
-	 * If you pass 'ENUM' only, this method will return an array of its
9828
-	 * values:
9829
-	 *
9830
-	 * implode( ',', R::gatherLabels( R::enum( 'flavour' ) ) ) //'BANANA,MOCCA'
9831
-	 *
9832
-	 * If you pass 'ENUM:VALUE' this method will return the specified enum bean
9833
-	 * and create it in the database if it does not exist yet:
9834
-	 *
9835
-	 * $bananaFlavour = R::enum( 'flavour:banana' );
9836
-	 * $bananaFlavour->name;
9837
-	 *
9838
-	 * So you can use this method to set an ENUM value in a bean:
9839
-	 *
9840
-	 * $shake->flavour = R::enum( 'flavour:banana' );
9841
-	 *
9842
-	 * the property flavour now contains the enum bean, a parent bean.
9843
-	 * In the database, flavour_id will point to the flavour record with name 'banana'.
9844
-	 *
9845
-	 * @param string $enum ENUM specification for label
9846
-	 * 
9847
-	 * @return array|OODBBean
9848
-	 */
9849
-	public function enum( $enum )
9850
-	{
9851
-		$oodb = $this->toolbox->getRedBean();
9821
+    /**
9822
+     * Fetches an ENUM from the database and creates it if necessary.
9823
+     * An ENUM has the following format:
9824
+     *
9825
+     * ENUM:VALUE
9826
+     *
9827
+     * If you pass 'ENUM' only, this method will return an array of its
9828
+     * values:
9829
+     *
9830
+     * implode( ',', R::gatherLabels( R::enum( 'flavour' ) ) ) //'BANANA,MOCCA'
9831
+     *
9832
+     * If you pass 'ENUM:VALUE' this method will return the specified enum bean
9833
+     * and create it in the database if it does not exist yet:
9834
+     *
9835
+     * $bananaFlavour = R::enum( 'flavour:banana' );
9836
+     * $bananaFlavour->name;
9837
+     *
9838
+     * So you can use this method to set an ENUM value in a bean:
9839
+     *
9840
+     * $shake->flavour = R::enum( 'flavour:banana' );
9841
+     *
9842
+     * the property flavour now contains the enum bean, a parent bean.
9843
+     * In the database, flavour_id will point to the flavour record with name 'banana'.
9844
+     *
9845
+     * @param string $enum ENUM specification for label
9846
+     * 
9847
+     * @return array|OODBBean
9848
+     */
9849
+    public function enum( $enum )
9850
+    {
9851
+        $oodb = $this->toolbox->getRedBean();
9852 9852
 		
9853
-		if ( strpos( $enum, ':' ) === FALSE ) {
9854
-			$type  = $enum;
9855
-			$value = FALSE;
9856
-		} else {
9857
-			list( $type, $value ) = explode( ':', $enum );
9858
-			$value                = preg_replace( '/\W+/', '_', strtoupper( trim( $value ) ) );
9859
-		}
9853
+        if ( strpos( $enum, ':' ) === FALSE ) {
9854
+            $type  = $enum;
9855
+            $value = FALSE;
9856
+        } else {
9857
+            list( $type, $value ) = explode( ':', $enum );
9858
+            $value                = preg_replace( '/\W+/', '_', strtoupper( trim( $value ) ) );
9859
+        }
9860 9860
 		
9861
-		$values = $oodb->find( $type );
9861
+        $values = $oodb->find( $type );
9862 9862
 		
9863
-		if ( $value === FALSE ) {
9864
-			return $values;
9865
-		}
9863
+        if ( $value === FALSE ) {
9864
+            return $values;
9865
+        }
9866 9866
 		
9867
-		foreach( $values as $enumItem ) {
9868
-				if ( $enumItem->name === $value ) return $enumItem;	
9869
-		}
9867
+        foreach( $values as $enumItem ) {
9868
+                if ( $enumItem->name === $value ) return $enumItem;	
9869
+        }
9870 9870
 		
9871
-		$newEnumItems = $this->dispenseLabels( $type, array( $value ) );
9872
-		$newEnumItem  = reset( $newEnumItems );
9871
+        $newEnumItems = $this->dispenseLabels( $type, array( $value ) );
9872
+        $newEnumItem  = reset( $newEnumItems );
9873 9873
 		
9874
-		$oodb->store( $newEnumItem );
9874
+        $oodb->store( $newEnumItem );
9875 9875
 		
9876
-		return $newEnumItem;
9877
-	}
9876
+        return $newEnumItem;
9877
+    }
9878 9878
 }
9879 9879
 }
9880 9880
 
@@ -9924,1853 +9924,1853 @@  discard block
 block discarded – undo
9924 9924
  */
9925 9925
 class Facade
9926 9926
 {
9927
-	/**
9928
-	 * RedBeanPHP version constant.
9929
-	 */
9930
-	const C_REDBEANPHP_VERSION = '4.2';
9931
-
9932
-	/**
9933
-	 * @var ToolBox
9934
-	 */
9935
-	public static $toolbox;
9936
-
9937
-	/**
9938
-	 * @var OODB
9939
-	 */
9940
-	private static $redbean;
9941
-
9942
-	/**
9943
-	 * @var QueryWriter
9944
-	 */
9945
-	private static $writer;
9946
-
9947
-	/**
9948
-	 * @var DBAdapter
9949
-	 */
9950
-	private static $adapter;
9951
-
9952
-	/**
9953
-	 * @var AssociationManager
9954
-	 */
9955
-	private static $associationManager;
9956
-
9957
-	/**
9958
-	 * @var TagManager
9959
-	 */
9960
-	private static $tagManager;
9961
-
9962
-	/**
9963
-	 * @var DuplicationManager
9964
-	 */
9965
-	private static $duplicationManager;
9966
-
9967
-	/**
9968
-	 * @var LabelMaker
9969
-	 */
9970
-	private static $labelMaker;
9971
-
9972
-	/**
9973
-	 * @var Finder
9974
-	 */
9975
-	private static $finder;
9976
-
9977
-	/**
9978
-	 * @var Logger
9979
-	 */
9980
-	private static $logger;
9981
-
9982
-	/**
9983
-	 * @var array
9984
-	 */
9985
-	private static $plugins = array();
9986
-
9987
-	/**
9988
-	 * @var string
9989
-	 */
9990
-	private static $exportCaseStyle = 'default';
9927
+    /**
9928
+     * RedBeanPHP version constant.
9929
+     */
9930
+    const C_REDBEANPHP_VERSION = '4.2';
9931
+
9932
+    /**
9933
+     * @var ToolBox
9934
+     */
9935
+    public static $toolbox;
9936
+
9937
+    /**
9938
+     * @var OODB
9939
+     */
9940
+    private static $redbean;
9941
+
9942
+    /**
9943
+     * @var QueryWriter
9944
+     */
9945
+    private static $writer;
9946
+
9947
+    /**
9948
+     * @var DBAdapter
9949
+     */
9950
+    private static $adapter;
9951
+
9952
+    /**
9953
+     * @var AssociationManager
9954
+     */
9955
+    private static $associationManager;
9956
+
9957
+    /**
9958
+     * @var TagManager
9959
+     */
9960
+    private static $tagManager;
9961
+
9962
+    /**
9963
+     * @var DuplicationManager
9964
+     */
9965
+    private static $duplicationManager;
9966
+
9967
+    /**
9968
+     * @var LabelMaker
9969
+     */
9970
+    private static $labelMaker;
9971
+
9972
+    /**
9973
+     * @var Finder
9974
+     */
9975
+    private static $finder;
9976
+
9977
+    /**
9978
+     * @var Logger
9979
+     */
9980
+    private static $logger;
9981
+
9982
+    /**
9983
+     * @var array
9984
+     */
9985
+    private static $plugins = array();
9986
+
9987
+    /**
9988
+     * @var string
9989
+     */
9990
+    private static $exportCaseStyle = 'default';
9991 9991
 	
9992
-	/**
9993
-	 * Not in use (backward compatibility SQLHelper)
9994
-	 */
9995
-	public static $f;
9996
-
9997
-	/**
9998
-	 * @var string
9999
-	 */
10000
-	public static $currentDB = '';
10001
-
10002
-	/**
10003
-	 * @var array
10004
-	 */
10005
-	public static $toolboxes = array();
10006
-
10007
-	/**
10008
-	 * Internal Query function, executes the desired query. Used by
10009
-	 * all facade query functions. This keeps things DRY.
10010
-	 *
10011
-	 * @throws SQL
10012
-	 *
10013
-	 * @param string $method   desired query method (i.e. 'cell', 'col', 'exec' etc..)
10014
-	 * @param string $sql      the sql you want to execute
10015
-	 * @param array  $bindings array of values to be bound to query statement
10016
-	 *
10017
-	 * @return array
10018
-	 */
10019
-	private static function query( $method, $sql, $bindings )
10020
-	{
10021
-		if ( !self::$redbean->isFrozen() ) {
10022
-			try {
10023
-				$rs = Facade::$adapter->$method( $sql, $bindings );
10024
-			} catch ( SQLException $exception ) {
10025
-				if ( self::$writer->sqlStateIn( $exception->getSQLState(),
10026
-					array(
10027
-						QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
10028
-						QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
10029
-					)
10030
-				) {
10031
-					return ( $method === 'getCell' ) ? NULL : array();
10032
-				} else {
10033
-					throw $exception;
10034
-				}
10035
-			}
10036
-
10037
-			return $rs;
10038
-		} else {
10039
-			return Facade::$adapter->$method( $sql, $bindings );
10040
-		}
10041
-	}
10042
-
10043
-	/**
10044
-	 * Returns the RedBeanPHP version string.
10045
-	 * The RedBeanPHP version string always has the same format "X.Y"
10046
-	 * where X is the major version number and Y is the minor version number.
10047
-	 * Point releases are not mentioned in the version string.
10048
-	 *
10049
-	 * @return string
10050
-	 */
10051
-	public static function getVersion()
10052
-	{
10053
-		return self::C_REDBEANPHP_VERSION;
10054
-	}
10055
-
10056
-	/**
10057
-	 * Tests the connection.
10058
-	 * Returns TRUE if connection has been established and
10059
-	 * FALSE otherwise.
10060
-	 *
10061
-	 * @return boolean
10062
-	 */
10063
-	public static function testConnection()
10064
-	{
10065
-		if ( !isset( self::$adapter ) ) return FALSE;
10066
-
10067
-		$database = self::$adapter->getDatabase();
10068
-		try {
10069
-			@$database->connect();
10070
-		} catch ( \Exception $e ) {}
10071
-		return $database->isConnected();
10072
-	}
10073
-
10074
-	/**
10075
-	 * Kickstarts redbean for you. This method should be called before you start using
10076
-	 * RedBean. The Setup() method can be called without any arguments, in this case it will
10077
-	 * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems).
10078
-	 *
10079
-	 * @param string  $dsn      Database connection string
10080
-	 * @param string  $username Username for database
10081
-	 * @param string  $password Password for database
10082
-	 * @param boolean $frozen   TRUE if you want to setup in frozen mode
10083
-	 *
10084
-	 * @return ToolBox
10085
-	 */
10086
-	public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE )
10087
-	{
10088
-		if ( is_null( $dsn ) ) {
10089
-			$dsn = 'sqlite:/' . sys_get_temp_dir() . '/red.db';
10090
-		}
10091
-
10092
-		self::addDatabase( 'default', $dsn, $username, $password, $frozen );
10093
-		self::selectDatabase( 'default' );
10094
-
10095
-		return self::$toolbox;
10096
-	}
10097
-
10098
-	/**
10099
-	 * Toggles Narrow Field Mode.
10100
-	 * See documentation in QueryWriter.
10101
-	 *
10102
-	 * @param boolean $mode TRUE = Narrow Field Mode
10103
-	 *
10104
-	 * @return void
10105
-	 */
10106
-	public static function setNarrowFieldMode( $mode )
10107
-	{
10108
-		AQueryWriter::setNarrowFieldMode( $mode );
10109
-	}
10110
-
10111
-	/**
10112
-	 * Starts a transaction within a closure (or other valid callback).
10113
-	 * If an\Exception is thrown inside, the operation is automatically rolled back.
10114
-	 * If no\Exception happens, it commits automatically.
10115
-	 * It also supports (simulated) nested transactions (that is useful when
10116
-	 * you have many methods that needs transactions but are unaware of
10117
-	 * each other).
10118
-	 * ex:
10119
-	 *        $from = 1;
10120
-	 *        $to = 2;
10121
-	 *        $amount = 300;
10122
-	 *
10123
-	 *        R::transaction(function() use($from, $to, $amount)
10124
-	 *        {
10125
-	 *            $accountFrom = R::load('account', $from);
10126
-	 *            $accountTo = R::load('account', $to);
10127
-	 *
10128
-	 *            $accountFrom->money -= $amount;
10129
-	 *            $accountTo->money += $amount;
10130
-	 *
10131
-	 *            R::store($accountFrom);
10132
-	 *            R::store($accountTo);
10133
-	 *      });
10134
-	 *
10135
-	 * @param callable $callback Closure (or other callable) with the transaction logic
10136
-	 *
10137
-	 * @throws Security
10138
-	 *
10139
-	 * @return mixed
10140
-	 *
10141
-	 */
10142
-	public static function transaction( $callback )
10143
-	{
10144
-		if ( !is_callable( $callback ) ) {
10145
-			throw new RedException( 'R::transaction needs a valid callback.' );
10146
-		}
10147
-
10148
-		static $depth = 0;
10149
-		$result = null;
10150
-		try {
10151
-			if ( $depth == 0 ) {
10152
-				self::begin();
10153
-			}
10154
-			$depth++;
10155
-			$result = call_user_func( $callback ); //maintain 5.2 compatibility
10156
-			$depth--;
10157
-			if ( $depth == 0 ) {
10158
-				self::commit();
10159
-			}
10160
-		} catch (\Exception $exception ) {
10161
-			$depth--;
10162
-			if ( $depth == 0 ) {
10163
-				self::rollback();
10164
-			}
10165
-			throw $exception;
10166
-		}
10167
-		return $result;
10168
-	}
10169
-
10170
-	/**
10171
-	 * Adds a database to the facade, afterwards you can select the database using
10172
-	 * selectDatabase($key), where $key is the name you assigned to this database.
10173
-	 *
10174
-	 * Usage:
10175
-	 *
10176
-	 * R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' );
10177
-	 * R::selectDatabase( 'database-1' ); //to select database again
10178
-	 *
10179
-	 * This method allows you to dynamically add (and select) new databases
10180
-	 * to the facade. Adding a database with the same key will cause an exception.
10181
-	 *
10182
-	 * @param string      $key    ID for the database
10183
-	 * @param string      $dsn    DSN for the database
10184
-	 * @param string      $user   User for connection
10185
-	 * @param NULL|string $pass   Password for connection
10186
-	 * @param bool        $frozen Whether this database is frozen or not
10187
-	 *
10188
-	 * @return void
10189
-	 */
10190
-	public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE )
10191
-	{
10192
-		if ( isset( self::$toolboxes[$key] ) ) {
10193
-			throw new RedException( 'A database has already be specified for this key.' );
10194
-		}
10195
-
10196
-		if ( is_object($dsn) ) {
10197
-			$db  = new RPDO( $dsn );
10198
-			$dbType = $db->getDatabaseType();
10199
-		} else {
10200
-			$db = new RPDO( $dsn, $user, $pass, TRUE );
10201
-			$dbType = substr( $dsn, 0, strpos( $dsn, ':' ) );
10202
-		}
10203
-
10204
-		$adapter = new DBAdapter( $db );
10205
-
10206
-		$writers     = array(
9992
+    /**
9993
+     * Not in use (backward compatibility SQLHelper)
9994
+     */
9995
+    public static $f;
9996
+
9997
+    /**
9998
+     * @var string
9999
+     */
10000
+    public static $currentDB = '';
10001
+
10002
+    /**
10003
+     * @var array
10004
+     */
10005
+    public static $toolboxes = array();
10006
+
10007
+    /**
10008
+     * Internal Query function, executes the desired query. Used by
10009
+     * all facade query functions. This keeps things DRY.
10010
+     *
10011
+     * @throws SQL
10012
+     *
10013
+     * @param string $method   desired query method (i.e. 'cell', 'col', 'exec' etc..)
10014
+     * @param string $sql      the sql you want to execute
10015
+     * @param array  $bindings array of values to be bound to query statement
10016
+     *
10017
+     * @return array
10018
+     */
10019
+    private static function query( $method, $sql, $bindings )
10020
+    {
10021
+        if ( !self::$redbean->isFrozen() ) {
10022
+            try {
10023
+                $rs = Facade::$adapter->$method( $sql, $bindings );
10024
+            } catch ( SQLException $exception ) {
10025
+                if ( self::$writer->sqlStateIn( $exception->getSQLState(),
10026
+                    array(
10027
+                        QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
10028
+                        QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
10029
+                    )
10030
+                ) {
10031
+                    return ( $method === 'getCell' ) ? NULL : array();
10032
+                } else {
10033
+                    throw $exception;
10034
+                }
10035
+            }
10036
+
10037
+            return $rs;
10038
+        } else {
10039
+            return Facade::$adapter->$method( $sql, $bindings );
10040
+        }
10041
+    }
10042
+
10043
+    /**
10044
+     * Returns the RedBeanPHP version string.
10045
+     * The RedBeanPHP version string always has the same format "X.Y"
10046
+     * where X is the major version number and Y is the minor version number.
10047
+     * Point releases are not mentioned in the version string.
10048
+     *
10049
+     * @return string
10050
+     */
10051
+    public static function getVersion()
10052
+    {
10053
+        return self::C_REDBEANPHP_VERSION;
10054
+    }
10055
+
10056
+    /**
10057
+     * Tests the connection.
10058
+     * Returns TRUE if connection has been established and
10059
+     * FALSE otherwise.
10060
+     *
10061
+     * @return boolean
10062
+     */
10063
+    public static function testConnection()
10064
+    {
10065
+        if ( !isset( self::$adapter ) ) return FALSE;
10066
+
10067
+        $database = self::$adapter->getDatabase();
10068
+        try {
10069
+            @$database->connect();
10070
+        } catch ( \Exception $e ) {}
10071
+        return $database->isConnected();
10072
+    }
10073
+
10074
+    /**
10075
+     * Kickstarts redbean for you. This method should be called before you start using
10076
+     * RedBean. The Setup() method can be called without any arguments, in this case it will
10077
+     * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems).
10078
+     *
10079
+     * @param string  $dsn      Database connection string
10080
+     * @param string  $username Username for database
10081
+     * @param string  $password Password for database
10082
+     * @param boolean $frozen   TRUE if you want to setup in frozen mode
10083
+     *
10084
+     * @return ToolBox
10085
+     */
10086
+    public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE )
10087
+    {
10088
+        if ( is_null( $dsn ) ) {
10089
+            $dsn = 'sqlite:/' . sys_get_temp_dir() . '/red.db';
10090
+        }
10091
+
10092
+        self::addDatabase( 'default', $dsn, $username, $password, $frozen );
10093
+        self::selectDatabase( 'default' );
10094
+
10095
+        return self::$toolbox;
10096
+    }
10097
+
10098
+    /**
10099
+     * Toggles Narrow Field Mode.
10100
+     * See documentation in QueryWriter.
10101
+     *
10102
+     * @param boolean $mode TRUE = Narrow Field Mode
10103
+     *
10104
+     * @return void
10105
+     */
10106
+    public static function setNarrowFieldMode( $mode )
10107
+    {
10108
+        AQueryWriter::setNarrowFieldMode( $mode );
10109
+    }
10110
+
10111
+    /**
10112
+     * Starts a transaction within a closure (or other valid callback).
10113
+     * If an\Exception is thrown inside, the operation is automatically rolled back.
10114
+     * If no\Exception happens, it commits automatically.
10115
+     * It also supports (simulated) nested transactions (that is useful when
10116
+     * you have many methods that needs transactions but are unaware of
10117
+     * each other).
10118
+     * ex:
10119
+     *        $from = 1;
10120
+     *        $to = 2;
10121
+     *        $amount = 300;
10122
+     *
10123
+     *        R::transaction(function() use($from, $to, $amount)
10124
+     *        {
10125
+     *            $accountFrom = R::load('account', $from);
10126
+     *            $accountTo = R::load('account', $to);
10127
+     *
10128
+     *            $accountFrom->money -= $amount;
10129
+     *            $accountTo->money += $amount;
10130
+     *
10131
+     *            R::store($accountFrom);
10132
+     *            R::store($accountTo);
10133
+     *      });
10134
+     *
10135
+     * @param callable $callback Closure (or other callable) with the transaction logic
10136
+     *
10137
+     * @throws Security
10138
+     *
10139
+     * @return mixed
10140
+     *
10141
+     */
10142
+    public static function transaction( $callback )
10143
+    {
10144
+        if ( !is_callable( $callback ) ) {
10145
+            throw new RedException( 'R::transaction needs a valid callback.' );
10146
+        }
10147
+
10148
+        static $depth = 0;
10149
+        $result = null;
10150
+        try {
10151
+            if ( $depth == 0 ) {
10152
+                self::begin();
10153
+            }
10154
+            $depth++;
10155
+            $result = call_user_func( $callback ); //maintain 5.2 compatibility
10156
+            $depth--;
10157
+            if ( $depth == 0 ) {
10158
+                self::commit();
10159
+            }
10160
+        } catch (\Exception $exception ) {
10161
+            $depth--;
10162
+            if ( $depth == 0 ) {
10163
+                self::rollback();
10164
+            }
10165
+            throw $exception;
10166
+        }
10167
+        return $result;
10168
+    }
10169
+
10170
+    /**
10171
+     * Adds a database to the facade, afterwards you can select the database using
10172
+     * selectDatabase($key), where $key is the name you assigned to this database.
10173
+     *
10174
+     * Usage:
10175
+     *
10176
+     * R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' );
10177
+     * R::selectDatabase( 'database-1' ); //to select database again
10178
+     *
10179
+     * This method allows you to dynamically add (and select) new databases
10180
+     * to the facade. Adding a database with the same key will cause an exception.
10181
+     *
10182
+     * @param string      $key    ID for the database
10183
+     * @param string      $dsn    DSN for the database
10184
+     * @param string      $user   User for connection
10185
+     * @param NULL|string $pass   Password for connection
10186
+     * @param bool        $frozen Whether this database is frozen or not
10187
+     *
10188
+     * @return void
10189
+     */
10190
+    public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE )
10191
+    {
10192
+        if ( isset( self::$toolboxes[$key] ) ) {
10193
+            throw new RedException( 'A database has already be specified for this key.' );
10194
+        }
10195
+
10196
+        if ( is_object($dsn) ) {
10197
+            $db  = new RPDO( $dsn );
10198
+            $dbType = $db->getDatabaseType();
10199
+        } else {
10200
+            $db = new RPDO( $dsn, $user, $pass, TRUE );
10201
+            $dbType = substr( $dsn, 0, strpos( $dsn, ':' ) );
10202
+        }
10203
+
10204
+        $adapter = new DBAdapter( $db );
10205
+
10206
+        $writers     = array(
10207 10207
                     'pgsql'  => 'PostgreSQL',
10208 10208
                     'sqlite' => 'SQLiteT',
10209 10209
                     'cubrid' => 'CUBRID',
10210 10210
                     'mysql'  => 'MySQL',
10211 10211
                     'sqlsrv' => 'SQLServer',
10212
-                  );
10213
-
10214
-		$wkey = trim( strtolower( $dbType ) );
10215
-		if ( !isset( $writers[$wkey] ) ) trigger_error( 'Unsupported DSN: '.$wkey );
10216
-		$writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey];
10217
-		$writer      = new $writerClass( $adapter );
10218
-		$redbean     = new OODB( $writer, $frozen );
10219
-
10220
-		self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer );
10221
-	}
10222
-
10223
-	/**
10224
-	 * Selects a different database for the Facade to work with.
10225
-	 * If you use the R::setup() you don't need this method. This method is meant
10226
-	 * for multiple database setups. This method selects the database identified by the
10227
-	 * database ID ($key). Use addDatabase() to add a new database, which in turn
10228
-	 * can be selected using selectDatabase(). If you use R::setup(), the resulting
10229
-	 * database will be stored under key 'default', to switch (back) to this database
10230
-	 * use R::selectDatabase( 'default' ). This method returns TRUE if the database has been
10231
-	 * switched and FALSE otherwise (for instance if you already using the specified database).
10232
-	 *
10233
-	 * @param  string $key Key of the database to select
10234
-	 *
10235
-	 * @return boolean
10236
-	 */
10237
-	public static function selectDatabase( $key )
10238
-	{
10239
-		if ( self::$currentDB === $key ) {
10240
-			return FALSE;
10241
-		}
10242
-
10243
-		self::configureFacadeWithToolbox( self::$toolboxes[$key] );
10244
-		self::$currentDB = $key;
10245
-
10246
-		return TRUE;
10247
-	}
10248
-
10249
-	/**
10250
-	 * Toggles DEBUG mode.
10251
-	 * In Debug mode all SQL that happens under the hood will
10252
-	 * be printed to the screen or logged by provided logger.
10253
-	 * If no database connection has been configured using R::setup() or
10254
-	 * R::selectDatabase() this method will throw an exception.
10255
-	 * Returns the attached logger instance.
10256
-	 *
10257
-	 * @param boolean $tf
10258
-	 * @param integer $mode (0 = to STDOUT, 1 = to ARRAY)
10259
-	 *
10260
-	 * @throws Security
10261
-	 *
10262
-	 * @return Logger\RDefault
10263
-	 */
10264
-	public static function debug( $tf = TRUE, $mode = 0 )
10265
-	{
10266
-		if ($mode > 1) {
10267
-			$mode -= 2;
10268
-			$logger = new Debug;
10269
-		} else {
10270
-			$logger = new RDefault;
10271
-		}
10272
-
10273
-		if ( !isset( self::$adapter ) ) {
10274
-			throw new RedException( 'Use R::setup() first.' );
10275
-		}
10276
-		$logger->setMode($mode);
10277
-		self::$adapter->getDatabase()->setDebugMode( $tf, $logger );
10278
-
10279
-		return $logger;
10280
-	}
10281
-
10282
-	/**
10283
-	 * Turns on the fancy debugger.
10284
-	 */
10285
-	public static function fancyDebug( $toggle )
10286
-	{
10287
-		self::debug( $toggle, 2 );
10288
-	}
10289
-
10290
-	/**
10291
-	* Inspects the database schema. If you pass the type of a bean this
10292
-	* method will return the fields of its table in the database.
10293
-	* The keys of this array will be the field names and the values will be
10294
-	* the column types used to store their values.
10295
-	* If no type is passed, this method returns a list of all tables in the database.
10296
-	*
10297
-	* @param string $type Type of bean (i.e. table) you want to inspect
10298
-	*
10299
-	* @return array
10300
-	*/
10301
-	public static function inspect( $type = NULL )
10302
-	{
10303
-		return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type );
10304
-	}
10305
-
10306
-	/**
10307
-	 * Stores a bean in the database. This method takes a
10308
-	 * OODBBean Bean Object $bean and stores it
10309
-	 * in the database. If the database schema is not compatible
10310
-	 * with this bean and RedBean runs in fluid mode the schema
10311
-	 * will be altered to store the bean correctly.
10312
-	 * If the database schema is not compatible with this bean and
10313
-	 * RedBean runs in frozen mode it will throw an exception.
10314
-	 * This function returns the primary key ID of the inserted
10315
-	 * bean.
10316
-	 *
10317
-	 * The return value is an integer if possible. If it is not possible to
10318
-	 * represent the value as an integer a string will be returned.
10319
-	 *
10320
-	 * @param OODBBean|SimpleModel $bean bean to store
10321
-	 *
10322
-	 * @return integer|string
10323
-	 *
10324
-	 * @throws Security
10325
-	 */
10326
-	public static function store( $bean )
10327
-	{
10328
-		return self::$redbean->store( $bean );
10329
-	}
10330
-
10331
-	/**
10332
-	 * Toggles fluid or frozen mode. In fluid mode the database
10333
-	 * structure is adjusted to accomodate your objects. In frozen mode
10334
-	 * this is not the case.
10335
-	 *
10336
-	 * You can also pass an array containing a selection of frozen types.
10337
-	 * Let's call this chilly mode, it's just like fluid mode except that
10338
-	 * certain types (i.e. tables) aren't touched.
10339
-	 *
10340
-	 * @param boolean|array $trueFalse
10341
-	 */
10342
-	public static function freeze( $tf = TRUE )
10343
-	{
10344
-		self::$redbean->freeze( $tf );
10345
-	}
10346
-
10347
-	/**
10348
-	 * Loads multiple types of beans with the same ID.
10349
-	 * This might look like a strange method, however it can be useful
10350
-	 * for loading a one-to-one relation.
10351
-	 *
10352
-	 * Usage:
10353
-	 * list($author, $bio) = R::load('author, bio', $id);
10354
-	 *
10355
-	 * @param string|array $types
10356
-	 * @param mixed        $id
10357
-	 *
10358
-	 * @return OODBBean
10359
-	 */
10360
-	public static function loadMulti( $types, $id )
10361
-	{
10362
-		if ( is_string( $types ) ) {
10363
-			$types = explode( ',', $types );
10364
-		}
10365
-
10366
-		if ( !is_array( $types ) ) {
10367
-			return array();
10368
-		}
10369
-
10370
-		foreach ( $types as $k => $typeItem ) {
10371
-			$types[$k] = self::$redbean->load( $typeItem, $id );
10372
-		}
10373
-
10374
-		return $types;
10375
-	}
10376
-
10377
-	/**
10378
-	 * Loads a bean from the object database.
10379
-	 * It searches for a OODBBean Bean Object in the
10380
-	 * database. It does not matter how this bean has been stored.
10381
-	 * RedBean uses the primary key ID $id and the string $type
10382
-	 * to find the bean. The $type specifies what kind of bean you
10383
-	 * are looking for; this is the same type as used with the
10384
-	 * dispense() function. If RedBean finds the bean it will return
10385
-	 * the OODB Bean object; if it cannot find the bean
10386
-	 * RedBean will return a new bean of type $type and with
10387
-	 * primary key ID 0. In the latter case it acts basically the
10388
-	 * same as dispense().
10389
-	 *
10390
-	 * Important note:
10391
-	 * If the bean cannot be found in the database a new bean of
10392
-	 * the specified type will be generated and returned.
10393
-	 *
10394
-	 * @param string  $type type of bean you want to load
10395
-	 * @param integer $id   ID of the bean you want to load
10396
-	 *
10397
-	 * @throws SQL
10398
-	 *
10399
-	 * @return OODBBean
10400
-	 */
10401
-	public static function load( $type, $id )
10402
-	{
10403
-		return self::$redbean->load( $type, $id );
10404
-	}
10405
-
10406
-	/**
10407
-	 * Removes a bean from the database.
10408
-	 * This function will remove the specified OODBBean
10409
-	 * Bean Object from the database.
10410
-	 *
10411
-	 * This facade method also accepts a type-id combination,
10412
-	 * in the latter case this method will attempt to load the specified bean
10413
-	 * and THEN trash it.
10414
-	 *
10415
-	 * @param string|OODBBean|SimpleModel $bean bean you want to remove from database
10416
-	 * @param integer $id (optional)
10417
-	 *
10418
-	 * @return void
10419
-	 */
10420
-	public static function trash( $beanOrType, $id = NULL )
10421
-	{
10422
-		if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) );
10423
-		return self::$redbean->trash( $beanOrType );
10424
-	}
10425
-
10426
-	/**
10427
-	 * Dispenses a new RedBean OODB Bean for use with
10428
-	 * the rest of the methods.
10429
-	 *
10430
-	 * @param string|array $typeOrBeanArray   type or bean array to import
10431
-	 * @param integer      $number            number of beans to dispense
10432
-	 * @param boolean	     $alwaysReturnArray if TRUE always returns the result as an array
10433
-	 *
10434
-	 * @return array|OODBBean
10435
-	 *
10436
-	 * @throws Security
10437
-	 */
10438
-	public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE )
10439
-	{
10440
-		if ( is_array($typeOrBeanArray) ) {
10441
-
10442
-			if ( !isset( $typeOrBeanArray['_type'] ) ) {
10443
-				$list = array();
10444
-				foreach( $typeOrBeanArray as $beanArray ) if ( !( is_array( $beanArray ) && isset( $beanArray['_type'] ) ) ) throw new RedException( 'Invalid Array Bean' );
10445
-				foreach( $typeOrBeanArray as $beanArray ) $list[] = self::dispense( $beanArray );
10446
-				return $list;
10447
-			}
10448
-
10449
-			$import = $typeOrBeanArray;
10450
-			$type = $import['_type'];
10451
-			unset( $import['_type'] );
10452
-		} else {
10453
-			$type = $typeOrBeanArray;
10454
-		}
10455
-
10456
-		if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) {
10457
-			throw new RedException( 'Invalid type: ' . $type );
10458
-		}
10459
-
10460
-		$beanOrBeans = self::$redbean->dispense( $type, $num, $alwaysReturnArray );
10461
-
10462
-		if ( isset( $import ) ) {
10463
-			$beanOrBeans->import( $import );
10464
-		}
10465
-
10466
-		return $beanOrBeans;
10467
-	}
10468
-
10469
-	/**
10470
-	 * Takes a comma separated list of bean types
10471
-	 * and dispenses these beans. For each type in the list
10472
-	 * you can specify the number of beans to be dispensed.
10473
-	 *
10474
-	 * Usage:
10475
-	 *
10476
-	 * list($book, $page, $text) = R::dispenseAll('book,page,text');
10477
-	 *
10478
-	 * This will dispense a book, a page and a text. This way you can
10479
-	 * quickly dispense beans of various types in just one line of code.
10480
-	 *
10481
-	 * Usage:
10482
-	 *
10483
-	 * list($book, $pages) = R::dispenseAll('book,page*100');
10484
-	 *
10485
-	 * This returns an array with a book bean and then another array
10486
-	 * containing 100 page beans.
10487
-	 *
10488
-	 * @param string  $order      a description of the desired dispense order using the syntax above
10489
-	 * @param boolean $onlyArrays return only arrays even if amount < 2
10490
-	 *
10491
-	 * @return array
10492
-	 */
10493
-	public static function dispenseAll( $order, $onlyArrays = FALSE )
10494
-	{
10495
-
10496
-		$list = array();
10497
-
10498
-		foreach( explode( ',', $order ) as $order ) {
10499
-			if ( strpos( $order, '*' ) !== false ) {
10500
-				list( $type, $amount ) = explode( '*', $order );
10501
-			} else {
10502
-				$type   = $order;
10503
-				$amount = 1;
10504
-			}
10505
-
10506
-			$list[] = self::dispense( $type, $amount, $onlyArrays );
10507
-		}
10508
-
10509
-		return $list;
10510
-	}
10511
-
10512
-	/**
10513
-	 * Convience method. Tries to find beans of a certain type,
10514
-	 * if no beans are found, it dispenses a bean of that type.
10515
-	 *
10516
-	 * @param  string $type     type of bean you are looking for
10517
-	 * @param  string $sql      SQL code for finding the bean
10518
-	 * @param  array  $bindings parameters to bind to SQL
10519
-	 *
10520
-	 * @return array
10521
-	 */
10522
-	public static function findOrDispense( $type, $sql = NULL, $bindings = array() )
10523
-	{
10524
-		return self::$finder->findOrDispense( $type, $sql, $bindings );
10525
-	}
10526
-
10527
-	/**
10528
-	 * Finds a bean using a type and a where clause (SQL).
10529
-	 * As with most Query tools in RedBean you can provide values to
10530
-	 * be inserted in the SQL statement by populating the value
10531
-	 * array parameter; you can either use the question mark notation
10532
-	 * or the slot-notation (:keyname).
10533
-	 *
10534
-	 * @param string $type     type   the type of bean you are looking for
10535
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10536
-	 * @param array  $bindings values array of values to be bound to parameters in query
10537
-	 *
10538
-	 * @return array
10539
-	 */
10540
-	public static function find( $type, $sql = NULL, $bindings = array() )
10541
-	{
10542
-		return self::$finder->find( $type, $sql, $bindings );
10543
-	}
10544
-
10545
-	/**
10546
-	 * @see Facade::find
10547
-	 *      The findAll() method differs from the find() method in that it does
10548
-	 *      not assume a WHERE-clause, so this is valid:
10549
-	 *
10550
-	 * R::findAll('person',' ORDER BY name DESC ');
10551
-	 *
10552
-	 * Your SQL does not have to start with a valid WHERE-clause condition.
10553
-	 *
10554
-	 * @param string $type     type   the type of bean you are looking for
10555
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10556
-	 * @param array  $bindings values array of values to be bound to parameters in query
10557
-	 *
10558
-	 * @return array
10559
-	 */
10560
-	public static function findAll( $type, $sql = NULL, $bindings = array() )
10561
-	{
10562
-		return self::$finder->find( $type, $sql, $bindings );
10563
-	}
10564
-
10565
-	/**
10566
-	 * @see Facade::find
10567
-	 * The variation also exports the beans (i.e. it returns arrays).
10568
-	 *
10569
-	 * @param string $type     type   the type of bean you are looking for
10570
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10571
-	 * @param array  $bindings values array of values to be bound to parameters in query
10572
-	 *
10573
-	 * @return array
10574
-	 */
10575
-	public static function findAndExport( $type, $sql = NULL, $bindings = array() )
10576
-	{
10577
-		return self::$finder->findAndExport( $type, $sql, $bindings );
10578
-	}
10579
-
10580
-	/**
10581
-	 * @see Facade::find
10582
-	 * This variation returns the first bean only.
10583
-	 *
10584
-	 * @param string $type     type   the type of bean you are looking for
10585
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10586
-	 * @param array  $bindings values array of values to be bound to parameters in query
10587
-	 *
10588
-	 * @return OODBBean
10589
-	 */
10590
-	public static function findOne( $type, $sql = NULL, $bindings = array() )
10591
-	{
10592
-		return self::$finder->findOne( $type, $sql, $bindings );
10593
-	}
10594
-
10595
-	/**
10596
-	 * @see Facade::find
10597
-	 * This variation returns the last bean only.
10598
-	 *
10599
-	 * @param string $type     type   the type of bean you are looking for
10600
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10601
-	 * @param array  $bindings values array of values to be bound to parameters in query
10602
-	 *
10603
-	 * @return OODBBean
10604
-	 */
10605
-	public static function findLast( $type, $sql = NULL, $bindings = array() )
10606
-	{
10607
-		return self::$finder->findLast( $type, $sql, $bindings );
10608
-	}
10609
-
10610
-	/**
10611
-	 * Finds a bean collection.
10612
-	 * Use this for large datasets.
10613
-	 *
10614
-	 * @param string $type     type   the type of bean you are looking for
10615
-	 * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10616
-	 * @param array  $bindings values array of values to be bound to parameters in query
10617
-	 *
10618
-	 * @return BeanCollection
10619
-	 */
10620
-	public static function findCollection( $type, $sql = NULL, $bindings = array() )
10621
-	{
10622
-		return self::$finder->findCollection( $type, $sql, $bindings );
10623
-	}
10624
-
10625
-	/**
10626
-	 * Finds multiple types of beans at once and offers additional
10627
-	 * remapping functionality. This is a very powerful yet complex function.
10628
-	 * For details see Finder::findMulti().
10629
-	 *
10630
-	 * @see Finder::findMulti()
10631
-	 *
10632
-	 * @param array|string $types      a list of bean types to find
10633
-	 * @param string|array $sqlOrArr   SQL query string or result set array
10634
-	 * @param array        $bindings   SQL bindings
10635
-	 * @param array        $remappings An array of remapping arrays containing closures
10636
-	 *
10637
-	 * @return array
10638
-	 */
10639
-	public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() )
10640
-	{
10641
-		return self::$finder->findMulti( $types, $sql, $bindings, $remappings );
10642
-	}
10643
-
10644
-	/**
10645
-	 * Returns an array of beans. Pass a type and a series of ids and
10646
-	 * this method will bring you the corresponding beans.
10647
-	 *
10648
-	 * important note: Because this method loads beans using the load()
10649
-	 * function (but faster) it will return empty beans with ID 0 for
10650
-	 * every bean that could not be located. The resulting beans will have the
10651
-	 * passed IDs as their keys.
10652
-	 *
10653
-	 * @param string $type type of beans
10654
-	 * @param array  $ids  ids to load
10655
-	 *
10656
-	 * @return array
10657
-	 */
10658
-	public static function batch( $type, $ids )
10659
-	{
10660
-		return self::$redbean->batch( $type, $ids );
10661
-	}
10662
-
10663
-	/**
10664
-	 * @see Facade::batch
10665
-	 *
10666
-	 * Alias for batch(). Batch method is older but since we added so-called *All
10667
-	 * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to
10668
-	 * improve the consistency of the Facade API and also add an alias for batch() called
10669
-	 * loadAll.
10670
-	 *
10671
-	 * @param string $type type of beans
10672
-	 * @param array  $ids  ids to load
10673
-	 *
10674
-	 * @return array
10675
-	 */
10676
-	public static function loadAll( $type, $ids )
10677
-	{
10678
-		return self::$redbean->batch( $type, $ids );
10679
-	}
10680
-
10681
-	/**
10682
-	 * Convenience function to execute Queries directly.
10683
-	 * Executes SQL.
10684
-	 *
10685
-	 * @param string $sql       sql    SQL query to execute
10686
-	 * @param array  $bindings  values a list of values to be bound to query parameters
10687
-	 *
10688
-	 * @return integer
10689
-	 */
10690
-	public static function exec( $sql, $bindings = array() )
10691
-	{
10692
-		return self::query( 'exec', $sql, $bindings );
10693
-	}
10694
-
10695
-	/**
10696
-	 * Convenience function to execute Queries directly.
10697
-	 * Executes SQL.
10698
-	 *
10699
-	 * @param string $sql       sql    SQL query to execute
10700
-	 * @param array  $bindings  values a list of values to be bound to query parameters
10701
-	 *
10702
-	 * @return array
10703
-	 */
10704
-	public static function getAll( $sql, $bindings = array() )
10705
-	{
10706
-		return self::query( 'get', $sql, $bindings );
10707
-	}
10708
-
10709
-	/**
10710
-	 * Convenience function to execute Queries directly.
10711
-	 * Executes SQL.
10712
-	 *
10713
-	 * @param string $sql       sql    SQL query to execute
10714
-	 * @param array  $bindings  values a list of values to be bound to query parameters
10715
-	 *
10716
-	 * @return string
10717
-	 */
10718
-	public static function getCell( $sql, $bindings = array() )
10719
-	{
10720
-		return self::query( 'getCell', $sql, $bindings );
10721
-	}
10722
-
10723
-	/**
10724
-	 * Convenience function to execute Queries directly.
10725
-	 * Executes SQL.
10726
-	 *
10727
-	 * @param string $sql       sql    SQL query to execute
10728
-	 * @param array  $bindings  values a list of values to be bound to query parameters
10729
-	 *
10730
-	 * @return array
10731
-	 */
10732
-	public static function getRow( $sql, $bindings = array() )
10733
-	{
10734
-		return self::query( 'getRow', $sql, $bindings );
10735
-	}
10736
-
10737
-	/**
10738
-	 * Convenience function to execute Queries directly.
10739
-	 * Executes SQL.
10740
-	 *
10741
-	 * @param string $sql       sql    SQL query to execute
10742
-	 * @param array  $bindings  values a list of values to be bound to query parameters
10743
-	 *
10744
-	 * @return array
10745
-	 */
10746
-	public static function getCol( $sql, $bindings = array() )
10747
-	{
10748
-		return self::query( 'getCol', $sql, $bindings );
10749
-	}
10750
-
10751
-	/**
10752
-	 * Convenience function to execute Queries directly.
10753
-	 * Executes SQL.
10754
-	 * Results will be returned as an associative array. The first
10755
-	 * column in the select clause will be used for the keys in this array and
10756
-	 * the second column will be used for the values. If only one column is
10757
-	 * selected in the query, both key and value of the array will have the
10758
-	 * value of this field for each row.
10759
-	 *
10760
-	 * @param string $sql       sql    SQL query to execute
10761
-	 * @param array  $bindings  values a list of values to be bound to query parameters
10762
-	 *
10763
-	 * @return array
10764
-	 */
10765
-	public static function getAssoc( $sql, $bindings = array() )
10766
-	{
10767
-		return self::query( 'getAssoc', $sql, $bindings );
10768
-	}
10769
-
10770
-	/**
10771
-	 * Convenience function to execute Queries directly.
10772
-	 * Executes SQL.
10773
-	 * Results will be returned as an associative array indexed by the first
10774
-	 * column in the select.
10775
-	 *
10776
-	 * @param string $sql       sql    SQL query to execute
10777
-	 * @param array  $bindings  values a list of values to be bound to query parameters
10778
-	 *
10779
-	 * @return array
10780
-	 */
10781
-	public static function getAssocRow( $sql, $bindings = array() )
10782
-	{
10783
-		return self::query( 'getAssocRow', $sql, $bindings );
10784
-	}
10785
-
10786
-	/**
10787
-	 * Returns the insert ID for databases that support/require this
10788
-	 * functionality. Alias for R::getAdapter()->getInsertID().
10789
-	 *
10790
-	 * @return mixed
10791
-	 */
10792
-	public static function getInsertID()
10793
-	{
10794
-		return self::$adapter->getInsertID();
10795
-	}
10796
-
10797
-	/**
10798
-	 * Makes a copy of a bean. This method makes a deep copy
10799
-	 * of the bean.The copy will have the following features.
10800
-	 * - All beans in own-lists will be duplicated as well
10801
-	 * - All references to shared beans will be copied but not the shared beans themselves
10802
-	 * - All references to parent objects (_id fields) will be copied but not the parents themselves
10803
-	 * In most cases this is the desired scenario for copying beans.
10804
-	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
10805
-	 * (i.e. one that already has been processed) the ID of the bean will be returned.
10806
-	 * This should not happen though.
10807
-	 *
10808
-	 * Note:
10809
-	 * This function does a reflectional database query so it may be slow.
10810
-	 *
10811
-	 * @deprecated
10812
-	 * This function is deprecated in favour of R::duplicate().
10813
-	 * This function has a confusing method signature, the R::duplicate() function
10814
-	 * only accepts two arguments: bean and filters.
10815
-	 *
10816
-	 * @param OODBBean $bean  bean to be copied
10817
-	 * @param array    $trail for internal usage, pass array()
10818
-	 * @param boolean  $pid   for internal usage
10819
-	 * @param array	   $white white list filter with bean types to duplicate
10820
-	 *
10821
-	 * @return array
10822
-	 */
10823
-	public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() )
10824
-	{
10825
-		self::$duplicationManager->setFilters( $filters );
10826
-		return self::$duplicationManager->dup( $bean, $trail, $pid );
10827
-	}
10828
-
10829
-	/**
10830
-	 * Makes a deep copy of a bean. This method makes a deep copy
10831
-	 * of the bean.The copy will have the following:
10832
-	 *
10833
-	 * - All beans in own-lists will be duplicated as well
10834
-	 * - All references to shared beans will be copied but not the shared beans themselves
10835
-	 * - All references to parent objects (_id fields) will be copied but not the parents themselves
10836
-	 *
10837
-	 * In most cases this is the desired scenario for copying beans.
10838
-	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
10839
-	 * (i.e. one that already has been processed) the ID of the bean will be returned.
10840
-	 * This should not happen though.
10841
-	 *
10842
-	 * Note:
10843
-	 * This function does a reflectional database query so it may be slow.
10844
-	 *
10845
-	 * Note:
10846
-	 * This is a simplified version of the deprecated R::dup() function.
10847
-	 *
10848
-	 * @param OODBBean $bean  bean to be copied
10849
-	 * @param array	   $white white list filter with bean types to duplicate
10850
-	 *
10851
-	 * @return array
10852
-	 */
10853
-	public static function duplicate( $bean, $filters = array() )
10854
-	{
10855
-		return self::dup( $bean, array(), FALSE, $filters );
10856
-	}
10857
-
10858
-	/**
10859
-	 * Exports a collection of beans. Handy for XML/JSON exports with a
10860
-	 * Javascript framework like Dojo or ExtJS.
10861
-	 * What will be exported:
10862
-	 * - contents of the bean
10863
-	 * - all own bean lists (recursively)
10864
-	 * - all shared beans (not THEIR own lists)
10865
-	 *
10866
-	 * @param    array|OODBBean $beans   beans to be exported
10867
-	 * @param    boolean        $parents whether you want parent beans to be exported
10868
-	 * @param    array          $filters whitelist of types
10869
-	 *
10870
-	 * @return    array
10871
-	 */
10872
-	public static function exportAll( $beans, $parents = FALSE, $filters = array())
10873
-	{
10874
-		return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle );
10875
-	}
10876
-
10877
-	/**
10878
-	 * Selects case style for export.
10879
-	 * This will determine the case style for the keys of exported beans (see exportAll).
10880
-	 * The following options are accepted:
10881
-	 *
10882
-	 * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid )
10883
-	 * 'camel'   Camel Case   (i.e. bookId isValid   )
10884
-	 * 'dolphin' Dolphin Case (i.e. bookID isValid   ) Like CamelCase but ID is written all uppercase
10885
-	 *
10886
-	 * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different
10887
-	 * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id.
10888
-	 * Due to information loss this cannot be corrected. However if you might try
10889
-	 * DolphinCase for IDs it takes into account the exception concerning IDs.
10890
-	 *
10891
-	 * @param string $caseStyle case style identifier
10892
-	 *
10893
-	 * @return void
10894
-	 */
10895
-	public static function useExportCase( $caseStyle = 'default' )
10896
-	{
10897
-		if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' );
10898
-		self::$exportCaseStyle = $caseStyle;
10899
-	}
10900
-
10901
-	/**
10902
-	 * Converts a series of rows to beans.
10903
-	 * This method converts a series of rows to beans.
10904
-	 * The type of the desired output beans can be specified in the
10905
-	 * first parameter. The second parameter is meant for the database
10906
-	 * result rows.
10907
-	 *
10908
-	 * @param string $type type of beans to produce
10909
-	 * @param array  $rows must contain an array of array
10910
-	 *
10911
-	 * @return array
10912
-	 */
10913
-	public static function convertToBeans( $type, $rows )
10914
-	{
10915
-		return self::$redbean->convertToBeans( $type, $rows );
10916
-	}
10917
-
10918
-	/**
10919
-	 * Part of RedBeanPHP Tagging API.
10920
-	 * Tests whether a bean has been associated with one ore more
10921
-	 * of the listed tags. If the third parameter is TRUE this method
10922
-	 * will return TRUE only if all tags that have been specified are indeed
10923
-	 * associated with the given bean, otherwise FALSE.
10924
-	 * If the third parameter is FALSE this
10925
-	 * method will return TRUE if one of the tags matches, FALSE if none
10926
-	 * match.
10927
-	 *
10928
-	 * @param  OODBBean $bean bean to check for tags
10929
-	 * @param  array            $tags list of tags
10930
-	 * @param  boolean          $all  whether they must all match or just some
10931
-	 *
10932
-	 * @return boolean
10933
-	 */
10934
-	public static function hasTag( $bean, $tags, $all = FALSE )
10935
-	{
10936
-		return self::$tagManager->hasTag( $bean, $tags, $all );
10937
-	}
10938
-
10939
-	/**
10940
-	 * Part of RedBeanPHP Tagging API.
10941
-	 * Removes all specified tags from the bean. The tags specified in
10942
-	 * the second parameter will no longer be associated with the bean.
10943
-	 *
10944
-	 * @param  OODBBean $bean    tagged bean
10945
-	 * @param  array            $tagList list of tags (names)
10946
-	 *
10947
-	 * @return void
10948
-	 */
10949
-	public static function untag( $bean, $tagList )
10950
-	{
10951
-		self::$tagManager->untag( $bean, $tagList );
10952
-	}
10953
-
10954
-	/**
10955
-	 * Part of RedBeanPHP Tagging API.
10956
-	 * Tags a bean or returns tags associated with a bean.
10957
-	 * If $tagList is NULL or omitted this method will return a
10958
-	 * comma separated list of tags associated with the bean provided.
10959
-	 * If $tagList is a comma separated list (string) of tags all tags will
10960
-	 * be associated with the bean.
10961
-	 * You may also pass an array instead of a string.
10962
-	 *
10963
-	 * @param OODBBean $bean    bean
10964
-	 * @param mixed            $tagList tags
10965
-	 *
10966
-	 * @return string
10967
-	 */
10968
-	public static function tag( OODBBean $bean, $tagList = NULL )
10969
-	{
10970
-		return self::$tagManager->tag( $bean, $tagList );
10971
-	}
10972
-
10973
-	/**
10974
-	 * Part of RedBeanPHP Tagging API.
10975
-	 * Adds tags to a bean.
10976
-	 * If $tagList is a comma separated list of tags all tags will
10977
-	 * be associated with the bean.
10978
-	 * You may also pass an array instead of a string.
10979
-	 *
10980
-	 * @param OODBBean $bean    bean
10981
-	 * @param array            $tagList list of tags to add to bean
10982
-	 *
10983
-	 * @return void
10984
-	 */
10985
-	public static function addTags( OODBBean $bean, $tagList )
10986
-	{
10987
-		self::$tagManager->addTags( $bean, $tagList );
10988
-	}
10989
-
10990
-	/**
10991
-	 * Part of RedBeanPHP Tagging API.
10992
-	 * Returns all beans that have been tagged with one of the tags given.
10993
-	 *
10994
-	 * @param string $beanType type of bean you are looking for
10995
-	 * @param array  $tagList  list of tags to match
10996
-	 * @param string $sql      additional SQL
10997
-	 * @param array  $bindings bindings
10998
-	 *
10999
-	 * @return array
11000
-	 */
11001
-	public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
11002
-	{
11003
-		return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings );
11004
-	}
11005
-
11006
-	/**
11007
-	 * Part of RedBeanPHP Tagging API.
11008
-	 * Returns all beans that have been tagged with ALL of the tags given.
11009
-	 *
11010
-	 * @param string $beanType type of bean you are looking for
11011
-	 * @param array  $tagList  list of tags to match
11012
-	 * @param string $sql      additional SQL
11013
-	 * @param array  $bindings bindings
11014
-	 *
11015
-	 * @return array
11016
-	 */
11017
-	public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
11018
-	{
11019
-		return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings );
11020
-	}
11021
-
11022
-	/**
11023
-	 * Wipes all beans of type $beanType.
11024
-	 *
11025
-	 * @param string $beanType type of bean you want to destroy entirely
11026
-	 *
11027
-	 * @return boolean
11028
-	 */
11029
-	public static function wipe( $beanType )
11030
-	{
11031
-		return Facade::$redbean->wipe( $beanType );
11032
-	}
11033
-
11034
-	/**
11035
-	 * Counts the number of beans of type $type.
11036
-	 * This method accepts a second argument to modify the count-query.
11037
-	 * A third argument can be used to provide bindings for the SQL snippet.
11038
-	 *
11039
-	 * @param string $type     type of bean we are looking for
11040
-	 * @param string $addSQL   additional SQL snippet
11041
-	 * @param array  $bindings parameters to bind to SQL
11042
-	 *
11043
-	 * @return integer
11044
-	 *
11045
-	 * @throws SQL
11046
-	 */
11047
-	public static function count( $type, $addSQL = '', $bindings = array() )
11048
-	{
11049
-		return Facade::$redbean->count( $type, $addSQL, $bindings );
11050
-	}
11051
-
11052
-	/**
11053
-	 * Configures the facade, want to have a new Writer? A new Object Database or a new
11054
-	 * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new
11055
-	 * toolbox.
11056
-	 *
11057
-	 * @param ToolBox $tb toolbox
11058
-	 *
11059
-	 * @return ToolBox
11060
-	 */
11061
-	public static function configureFacadeWithToolbox( ToolBox $tb )
11062
-	{
11063
-		$oldTools                 = self::$toolbox;
11064
-
11065
-		self::$toolbox            = $tb;
11066
-
11067
-		self::$writer             = self::$toolbox->getWriter();
11068
-		self::$adapter            = self::$toolbox->getDatabaseAdapter();
11069
-		self::$redbean            = self::$toolbox->getRedBean();
11070
-		self::$finder             = new Finder( self::$toolbox );
11071
-
11072
-		self::$associationManager = new AssociationManager( self::$toolbox );
11073
-
11074
-		self::$redbean->setAssociationManager( self::$associationManager );
11075
-
11076
-		self::$labelMaker         = new LabelMaker( self::$toolbox );
11077
-
11078
-		$helper                   = new SimpleModelHelper();
11079
-
11080
-		$helper->attachEventListeners( self::$redbean );
11081
-
11082
-		self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper );
11083
-
11084
-		self::$duplicationManager = new DuplicationManager( self::$toolbox );
11085
-		self::$tagManager         = new TagManager( self::$toolbox );
11086
-
11087
-		return $oldTools;
11088
-	}
11089
-
11090
-	/**
11091
-	 * Facade Convience method for adapter transaction system.
11092
-	 * Begins a transaction.
11093
-	 *
11094
-	 * @return bool
11095
-	 */
11096
-	public static function begin()
11097
-	{
11098
-		if ( !self::$redbean->isFrozen() ) return FALSE;
11099
-
11100
-		self::$adapter->startTransaction();
11101
-
11102
-		return TRUE;
11103
-	}
11104
-
11105
-	/**
11106
-	 * Facade Convience method for adapter transaction system.
11107
-	 * Commits a transaction.
11108
-	 *
11109
-	 * @return bool
11110
-	 */
11111
-	public static function commit()
11112
-	{
11113
-		if ( !self::$redbean->isFrozen() ) return FALSE;
11114
-
11115
-		self::$adapter->commit();
11116
-
11117
-		return TRUE;
11118
-	}
11119
-
11120
-	/**
11121
-	 * Facade Convience method for adapter transaction system.
11122
-	 * Rolls back a transaction.
11123
-	 *
11124
-	 * @return bool
11125
-	 */
11126
-	public static function rollback()
11127
-	{
11128
-		if ( !self::$redbean->isFrozen() ) return FALSE;
11129
-
11130
-		self::$adapter->rollback();
11131
-
11132
-		return TRUE;
11133
-	}
11134
-
11135
-	/**
11136
-	 * Returns a list of columns. Format of this array:
11137
-	 * array( fieldname => type )
11138
-	 * Note that this method only works in fluid mode because it might be
11139
-	 * quite heavy on production servers!
11140
-	 *
11141
-	 * @param  string $table   name of the table (not type) you want to get columns of
11142
-	 *
11143
-	 * @return array
11144
-	 */
11145
-	public static function getColumns( $table )
11146
-	{
11147
-		return self::$writer->getColumns( $table );
11148
-	}
11149
-
11150
-	/**
11151
-	 * Generates question mark slots for an array of values.
11152
-	 *
11153
-	 * @param array  $array    array to generate question mark slots for
11154
-	 *
11155
-	 * @return string
11156
-	 */
11157
-	public static function genSlots( $array, $template = NULL )
11158
-	{
11159
-		$str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : '';
11160
-		return ( is_null( $template ) ||  $str === '' ) ? $str : sprintf( $template, $str );
11161
-	}
11162
-
11163
-	/**
11164
-	 * Flattens a multi dimensional bindings array for use with genSlots().
11165
-	 *
11166
-	 * @param array $array array to flatten
11167
-	 *
11168
-	 * @return array
11169
-	 */
11170
-	public static function flat( $array, $result = array() )
11171
-	{
11172
-		foreach( $array as $value ) {
11173
-			if ( is_array( $value ) ) $result = self::flat( $value, $result );
11174
-			else $result[] = $value;
11175
-		}
11176
-		return $result;
11177
-	}
11178
-
11179
-	/**
11180
-	 * Nukes the entire database.
11181
-	 * This will remove all schema structures from the database.
11182
-	 * Only works in fluid mode. Be careful with this method.
11183
-	 *
11184
-	 * @warning dangerous method, will remove all tables, columns etc.
11185
-	 *
11186
-	 * @return void
11187
-	 */
11188
-	public static function nuke()
11189
-	{
11190
-		if ( !self::$redbean->isFrozen() ) {
11191
-			self::$writer->wipeAll();
11192
-		}
11193
-	}
11194
-
11195
-	/**
11196
-	 * Short hand function to store a set of beans at once, IDs will be
11197
-	 * returned as an array. For information please consult the R::store()
11198
-	 * function.
11199
-	 * A loop saver.
11200
-	 *
11201
-	 * @param array $beans list of beans to be stored
11202
-	 *
11203
-	 * @return array
11204
-	 */
11205
-	public static function storeAll( $beans )
11206
-	{
11207
-		$ids = array();
11208
-		foreach ( $beans as $bean ) {
11209
-			$ids[] = self::store( $bean );
11210
-		}
11211
-
11212
-		return $ids;
11213
-	}
11214
-
11215
-	/**
11216
-	 * Short hand function to trash a set of beans at once.
11217
-	 * For information please consult the R::trash() function.
11218
-	 * A loop saver.
11219
-	 *
11220
-	 * @param array $beans list of beans to be trashed
11221
-	 *
11222
-	 * @return void
11223
-	 */
11224
-	public static function trashAll( $beans )
11225
-	{
11226
-		foreach ( $beans as $bean ) {
11227
-			self::trash( $bean );
11228
-		}
11229
-	}
11230
-
11231
-	/**
11232
-	 * Toggles Writer Cache.
11233
-	 * Turns the Writer Cache on or off. The Writer Cache is a simple
11234
-	 * query based caching system that may improve performance without the need
11235
-	 * for cache management. This caching system will cache non-modifying queries
11236
-	 * that are marked with special SQL comments. As soon as a non-marked query
11237
-	 * gets executed the cache will be flushed. Only non-modifying select queries
11238
-	 * have been marked therefore this mechanism is a rather safe way of caching, requiring
11239
-	 * no explicit flushes or reloads. Of course this does not apply if you intend to test
11240
-	 * or simulate concurrent querying.
11241
-	 *
11242
-	 * @param boolean $yesNo TRUE to enable cache, FALSE to disable cache
11243
-	 *
11244
-	 * @return void
11245
-	 */
11246
-	public static function useWriterCache( $yesNo )
11247
-	{
11248
-		self::getWriter()->setUseCache( $yesNo );
11249
-	}
11250
-
11251
-
11252
-	/**
11253
-	 * A label is a bean with only an id, type and name property.
11254
-	 * This function will dispense beans for all entries in the array. The
11255
-	 * values of the array will be assigned to the name property of each
11256
-	 * individual bean.
11257
-	 *
11258
-	 * @param string $type   type of beans you would like to have
11259
-	 * @param array  $labels list of labels, names for each bean
11260
-	 *
11261
-	 * @return array
11262
-	 */
11263
-	public static function dispenseLabels( $type, $labels )
11264
-	{
11265
-		return self::$labelMaker->dispenseLabels( $type, $labels );
11266
-	}
11267
-
11268
-	/**
11269
-	 * Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs.
11270
-	 * Either returns a (newly created) bean respresenting the desired ENUM
11271
-	 * value or returns a list of all enums for the type.
11272
-	 *
11273
-	 * To obtain (and add if necessary) an ENUM value:
11274
-	 *
11275
-	 * $tea->flavour = R::enum( 'flavour:apple' );
11276
-	 *
11277
-	 * Returns a bean of type 'flavour' with  name = apple.
11278
-	 * This will add a bean with property name (set to APPLE) to the database
11279
-	 * if it does not exist yet.
11280
-	 *
11281
-	 * To obtain all flavours:
11282
-	 *
11283
-	 * R::enum('flavour');
11284
-	 *
11285
-	 * To get a list of all flavour names:
11286
-	 *
11287
-	 * R::gatherLabels( R::enum( 'flavour' ) );
11288
-	 *
11289
-	 * @param string $enum either type or type-value
11290
-	 *
11291
-	 * @return array|OODBBean
11292
-	 */
11293
-	public static function enum( $enum )
11294
-	{
11295
-		return self::$labelMaker->enum( $enum );
11296
-	}
11297
-
11298
-	/**
11299
-	 * Gathers labels from beans. This function loops through the beans,
11300
-	 * collects the values of the name properties of each individual bean
11301
-	 * and stores the names in a new array. The array then gets sorted using the
11302
-	 * default sort function of PHP (sort).
11303
-	 *
11304
-	 * @param array $beans list of beans to loop
11305
-	 *
11306
-	 * @return array
11307
-	 */
11308
-	public static function gatherLabels( $beans )
11309
-	{
11310
-		return self::$labelMaker->gatherLabels( $beans );
11311
-	}
11312
-
11313
-	/**
11314
-	 * Closes the database connection.
11315
-	 *
11316
-	 * @return void
11317
-	 */
11318
-	public static function close()
11319
-	{
11320
-		if ( isset( self::$adapter ) ) {
11321
-			self::$adapter->close();
11322
-		}
11323
-	}
11324
-
11325
-	/**
11326
-	 * Simple convenience function, returns ISO date formatted representation
11327
-	 * of $time.
11328
-	 *
11329
-	 * @param mixed $time UNIX timestamp
11330
-	 *
11331
-	 * @return string
11332
-	 */
11333
-	public static function isoDate( $time = NULL )
11334
-	{
11335
-		if ( !$time ) {
11336
-			$time = time();
11337
-		}
11338
-
11339
-		return @date( 'Y-m-d', $time );
11340
-	}
11341
-
11342
-	/**
11343
-	 * Simple convenience function, returns ISO date time
11344
-	 * formatted representation
11345
-	 * of $time.
11346
-	 *
11347
-	 * @param mixed $time UNIX timestamp
11348
-	 *
11349
-	 * @return string
11350
-	 */
11351
-	public static function isoDateTime( $time = NULL )
11352
-	{
11353
-		if ( !$time ) $time = time();
11354
-
11355
-		return @date( 'Y-m-d H:i:s', $time );
11356
-	}
11357
-
11358
-	/**
11359
-	 * Optional accessor for neat code.
11360
-	 * Sets the database adapter you want to use.
11361
-	 *
11362
-	 * @param Adapter $adapter
11363
-	 *
11364
-	 * @return void
11365
-	 */
11366
-	public static function setDatabaseAdapter( Adapter $adapter )
11367
-	{
11368
-		self::$adapter = $adapter;
11369
-	}
11370
-
11371
-	/**
11372
-	 * Optional accessor for neat code.
11373
-	 * Sets the database adapter you want to use.
11374
-	 *
11375
-	 * @param QueryWriter $writer
11376
-	 *
11377
-	 * @return void
11378
-	 */
11379
-	public static function setWriter( QueryWriter $writer )
11380
-	{
11381
-		self::$writer = $writer;
11382
-	}
11383
-
11384
-	/**
11385
-	 * Optional accessor for neat code.
11386
-	 * Sets the database adapter you want to use.
11387
-	 *
11388
-	 * @param OODB $redbean
11389
-	 */
11390
-	public static function setRedBean( OODB $redbean )
11391
-	{
11392
-		self::$redbean = $redbean;
11393
-	}
11394
-
11395
-	/**
11396
-	 * Optional accessor for neat code.
11397
-	 * Sets the database adapter you want to use.
11398
-	 *
11399
-	 * @return DBAdapter
11400
-	 */
11401
-	public static function getDatabaseAdapter()
11402
-	{
11403
-		return self::$adapter;
11404
-	}
11405
-
11406
-	/**
11407
-	 * Returns the current duplication manager instance.
11408
-	 *
11409
-	 * @return DuplicationManager
11410
-	 */
11411
-	public static function getDuplicationManager()
11412
-	{
11413
-		return self::$duplicationManager;
11414
-	}
11415
-
11416
-	/**
11417
-	 * Optional accessor for neat code.
11418
-	 * Sets the database adapter you want to use.
11419
-	 *
11420
-	 * @return QueryWriter
11421
-	 */
11422
-	public static function getWriter()
11423
-	{
11424
-		return self::$writer;
11425
-	}
11426
-
11427
-	/**
11428
-	 * Optional accessor for neat code.
11429
-	 * Sets the database adapter you want to use.
11430
-	 *
11431
-	 * @return OODB
11432
-	 */
11433
-	public static function getRedBean()
11434
-	{
11435
-		return self::$redbean;
11436
-	}
11437
-
11438
-	/**
11439
-	 * Returns the toolbox currently used by the facade.
11440
-	 * To set the toolbox use R::setup() or R::configureFacadeWithToolbox().
11441
-	 * To create a toolbox use Setup::kickstart(). Or create a manual
11442
-	 * toolbox using the ToolBox class.
11443
-	 *
11444
-	 * @return ToolBox
11445
-	 */
11446
-	public static function getToolBox()
11447
-	{
11448
-		return self::$toolbox;
11449
-	}
11450
-
11451
-	/**
11452
-	 * Mostly for internal use, but might be handy
11453
-	 * for some users.
11454
-	 * This returns all the components of the currently
11455
-	 * selected toolbox.
11456
-	 *
11457
-	 * Returns the components in the following order:
11458
-	 *
11459
-	 * 0 - OODB instance (getRedBean())
11460
-	 * 1 - Database Adapter
11461
-	 * 2 - Query Writer
11462
-	 * 3 - Toolbox itself
11463
-	 *
11464
-	 * @return array
11465
-	 */
11466
-	public static function getExtractedToolbox()
11467
-	{
11468
-		return array(
11469
-			self::$redbean,
11470
-			self::$adapter,
11471
-			self::$writer,
11472
-			self::$toolbox
11473
-		);
11474
-	}
11475
-
11476
-	/**
11477
-	 * Facade method for AQueryWriter::renameAssociation()
11478
-	 *
11479
-	 * @param string|array $from
11480
-	 * @param string       $to
11481
-	 *
11482
-	 * @return void
11483
-	 */
11484
-	public static function renameAssociation( $from, $to = NULL )
11485
-	{
11486
-		AQueryWriter::renameAssociation( $from, $to );
11487
-	}
11488
-
11489
-	/**
11490
-	 * Little helper method for Resty Bean Can server and others.
11491
-	 * Takes an array of beans and exports each bean.
11492
-	 * Unlike exportAll this method does not recurse into own lists
11493
-	 * and shared lists, the beans are exported as-is, only loaded lists
11494
-	 * are exported.
11495
-	 *
11496
-	 * @param array $beans beans
11497
-	 *
11498
-	 * @return array
11499
-	 */
11500
-	public static function beansToArray( $beans )
11501
-	{
11502
-		$list = array();
11503
-		foreach( $beans as $bean ) {
11504
-			$list[] = $bean->export();
11505
-		}
11506
-		return $list;
11507
-	}
10212
+                    );
10213
+
10214
+        $wkey = trim( strtolower( $dbType ) );
10215
+        if ( !isset( $writers[$wkey] ) ) trigger_error( 'Unsupported DSN: '.$wkey );
10216
+        $writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey];
10217
+        $writer      = new $writerClass( $adapter );
10218
+        $redbean     = new OODB( $writer, $frozen );
10219
+
10220
+        self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer );
10221
+    }
10222
+
10223
+    /**
10224
+     * Selects a different database for the Facade to work with.
10225
+     * If you use the R::setup() you don't need this method. This method is meant
10226
+     * for multiple database setups. This method selects the database identified by the
10227
+     * database ID ($key). Use addDatabase() to add a new database, which in turn
10228
+     * can be selected using selectDatabase(). If you use R::setup(), the resulting
10229
+     * database will be stored under key 'default', to switch (back) to this database
10230
+     * use R::selectDatabase( 'default' ). This method returns TRUE if the database has been
10231
+     * switched and FALSE otherwise (for instance if you already using the specified database).
10232
+     *
10233
+     * @param  string $key Key of the database to select
10234
+     *
10235
+     * @return boolean
10236
+     */
10237
+    public static function selectDatabase( $key )
10238
+    {
10239
+        if ( self::$currentDB === $key ) {
10240
+            return FALSE;
10241
+        }
10242
+
10243
+        self::configureFacadeWithToolbox( self::$toolboxes[$key] );
10244
+        self::$currentDB = $key;
10245
+
10246
+        return TRUE;
10247
+    }
10248
+
10249
+    /**
10250
+     * Toggles DEBUG mode.
10251
+     * In Debug mode all SQL that happens under the hood will
10252
+     * be printed to the screen or logged by provided logger.
10253
+     * If no database connection has been configured using R::setup() or
10254
+     * R::selectDatabase() this method will throw an exception.
10255
+     * Returns the attached logger instance.
10256
+     *
10257
+     * @param boolean $tf
10258
+     * @param integer $mode (0 = to STDOUT, 1 = to ARRAY)
10259
+     *
10260
+     * @throws Security
10261
+     *
10262
+     * @return Logger\RDefault
10263
+     */
10264
+    public static function debug( $tf = TRUE, $mode = 0 )
10265
+    {
10266
+        if ($mode > 1) {
10267
+            $mode -= 2;
10268
+            $logger = new Debug;
10269
+        } else {
10270
+            $logger = new RDefault;
10271
+        }
10272
+
10273
+        if ( !isset( self::$adapter ) ) {
10274
+            throw new RedException( 'Use R::setup() first.' );
10275
+        }
10276
+        $logger->setMode($mode);
10277
+        self::$adapter->getDatabase()->setDebugMode( $tf, $logger );
10278
+
10279
+        return $logger;
10280
+    }
10281
+
10282
+    /**
10283
+     * Turns on the fancy debugger.
10284
+     */
10285
+    public static function fancyDebug( $toggle )
10286
+    {
10287
+        self::debug( $toggle, 2 );
10288
+    }
10289
+
10290
+    /**
10291
+     * Inspects the database schema. If you pass the type of a bean this
10292
+     * method will return the fields of its table in the database.
10293
+     * The keys of this array will be the field names and the values will be
10294
+     * the column types used to store their values.
10295
+     * If no type is passed, this method returns a list of all tables in the database.
10296
+     *
10297
+     * @param string $type Type of bean (i.e. table) you want to inspect
10298
+     *
10299
+     * @return array
10300
+     */
10301
+    public static function inspect( $type = NULL )
10302
+    {
10303
+        return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type );
10304
+    }
10305
+
10306
+    /**
10307
+     * Stores a bean in the database. This method takes a
10308
+     * OODBBean Bean Object $bean and stores it
10309
+     * in the database. If the database schema is not compatible
10310
+     * with this bean and RedBean runs in fluid mode the schema
10311
+     * will be altered to store the bean correctly.
10312
+     * If the database schema is not compatible with this bean and
10313
+     * RedBean runs in frozen mode it will throw an exception.
10314
+     * This function returns the primary key ID of the inserted
10315
+     * bean.
10316
+     *
10317
+     * The return value is an integer if possible. If it is not possible to
10318
+     * represent the value as an integer a string will be returned.
10319
+     *
10320
+     * @param OODBBean|SimpleModel $bean bean to store
10321
+     *
10322
+     * @return integer|string
10323
+     *
10324
+     * @throws Security
10325
+     */
10326
+    public static function store( $bean )
10327
+    {
10328
+        return self::$redbean->store( $bean );
10329
+    }
10330
+
10331
+    /**
10332
+     * Toggles fluid or frozen mode. In fluid mode the database
10333
+     * structure is adjusted to accomodate your objects. In frozen mode
10334
+     * this is not the case.
10335
+     *
10336
+     * You can also pass an array containing a selection of frozen types.
10337
+     * Let's call this chilly mode, it's just like fluid mode except that
10338
+     * certain types (i.e. tables) aren't touched.
10339
+     *
10340
+     * @param boolean|array $trueFalse
10341
+     */
10342
+    public static function freeze( $tf = TRUE )
10343
+    {
10344
+        self::$redbean->freeze( $tf );
10345
+    }
10346
+
10347
+    /**
10348
+     * Loads multiple types of beans with the same ID.
10349
+     * This might look like a strange method, however it can be useful
10350
+     * for loading a one-to-one relation.
10351
+     *
10352
+     * Usage:
10353
+     * list($author, $bio) = R::load('author, bio', $id);
10354
+     *
10355
+     * @param string|array $types
10356
+     * @param mixed        $id
10357
+     *
10358
+     * @return OODBBean
10359
+     */
10360
+    public static function loadMulti( $types, $id )
10361
+    {
10362
+        if ( is_string( $types ) ) {
10363
+            $types = explode( ',', $types );
10364
+        }
10365
+
10366
+        if ( !is_array( $types ) ) {
10367
+            return array();
10368
+        }
10369
+
10370
+        foreach ( $types as $k => $typeItem ) {
10371
+            $types[$k] = self::$redbean->load( $typeItem, $id );
10372
+        }
10373
+
10374
+        return $types;
10375
+    }
10376
+
10377
+    /**
10378
+     * Loads a bean from the object database.
10379
+     * It searches for a OODBBean Bean Object in the
10380
+     * database. It does not matter how this bean has been stored.
10381
+     * RedBean uses the primary key ID $id and the string $type
10382
+     * to find the bean. The $type specifies what kind of bean you
10383
+     * are looking for; this is the same type as used with the
10384
+     * dispense() function. If RedBean finds the bean it will return
10385
+     * the OODB Bean object; if it cannot find the bean
10386
+     * RedBean will return a new bean of type $type and with
10387
+     * primary key ID 0. In the latter case it acts basically the
10388
+     * same as dispense().
10389
+     *
10390
+     * Important note:
10391
+     * If the bean cannot be found in the database a new bean of
10392
+     * the specified type will be generated and returned.
10393
+     *
10394
+     * @param string  $type type of bean you want to load
10395
+     * @param integer $id   ID of the bean you want to load
10396
+     *
10397
+     * @throws SQL
10398
+     *
10399
+     * @return OODBBean
10400
+     */
10401
+    public static function load( $type, $id )
10402
+    {
10403
+        return self::$redbean->load( $type, $id );
10404
+    }
10405
+
10406
+    /**
10407
+     * Removes a bean from the database.
10408
+     * This function will remove the specified OODBBean
10409
+     * Bean Object from the database.
10410
+     *
10411
+     * This facade method also accepts a type-id combination,
10412
+     * in the latter case this method will attempt to load the specified bean
10413
+     * and THEN trash it.
10414
+     *
10415
+     * @param string|OODBBean|SimpleModel $bean bean you want to remove from database
10416
+     * @param integer $id (optional)
10417
+     *
10418
+     * @return void
10419
+     */
10420
+    public static function trash( $beanOrType, $id = NULL )
10421
+    {
10422
+        if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) );
10423
+        return self::$redbean->trash( $beanOrType );
10424
+    }
10425
+
10426
+    /**
10427
+     * Dispenses a new RedBean OODB Bean for use with
10428
+     * the rest of the methods.
10429
+     *
10430
+     * @param string|array $typeOrBeanArray   type or bean array to import
10431
+     * @param integer      $number            number of beans to dispense
10432
+     * @param boolean	     $alwaysReturnArray if TRUE always returns the result as an array
10433
+     *
10434
+     * @return array|OODBBean
10435
+     *
10436
+     * @throws Security
10437
+     */
10438
+    public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE )
10439
+    {
10440
+        if ( is_array($typeOrBeanArray) ) {
10441
+
10442
+            if ( !isset( $typeOrBeanArray['_type'] ) ) {
10443
+                $list = array();
10444
+                foreach( $typeOrBeanArray as $beanArray ) if ( !( is_array( $beanArray ) && isset( $beanArray['_type'] ) ) ) throw new RedException( 'Invalid Array Bean' );
10445
+                foreach( $typeOrBeanArray as $beanArray ) $list[] = self::dispense( $beanArray );
10446
+                return $list;
10447
+            }
10448
+
10449
+            $import = $typeOrBeanArray;
10450
+            $type = $import['_type'];
10451
+            unset( $import['_type'] );
10452
+        } else {
10453
+            $type = $typeOrBeanArray;
10454
+        }
10455
+
10456
+        if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) {
10457
+            throw new RedException( 'Invalid type: ' . $type );
10458
+        }
10459
+
10460
+        $beanOrBeans = self::$redbean->dispense( $type, $num, $alwaysReturnArray );
10461
+
10462
+        if ( isset( $import ) ) {
10463
+            $beanOrBeans->import( $import );
10464
+        }
10465
+
10466
+        return $beanOrBeans;
10467
+    }
10468
+
10469
+    /**
10470
+     * Takes a comma separated list of bean types
10471
+     * and dispenses these beans. For each type in the list
10472
+     * you can specify the number of beans to be dispensed.
10473
+     *
10474
+     * Usage:
10475
+     *
10476
+     * list($book, $page, $text) = R::dispenseAll('book,page,text');
10477
+     *
10478
+     * This will dispense a book, a page and a text. This way you can
10479
+     * quickly dispense beans of various types in just one line of code.
10480
+     *
10481
+     * Usage:
10482
+     *
10483
+     * list($book, $pages) = R::dispenseAll('book,page*100');
10484
+     *
10485
+     * This returns an array with a book bean and then another array
10486
+     * containing 100 page beans.
10487
+     *
10488
+     * @param string  $order      a description of the desired dispense order using the syntax above
10489
+     * @param boolean $onlyArrays return only arrays even if amount < 2
10490
+     *
10491
+     * @return array
10492
+     */
10493
+    public static function dispenseAll( $order, $onlyArrays = FALSE )
10494
+    {
10495
+
10496
+        $list = array();
10497
+
10498
+        foreach( explode( ',', $order ) as $order ) {
10499
+            if ( strpos( $order, '*' ) !== false ) {
10500
+                list( $type, $amount ) = explode( '*', $order );
10501
+            } else {
10502
+                $type   = $order;
10503
+                $amount = 1;
10504
+            }
10505
+
10506
+            $list[] = self::dispense( $type, $amount, $onlyArrays );
10507
+        }
10508
+
10509
+        return $list;
10510
+    }
10511
+
10512
+    /**
10513
+     * Convience method. Tries to find beans of a certain type,
10514
+     * if no beans are found, it dispenses a bean of that type.
10515
+     *
10516
+     * @param  string $type     type of bean you are looking for
10517
+     * @param  string $sql      SQL code for finding the bean
10518
+     * @param  array  $bindings parameters to bind to SQL
10519
+     *
10520
+     * @return array
10521
+     */
10522
+    public static function findOrDispense( $type, $sql = NULL, $bindings = array() )
10523
+    {
10524
+        return self::$finder->findOrDispense( $type, $sql, $bindings );
10525
+    }
10526
+
10527
+    /**
10528
+     * Finds a bean using a type and a where clause (SQL).
10529
+     * As with most Query tools in RedBean you can provide values to
10530
+     * be inserted in the SQL statement by populating the value
10531
+     * array parameter; you can either use the question mark notation
10532
+     * or the slot-notation (:keyname).
10533
+     *
10534
+     * @param string $type     type   the type of bean you are looking for
10535
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10536
+     * @param array  $bindings values array of values to be bound to parameters in query
10537
+     *
10538
+     * @return array
10539
+     */
10540
+    public static function find( $type, $sql = NULL, $bindings = array() )
10541
+    {
10542
+        return self::$finder->find( $type, $sql, $bindings );
10543
+    }
10544
+
10545
+    /**
10546
+     * @see Facade::find
10547
+     *      The findAll() method differs from the find() method in that it does
10548
+     *      not assume a WHERE-clause, so this is valid:
10549
+     *
10550
+     * R::findAll('person',' ORDER BY name DESC ');
10551
+     *
10552
+     * Your SQL does not have to start with a valid WHERE-clause condition.
10553
+     *
10554
+     * @param string $type     type   the type of bean you are looking for
10555
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10556
+     * @param array  $bindings values array of values to be bound to parameters in query
10557
+     *
10558
+     * @return array
10559
+     */
10560
+    public static function findAll( $type, $sql = NULL, $bindings = array() )
10561
+    {
10562
+        return self::$finder->find( $type, $sql, $bindings );
10563
+    }
10564
+
10565
+    /**
10566
+     * @see Facade::find
10567
+     * The variation also exports the beans (i.e. it returns arrays).
10568
+     *
10569
+     * @param string $type     type   the type of bean you are looking for
10570
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10571
+     * @param array  $bindings values array of values to be bound to parameters in query
10572
+     *
10573
+     * @return array
10574
+     */
10575
+    public static function findAndExport( $type, $sql = NULL, $bindings = array() )
10576
+    {
10577
+        return self::$finder->findAndExport( $type, $sql, $bindings );
10578
+    }
10579
+
10580
+    /**
10581
+     * @see Facade::find
10582
+     * This variation returns the first bean only.
10583
+     *
10584
+     * @param string $type     type   the type of bean you are looking for
10585
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10586
+     * @param array  $bindings values array of values to be bound to parameters in query
10587
+     *
10588
+     * @return OODBBean
10589
+     */
10590
+    public static function findOne( $type, $sql = NULL, $bindings = array() )
10591
+    {
10592
+        return self::$finder->findOne( $type, $sql, $bindings );
10593
+    }
10594
+
10595
+    /**
10596
+     * @see Facade::find
10597
+     * This variation returns the last bean only.
10598
+     *
10599
+     * @param string $type     type   the type of bean you are looking for
10600
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10601
+     * @param array  $bindings values array of values to be bound to parameters in query
10602
+     *
10603
+     * @return OODBBean
10604
+     */
10605
+    public static function findLast( $type, $sql = NULL, $bindings = array() )
10606
+    {
10607
+        return self::$finder->findLast( $type, $sql, $bindings );
10608
+    }
10609
+
10610
+    /**
10611
+     * Finds a bean collection.
10612
+     * Use this for large datasets.
10613
+     *
10614
+     * @param string $type     type   the type of bean you are looking for
10615
+     * @param string $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
10616
+     * @param array  $bindings values array of values to be bound to parameters in query
10617
+     *
10618
+     * @return BeanCollection
10619
+     */
10620
+    public static function findCollection( $type, $sql = NULL, $bindings = array() )
10621
+    {
10622
+        return self::$finder->findCollection( $type, $sql, $bindings );
10623
+    }
10624
+
10625
+    /**
10626
+     * Finds multiple types of beans at once and offers additional
10627
+     * remapping functionality. This is a very powerful yet complex function.
10628
+     * For details see Finder::findMulti().
10629
+     *
10630
+     * @see Finder::findMulti()
10631
+     *
10632
+     * @param array|string $types      a list of bean types to find
10633
+     * @param string|array $sqlOrArr   SQL query string or result set array
10634
+     * @param array        $bindings   SQL bindings
10635
+     * @param array        $remappings An array of remapping arrays containing closures
10636
+     *
10637
+     * @return array
10638
+     */
10639
+    public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() )
10640
+    {
10641
+        return self::$finder->findMulti( $types, $sql, $bindings, $remappings );
10642
+    }
10643
+
10644
+    /**
10645
+     * Returns an array of beans. Pass a type and a series of ids and
10646
+     * this method will bring you the corresponding beans.
10647
+     *
10648
+     * important note: Because this method loads beans using the load()
10649
+     * function (but faster) it will return empty beans with ID 0 for
10650
+     * every bean that could not be located. The resulting beans will have the
10651
+     * passed IDs as their keys.
10652
+     *
10653
+     * @param string $type type of beans
10654
+     * @param array  $ids  ids to load
10655
+     *
10656
+     * @return array
10657
+     */
10658
+    public static function batch( $type, $ids )
10659
+    {
10660
+        return self::$redbean->batch( $type, $ids );
10661
+    }
10662
+
10663
+    /**
10664
+     * @see Facade::batch
10665
+     *
10666
+     * Alias for batch(). Batch method is older but since we added so-called *All
10667
+     * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to
10668
+     * improve the consistency of the Facade API and also add an alias for batch() called
10669
+     * loadAll.
10670
+     *
10671
+     * @param string $type type of beans
10672
+     * @param array  $ids  ids to load
10673
+     *
10674
+     * @return array
10675
+     */
10676
+    public static function loadAll( $type, $ids )
10677
+    {
10678
+        return self::$redbean->batch( $type, $ids );
10679
+    }
10680
+
10681
+    /**
10682
+     * Convenience function to execute Queries directly.
10683
+     * Executes SQL.
10684
+     *
10685
+     * @param string $sql       sql    SQL query to execute
10686
+     * @param array  $bindings  values a list of values to be bound to query parameters
10687
+     *
10688
+     * @return integer
10689
+     */
10690
+    public static function exec( $sql, $bindings = array() )
10691
+    {
10692
+        return self::query( 'exec', $sql, $bindings );
10693
+    }
10694
+
10695
+    /**
10696
+     * Convenience function to execute Queries directly.
10697
+     * Executes SQL.
10698
+     *
10699
+     * @param string $sql       sql    SQL query to execute
10700
+     * @param array  $bindings  values a list of values to be bound to query parameters
10701
+     *
10702
+     * @return array
10703
+     */
10704
+    public static function getAll( $sql, $bindings = array() )
10705
+    {
10706
+        return self::query( 'get', $sql, $bindings );
10707
+    }
10708
+
10709
+    /**
10710
+     * Convenience function to execute Queries directly.
10711
+     * Executes SQL.
10712
+     *
10713
+     * @param string $sql       sql    SQL query to execute
10714
+     * @param array  $bindings  values a list of values to be bound to query parameters
10715
+     *
10716
+     * @return string
10717
+     */
10718
+    public static function getCell( $sql, $bindings = array() )
10719
+    {
10720
+        return self::query( 'getCell', $sql, $bindings );
10721
+    }
10722
+
10723
+    /**
10724
+     * Convenience function to execute Queries directly.
10725
+     * Executes SQL.
10726
+     *
10727
+     * @param string $sql       sql    SQL query to execute
10728
+     * @param array  $bindings  values a list of values to be bound to query parameters
10729
+     *
10730
+     * @return array
10731
+     */
10732
+    public static function getRow( $sql, $bindings = array() )
10733
+    {
10734
+        return self::query( 'getRow', $sql, $bindings );
10735
+    }
10736
+
10737
+    /**
10738
+     * Convenience function to execute Queries directly.
10739
+     * Executes SQL.
10740
+     *
10741
+     * @param string $sql       sql    SQL query to execute
10742
+     * @param array  $bindings  values a list of values to be bound to query parameters
10743
+     *
10744
+     * @return array
10745
+     */
10746
+    public static function getCol( $sql, $bindings = array() )
10747
+    {
10748
+        return self::query( 'getCol', $sql, $bindings );
10749
+    }
10750
+
10751
+    /**
10752
+     * Convenience function to execute Queries directly.
10753
+     * Executes SQL.
10754
+     * Results will be returned as an associative array. The first
10755
+     * column in the select clause will be used for the keys in this array and
10756
+     * the second column will be used for the values. If only one column is
10757
+     * selected in the query, both key and value of the array will have the
10758
+     * value of this field for each row.
10759
+     *
10760
+     * @param string $sql       sql    SQL query to execute
10761
+     * @param array  $bindings  values a list of values to be bound to query parameters
10762
+     *
10763
+     * @return array
10764
+     */
10765
+    public static function getAssoc( $sql, $bindings = array() )
10766
+    {
10767
+        return self::query( 'getAssoc', $sql, $bindings );
10768
+    }
10769
+
10770
+    /**
10771
+     * Convenience function to execute Queries directly.
10772
+     * Executes SQL.
10773
+     * Results will be returned as an associative array indexed by the first
10774
+     * column in the select.
10775
+     *
10776
+     * @param string $sql       sql    SQL query to execute
10777
+     * @param array  $bindings  values a list of values to be bound to query parameters
10778
+     *
10779
+     * @return array
10780
+     */
10781
+    public static function getAssocRow( $sql, $bindings = array() )
10782
+    {
10783
+        return self::query( 'getAssocRow', $sql, $bindings );
10784
+    }
10785
+
10786
+    /**
10787
+     * Returns the insert ID for databases that support/require this
10788
+     * functionality. Alias for R::getAdapter()->getInsertID().
10789
+     *
10790
+     * @return mixed
10791
+     */
10792
+    public static function getInsertID()
10793
+    {
10794
+        return self::$adapter->getInsertID();
10795
+    }
10796
+
10797
+    /**
10798
+     * Makes a copy of a bean. This method makes a deep copy
10799
+     * of the bean.The copy will have the following features.
10800
+     * - All beans in own-lists will be duplicated as well
10801
+     * - All references to shared beans will be copied but not the shared beans themselves
10802
+     * - All references to parent objects (_id fields) will be copied but not the parents themselves
10803
+     * In most cases this is the desired scenario for copying beans.
10804
+     * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
10805
+     * (i.e. one that already has been processed) the ID of the bean will be returned.
10806
+     * This should not happen though.
10807
+     *
10808
+     * Note:
10809
+     * This function does a reflectional database query so it may be slow.
10810
+     *
10811
+     * @deprecated
10812
+     * This function is deprecated in favour of R::duplicate().
10813
+     * This function has a confusing method signature, the R::duplicate() function
10814
+     * only accepts two arguments: bean and filters.
10815
+     *
10816
+     * @param OODBBean $bean  bean to be copied
10817
+     * @param array    $trail for internal usage, pass array()
10818
+     * @param boolean  $pid   for internal usage
10819
+     * @param array	   $white white list filter with bean types to duplicate
10820
+     *
10821
+     * @return array
10822
+     */
10823
+    public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() )
10824
+    {
10825
+        self::$duplicationManager->setFilters( $filters );
10826
+        return self::$duplicationManager->dup( $bean, $trail, $pid );
10827
+    }
10828
+
10829
+    /**
10830
+     * Makes a deep copy of a bean. This method makes a deep copy
10831
+     * of the bean.The copy will have the following:
10832
+     *
10833
+     * - All beans in own-lists will be duplicated as well
10834
+     * - All references to shared beans will be copied but not the shared beans themselves
10835
+     * - All references to parent objects (_id fields) will be copied but not the parents themselves
10836
+     *
10837
+     * In most cases this is the desired scenario for copying beans.
10838
+     * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
10839
+     * (i.e. one that already has been processed) the ID of the bean will be returned.
10840
+     * This should not happen though.
10841
+     *
10842
+     * Note:
10843
+     * This function does a reflectional database query so it may be slow.
10844
+     *
10845
+     * Note:
10846
+     * This is a simplified version of the deprecated R::dup() function.
10847
+     *
10848
+     * @param OODBBean $bean  bean to be copied
10849
+     * @param array	   $white white list filter with bean types to duplicate
10850
+     *
10851
+     * @return array
10852
+     */
10853
+    public static function duplicate( $bean, $filters = array() )
10854
+    {
10855
+        return self::dup( $bean, array(), FALSE, $filters );
10856
+    }
10857
+
10858
+    /**
10859
+     * Exports a collection of beans. Handy for XML/JSON exports with a
10860
+     * Javascript framework like Dojo or ExtJS.
10861
+     * What will be exported:
10862
+     * - contents of the bean
10863
+     * - all own bean lists (recursively)
10864
+     * - all shared beans (not THEIR own lists)
10865
+     *
10866
+     * @param    array|OODBBean $beans   beans to be exported
10867
+     * @param    boolean        $parents whether you want parent beans to be exported
10868
+     * @param    array          $filters whitelist of types
10869
+     *
10870
+     * @return    array
10871
+     */
10872
+    public static function exportAll( $beans, $parents = FALSE, $filters = array())
10873
+    {
10874
+        return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle );
10875
+    }
10876
+
10877
+    /**
10878
+     * Selects case style for export.
10879
+     * This will determine the case style for the keys of exported beans (see exportAll).
10880
+     * The following options are accepted:
10881
+     *
10882
+     * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid )
10883
+     * 'camel'   Camel Case   (i.e. bookId isValid   )
10884
+     * 'dolphin' Dolphin Case (i.e. bookID isValid   ) Like CamelCase but ID is written all uppercase
10885
+     *
10886
+     * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different
10887
+     * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id.
10888
+     * Due to information loss this cannot be corrected. However if you might try
10889
+     * DolphinCase for IDs it takes into account the exception concerning IDs.
10890
+     *
10891
+     * @param string $caseStyle case style identifier
10892
+     *
10893
+     * @return void
10894
+     */
10895
+    public static function useExportCase( $caseStyle = 'default' )
10896
+    {
10897
+        if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' );
10898
+        self::$exportCaseStyle = $caseStyle;
10899
+    }
10900
+
10901
+    /**
10902
+     * Converts a series of rows to beans.
10903
+     * This method converts a series of rows to beans.
10904
+     * The type of the desired output beans can be specified in the
10905
+     * first parameter. The second parameter is meant for the database
10906
+     * result rows.
10907
+     *
10908
+     * @param string $type type of beans to produce
10909
+     * @param array  $rows must contain an array of array
10910
+     *
10911
+     * @return array
10912
+     */
10913
+    public static function convertToBeans( $type, $rows )
10914
+    {
10915
+        return self::$redbean->convertToBeans( $type, $rows );
10916
+    }
10917
+
10918
+    /**
10919
+     * Part of RedBeanPHP Tagging API.
10920
+     * Tests whether a bean has been associated with one ore more
10921
+     * of the listed tags. If the third parameter is TRUE this method
10922
+     * will return TRUE only if all tags that have been specified are indeed
10923
+     * associated with the given bean, otherwise FALSE.
10924
+     * If the third parameter is FALSE this
10925
+     * method will return TRUE if one of the tags matches, FALSE if none
10926
+     * match.
10927
+     *
10928
+     * @param  OODBBean $bean bean to check for tags
10929
+     * @param  array            $tags list of tags
10930
+     * @param  boolean          $all  whether they must all match or just some
10931
+     *
10932
+     * @return boolean
10933
+     */
10934
+    public static function hasTag( $bean, $tags, $all = FALSE )
10935
+    {
10936
+        return self::$tagManager->hasTag( $bean, $tags, $all );
10937
+    }
10938
+
10939
+    /**
10940
+     * Part of RedBeanPHP Tagging API.
10941
+     * Removes all specified tags from the bean. The tags specified in
10942
+     * the second parameter will no longer be associated with the bean.
10943
+     *
10944
+     * @param  OODBBean $bean    tagged bean
10945
+     * @param  array            $tagList list of tags (names)
10946
+     *
10947
+     * @return void
10948
+     */
10949
+    public static function untag( $bean, $tagList )
10950
+    {
10951
+        self::$tagManager->untag( $bean, $tagList );
10952
+    }
10953
+
10954
+    /**
10955
+     * Part of RedBeanPHP Tagging API.
10956
+     * Tags a bean or returns tags associated with a bean.
10957
+     * If $tagList is NULL or omitted this method will return a
10958
+     * comma separated list of tags associated with the bean provided.
10959
+     * If $tagList is a comma separated list (string) of tags all tags will
10960
+     * be associated with the bean.
10961
+     * You may also pass an array instead of a string.
10962
+     *
10963
+     * @param OODBBean $bean    bean
10964
+     * @param mixed            $tagList tags
10965
+     *
10966
+     * @return string
10967
+     */
10968
+    public static function tag( OODBBean $bean, $tagList = NULL )
10969
+    {
10970
+        return self::$tagManager->tag( $bean, $tagList );
10971
+    }
10972
+
10973
+    /**
10974
+     * Part of RedBeanPHP Tagging API.
10975
+     * Adds tags to a bean.
10976
+     * If $tagList is a comma separated list of tags all tags will
10977
+     * be associated with the bean.
10978
+     * You may also pass an array instead of a string.
10979
+     *
10980
+     * @param OODBBean $bean    bean
10981
+     * @param array            $tagList list of tags to add to bean
10982
+     *
10983
+     * @return void
10984
+     */
10985
+    public static function addTags( OODBBean $bean, $tagList )
10986
+    {
10987
+        self::$tagManager->addTags( $bean, $tagList );
10988
+    }
10989
+
10990
+    /**
10991
+     * Part of RedBeanPHP Tagging API.
10992
+     * Returns all beans that have been tagged with one of the tags given.
10993
+     *
10994
+     * @param string $beanType type of bean you are looking for
10995
+     * @param array  $tagList  list of tags to match
10996
+     * @param string $sql      additional SQL
10997
+     * @param array  $bindings bindings
10998
+     *
10999
+     * @return array
11000
+     */
11001
+    public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
11002
+    {
11003
+        return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings );
11004
+    }
11005
+
11006
+    /**
11007
+     * Part of RedBeanPHP Tagging API.
11008
+     * Returns all beans that have been tagged with ALL of the tags given.
11009
+     *
11010
+     * @param string $beanType type of bean you are looking for
11011
+     * @param array  $tagList  list of tags to match
11012
+     * @param string $sql      additional SQL
11013
+     * @param array  $bindings bindings
11014
+     *
11015
+     * @return array
11016
+     */
11017
+    public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
11018
+    {
11019
+        return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings );
11020
+    }
11021
+
11022
+    /**
11023
+     * Wipes all beans of type $beanType.
11024
+     *
11025
+     * @param string $beanType type of bean you want to destroy entirely
11026
+     *
11027
+     * @return boolean
11028
+     */
11029
+    public static function wipe( $beanType )
11030
+    {
11031
+        return Facade::$redbean->wipe( $beanType );
11032
+    }
11033
+
11034
+    /**
11035
+     * Counts the number of beans of type $type.
11036
+     * This method accepts a second argument to modify the count-query.
11037
+     * A third argument can be used to provide bindings for the SQL snippet.
11038
+     *
11039
+     * @param string $type     type of bean we are looking for
11040
+     * @param string $addSQL   additional SQL snippet
11041
+     * @param array  $bindings parameters to bind to SQL
11042
+     *
11043
+     * @return integer
11044
+     *
11045
+     * @throws SQL
11046
+     */
11047
+    public static function count( $type, $addSQL = '', $bindings = array() )
11048
+    {
11049
+        return Facade::$redbean->count( $type, $addSQL, $bindings );
11050
+    }
11051
+
11052
+    /**
11053
+     * Configures the facade, want to have a new Writer? A new Object Database or a new
11054
+     * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new
11055
+     * toolbox.
11056
+     *
11057
+     * @param ToolBox $tb toolbox
11058
+     *
11059
+     * @return ToolBox
11060
+     */
11061
+    public static function configureFacadeWithToolbox( ToolBox $tb )
11062
+    {
11063
+        $oldTools                 = self::$toolbox;
11064
+
11065
+        self::$toolbox            = $tb;
11066
+
11067
+        self::$writer             = self::$toolbox->getWriter();
11068
+        self::$adapter            = self::$toolbox->getDatabaseAdapter();
11069
+        self::$redbean            = self::$toolbox->getRedBean();
11070
+        self::$finder             = new Finder( self::$toolbox );
11071
+
11072
+        self::$associationManager = new AssociationManager( self::$toolbox );
11073
+
11074
+        self::$redbean->setAssociationManager( self::$associationManager );
11075
+
11076
+        self::$labelMaker         = new LabelMaker( self::$toolbox );
11077
+
11078
+        $helper                   = new SimpleModelHelper();
11079
+
11080
+        $helper->attachEventListeners( self::$redbean );
11081
+
11082
+        self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper );
11083
+
11084
+        self::$duplicationManager = new DuplicationManager( self::$toolbox );
11085
+        self::$tagManager         = new TagManager( self::$toolbox );
11086
+
11087
+        return $oldTools;
11088
+    }
11089
+
11090
+    /**
11091
+     * Facade Convience method for adapter transaction system.
11092
+     * Begins a transaction.
11093
+     *
11094
+     * @return bool
11095
+     */
11096
+    public static function begin()
11097
+    {
11098
+        if ( !self::$redbean->isFrozen() ) return FALSE;
11099
+
11100
+        self::$adapter->startTransaction();
11101
+
11102
+        return TRUE;
11103
+    }
11104
+
11105
+    /**
11106
+     * Facade Convience method for adapter transaction system.
11107
+     * Commits a transaction.
11108
+     *
11109
+     * @return bool
11110
+     */
11111
+    public static function commit()
11112
+    {
11113
+        if ( !self::$redbean->isFrozen() ) return FALSE;
11114
+
11115
+        self::$adapter->commit();
11116
+
11117
+        return TRUE;
11118
+    }
11119
+
11120
+    /**
11121
+     * Facade Convience method for adapter transaction system.
11122
+     * Rolls back a transaction.
11123
+     *
11124
+     * @return bool
11125
+     */
11126
+    public static function rollback()
11127
+    {
11128
+        if ( !self::$redbean->isFrozen() ) return FALSE;
11129
+
11130
+        self::$adapter->rollback();
11131
+
11132
+        return TRUE;
11133
+    }
11134
+
11135
+    /**
11136
+     * Returns a list of columns. Format of this array:
11137
+     * array( fieldname => type )
11138
+     * Note that this method only works in fluid mode because it might be
11139
+     * quite heavy on production servers!
11140
+     *
11141
+     * @param  string $table   name of the table (not type) you want to get columns of
11142
+     *
11143
+     * @return array
11144
+     */
11145
+    public static function getColumns( $table )
11146
+    {
11147
+        return self::$writer->getColumns( $table );
11148
+    }
11149
+
11150
+    /**
11151
+     * Generates question mark slots for an array of values.
11152
+     *
11153
+     * @param array  $array    array to generate question mark slots for
11154
+     *
11155
+     * @return string
11156
+     */
11157
+    public static function genSlots( $array, $template = NULL )
11158
+    {
11159
+        $str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : '';
11160
+        return ( is_null( $template ) ||  $str === '' ) ? $str : sprintf( $template, $str );
11161
+    }
11162
+
11163
+    /**
11164
+     * Flattens a multi dimensional bindings array for use with genSlots().
11165
+     *
11166
+     * @param array $array array to flatten
11167
+     *
11168
+     * @return array
11169
+     */
11170
+    public static function flat( $array, $result = array() )
11171
+    {
11172
+        foreach( $array as $value ) {
11173
+            if ( is_array( $value ) ) $result = self::flat( $value, $result );
11174
+            else $result[] = $value;
11175
+        }
11176
+        return $result;
11177
+    }
11178
+
11179
+    /**
11180
+     * Nukes the entire database.
11181
+     * This will remove all schema structures from the database.
11182
+     * Only works in fluid mode. Be careful with this method.
11183
+     *
11184
+     * @warning dangerous method, will remove all tables, columns etc.
11185
+     *
11186
+     * @return void
11187
+     */
11188
+    public static function nuke()
11189
+    {
11190
+        if ( !self::$redbean->isFrozen() ) {
11191
+            self::$writer->wipeAll();
11192
+        }
11193
+    }
11194
+
11195
+    /**
11196
+     * Short hand function to store a set of beans at once, IDs will be
11197
+     * returned as an array. For information please consult the R::store()
11198
+     * function.
11199
+     * A loop saver.
11200
+     *
11201
+     * @param array $beans list of beans to be stored
11202
+     *
11203
+     * @return array
11204
+     */
11205
+    public static function storeAll( $beans )
11206
+    {
11207
+        $ids = array();
11208
+        foreach ( $beans as $bean ) {
11209
+            $ids[] = self::store( $bean );
11210
+        }
11211
+
11212
+        return $ids;
11213
+    }
11214
+
11215
+    /**
11216
+     * Short hand function to trash a set of beans at once.
11217
+     * For information please consult the R::trash() function.
11218
+     * A loop saver.
11219
+     *
11220
+     * @param array $beans list of beans to be trashed
11221
+     *
11222
+     * @return void
11223
+     */
11224
+    public static function trashAll( $beans )
11225
+    {
11226
+        foreach ( $beans as $bean ) {
11227
+            self::trash( $bean );
11228
+        }
11229
+    }
11230
+
11231
+    /**
11232
+     * Toggles Writer Cache.
11233
+     * Turns the Writer Cache on or off. The Writer Cache is a simple
11234
+     * query based caching system that may improve performance without the need
11235
+     * for cache management. This caching system will cache non-modifying queries
11236
+     * that are marked with special SQL comments. As soon as a non-marked query
11237
+     * gets executed the cache will be flushed. Only non-modifying select queries
11238
+     * have been marked therefore this mechanism is a rather safe way of caching, requiring
11239
+     * no explicit flushes or reloads. Of course this does not apply if you intend to test
11240
+     * or simulate concurrent querying.
11241
+     *
11242
+     * @param boolean $yesNo TRUE to enable cache, FALSE to disable cache
11243
+     *
11244
+     * @return void
11245
+     */
11246
+    public static function useWriterCache( $yesNo )
11247
+    {
11248
+        self::getWriter()->setUseCache( $yesNo );
11249
+    }
11250
+
11251
+
11252
+    /**
11253
+     * A label is a bean with only an id, type and name property.
11254
+     * This function will dispense beans for all entries in the array. The
11255
+     * values of the array will be assigned to the name property of each
11256
+     * individual bean.
11257
+     *
11258
+     * @param string $type   type of beans you would like to have
11259
+     * @param array  $labels list of labels, names for each bean
11260
+     *
11261
+     * @return array
11262
+     */
11263
+    public static function dispenseLabels( $type, $labels )
11264
+    {
11265
+        return self::$labelMaker->dispenseLabels( $type, $labels );
11266
+    }
11267
+
11268
+    /**
11269
+     * Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs.
11270
+     * Either returns a (newly created) bean respresenting the desired ENUM
11271
+     * value or returns a list of all enums for the type.
11272
+     *
11273
+     * To obtain (and add if necessary) an ENUM value:
11274
+     *
11275
+     * $tea->flavour = R::enum( 'flavour:apple' );
11276
+     *
11277
+     * Returns a bean of type 'flavour' with  name = apple.
11278
+     * This will add a bean with property name (set to APPLE) to the database
11279
+     * if it does not exist yet.
11280
+     *
11281
+     * To obtain all flavours:
11282
+     *
11283
+     * R::enum('flavour');
11284
+     *
11285
+     * To get a list of all flavour names:
11286
+     *
11287
+     * R::gatherLabels( R::enum( 'flavour' ) );
11288
+     *
11289
+     * @param string $enum either type or type-value
11290
+     *
11291
+     * @return array|OODBBean
11292
+     */
11293
+    public static function enum( $enum )
11294
+    {
11295
+        return self::$labelMaker->enum( $enum );
11296
+    }
11297
+
11298
+    /**
11299
+     * Gathers labels from beans. This function loops through the beans,
11300
+     * collects the values of the name properties of each individual bean
11301
+     * and stores the names in a new array. The array then gets sorted using the
11302
+     * default sort function of PHP (sort).
11303
+     *
11304
+     * @param array $beans list of beans to loop
11305
+     *
11306
+     * @return array
11307
+     */
11308
+    public static function gatherLabels( $beans )
11309
+    {
11310
+        return self::$labelMaker->gatherLabels( $beans );
11311
+    }
11312
+
11313
+    /**
11314
+     * Closes the database connection.
11315
+     *
11316
+     * @return void
11317
+     */
11318
+    public static function close()
11319
+    {
11320
+        if ( isset( self::$adapter ) ) {
11321
+            self::$adapter->close();
11322
+        }
11323
+    }
11324
+
11325
+    /**
11326
+     * Simple convenience function, returns ISO date formatted representation
11327
+     * of $time.
11328
+     *
11329
+     * @param mixed $time UNIX timestamp
11330
+     *
11331
+     * @return string
11332
+     */
11333
+    public static function isoDate( $time = NULL )
11334
+    {
11335
+        if ( !$time ) {
11336
+            $time = time();
11337
+        }
11338
+
11339
+        return @date( 'Y-m-d', $time );
11340
+    }
11341
+
11342
+    /**
11343
+     * Simple convenience function, returns ISO date time
11344
+     * formatted representation
11345
+     * of $time.
11346
+     *
11347
+     * @param mixed $time UNIX timestamp
11348
+     *
11349
+     * @return string
11350
+     */
11351
+    public static function isoDateTime( $time = NULL )
11352
+    {
11353
+        if ( !$time ) $time = time();
11354
+
11355
+        return @date( 'Y-m-d H:i:s', $time );
11356
+    }
11357
+
11358
+    /**
11359
+     * Optional accessor for neat code.
11360
+     * Sets the database adapter you want to use.
11361
+     *
11362
+     * @param Adapter $adapter
11363
+     *
11364
+     * @return void
11365
+     */
11366
+    public static function setDatabaseAdapter( Adapter $adapter )
11367
+    {
11368
+        self::$adapter = $adapter;
11369
+    }
11370
+
11371
+    /**
11372
+     * Optional accessor for neat code.
11373
+     * Sets the database adapter you want to use.
11374
+     *
11375
+     * @param QueryWriter $writer
11376
+     *
11377
+     * @return void
11378
+     */
11379
+    public static function setWriter( QueryWriter $writer )
11380
+    {
11381
+        self::$writer = $writer;
11382
+    }
11383
+
11384
+    /**
11385
+     * Optional accessor for neat code.
11386
+     * Sets the database adapter you want to use.
11387
+     *
11388
+     * @param OODB $redbean
11389
+     */
11390
+    public static function setRedBean( OODB $redbean )
11391
+    {
11392
+        self::$redbean = $redbean;
11393
+    }
11394
+
11395
+    /**
11396
+     * Optional accessor for neat code.
11397
+     * Sets the database adapter you want to use.
11398
+     *
11399
+     * @return DBAdapter
11400
+     */
11401
+    public static function getDatabaseAdapter()
11402
+    {
11403
+        return self::$adapter;
11404
+    }
11405
+
11406
+    /**
11407
+     * Returns the current duplication manager instance.
11408
+     *
11409
+     * @return DuplicationManager
11410
+     */
11411
+    public static function getDuplicationManager()
11412
+    {
11413
+        return self::$duplicationManager;
11414
+    }
11415
+
11416
+    /**
11417
+     * Optional accessor for neat code.
11418
+     * Sets the database adapter you want to use.
11419
+     *
11420
+     * @return QueryWriter
11421
+     */
11422
+    public static function getWriter()
11423
+    {
11424
+        return self::$writer;
11425
+    }
11426
+
11427
+    /**
11428
+     * Optional accessor for neat code.
11429
+     * Sets the database adapter you want to use.
11430
+     *
11431
+     * @return OODB
11432
+     */
11433
+    public static function getRedBean()
11434
+    {
11435
+        return self::$redbean;
11436
+    }
11437
+
11438
+    /**
11439
+     * Returns the toolbox currently used by the facade.
11440
+     * To set the toolbox use R::setup() or R::configureFacadeWithToolbox().
11441
+     * To create a toolbox use Setup::kickstart(). Or create a manual
11442
+     * toolbox using the ToolBox class.
11443
+     *
11444
+     * @return ToolBox
11445
+     */
11446
+    public static function getToolBox()
11447
+    {
11448
+        return self::$toolbox;
11449
+    }
11450
+
11451
+    /**
11452
+     * Mostly for internal use, but might be handy
11453
+     * for some users.
11454
+     * This returns all the components of the currently
11455
+     * selected toolbox.
11456
+     *
11457
+     * Returns the components in the following order:
11458
+     *
11459
+     * 0 - OODB instance (getRedBean())
11460
+     * 1 - Database Adapter
11461
+     * 2 - Query Writer
11462
+     * 3 - Toolbox itself
11463
+     *
11464
+     * @return array
11465
+     */
11466
+    public static function getExtractedToolbox()
11467
+    {
11468
+        return array(
11469
+            self::$redbean,
11470
+            self::$adapter,
11471
+            self::$writer,
11472
+            self::$toolbox
11473
+        );
11474
+    }
11475
+
11476
+    /**
11477
+     * Facade method for AQueryWriter::renameAssociation()
11478
+     *
11479
+     * @param string|array $from
11480
+     * @param string       $to
11481
+     *
11482
+     * @return void
11483
+     */
11484
+    public static function renameAssociation( $from, $to = NULL )
11485
+    {
11486
+        AQueryWriter::renameAssociation( $from, $to );
11487
+    }
11488
+
11489
+    /**
11490
+     * Little helper method for Resty Bean Can server and others.
11491
+     * Takes an array of beans and exports each bean.
11492
+     * Unlike exportAll this method does not recurse into own lists
11493
+     * and shared lists, the beans are exported as-is, only loaded lists
11494
+     * are exported.
11495
+     *
11496
+     * @param array $beans beans
11497
+     *
11498
+     * @return array
11499
+     */
11500
+    public static function beansToArray( $beans )
11501
+    {
11502
+        $list = array();
11503
+        foreach( $beans as $bean ) {
11504
+            $list[] = $bean->export();
11505
+        }
11506
+        return $list;
11507
+    }
11508 11508
 	
11509
-	/**
11510
-	 * Sets the error mode for FUSE.
11511
-	 * What to do if a FUSE model method does not exist?
11512
-	 * You can set the following options:
11513
-	 *
11514
-	 * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
11515
-	 * OODBBean::C_ERR_LOG, logs the incident using error_log
11516
-	 * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
11517
-	 * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
11518
-	 * OODBBean::C_ERR_EXCEPTION, throws an exception
11519
-	 * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
11520
-	 * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
11521
-	 * 
11522
-	 * Custom handler method signature: handler( array (
11523
-	 * 	'message' => string
11524
-	 * 	'bean' => OODBBean
11525
-	 * 	'method' => string
11526
-	 * ) )
11527
-	 *
11528
-	 * This method returns the old mode and handler as an array.
11529
-	 *
11530
-	 * @param integer       $mode mode
11531
-	 * @param callable|NULL $func custom handler
11532
-	 * 
11533
-	 * @return array
11534
-	 */
11535
-	public static function setErrorHandlingFUSE( $mode, $func = NULL )
11536
-	{
11537
-		return OODBBean::setErrorHandlingFUSE( $mode, $func );
11538
-	}
11539
-
11540
-	/**
11541
-	 * Simple but effective debug function.
11542
-	 * Given a one or more beans this method will
11543
-	 * return an array containing first part of the string
11544
-	 * representation of each item in the array.
11545
-	 *
11546
-	 * @param OODBBean|array $data either a bean or an array of beans
11547
-	 *
11548
-	 * @return array
11549
-	 *
11550
-	 */
11551
-	public static function dump( $data )
11552
-	{
11553
-		$array = array();
11554
-
11555
-		if ( $data instanceof OODBBean ) {
11556
-			$str = strval( $data );
11557
-			if (strlen($str) > 35) {
11558
-				$beanStr = substr( $str, 0, 35 ).'... ';
11559
-			} else {
11560
-				$beanStr = $str;
11561
-			}
11562
-			return $beanStr;
11563
-		}
11564
-
11565
-		if ( is_array( $data ) ) {
11566
-			foreach( $data as $key => $item ) {
11567
-				$array[$key] = self::dump( $item );
11568
-			}
11569
-		}
11570
-
11571
-		return $array;
11572
-	}
11573
-
11574
-	/**
11575
-	 * Binds an SQL function to a column.
11576
-	 * This method can be used to setup a decode/encode scheme or
11577
-	 * perform UUID insertion. This method is especially useful for handling
11578
-	 * MySQL spatial columns, because they need to be processed first using
11579
-	 * the asText/GeomFromText functions.
11580
-	 *
11581
-	 * Example:
11582
-	 *
11583
-	 * R::bindFunc( 'read', 'location.point', 'asText' );
11584
-	 * R::bindFunc( 'write', 'location.point', 'GeomFromText' );
11585
-	 *
11586
-	 * Passing NULL as the function will reset (clear) the function
11587
-	 * for this column/mode.
11588
-	 *
11589
-	 * @param string $mode (read or write)
11590
-	 * @param string $field
11591
-	 * @param string $function
11592
-	 *
11593
-	 */
11594
-	public static function bindFunc( $mode, $field, $function )
11595
-	{
11596
-		self::$redbean->bindFunc( $mode, $field, $function );
11597
-	}
11598
-
11599
-	/**
11600
-	 * Sets global aliases.
11601
-	 *
11602
-	 * @param array $list
11603
-	 *
11604
-	 * @return void
11605
-	 */
11606
-	public static function aliases( $list )
11607
-	{
11608
-		OODBBean::aliases( $list );
11609
-	}
11610
-
11611
-	/**
11612
-	 * Tries to find a bean matching a certain type and
11613
-	 * criteria set. If no beans are found a new bean
11614
-	 * will be created, the criteria will be imported into this
11615
-	 * bean and the bean will be stored and returned.
11616
-	 * If multiple beans match the criteria only the first one
11617
-	 * will be returned.
11618
-	 *
11619
-	 * @param string $type type of bean to search for
11620
-	 * @param array  $like criteria set describing the bean to search for
11621
-	 *
11622
-	 * @return OODBBean
11623
-	 */
11624
-	public static function findOrCreate( $type, $like = array() )
11625
-	{
11626
-		return self::$finder->findOrCreate( $type, $like );
11627
-	}
11628
-
11629
-	/**
11630
-	 * Tries to find beans matching the specified type and
11631
-	 * criteria set.
11632
-	 *
11633
-	 * If the optional additional SQL snippet is a condition, it will
11634
-	 * be glued to the rest of the query using the AND operator.
11635
-	 *
11636
-	 * @param string $type type of bean to search for
11637
-	 * @param array  $like optional criteria set describing the bean to search for
11638
-	 * @param string $sql  optional additional SQL for sorting
11639
-	 *
11640
-	 * @return array
11641
-	 */
11642
-	public static function findLike( $type, $like = array(), $sql = '' )
11643
-	{
11644
-		return self::$finder->findLike( $type, $like, $sql );
11645
-	}
11646
-
11647
-	/**
11648
-	 * Starts logging queries.
11649
-	 * Use this method to start logging SQL queries being
11650
-	 * executed by the adapter.
11651
-	 *
11652
-	 * @note you cannot use R::debug and R::startLogging
11653
-	 * at the same time because R::debug is essentially a
11654
-	 * special kind of logging.
11655
-	 *
11656
-	 * @return void
11657
-	 */
11658
-	public static function startLogging()
11659
-	{
11660
-		self::debug( TRUE, RDefault::C_LOGGER_ARRAY );
11661
-	}
11662
-
11663
-	/**
11664
-	 * Stops logging, comfortable method to stop logging of queries.
11665
-	 *
11666
-	 * @return void
11667
-	 */
11668
-	public static function stopLogging()
11669
-	{
11670
-		self::debug( FALSE );
11671
-	}
11672
-
11673
-	/**
11674
-	 * Returns the log entries written after the startLogging.
11675
-	 *
11676
-	 * @return array
11677
-	 */
11678
-	public static function getLogs()
11679
-	{
11680
-		return self::getLogger()->getLogs();
11681
-	}
11682
-
11683
-	/**
11684
-	 * Resets the Query counter.
11685
-	 *
11686
-	 * @return integer
11687
-	 */
11688
-	public static function resetQueryCount()
11689
-	{
11690
-		self::$adapter->getDatabase()->resetCounter();
11691
-	}
11692
-
11693
-	/**
11694
-	 * Returns the number of SQL queries processed.
11695
-	 *
11696
-	 * @return integer
11697
-	 */
11698
-	public static function getQueryCount()
11699
-	{
11700
-		return self::$adapter->getDatabase()->getQueryCount();
11701
-	}
11702
-
11703
-	/**
11704
-	 * Returns the current logger instance being used by the
11705
-	 * database object.
11706
-	 *
11707
-	 * @return Logger
11708
-	 */
11709
-	public static function getLogger()
11710
-	{
11711
-		return self::$adapter->getDatabase()->getLogger();
11712
-	}
11713
-
11714
-	/**
11715
-	 * Alias for setAutoResolve() method on OODBBean.
11716
-	 * Enables or disables auto-resolving fetch types.
11717
-	 * Auto-resolving aliased parent beans is convenient but can
11718
-	 * be slower and can create infinite recursion if you
11719
-	 * used aliases to break cyclic relations in your domain.
11720
-	 *
11721
-	 * @param boolean $automatic TRUE to enable automatic resolving aliased parents
11722
-	 *
11723
-	 * @return void
11724
-	 */
11725
-	public static function setAutoResolve( $automatic = TRUE )
11726
-	{
11727
-		OODBBean::setAutoResolve( (boolean) $automatic );
11728
-	}
11729
-
11730
-	/**
11731
-	 * Dynamically extends the facade with a plugin.
11732
-	 * Using this method you can register your plugin with the facade and then
11733
-	 * use the plugin by invoking the name specified plugin name as a method on
11734
-	 * the facade.
11735
-	 *
11736
-	 * Usage:
11737
-	 *
11738
-	 * R::ext( 'makeTea', function() { ... }  );
11739
-	 *
11740
-	 * Now you can use your makeTea plugin like this:
11741
-	 *
11742
-	 * R::makeTea();
11743
-	 *
11744
-	 * @param string   $pluginName name of the method to call the plugin
11745
-	 * @param callable $callable   a PHP callable
11746
-	 */
11747
-	public static function ext( $pluginName, $callable )
11748
-	{
11749
-		if ( !ctype_alnum( $pluginName ) ) {
11750
-			throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
11751
-		}
11752
-		self::$plugins[$pluginName] = $callable;
11753
-	}
11754
-
11755
-	/**
11756
-	 * Call static for use with dynamic plugins. This magic method will
11757
-	 * intercept static calls and route them to the specified plugin.
11758
-	 *
11759
-	 * @param string $pluginName name of the plugin
11760
-	 * @param array  $params     list of arguments to pass to plugin method
11761
-	 *
11762
-	 * @return mixed
11763
-	 */
11764
-	public static function __callStatic( $pluginName, $params )
11765
-	{
11766
-		if ( !ctype_alnum( $pluginName) ) {
11767
-			throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
11768
-		}
11769
-		if ( !isset( self::$plugins[$pluginName] ) ) {
11770
-			throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' );
11771
-		}
11772
-		return call_user_func_array( self::$plugins[$pluginName], $params );
11773
-	}
11509
+    /**
11510
+     * Sets the error mode for FUSE.
11511
+     * What to do if a FUSE model method does not exist?
11512
+     * You can set the following options:
11513
+     *
11514
+     * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
11515
+     * OODBBean::C_ERR_LOG, logs the incident using error_log
11516
+     * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
11517
+     * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
11518
+     * OODBBean::C_ERR_EXCEPTION, throws an exception
11519
+     * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
11520
+     * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
11521
+     * 
11522
+     * Custom handler method signature: handler( array (
11523
+     * 	'message' => string
11524
+     * 	'bean' => OODBBean
11525
+     * 	'method' => string
11526
+     * ) )
11527
+     *
11528
+     * This method returns the old mode and handler as an array.
11529
+     *
11530
+     * @param integer       $mode mode
11531
+     * @param callable|NULL $func custom handler
11532
+     * 
11533
+     * @return array
11534
+     */
11535
+    public static function setErrorHandlingFUSE( $mode, $func = NULL )
11536
+    {
11537
+        return OODBBean::setErrorHandlingFUSE( $mode, $func );
11538
+    }
11539
+
11540
+    /**
11541
+     * Simple but effective debug function.
11542
+     * Given a one or more beans this method will
11543
+     * return an array containing first part of the string
11544
+     * representation of each item in the array.
11545
+     *
11546
+     * @param OODBBean|array $data either a bean or an array of beans
11547
+     *
11548
+     * @return array
11549
+     *
11550
+     */
11551
+    public static function dump( $data )
11552
+    {
11553
+        $array = array();
11554
+
11555
+        if ( $data instanceof OODBBean ) {
11556
+            $str = strval( $data );
11557
+            if (strlen($str) > 35) {
11558
+                $beanStr = substr( $str, 0, 35 ).'... ';
11559
+            } else {
11560
+                $beanStr = $str;
11561
+            }
11562
+            return $beanStr;
11563
+        }
11564
+
11565
+        if ( is_array( $data ) ) {
11566
+            foreach( $data as $key => $item ) {
11567
+                $array[$key] = self::dump( $item );
11568
+            }
11569
+        }
11570
+
11571
+        return $array;
11572
+    }
11573
+
11574
+    /**
11575
+     * Binds an SQL function to a column.
11576
+     * This method can be used to setup a decode/encode scheme or
11577
+     * perform UUID insertion. This method is especially useful for handling
11578
+     * MySQL spatial columns, because they need to be processed first using
11579
+     * the asText/GeomFromText functions.
11580
+     *
11581
+     * Example:
11582
+     *
11583
+     * R::bindFunc( 'read', 'location.point', 'asText' );
11584
+     * R::bindFunc( 'write', 'location.point', 'GeomFromText' );
11585
+     *
11586
+     * Passing NULL as the function will reset (clear) the function
11587
+     * for this column/mode.
11588
+     *
11589
+     * @param string $mode (read or write)
11590
+     * @param string $field
11591
+     * @param string $function
11592
+     *
11593
+     */
11594
+    public static function bindFunc( $mode, $field, $function )
11595
+    {
11596
+        self::$redbean->bindFunc( $mode, $field, $function );
11597
+    }
11598
+
11599
+    /**
11600
+     * Sets global aliases.
11601
+     *
11602
+     * @param array $list
11603
+     *
11604
+     * @return void
11605
+     */
11606
+    public static function aliases( $list )
11607
+    {
11608
+        OODBBean::aliases( $list );
11609
+    }
11610
+
11611
+    /**
11612
+     * Tries to find a bean matching a certain type and
11613
+     * criteria set. If no beans are found a new bean
11614
+     * will be created, the criteria will be imported into this
11615
+     * bean and the bean will be stored and returned.
11616
+     * If multiple beans match the criteria only the first one
11617
+     * will be returned.
11618
+     *
11619
+     * @param string $type type of bean to search for
11620
+     * @param array  $like criteria set describing the bean to search for
11621
+     *
11622
+     * @return OODBBean
11623
+     */
11624
+    public static function findOrCreate( $type, $like = array() )
11625
+    {
11626
+        return self::$finder->findOrCreate( $type, $like );
11627
+    }
11628
+
11629
+    /**
11630
+     * Tries to find beans matching the specified type and
11631
+     * criteria set.
11632
+     *
11633
+     * If the optional additional SQL snippet is a condition, it will
11634
+     * be glued to the rest of the query using the AND operator.
11635
+     *
11636
+     * @param string $type type of bean to search for
11637
+     * @param array  $like optional criteria set describing the bean to search for
11638
+     * @param string $sql  optional additional SQL for sorting
11639
+     *
11640
+     * @return array
11641
+     */
11642
+    public static function findLike( $type, $like = array(), $sql = '' )
11643
+    {
11644
+        return self::$finder->findLike( $type, $like, $sql );
11645
+    }
11646
+
11647
+    /**
11648
+     * Starts logging queries.
11649
+     * Use this method to start logging SQL queries being
11650
+     * executed by the adapter.
11651
+     *
11652
+     * @note you cannot use R::debug and R::startLogging
11653
+     * at the same time because R::debug is essentially a
11654
+     * special kind of logging.
11655
+     *
11656
+     * @return void
11657
+     */
11658
+    public static function startLogging()
11659
+    {
11660
+        self::debug( TRUE, RDefault::C_LOGGER_ARRAY );
11661
+    }
11662
+
11663
+    /**
11664
+     * Stops logging, comfortable method to stop logging of queries.
11665
+     *
11666
+     * @return void
11667
+     */
11668
+    public static function stopLogging()
11669
+    {
11670
+        self::debug( FALSE );
11671
+    }
11672
+
11673
+    /**
11674
+     * Returns the log entries written after the startLogging.
11675
+     *
11676
+     * @return array
11677
+     */
11678
+    public static function getLogs()
11679
+    {
11680
+        return self::getLogger()->getLogs();
11681
+    }
11682
+
11683
+    /**
11684
+     * Resets the Query counter.
11685
+     *
11686
+     * @return integer
11687
+     */
11688
+    public static function resetQueryCount()
11689
+    {
11690
+        self::$adapter->getDatabase()->resetCounter();
11691
+    }
11692
+
11693
+    /**
11694
+     * Returns the number of SQL queries processed.
11695
+     *
11696
+     * @return integer
11697
+     */
11698
+    public static function getQueryCount()
11699
+    {
11700
+        return self::$adapter->getDatabase()->getQueryCount();
11701
+    }
11702
+
11703
+    /**
11704
+     * Returns the current logger instance being used by the
11705
+     * database object.
11706
+     *
11707
+     * @return Logger
11708
+     */
11709
+    public static function getLogger()
11710
+    {
11711
+        return self::$adapter->getDatabase()->getLogger();
11712
+    }
11713
+
11714
+    /**
11715
+     * Alias for setAutoResolve() method on OODBBean.
11716
+     * Enables or disables auto-resolving fetch types.
11717
+     * Auto-resolving aliased parent beans is convenient but can
11718
+     * be slower and can create infinite recursion if you
11719
+     * used aliases to break cyclic relations in your domain.
11720
+     *
11721
+     * @param boolean $automatic TRUE to enable automatic resolving aliased parents
11722
+     *
11723
+     * @return void
11724
+     */
11725
+    public static function setAutoResolve( $automatic = TRUE )
11726
+    {
11727
+        OODBBean::setAutoResolve( (boolean) $automatic );
11728
+    }
11729
+
11730
+    /**
11731
+     * Dynamically extends the facade with a plugin.
11732
+     * Using this method you can register your plugin with the facade and then
11733
+     * use the plugin by invoking the name specified plugin name as a method on
11734
+     * the facade.
11735
+     *
11736
+     * Usage:
11737
+     *
11738
+     * R::ext( 'makeTea', function() { ... }  );
11739
+     *
11740
+     * Now you can use your makeTea plugin like this:
11741
+     *
11742
+     * R::makeTea();
11743
+     *
11744
+     * @param string   $pluginName name of the method to call the plugin
11745
+     * @param callable $callable   a PHP callable
11746
+     */
11747
+    public static function ext( $pluginName, $callable )
11748
+    {
11749
+        if ( !ctype_alnum( $pluginName ) ) {
11750
+            throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
11751
+        }
11752
+        self::$plugins[$pluginName] = $callable;
11753
+    }
11754
+
11755
+    /**
11756
+     * Call static for use with dynamic plugins. This magic method will
11757
+     * intercept static calls and route them to the specified plugin.
11758
+     *
11759
+     * @param string $pluginName name of the plugin
11760
+     * @param array  $params     list of arguments to pass to plugin method
11761
+     *
11762
+     * @return mixed
11763
+     */
11764
+    public static function __callStatic( $pluginName, $params )
11765
+    {
11766
+        if ( !ctype_alnum( $pluginName) ) {
11767
+            throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
11768
+        }
11769
+        if ( !isset( self::$plugins[$pluginName] ) ) {
11770
+            throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' );
11771
+        }
11772
+        return call_user_func_array( self::$plugins[$pluginName], $params );
11773
+    }
11774 11774
 }
11775 11775
 
11776 11776
 }
@@ -11798,429 +11798,429 @@  discard block
 block discarded – undo
11798 11798
  */
11799 11799
 class DuplicationManager
11800 11800
 {
11801
-	/**
11802
-	 * @var ToolBox
11803
-	 */
11804
-	protected $toolbox;
11805
-
11806
-	/**
11807
-	 * @var AssociationManager
11808
-	 */
11809
-	protected $associationManager;
11810
-
11811
-	/**
11812
-	 * @var OODB
11813
-	 */
11814
-	protected $redbean;
11815
-
11816
-	/**
11817
-	 * @var array
11818
-	 */
11819
-	protected $tables = array();
11820
-
11821
-	/**
11822
-	 * @var array
11823
-	 */
11824
-	protected $columns = array();
11825
-
11826
-	/**
11827
-	 * @var array
11828
-	 */
11829
-	protected $filters = array();
11830
-
11831
-	/**
11832
-	 * @var array
11833
-	 */
11834
-	protected $cacheTables = FALSE;
11835
-
11836
-	/**
11837
-	 * Copies the shared beans in a bean, i.e. all the sharedBean-lists.
11838
-	 *
11839
-	 * @param OODBBean $copy   target bean to copy lists to
11840
-	 * @param string   $shared name of the shared list
11841
-	 * @param array    $beans  array with shared beans to copy
11842
-	 *
11843
-	 * @return void
11844
-	 */
11845
-	private function copySharedBeans( OODBBean $copy, $shared, $beans )
11846
-	{
11847
-		$copy->$shared = array();
11848
-
11849
-		foreach ( $beans as $subBean ) {
11850
-			array_push( $copy->$shared, $subBean );
11851
-		}
11852
-	}
11853
-
11854
-	/**
11855
-	 * Copies the own beans in a bean, i.e. all the ownBean-lists.
11856
-	 * Each bean in the own-list belongs exclusively to its owner so
11857
-	 * we need to invoke the duplicate method again to duplicate each bean here.
11858
-	 *
11859
-	 * @param OODBBean $copy        target bean to copy lists to
11860
-	 * @param string   $owned       name of the own list
11861
-	 * @param array    $beans       array with shared beans to copy
11862
-	 * @param array    $trail       array with former beans to detect recursion
11863
-	 * @param boolean  $preserveIDs TRUE means preserve IDs, for export only
11864
-	 *
11865
-	 * @return void
11866
-	 */
11867
-	private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs )
11868
-	{
11869
-		$copy->$owned = array();
11870
-		foreach ( $beans as $subBean ) {
11871
-			array_push( $copy->$owned, $this->duplicate( $subBean, $trail, $preserveIDs ) );
11872
-		}
11873
-	}
11874
-
11875
-	/**
11876
-	 * Creates a copy of bean $bean and copies all primitive properties (not lists)
11877
-	 * and the parents beans to the newly created bean. Also sets the ID of the bean
11878
-	 * to 0.
11879
-	 *
11880
-	 * @param OODBBean $bean bean to copy
11881
-	 *
11882
-	 * @return OODBBean
11883
-	 */
11884
-	private function createCopy( OODBBean $bean )
11885
-	{
11886
-		$type = $bean->getMeta( 'type' );
11887
-
11888
-		$copy = $this->redbean->dispense( $type );
11889
-		$copy->setMeta( 'sys.dup-from-id', $bean->id );
11890
-		$copy->setMeta( 'sys.old-id', $bean->id );
11891
-		$copy->importFrom( $bean );
11892
-		$copy->id = 0;
11893
-
11894
-		return $copy;
11895
-	}
11896
-
11897
-	/**
11898
-	 * Generates a key from the bean type and its ID and determines if the bean
11899
-	 * occurs in the trail, if not the bean will be added to the trail.
11900
-	 * Returns TRUE if the bean occurs in the trail and FALSE otherwise.
11901
-	 *
11902
-	 * @param array    $trail list of former beans
11903
-	 * @param OODBBean $bean  currently selected bean
11904
-	 *
11905
-	 * @return boolean
11906
-	 */
11907
-	private function inTrailOrAdd( &$trail, OODBBean $bean )
11908
-	{
11909
-		$type = $bean->getMeta( 'type' );
11910
-		$key  = $type . $bean->getID();
11911
-
11912
-		if ( isset( $trail[$key] ) ) {
11913
-			return TRUE;
11914
-		}
11915
-
11916
-		$trail[$key] = $bean;
11917
-
11918
-		return FALSE;
11919
-	}
11920
-
11921
-	/**
11922
-	 * Given the type name of a bean this method returns the canonical names
11923
-	 * of the own-list and the shared-list properties respectively.
11924
-	 * Returns a list with two elements: name of the own-list, and name
11925
-	 * of the shared list.
11926
-	 *
11927
-	 * @param string $typeName bean type name
11928
-	 *
11929
-	 * @return array
11930
-	 */
11931
-	private function getListNames( $typeName )
11932
-	{
11933
-		$owned  = 'own' . ucfirst( $typeName );
11934
-		$shared = 'shared' . ucfirst( $typeName );
11935
-
11936
-		return array( $owned, $shared );
11937
-	}
11938
-
11939
-	/**
11940
-	 * Determines whether the bean has an own list based on
11941
-	 * schema inspection from realtime schema or cache.
11942
-	 *
11943
-	 * @param string $type   bean type to get list for
11944
-	 * @param string $target type of list you want to detect
11945
-	 *
11946
-	 * @return boolean
11947
-	 */
11948
-	protected function hasOwnList( $type, $target )
11949
-	{
11950
-		return isset( $this->columns[$target][$type . '_id'] );
11951
-	}
11952
-
11953
-	/**
11954
-	 * Determines whether the bea has a shared list based on
11955
-	 * schema inspection from realtime schema or cache.
11956
-	 *
11957
-	 * @param string $type   bean type to get list for
11958
-	 * @param string $target type of list you are looking for
11959
-	 *
11960
-	 * @return boolean
11961
-	 */
11962
-	protected function hasSharedList( $type, $target )
11963
-	{
11964
-		return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables );
11965
-	}
11966
-
11967
-	/**
11968
-	 * @see DuplicationManager::dup
11969
-	 *
11970
-	 * @param OODBBean $bean        bean to be copied
11971
-	 * @param array    $trail       trail to prevent infinite loops
11972
-	 * @param boolean  $preserveIDs preserve IDs
11973
-	 *
11974
-	 * @return OODBBean
11975
-	 */
11976
-	protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
11977
-	{
11978
-		if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean;
11979
-
11980
-		$type = $bean->getMeta( 'type' );
11981
-
11982
-		$copy = $this->createCopy( $bean );
11983
-		foreach ( $this->tables as $table ) {
11984
-
11985
-			if ( !empty( $this->filters ) ) {
11986
-				if ( !in_array( $table, $this->filters ) ) continue;
11987
-			}
11988
-
11989
-			list( $owned, $shared ) = $this->getListNames( $table );
11990
-
11991
-			if ( $this->hasSharedList( $type, $table ) ) {
11992
-				if ( $beans = $bean->$shared ) {
11993
-					$this->copySharedBeans( $copy, $shared, $beans );
11994
-				}
11995
-			} elseif ( $this->hasOwnList( $type, $table ) ) {
11996
-				if ( $beans = $bean->$owned ) {
11997
-					$this->copyOwnBeans( $copy, $owned, $beans, $trail, $preserveIDs );
11998
-				}
11999
-
12000
-				$copy->setMeta( 'sys.shadow.' . $owned, NULL );
12001
-			}
12002
-
12003
-			$copy->setMeta( 'sys.shadow.' . $shared, NULL );
12004
-		}
12005
-
12006
-		$copy->id = ( $preserveIDs ) ? $bean->id : $copy->id;
12007
-
12008
-		return $copy;
12009
-	}
12010
-
12011
-	/**
12012
-	 * Constructor,
12013
-	 * creates a new instance of DupManager.
12014
-	 *
12015
-	 * @param ToolBox $toolbox
12016
-	 */
12017
-	public function __construct( ToolBox $toolbox )
12018
-	{
12019
-		$this->toolbox            = $toolbox;
12020
-		$this->redbean            = $toolbox->getRedBean();
12021
-		$this->associationManager = $this->redbean->getAssociationManager();
12022
-	}
12023
-
12024
-	/**
12025
-	 * Recursively turns the keys of an array into
12026
-	 * camelCase.
12027
-	 *
12028
-	 * @param array   $array       array to camelize
12029
-	 * @param boolean $dolphinMode whether you want the exception for IDs.
12030
-	 *
12031
-	 * @return array
12032
-	 */
12033
-	public function camelfy( $array, $dolphinMode = false ) {
12034
-		$newArray = array();
12035
-		foreach( $array as $key => $element ) {
12036
-			$newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){
12037
-				return strtoupper( $matches[1] );
12038
-			}, $key);
12039
-
12040
-			if ( $dolphinMode ) {
12041
-				$newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey );
12042
-			}
12043
-
12044
-			$newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element;
12045
-		}
12046
-		return $newArray;
12047
-	}
12048
-
12049
-	/**
12050
-	 * For better performance you can pass the tables in an array to this method.
12051
-	 * If the tables are available the duplication manager will not query them so
12052
-	 * this might be beneficial for performance.
12053
-	 *
12054
-	 * This method allows two array formats:
12055
-	 *
12056
-	 * array( TABLE1, TABLE2 ... )
12057
-	 *
12058
-	 * or
12059
-	 *
12060
-	 * array( TABLE1 => array( COLUMN1, COLUMN2 ... ) ... )
12061
-	 *
12062
-	 * @param array $tables a table cache array
12063
-	 *
12064
-	 * @return void
12065
-	 */
12066
-	public function setTables( $tables )
12067
-	{
12068
-		foreach ( $tables as $key => $value ) {
12069
-			if ( is_numeric( $key ) ) {
12070
-				$this->tables[] = $value;
12071
-			} else {
12072
-				$this->tables[]      = $key;
12073
-				$this->columns[$key] = $value;
12074
-			}
12075
-		}
12076
-
12077
-		$this->cacheTables = TRUE;
12078
-	}
12079
-
12080
-	/**
12081
-	 * Returns a schema array for cache.
12082
-	 * You can use the return value of this method as a cache,
12083
-	 * store it in RAM or on disk and pass it to setTables later.
12084
-	 *
12085
-	 * @return array
12086
-	 */
12087
-	public function getSchema()
12088
-	{
12089
-		return $this->columns;
12090
-	}
12091
-
12092
-	/**
12093
-	 * Indicates whether you want the duplication manager to cache the database schema.
12094
-	 * If this flag is set to TRUE the duplication manager will query the database schema
12095
-	 * only once. Otherwise the duplicationmanager will, by default, query the schema
12096
-	 * every time a duplication action is performed (dup()).
12097
-	 *
12098
-	 * @param boolean $yesNo TRUE to use caching, FALSE otherwise
12099
-	 */
12100
-	public function setCacheTables( $yesNo )
12101
-	{
12102
-		$this->cacheTables = $yesNo;
12103
-	}
12104
-
12105
-	/**
12106
-	 * A filter array is an array with table names.
12107
-	 * By setting a table filter you can make the duplication manager only take into account
12108
-	 * certain bean types. Other bean types will be ignored when exporting or making a
12109
-	 * deep copy. If no filters are set all types will be taking into account, this is
12110
-	 * the default behavior.
12111
-	 *
12112
-	 * @param array $filters list of tables to be filtered
12113
-	 */
12114
-	public function setFilters( $filters )
12115
-	{
12116
-		if ( !is_array( $filters ) ) {
12117
-			$filters = array( $filters );
12118
-		}
12119
-
12120
-		$this->filters = $filters;
12121
-	}
12122
-
12123
-	/**
12124
-	 * Makes a copy of a bean. This method makes a deep copy
12125
-	 * of the bean.The copy will have the following features.
12126
-	 * - All beans in own-lists will be duplicated as well
12127
-	 * - All references to shared beans will be copied but not the shared beans themselves
12128
-	 * - All references to parent objects (_id fields) will be copied but not the parents themselves
12129
-	 * In most cases this is the desired scenario for copying beans.
12130
-	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
12131
-	 * (i.e. one that already has been processed) the ID of the bean will be returned.
12132
-	 * This should not happen though.
12133
-	 *
12134
-	 * Note:
12135
-	 * This function does a reflectional database query so it may be slow.
12136
-	 *
12137
-	 * Note:
12138
-	 * this function actually passes the arguments to a protected function called
12139
-	 * duplicate() that does all the work. This method takes care of creating a clone
12140
-	 * of the bean to avoid the bean getting tainted (triggering saving when storing it).
12141
-	 *
12142
-	 * @param OODBBean $bean        bean to be copied
12143
-	 * @param array    $trail       for internal usage, pass array()
12144
-	 * @param boolean  $preserveIDs for internal usage
12145
-	 *
12146
-	 * @return OODBBean
12147
-	 */
12148
-	public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
12149
-	{
12150
-		if ( !count( $this->tables ) ) {
12151
-			$this->tables = $this->toolbox->getWriter()->getTables();
12152
-		}
12153
-
12154
-		if ( !count( $this->columns ) ) {
12155
-			foreach ( $this->tables as $table ) {
12156
-				$this->columns[$table] = $this->toolbox->getWriter()->getColumns( $table );
12157
-			}
12158
-		}
12159
-
12160
-		$rs = $this->duplicate( ( clone $bean ), $trail, $preserveIDs );
12161
-
12162
-		if ( !$this->cacheTables ) {
12163
-			$this->tables  = array();
12164
-			$this->columns = array();
12165
-		}
12166
-
12167
-		return $rs;
12168
-	}
12169
-
12170
-	/**
12171
-	 * Exports a collection of beans recursively.
12172
-	 * This method will export an array of beans in the first argument to a
12173
-	 * set of arrays. This can be used to send JSON or XML representations
12174
-	 * of bean hierarchies to the client.
12175
-	 *
12176
-	 * For every bean in the array this method will export:
12177
-	 *
12178
-	 * - contents of the bean
12179
-	 * - all own bean lists (recursively)
12180
-	 * - all shared beans (but not THEIR own lists)
12181
-	 *
12182
-	 * If the second parameter is set to TRUE the parents of the beans in the
12183
-	 * array will be exported as well (but not THEIR parents).
12184
-	 *
12185
-	 * The third parameter can be used to provide a white-list array
12186
-	 * for filtering. This is an array of strings representing type names,
12187
-	 * only the type names in the filter list will be exported.
12188
-	 *
12189
-	 * The fourth parameter can be used to change the keys of the resulting
12190
-	 * export arrays. The default mode is 'snake case' but this leaves the
12191
-	 * keys as-is, because 'snake' is the default case style used by
12192
-	 * RedBeanPHP in the database. You can set this to 'camel' for
12193
-	 * camel cased keys or 'dolphin' (same as camelcase but id will be
12194
-	 * converted to ID instead of Id).
12195
-	 *
12196
-	 * @param array|OODBBean $beans     beans to be exported
12197
-	 * @param boolean        $parents   also export parents
12198
-	 * @param array          $filters   only these types (whitelist)
12199
-	 * @param string         $caseStyle case style identifier
12200
-	 *
12201
-	 * @return array
12202
-	 */
12203
-	public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake')
12204
-	{
12205
-		$array = array();
12206
-
12207
-		if ( !is_array( $beans ) ) {
12208
-			$beans = array( $beans );
12209
-		}
12210
-
12211
-		foreach ( $beans as $bean ) {
12212
-			$this->setFilters( $filters );
12213
-
12214
-			$duplicate = $this->dup( $bean, array(), TRUE );
12215
-
12216
-			$array[]   = $duplicate->export( FALSE, $parents, FALSE, $filters );
12217
-		}
12218
-
12219
-		if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array );
12220
-		if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, true );
12221
-
12222
-		return $array;
12223
-	}
11801
+    /**
11802
+     * @var ToolBox
11803
+     */
11804
+    protected $toolbox;
11805
+
11806
+    /**
11807
+     * @var AssociationManager
11808
+     */
11809
+    protected $associationManager;
11810
+
11811
+    /**
11812
+     * @var OODB
11813
+     */
11814
+    protected $redbean;
11815
+
11816
+    /**
11817
+     * @var array
11818
+     */
11819
+    protected $tables = array();
11820
+
11821
+    /**
11822
+     * @var array
11823
+     */
11824
+    protected $columns = array();
11825
+
11826
+    /**
11827
+     * @var array
11828
+     */
11829
+    protected $filters = array();
11830
+
11831
+    /**
11832
+     * @var array
11833
+     */
11834
+    protected $cacheTables = FALSE;
11835
+
11836
+    /**
11837
+     * Copies the shared beans in a bean, i.e. all the sharedBean-lists.
11838
+     *
11839
+     * @param OODBBean $copy   target bean to copy lists to
11840
+     * @param string   $shared name of the shared list
11841
+     * @param array    $beans  array with shared beans to copy
11842
+     *
11843
+     * @return void
11844
+     */
11845
+    private function copySharedBeans( OODBBean $copy, $shared, $beans )
11846
+    {
11847
+        $copy->$shared = array();
11848
+
11849
+        foreach ( $beans as $subBean ) {
11850
+            array_push( $copy->$shared, $subBean );
11851
+        }
11852
+    }
11853
+
11854
+    /**
11855
+     * Copies the own beans in a bean, i.e. all the ownBean-lists.
11856
+     * Each bean in the own-list belongs exclusively to its owner so
11857
+     * we need to invoke the duplicate method again to duplicate each bean here.
11858
+     *
11859
+     * @param OODBBean $copy        target bean to copy lists to
11860
+     * @param string   $owned       name of the own list
11861
+     * @param array    $beans       array with shared beans to copy
11862
+     * @param array    $trail       array with former beans to detect recursion
11863
+     * @param boolean  $preserveIDs TRUE means preserve IDs, for export only
11864
+     *
11865
+     * @return void
11866
+     */
11867
+    private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs )
11868
+    {
11869
+        $copy->$owned = array();
11870
+        foreach ( $beans as $subBean ) {
11871
+            array_push( $copy->$owned, $this->duplicate( $subBean, $trail, $preserveIDs ) );
11872
+        }
11873
+    }
11874
+
11875
+    /**
11876
+     * Creates a copy of bean $bean and copies all primitive properties (not lists)
11877
+     * and the parents beans to the newly created bean. Also sets the ID of the bean
11878
+     * to 0.
11879
+     *
11880
+     * @param OODBBean $bean bean to copy
11881
+     *
11882
+     * @return OODBBean
11883
+     */
11884
+    private function createCopy( OODBBean $bean )
11885
+    {
11886
+        $type = $bean->getMeta( 'type' );
11887
+
11888
+        $copy = $this->redbean->dispense( $type );
11889
+        $copy->setMeta( 'sys.dup-from-id', $bean->id );
11890
+        $copy->setMeta( 'sys.old-id', $bean->id );
11891
+        $copy->importFrom( $bean );
11892
+        $copy->id = 0;
11893
+
11894
+        return $copy;
11895
+    }
11896
+
11897
+    /**
11898
+     * Generates a key from the bean type and its ID and determines if the bean
11899
+     * occurs in the trail, if not the bean will be added to the trail.
11900
+     * Returns TRUE if the bean occurs in the trail and FALSE otherwise.
11901
+     *
11902
+     * @param array    $trail list of former beans
11903
+     * @param OODBBean $bean  currently selected bean
11904
+     *
11905
+     * @return boolean
11906
+     */
11907
+    private function inTrailOrAdd( &$trail, OODBBean $bean )
11908
+    {
11909
+        $type = $bean->getMeta( 'type' );
11910
+        $key  = $type . $bean->getID();
11911
+
11912
+        if ( isset( $trail[$key] ) ) {
11913
+            return TRUE;
11914
+        }
11915
+
11916
+        $trail[$key] = $bean;
11917
+
11918
+        return FALSE;
11919
+    }
11920
+
11921
+    /**
11922
+     * Given the type name of a bean this method returns the canonical names
11923
+     * of the own-list and the shared-list properties respectively.
11924
+     * Returns a list with two elements: name of the own-list, and name
11925
+     * of the shared list.
11926
+     *
11927
+     * @param string $typeName bean type name
11928
+     *
11929
+     * @return array
11930
+     */
11931
+    private function getListNames( $typeName )
11932
+    {
11933
+        $owned  = 'own' . ucfirst( $typeName );
11934
+        $shared = 'shared' . ucfirst( $typeName );
11935
+
11936
+        return array( $owned, $shared );
11937
+    }
11938
+
11939
+    /**
11940
+     * Determines whether the bean has an own list based on
11941
+     * schema inspection from realtime schema or cache.
11942
+     *
11943
+     * @param string $type   bean type to get list for
11944
+     * @param string $target type of list you want to detect
11945
+     *
11946
+     * @return boolean
11947
+     */
11948
+    protected function hasOwnList( $type, $target )
11949
+    {
11950
+        return isset( $this->columns[$target][$type . '_id'] );
11951
+    }
11952
+
11953
+    /**
11954
+     * Determines whether the bea has a shared list based on
11955
+     * schema inspection from realtime schema or cache.
11956
+     *
11957
+     * @param string $type   bean type to get list for
11958
+     * @param string $target type of list you are looking for
11959
+     *
11960
+     * @return boolean
11961
+     */
11962
+    protected function hasSharedList( $type, $target )
11963
+    {
11964
+        return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables );
11965
+    }
11966
+
11967
+    /**
11968
+     * @see DuplicationManager::dup
11969
+     *
11970
+     * @param OODBBean $bean        bean to be copied
11971
+     * @param array    $trail       trail to prevent infinite loops
11972
+     * @param boolean  $preserveIDs preserve IDs
11973
+     *
11974
+     * @return OODBBean
11975
+     */
11976
+    protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
11977
+    {
11978
+        if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean;
11979
+
11980
+        $type = $bean->getMeta( 'type' );
11981
+
11982
+        $copy = $this->createCopy( $bean );
11983
+        foreach ( $this->tables as $table ) {
11984
+
11985
+            if ( !empty( $this->filters ) ) {
11986
+                if ( !in_array( $table, $this->filters ) ) continue;
11987
+            }
11988
+
11989
+            list( $owned, $shared ) = $this->getListNames( $table );
11990
+
11991
+            if ( $this->hasSharedList( $type, $table ) ) {
11992
+                if ( $beans = $bean->$shared ) {
11993
+                    $this->copySharedBeans( $copy, $shared, $beans );
11994
+                }
11995
+            } elseif ( $this->hasOwnList( $type, $table ) ) {
11996
+                if ( $beans = $bean->$owned ) {
11997
+                    $this->copyOwnBeans( $copy, $owned, $beans, $trail, $preserveIDs );
11998
+                }
11999
+
12000
+                $copy->setMeta( 'sys.shadow.' . $owned, NULL );
12001
+            }
12002
+
12003
+            $copy->setMeta( 'sys.shadow.' . $shared, NULL );
12004
+        }
12005
+
12006
+        $copy->id = ( $preserveIDs ) ? $bean->id : $copy->id;
12007
+
12008
+        return $copy;
12009
+    }
12010
+
12011
+    /**
12012
+     * Constructor,
12013
+     * creates a new instance of DupManager.
12014
+     *
12015
+     * @param ToolBox $toolbox
12016
+     */
12017
+    public function __construct( ToolBox $toolbox )
12018
+    {
12019
+        $this->toolbox            = $toolbox;
12020
+        $this->redbean            = $toolbox->getRedBean();
12021
+        $this->associationManager = $this->redbean->getAssociationManager();
12022
+    }
12023
+
12024
+    /**
12025
+     * Recursively turns the keys of an array into
12026
+     * camelCase.
12027
+     *
12028
+     * @param array   $array       array to camelize
12029
+     * @param boolean $dolphinMode whether you want the exception for IDs.
12030
+     *
12031
+     * @return array
12032
+     */
12033
+    public function camelfy( $array, $dolphinMode = false ) {
12034
+        $newArray = array();
12035
+        foreach( $array as $key => $element ) {
12036
+            $newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){
12037
+                return strtoupper( $matches[1] );
12038
+            }, $key);
12039
+
12040
+            if ( $dolphinMode ) {
12041
+                $newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey );
12042
+            }
12043
+
12044
+            $newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element;
12045
+        }
12046
+        return $newArray;
12047
+    }
12048
+
12049
+    /**
12050
+     * For better performance you can pass the tables in an array to this method.
12051
+     * If the tables are available the duplication manager will not query them so
12052
+     * this might be beneficial for performance.
12053
+     *
12054
+     * This method allows two array formats:
12055
+     *
12056
+     * array( TABLE1, TABLE2 ... )
12057
+     *
12058
+     * or
12059
+     *
12060
+     * array( TABLE1 => array( COLUMN1, COLUMN2 ... ) ... )
12061
+     *
12062
+     * @param array $tables a table cache array
12063
+     *
12064
+     * @return void
12065
+     */
12066
+    public function setTables( $tables )
12067
+    {
12068
+        foreach ( $tables as $key => $value ) {
12069
+            if ( is_numeric( $key ) ) {
12070
+                $this->tables[] = $value;
12071
+            } else {
12072
+                $this->tables[]      = $key;
12073
+                $this->columns[$key] = $value;
12074
+            }
12075
+        }
12076
+
12077
+        $this->cacheTables = TRUE;
12078
+    }
12079
+
12080
+    /**
12081
+     * Returns a schema array for cache.
12082
+     * You can use the return value of this method as a cache,
12083
+     * store it in RAM or on disk and pass it to setTables later.
12084
+     *
12085
+     * @return array
12086
+     */
12087
+    public function getSchema()
12088
+    {
12089
+        return $this->columns;
12090
+    }
12091
+
12092
+    /**
12093
+     * Indicates whether you want the duplication manager to cache the database schema.
12094
+     * If this flag is set to TRUE the duplication manager will query the database schema
12095
+     * only once. Otherwise the duplicationmanager will, by default, query the schema
12096
+     * every time a duplication action is performed (dup()).
12097
+     *
12098
+     * @param boolean $yesNo TRUE to use caching, FALSE otherwise
12099
+     */
12100
+    public function setCacheTables( $yesNo )
12101
+    {
12102
+        $this->cacheTables = $yesNo;
12103
+    }
12104
+
12105
+    /**
12106
+     * A filter array is an array with table names.
12107
+     * By setting a table filter you can make the duplication manager only take into account
12108
+     * certain bean types. Other bean types will be ignored when exporting or making a
12109
+     * deep copy. If no filters are set all types will be taking into account, this is
12110
+     * the default behavior.
12111
+     *
12112
+     * @param array $filters list of tables to be filtered
12113
+     */
12114
+    public function setFilters( $filters )
12115
+    {
12116
+        if ( !is_array( $filters ) ) {
12117
+            $filters = array( $filters );
12118
+        }
12119
+
12120
+        $this->filters = $filters;
12121
+    }
12122
+
12123
+    /**
12124
+     * Makes a copy of a bean. This method makes a deep copy
12125
+     * of the bean.The copy will have the following features.
12126
+     * - All beans in own-lists will be duplicated as well
12127
+     * - All references to shared beans will be copied but not the shared beans themselves
12128
+     * - All references to parent objects (_id fields) will be copied but not the parents themselves
12129
+     * In most cases this is the desired scenario for copying beans.
12130
+     * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
12131
+     * (i.e. one that already has been processed) the ID of the bean will be returned.
12132
+     * This should not happen though.
12133
+     *
12134
+     * Note:
12135
+     * This function does a reflectional database query so it may be slow.
12136
+     *
12137
+     * Note:
12138
+     * this function actually passes the arguments to a protected function called
12139
+     * duplicate() that does all the work. This method takes care of creating a clone
12140
+     * of the bean to avoid the bean getting tainted (triggering saving when storing it).
12141
+     *
12142
+     * @param OODBBean $bean        bean to be copied
12143
+     * @param array    $trail       for internal usage, pass array()
12144
+     * @param boolean  $preserveIDs for internal usage
12145
+     *
12146
+     * @return OODBBean
12147
+     */
12148
+    public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
12149
+    {
12150
+        if ( !count( $this->tables ) ) {
12151
+            $this->tables = $this->toolbox->getWriter()->getTables();
12152
+        }
12153
+
12154
+        if ( !count( $this->columns ) ) {
12155
+            foreach ( $this->tables as $table ) {
12156
+                $this->columns[$table] = $this->toolbox->getWriter()->getColumns( $table );
12157
+            }
12158
+        }
12159
+
12160
+        $rs = $this->duplicate( ( clone $bean ), $trail, $preserveIDs );
12161
+
12162
+        if ( !$this->cacheTables ) {
12163
+            $this->tables  = array();
12164
+            $this->columns = array();
12165
+        }
12166
+
12167
+        return $rs;
12168
+    }
12169
+
12170
+    /**
12171
+     * Exports a collection of beans recursively.
12172
+     * This method will export an array of beans in the first argument to a
12173
+     * set of arrays. This can be used to send JSON or XML representations
12174
+     * of bean hierarchies to the client.
12175
+     *
12176
+     * For every bean in the array this method will export:
12177
+     *
12178
+     * - contents of the bean
12179
+     * - all own bean lists (recursively)
12180
+     * - all shared beans (but not THEIR own lists)
12181
+     *
12182
+     * If the second parameter is set to TRUE the parents of the beans in the
12183
+     * array will be exported as well (but not THEIR parents).
12184
+     *
12185
+     * The third parameter can be used to provide a white-list array
12186
+     * for filtering. This is an array of strings representing type names,
12187
+     * only the type names in the filter list will be exported.
12188
+     *
12189
+     * The fourth parameter can be used to change the keys of the resulting
12190
+     * export arrays. The default mode is 'snake case' but this leaves the
12191
+     * keys as-is, because 'snake' is the default case style used by
12192
+     * RedBeanPHP in the database. You can set this to 'camel' for
12193
+     * camel cased keys or 'dolphin' (same as camelcase but id will be
12194
+     * converted to ID instead of Id).
12195
+     *
12196
+     * @param array|OODBBean $beans     beans to be exported
12197
+     * @param boolean        $parents   also export parents
12198
+     * @param array          $filters   only these types (whitelist)
12199
+     * @param string         $caseStyle case style identifier
12200
+     *
12201
+     * @return array
12202
+     */
12203
+    public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake')
12204
+    {
12205
+        $array = array();
12206
+
12207
+        if ( !is_array( $beans ) ) {
12208
+            $beans = array( $beans );
12209
+        }
12210
+
12211
+        foreach ( $beans as $bean ) {
12212
+            $this->setFilters( $filters );
12213
+
12214
+            $duplicate = $this->dup( $bean, array(), TRUE );
12215
+
12216
+            $array[]   = $duplicate->export( FALSE, $parents, FALSE, $filters );
12217
+        }
12218
+
12219
+        if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array );
12220
+        if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, true );
12221
+
12222
+        return $array;
12223
+    }
12224 12224
 }
12225 12225
 }
12226 12226
 
@@ -12245,14 +12245,14 @@  discard block
 block discarded – undo
12245 12245
 
12246 12246
 ;
12247 12247
 }
12248
-	namespace {
12248
+    namespace {
12249 12249
 
12250
-	//make some classes available for backward compatibility
12251
-	class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {};
12250
+    //make some classes available for backward compatibility
12251
+    class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {};
12252 12252
 
12253
-	if (!class_exists('R')) {
12254
-		class R extends \RedBeanPHP\Facade{};
12255
-	}
12253
+    if (!class_exists('R')) {
12254
+        class R extends \RedBeanPHP\Facade{};
12255
+    }
12256 12256
 
12257 12257
 	
12258 12258
 
@@ -12286,10 +12286,10 @@  discard block
 block discarded – undo
12286 12286
  */
12287 12287
 if (!function_exists('EID')) {
12288 12288
 
12289
-	function EID($enumName)
12290
-	{
12291
-		return \RedBeanPHP\Facade::enum( $enumName )->id;
12292
-	}
12289
+    function EID($enumName)
12290
+    {
12291
+        return \RedBeanPHP\Facade::enum( $enumName )->id;
12292
+    }
12293 12293
 
12294 12294
 }
12295 12295
 
@@ -12303,10 +12303,10 @@  discard block
 block discarded – undo
12303 12303
  */
12304 12304
 if ( !function_exists( 'dump' ) ) {
12305 12305
 
12306
-	function dmp( $list )
12307
-	{
12308
-		print_r( \RedBeanPHP\Facade::dump( $list ) );
12309
-	}
12306
+    function dmp( $list )
12307
+    {
12308
+        print_r( \RedBeanPHP\Facade::dump( $list ) );
12309
+    }
12310 12310
 }
12311 12311
 
12312 12312
 /**
@@ -12314,10 +12314,10 @@  discard block
 block discarded – undo
12314 12314
  */
12315 12315
 if ( !function_exists( 'genslots' ) ) {
12316 12316
 
12317
-	function genslots( $slots, $tpl = NULL )
12318
-	{
12319
-		return \RedBeanPHP\Facade::genSlots( $slots, $tpl );
12320
-	}
12317
+    function genslots( $slots, $tpl = NULL )
12318
+    {
12319
+        return \RedBeanPHP\Facade::genSlots( $slots, $tpl );
12320
+    }
12321 12321
 }
12322 12322
 
12323 12323
 /**
@@ -12325,12 +12325,12 @@  discard block
 block discarded – undo
12325 12325
  */
12326 12326
 if ( !function_exists( 'array_flatten' ) ) {
12327 12327
 
12328
-	function array_flatten( $array )
12329
-	{
12330
-		return \RedBeanPHP\Facade::flat( $array );
12331
-	}
12328
+    function array_flatten( $array )
12329
+    {
12330
+        return \RedBeanPHP\Facade::flat( $array );
12331
+    }
12332 12332
 }
12333 12333
 
12334 12334
 
12335
-	}
12336
-	
12337 12335
\ No newline at end of file
12336
+    }
12337
+    
12338 12338
\ No newline at end of file
Please login to merge, or discard this patch.