<?php

namespace RedBeanPHP;

use RedBeanPHP\QueryWriter as QueryWriter;
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
use RedBeanPHP\RedException\SQL as SQLException;
use RedBeanPHP\Logger as Logger;
use RedBeanPHP\Logger\RDefault as RDefault;
use RedBeanPHP\Logger\RDefault\Debug as Debug;
use RedBeanPHP\Adapter as Adapter;
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper;
use RedBeanPHP\Driver\RPDO as RPDO;
use RedBeanPHP\Util\MultiLoader as MultiLoader;
use RedBeanPHP\Util\Transaction as Transaction;
use RedBeanPHP\Util\Dump as Dump;
use RedBeanPHP\Util\DispenseHelper as DispenseHelper;
use RedBeanPHP\Util\ArrayTool as ArrayTool;
use RedBeanPHP\Util\QuickExport as QuickExport;
use RedBeanPHP\Util\MatchUp as MatchUp;
use RedBeanPHP\Util\Look as Look;
use RedBeanPHP\Util\Diff as Diff;
use RedBeanPHP\Util\Tree as Tree;
use RedBeanPHP\Util\Feature;
use RedBeanPHP\SimpleModelInterface as SimpleModelInterface;

/**
 * RedBean Facade
 *
 * Version Information
 * RedBean Version @version 5.7
 *
 * This class hides the object landscape of
 * RedBeanPHP behind a single letter class providing
 * almost all functionality with simple static calls.
 *
 * @file    RedBeanPHP/Facade.php
 * @author  Gabor de Mooij and the RedBeanPHP Community
 * @license BSD/GPLv2
 *
 * @copyright
 * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
 * This source file is subject to the BSD/GPLv2 License that is bundled
 * with this source code in the file license.txt.
 */
class Facade
{
	/**
	 * RedBeanPHP version constant.
	 */
	const C_REDBEANPHP_VERSION = '5.7';

	/**
	 * @var ToolBox
	 */
	public static $toolbox;

	/**
	 * @var OODB
	 */
	private static $redbean;

	/**
	 * @var QueryWriter
	 */
	private static $writer;

	/**
	 * @var Adapter
	 */
	private static $adapter;

	/**
	 * @var AssociationManager
	 */
	private static $associationManager;

	/**
	 * @var TagManager
	 */
	private static $tagManager;

	/**
	 * @var DuplicationManager
	 */
	private static $duplicationManager;

	/**
	 * @var LabelMaker
	 */
	private static $labelMaker;

	/**
	 * @var Finder
	 */
	private static $finder;

	/**
	 * @var Tree
	 */
	private static $tree;

	/**
	 * @var Logger
	 */
	private static $logger;

	/**
	 * @var callable[]
	 */
	private static $plugins = array();

	/**
	 * @var string
	 */
	private static $exportCaseStyle = 'default';

	/**
	 * @var bool flag allows transactions through facade in fluid mode
	 */
	private static $allowFluidTransactions = FALSE;

	/**
	 * @var bool flag allows to unfreeze if needed with store(all)
	 */
	private static $allowHybridMode = FALSE;

	/**
	 * Not in use (backward compatibility SQLHelper)
	 */
	public static $f;

	/**
	 * @var string
	 */
	public static $currentDB = '';

	/**
	 * @var ToolBox[]
	 */
	public static $toolboxes = array();

	/**
	 * Internal Query function, executes the desired query. Used by
	 * all facade query functions. This keeps things DRY.
	 *
	 * @param string $method   desired query method (i.e. 'cell', 'col', 'exec' etc..)
	 * @param string $sql      the sql you want to execute
	 * @param array  $bindings array of values to be bound to query statement
	 *
	 * @return array|int|Cursor|NULL
	 */
	private static function query( $method, $sql, $bindings )
	{
		if ( !self::$redbean->isFrozen() ) {
			try {
				$rs = Facade::$adapter->$method( $sql, $bindings );
			} catch ( SQLException $exception ) {
				if ( self::$writer->sqlStateIn( $exception->getSQLState(),
					array(
						QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
						QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
					,$exception->getDriverDetails()
					)
				) {
					return ( $method === 'getCell' ) ? NULL : array();
				} else {
					throw $exception;
				}
			}

			return $rs;
		} else {
			return Facade::$adapter->$method( $sql, $bindings );
		}
	}

	/**
	 * Sets allow hybrid mode flag. In Hybrid mode (default off),
	 * store/storeAll take an extra argument to switch to fluid
	 * mode in case of an exception. You can use this to speed up
	 * fluid mode. This method returns the previous value of the
	 * flag.
	 *
	 * @param boolean $hybrid
	 *
	 * @return bool
	 */
	public static function setAllowHybridMode( $hybrid )
	{
		$old = self::$allowHybridMode;
		self::$allowHybridMode = $hybrid;
		return $old;
	}

	/**
	 * Returns the RedBeanPHP version string.
	 * The RedBeanPHP version string always has the same format "X.Y"
	 * where X is the major version number and Y is the minor version number.
	 * Point releases are not mentioned in the version string.
	 *
	 * @return string
	 */
	public static function getVersion()
	{
		return self::C_REDBEANPHP_VERSION;
	}

	/**
	 * Returns the version string from the database server.
	 *
	 * @return string
	 */
	public static function getDatabaseServerVersion()
	{
		return self::$adapter->getDatabaseServerVersion();
	}

	/**
	 * Tests the database connection.
	 * Returns TRUE if connection has been established and
	 * FALSE otherwise. Suppresses any warnings that may
	 * occur during the testing process and catches all
	 * exceptions that might be thrown during the test.
	 *
	 * @return boolean
	 */
	public static function testConnection()
	{
		if ( !isset( self::$adapter ) ) return FALSE;

		$database = self::$adapter->getDatabase();
		try {
			@$database->connect();
		} catch ( \Exception $e ) {}
		return $database->isConnected();
	}

	/**
	 * Tests the database connection.
	 * Returns TRUE if connection has been established and
	 * FALSE otherwise. Suppresses any warnings that may
	 * occur during the testing process and catches all
	 * exceptions that might be thrown during the test.
	 *
	 * @param boolean   $autoReconnect   if the function attempts to reconnect to the server on failure
	 * @param string    $sql             the sql you want to execute to test the connection
	 *
	 * @return boolean
	 */
	public static function testConnectionSQL( $autoReconnect = FALSE, $sql = 'SELECT 1' )
	{
		if ( !isset( self::$adapter ) ) return FALSE;

		$database = self::$adapter->getDatabase();
		try {
			$database->getPDO()->query( $sql );
		} catch ( \Exception $e ) {
			if ( !$autoReconnect ) return FALSE;
			$database->close();
			$database->connect();
			return self::testConnectionSQL( FALSE, $sql );
		}
		return TRUE;
	}

	/**
	 * Kickstarts redbean for you. This method should be called before you start using
	 * RedBeanPHP. The Setup() method can be called without any arguments, in this case it will
	 * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems).
	 *
	 * Usage:
	 *
	 * <code>
	 * R::setup( 'mysql:host=localhost;dbname=mydatabase', 'dba', 'dbapassword' );
	 * </code>
	 *
	 * You can replace 'mysql:' with the name of the database you want to use.
	 * Possible values are:
	 *
	 * - pgsql  (PostgreSQL database)
	 * - sqlite (SQLite database)
	 * - mysql  (MySQL database)
	 * - mysql  (also for Maria database)
	 * - sqlsrv (MS SQL Server - community supported experimental driver)
	 * - CUBRID (CUBRID driver - basic support provided by Plugin)
	 *
	 * Note that setup() will not immediately establish a connection to the database.
	 * Instead, it will prepare the connection and connect 'lazily', i.e. the moment
	 * a connection is really required, for instance when attempting to load
	 * a bean.
	 *
	 * @param string|\PDO|NULL $dsn     Database connection string
	 * @param string|NULL $username     Username for database
	 * @param string|NULL $password     Password for database
	 * @param boolean|string[] $frozen       TRUE if you want to setup in frozen mode
	 * @param boolean|string[] $partialBeans TRUE to enable partial bean updates
	 * @param array   $options      Additional (PDO) options to pass
	 *
	 * @return ToolBox
	 */
	public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE, $partialBeans = FALSE, $options = array() )
	{
		if ( is_null( $dsn ) ) {
			$dsn = 'sqlite:' . DIRECTORY_SEPARATOR . sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'red.db';
		}

		self::addDatabase( 'default', $dsn, $username, $password, $frozen, $partialBeans, $options );
		self::selectDatabase( 'default' );

		return self::$toolbox;
	}

	/**
	 * Toggles 'Narrow Field Mode'.
	 * In Narrow Field mode the queryRecord method will
	 * narrow its selection field to
	 *
	 * <code>
	 * SELECT {table}.*
	 * </code>
	 *
	 * instead of
	 *
	 * <code>
	 * SELECT *
	 * </code>
	 *
	 * This is a better way of querying because it allows
	 * more flexibility (for instance joins). However if you need
	 * the wide selector for backward compatibility; use this method
	 * to turn OFF Narrow Field Mode by passing FALSE.
	 * Default is TRUE.
	 *
	 * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field
	 *
	 * @return void
	 */
	public static function setNarrowFieldMode( $mode )
	{
		AQueryWriter::setNarrowFieldMode( $mode );
	}

	/**
	 * Toggles fluid transactions. By default fluid transactions
	 * are not active. Starting, committing or rolling back a transaction
	 * through the facade in fluid mode will have no effect. If you wish
	 * to replace this standard portable behavior with behavior depending
	 * on how the used database platform handles fluid (DDL) transactions
	 * set this flag to TRUE.
	 *
	 * @param boolean $mode allow fluid transaction mode
	 *
	 * @return void
	 */
	public static function setAllowFluidTransactions( $mode )
	{
		self::$allowFluidTransactions = $mode;
	}

	/**
	 * Toggles support for IS-NULL-conditions.
	 * If IS-NULL-conditions are enabled condition arrays
	 * for functions including findLike() are treated so that
	 * 'field' => NULL will be interpreted as field IS NULL
	 * instead of being skipped. Returns the previous
	 * value of the flag.
	 *
	 * @param boolean $flag TRUE or FALSE
	 *
	 * @return boolean
	 */
	public static function useISNULLConditions( $mode )
	{
		self::getWriter()->flushCache(); /* otherwise same queries might fail (see Unit test XNull) */
		return AQueryWriter::useISNULLConditions( $mode );
	}

	/**
	 * Wraps a transaction around a closure or string callback.
	 * If an Exception is thrown inside, the operation is automatically rolled back.
	 * If no Exception happens, it commits automatically.
	 * It also supports (simulated) nested transactions (that is useful when
	 * you have many methods that needs transactions but are unaware of
	 * each other).
	 *
	 * Example:
	 *
	 * <code>
	 * $from = 1;
	 * $to = 2;
	 * $amount = 300;
	 *
	 * R::transaction(function() use($from, $to, $amount)
	 * {
	 *   $accountFrom = R::load('account', $from);
	 *   $accountTo = R::load('account', $to);
	 *   $accountFrom->money -= $amount;
	 *   $accountTo->money += $amount;
	 *   R::store($accountFrom);
	 *   R::store($accountTo);
	 * });
	 * </code>
	 *
	 * @param callable $callback Closure (or other callable) with the transaction logic
	 *
	 * @return mixed
	 */
	public static function transaction( $callback )
	{
		if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
		return Transaction::transaction( self::$adapter, $callback );
	}

	/**
	 * Adds a database to the facade, afterwards you can select the database using
	 * selectDatabase($key), where $key is the name you assigned to this database.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' );
	 * R::selectDatabase( 'database-1' ); //to select database again
	 * </code>
	 *
	 * This method allows you to dynamically add (and select) new databases
	 * to the facade. Adding a database with the same key will cause an exception.
	 *
	 * @param string      $key    		ID for the database
	 * @param string|\PDO $dsn    		DSN for the database
	 * @param string|NULL $user   		user for connection
	 * @param string|NULL $pass   		password for connection
	 * @param bool|string[]   $frozen 		  whether this database is frozen or not
	 * @param bool|string[]   $partialBeans should we load partial beans?
	 * @param array		        $options	   	additional options for the query writer
	 * @param BeanHelper|NULL $beanHelper  	Beanhelper to use (use this for DB specific model prefixes)
	 *
	 * @return void
	 */
	public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE, $partialBeans = FALSE, $options = array(), $beanHelper = NULL )
	{
		if ( isset( self::$toolboxes[$key] ) ) {
			throw new RedException( 'A database has already been specified for this key.' );
		}

		self::$toolboxes[$key] = self::createToolbox($dsn, $user, $pass, $frozen, $partialBeans, $options);

		if ( !is_null( $beanHelper ) ) {
			self::$toolboxes[$key]->getRedBean()->setBeanHelper( $beanHelper );
		}
	}

	/**
	 * Creates a toolbox. This method can be called if you want to use redbean non-static.
	 * It has the same interface as R::setup(). The createToolbox() method can be called
	 * without any arguments, in this case it will try to create a SQLite database in
	 * /tmp called red.db (this only works on UNIX-like systems).
	 *
	 * Usage:
	 *
	 * <code>
	 * R::createToolbox( 'mysql:host=localhost;dbname=mydatabase', 'dba', 'dbapassword' );
	 * </code>
	 *
	 * You can replace 'mysql:' with the name of the database you want to use.
	 * Possible values are:
	 *
	 * - pgsql  (PostgreSQL database)
	 * - sqlite (SQLite database)
	 * - mysql  (MySQL database)
	 * - mysql  (also for Maria database)
	 * - sqlsrv (MS SQL Server - community supported experimental driver)
	 * - CUBRID (CUBRID driver - basic support provided by Plugin)
	 *
	 * Note that createToolbox() will not immediately establish a connection to the database.
	 * Instead, it will prepare the connection and connect 'lazily', i.e. the moment
	 * a connection is really required, for instance when attempting to load a bean.
	 *
	 * @param string|\PDO $dsn      Database connection string
	 * @param string  $username Username for database
	 * @param string  $password Password for database
	 * @param boolean|string[] $frozen   TRUE if you want to setup in frozen mode
	 * @param boolean|string[] $partialBeans TRUE to enable partial bean updates
	 * @param array   $options
	 *
	 * @return ToolBox
	 */
	public static function createToolbox( $dsn, $username = NULL, $password = NULL, $frozen = FALSE, $partialBeans = FALSE, $options = array() )
	{
		if ( is_object($dsn) ) {
			$db  = new RPDO( $dsn );
			$dbType = $db->getDatabaseType();
		} else {
			$db = new RPDO( $dsn, $username, $password, $options );
			$dbType = substr( $dsn, 0, strpos( $dsn, ':' ) );
		}

		$adapter = new DBAdapter( $db );

		$writers = array(
			'pgsql'  => 'PostgreSQL',
			'sqlite' => 'SQLiteT',
			'cubrid' => 'CUBRID',
			'mysql'  => 'MySQL',
			'sqlsrv' => 'SQLServer',
		);

		$wkey = trim( strtolower( $dbType ) );
		if ( !isset( $writers[$wkey] ) ) {
			$wkey = preg_replace( '/\W/', '' , $wkey );
			throw new RedException( 'Unsupported database ('.$wkey.').' );
		}
		$writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey];
		/** @var AQueryWriter $writer */
		$writer      = new $writerClass( $adapter );
		$redbean     = new OODB( $writer, $frozen );

		if ( $partialBeans ) {
			$redbean->getCurrentRepository()->usePartialBeans( $partialBeans );
		}

		return new ToolBox( $redbean, $adapter, $writer );
	}

	/**
	 * Determines whether a database identified with the specified key has
	 * already been added to the facade. This function will return TRUE
	 * if the database indicated by the key is available and FALSE otherwise.
	 *
	 * @param string $key the key/name of the database to check for
	 *
	 * @return boolean
	 */
	public static function hasDatabase( $key )
	{
		return ( isset( self::$toolboxes[$key] ) );
	}

	/**
	 * Selects a different database for the Facade to work with.
	 * If you use the R::setup() you don't need this method. This method is meant
	 * for multiple database setups. This method selects the database identified by the
	 * database ID ($key). Use addDatabase() to add a new database, which in turn
	 * can be selected using selectDatabase(). If you use R::setup(), the resulting
	 * database will be stored under key 'default', to switch (back) to this database
	 * use R::selectDatabase( 'default' ). This method returns TRUE if the database has been
	 * switched and FALSE otherwise (for instance if you already using the specified database).
	 *
	 * @param  string $key Key of the database to select
	 * @param  bool   $force
	 *
	 * @return boolean
	 */
	public static function selectDatabase( $key, $force = FALSE )
	{
		if ( self::$currentDB === $key && !$force ) {
			return FALSE;
		}

		if ( !isset( self::$toolboxes[$key] ) ) {
			throw new RedException( 'Database not found in registry. Add database using R::addDatabase().' );
		}

		self::configureFacadeWithToolbox( self::$toolboxes[$key] );
		self::$currentDB = $key;

		return TRUE;
	}

	/**
	 * Toggles DEBUG mode.
	 * In Debug mode all SQL that happens under the hood will
	 * be printed to the screen and/or logged.
	 * If no database connection has been configured using R::setup() or
	 * R::selectDatabase() this method will throw an exception.
	 *
	 * There are 2 debug styles:
	 *
	 * Classic: separate parameter bindings, explicit and complete but less readable
	 * Fancy:   interspersed bindings, truncates large strings, highlighted schema changes
	 *
	 * Fancy style is more readable but sometimes incomplete.
	 *
	 * The first parameter turns debugging ON or OFF.
	 * The second parameter indicates the mode of operation:
	 *
	 * 0 Log and write to STDOUT classic style (default)
	 * 1 Log only, class style
	 * 2 Log and write to STDOUT fancy style
	 * 3 Log only, fancy style
	 *
	 * This function always returns the logger instance created to generate the
	 * debug messages.
	 *
	 * @param boolean $tf   debug mode (TRUE or FALSE)
	 * @param integer $mode mode of operation
	 *
	 * @return RDefault
	 * @throws RedException
	 */
	public static function debug( $tf = TRUE, $mode = 0 )
	{
		if ($mode > 1) {
			$mode -= 2;
			$logger = new Debug;
		} else {
			$logger = new RDefault;
		}

		if ( !isset( self::$adapter ) ) {
			throw new RedException( 'Use R::setup() first.' );
		}
		$logger->setMode($mode);
		self::$adapter->getDatabase()->setDebugMode( $tf, $logger );

		return $logger;
	}

	/**
	 * Turns on the fancy debugger.
	 * In 'fancy' mode the debugger will output queries with bound
	 * parameters inside the SQL itself. This method has been added to
	 * offer a convenient way to activate the fancy debugger system
	 * in one call.
	 *
	 * @param boolean $toggle TRUE to activate debugger and select 'fancy' mode
	 *
	 * @return void
	 */
	public static function fancyDebug( $toggle = TRUE )
	{
		self::debug( $toggle, 2 );
	}

	/**
	* Inspects the database schema. If you pass the type of a bean this
	* method will return the fields of its table in the database.
	* The keys of this array will be the field names and the values will be
	* the column types used to store their values.
	* If no type is passed, this method returns a list of all tables in the database.
	*
	* @param string|NULL $type Type of bean (i.e. table) you want to inspect, or NULL for a list of all tables
	*
	* @return string[]
	*/
	public static function inspect( $type = NULL )
	{
		return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type );
	}

	/**
	 * Stores a bean in the database. This method takes a
	 * OODBBean Bean Object $bean and stores it
	 * in the database. If the database schema is not compatible
	 * with this bean and RedBean runs in fluid mode the schema
	 * will be altered to store the bean correctly.
	 * If the database schema is not compatible with this bean and
	 * RedBean runs in frozen mode it will throw an exception.
	 * This function returns the primary key ID of the inserted
	 * bean.
	 *
	 * The return value is an integer if possible. If it is not possible to
	 * represent the value as an integer a string will be returned.
	 *
	 * Usage:
	 *
	 * <code>
	 * $post = R::dispense('post');
	 * $post->title = 'my post';
	 * $id = R::store( $post );
	 * $post = R::load( 'post', $id );
	 * R::trash( $post );
	 * </code>
	 *
	 * In the example above, we create a new bean of type 'post'.
	 * We then set the title of the bean to 'my post' and we
	 * store the bean. The store() method will return the primary
	 * key ID $id assigned by the database. We can now use this
	 * ID to load the bean from the database again and delete it.
	 *
	 * If the second parameter is set to TRUE and
	 * Hybrid mode is allowed (default OFF for novice), then RedBeanPHP
	 * will automatically temporarily switch to fluid mode to attempt to store the
	 * bean in case of an SQLException.
	 *
	 * @param OODBBean|SimpleModel|SimpleModelInterface $bean             bean to store
	 * @param boolean              $unfreezeIfNeeded retries in fluid mode in hybrid mode
	 *
	 * @return integer|string
	 */
	public static function store( $bean, $unfreezeIfNeeded = FALSE )
	{
		$result = NULL;
		try {
			$result = self::$redbean->store( $bean );
		} catch (SQLException $exception) {
			$wasFrozen = self::$redbean->isFrozen();
			if ( !self::$allowHybridMode || !$unfreezeIfNeeded ) throw $exception;
			self::freeze( FALSE );
			$result = self::$redbean->store( $bean );
			self::freeze( $wasFrozen );
		}
		return $result;
	}

	/**
	 * Toggles fluid or frozen mode. In fluid mode the database
	 * structure is adjusted to accommodate your objects. In frozen mode
	 * this is not the case.
	 *
	 * You can also pass an array containing a selection of frozen types.
	 * Let's call this chilly mode, it's just like fluid mode except that
	 * certain types (i.e. tables) aren't touched.
	 *
	 * @param boolean|string[] $tf mode of operation (TRUE means frozen)
	 *
	 * @return void
	 */
	public static function freeze( $tf = TRUE )
	{
		self::$redbean->freeze( $tf );
	}

	/**
	 * Loads multiple types of beans with the same ID.
	 * This might look like a strange method, however it can be useful
	 * for loading a one-to-one relation. In a typical 1-1 relation,
	 * you have two records sharing the same primary key.
	 * RedBeanPHP has only limited support for 1-1 relations.
	 * In general it is recommended to use 1-N for this.
	 *
	 * Usage:
	 *
	 * <code>
	 * list( $author, $bio ) = R::loadMulti( 'author, bio', $id );
	 * </code>
	 *
	 * @param string|string[] $types the set of types to load at once
	 * @param int             $id    the common ID
	 *
	 * @return OODBBean[]
	 */
	public static function loadMulti( $types, $id )
	{
		return MultiLoader::load( self::$redbean, $types, $id );
	}

	/**
	 * Loads a bean from the object database.
	 * It searches for a OODBBean Bean Object in the
	 * database. It does not matter how this bean has been stored.
	 * RedBean uses the primary key ID $id and the string $type
	 * to find the bean. The $type specifies what kind of bean you
	 * are looking for; this is the same type as used with the
	 * dispense() function. If RedBean finds the bean it will return
	 * the OODB Bean object; if it cannot find the bean
	 * RedBean will return a new bean of type $type and with
	 * primary key ID 0. In the latter case it acts basically the
	 * same as dispense().
	 *
	 * Important note:
	 * If the bean cannot be found in the database a new bean of
	 * the specified type will be generated and returned.
	 *
	 * Usage:
	 *
	 * <code>
	 * $post = R::dispense('post');
	 * $post->title = 'my post';
	 * $id = R::store( $post );
	 * $post = R::load( 'post', $id );
	 * R::trash( $post );
	 * </code>
	 *
	 * In the example above, we create a new bean of type 'post'.
	 * We then set the title of the bean to 'my post' and we
	 * store the bean. The store() method will return the primary
	 * key ID $id assigned by the database. We can now use this
	 * ID to load the bean from the database again and delete it.
	 *
	 * @param string  $type    type of bean you want to load
	 * @param integer $id      ID of the bean you want to load
	 * @param string|NULL $snippet string to use after select  (optional)
	 *
	 * @return OODBBean
	 */
	public static function load( $type, $id, $snippet = NULL )
	{
		if ( $snippet !== NULL ) self::$writer->setSQLSelectSnippet( $snippet );
		$bean = self::$redbean->load( $type, $id );
		return $bean;
	}

	/**
	 * Same as load, but selects the bean for update, thus locking the bean.
	 * This equals an SQL query like 'SELECT ... FROM ... FOR UPDATE'.
	 * Use this method if you want to load a bean you intend to UPDATE.
	 * This method should be used to 'LOCK a bean'.
	 *
	 * Usage:
	 *
	 * <code>
	 * $bean = R::loadForUpdate( 'bean', $id );
	 * ...update...
	 * R::store( $bean );
	 * </code>
	 *
	 * @param string  $type    type of bean you want to load
	 * @param integer $id      ID of the bean you want to load
	 *
	 * @return OODBBean
	 */
	public static function loadForUpdate( $type, $id )
	{
		return self::load( $type, $id, AQueryWriter::C_SELECT_SNIPPET_FOR_UPDATE );
	}

	/**
	 * Same as find(), but selects the beans for update, thus locking the beans.
	 * This equals an SQL query like 'SELECT ... FROM ... FOR UPDATE'.
	 * Use this method if you want to load a bean you intend to UPDATE.
	 * This method should be used to 'LOCK a bean'.
	 *
	 * Usage:
	 *
	 * <code>
	 * $bean = R::findForUpdate(
	 *    'bean',
	 *    ' title LIKE ? ',
	 *    array('title')
	 * );
	 * ...update...
	 * R::store( $bean );
	 * </code>
	 *
	 * @param string $type     the type of bean you are looking for
	 * @param string|NULL $sql      SQL query to find the desired bean, starting right after WHERE clause
	 * @param array  $bindings array of values to be bound to parameters in query
	 *
	 * @return OODBBean[]
	 */
	public static function findForUpdate( $type, $sql = NULL, $bindings = array() )
	{
		return self::find( $type, $sql, $bindings, AQueryWriter::C_SELECT_SNIPPET_FOR_UPDATE );
	}

	/**
	 * Convenience method.
	 * Same as findForUpdate but returns just one bean and adds LIMIT-clause.
	 *
	 * @param string $type     the type of bean you are looking for
	 * @param string|NULL $sql      SQL query to find the desired bean, starting right after WHERE clause
	 * @param array  $bindings array of values to be bound to parameters in query
	 *
	 * @return OODBBean|NULL
	 */
	public static function findOneForUpdate( $type, $sql = NULL, $bindings = array() )
	{
		$sql = self::getWriter()->glueLimitOne( $sql );
		$beans = self::findForUpdate($type, $sql, $bindings);
		return empty($beans) ? NULL : reset($beans);
	}

	/**
	 * Removes a bean from the database.
	 * This function will remove the specified OODBBean
	 * Bean Object from the database.
	 *
	 * This facade method also accepts a type-id combination,
	 * in the latter case this method will attempt to load the specified bean
	 * and THEN trash it.
	 *
	 * Usage:
	 *
	 * <code>
	 * $post = R::dispense('post');
	 * $post->title = 'my post';
	 * $id = R::store( $post );
	 * $post = R::load( 'post', $id );
	 * R::trash( $post );
	 * </code>
	 *
	 * In the example above, we create a new bean of type 'post'.
	 * We then set the title of the bean to 'my post' and we
	 * store the bean. The store() method will return the primary
	 * key ID $id assigned by the database. We can now use this
	 * ID to load the bean from the database again and delete it.
	 *
	 * @param string|OODBBean|SimpleModel|SimpleModelInterface $beanOrType bean you want to remove from database
	 * @param integer                     $id         ID if the bean to trash (optional, type-id variant only)
	 *
	 * @return int
	 */
	public static function trash( $beanOrType, $id = NULL )
	{
		if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) );
		return self::$redbean->trash( $beanOrType );
	}

	/**
	 * Dispenses a new RedBean OODB Bean for use with
	 * the rest of the methods. RedBeanPHP thinks in beans, the bean is the
	 * primary way to interact with RedBeanPHP and the database managed by
	 * RedBeanPHP. To load, store and delete data from the database using RedBeanPHP
	 * you exchange these RedBeanPHP OODB Beans. The only exception to this rule
	 * are the raw query methods like R::getCell() or R::exec() and so on.
	 * The dispense method is the 'preferred way' to create a new bean.
	 *
	 * Usage:
	 *
	 * <code>
	 * $book = R::dispense( 'book' );
	 * $book->title = 'My Book';
	 * R::store( $book );
	 * </code>
	 *
	 * This method can also be used to create an entire bean graph at once.
	 * Given an array with keys specifying the property names of the beans
	 * and a special _type key to indicate the type of bean, one can
	 * make the Dispense Helper generate an entire hierarchy of beans, including
	 * lists. To make dispense() generate a list, simply add a key like:
	 * ownXList or sharedXList where X is the type of beans it contains and
	 * a set its value to an array filled with arrays representing the beans.
	 * Note that, although the type may have been hinted at in the list name,
	 * you still have to specify a _type key for every bean array in the list.
	 * Note that, if you specify an array to generate a bean graph, the number
	 * parameter will be ignored.
	 *
	 * Usage:
	 *
	 * <code>
	 *  $book = R::dispense( [
	 *   '_type' => 'book',
	 *   'title'  => 'Gifted Programmers',
	 *   'author' => [ '_type' => 'author', 'name' => 'Xavier' ],
	 *   'ownPageList' => [ ['_type'=>'page', 'text' => '...'] ]
	 * ] );
	 * </code>
	 *
	 * @param string|OODBBean[] $typeOrBeanArray   type or bean array to import
	 * @param integer      $num               number of beans to dispense
	 * @param boolean      $alwaysReturnArray if TRUE always returns the result as an array
	 *
	 * @return OODBBean|OODBBean[]
	 */
	public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE )
	{
		return DispenseHelper::dispense( self::$redbean, $typeOrBeanArray, $num, $alwaysReturnArray );
	}

	/**
	 * Takes a comma separated list of bean types
	 * and dispenses these beans. For each type in the list
	 * you can specify the number of beans to be dispensed.
	 *
	 * Usage:
	 *
	 * <code>
	 * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' );
	 * </code>
	 *
	 * This will dispense a book, a page and a text. This way you can
	 * quickly dispense beans of various types in just one line of code.
	 *
	 * Usage:
	 *
	 * <code>
	 * list($book, $pages) = R::dispenseAll('book,page*100');
	 * </code>
	 *
	 * This returns an array with a book bean and then another array
	 * containing 100 page beans.
	 *
	 * @param string  $order      a description of the desired dispense order using the syntax above
	 * @param boolean $onlyArrays return only arrays even if amount < 2
	 *
	 * @return array
	 */
	public static function dispenseAll( $order, $onlyArrays = FALSE )
	{
		return DispenseHelper::dispenseAll( self::$redbean, $order, $onlyArrays );
	}

	/**
	 * Convenience method. Tries to find beans of a certain type,
	 * if no beans are found, it dispenses a bean of that type.
	 * Note that this function always returns an array.
	 *
	 * @param  string      $type     type of bean you are looking for
	 * @param  string|NULL $sql      SQL code for finding the bean
	 * @param  array       $bindings parameters to bind to SQL
	 *
	 * @return OODBBean[]
	 */
	public static function findOrDispense( $type, $sql = NULL, $bindings = array() )
	{
		DispenseHelper::checkType( $type );
		return self::$finder->findOrDispense( $type, $sql, $bindings );
	}

	/**
	 * Same as findOrDispense but returns just one element.
	 *
	 * @param  string      $type     type of bean you are looking for
	 * @param  string|NULL $sql      SQL code for finding the bean
	 * @param  array       $bindings parameters to bind to SQL
	 *
	 * @return OODBBean
	 */
	public static function findOneOrDispense( $type, $sql = NULL, $bindings = array() )
	{
		DispenseHelper::checkType( $type );
		$arrayOfBeans = self::findOrDispense( $type, $sql, $bindings );
		return reset($arrayOfBeans);
	}

	/**
	 * Finds beans using a type and optional SQL statement.
	 * As with most Query tools in RedBean you can provide values to
	 * be inserted in the SQL statement by populating the value
	 * array parameter; you can either use the question mark notation
	 * or the slot-notation (:keyname).
	 *
	 * Your SQL does not have to start with a WHERE-clause condition.
	 *
	 * @param string      $type     the type of bean you are looking for
	 * @param string|NULL $sql      SQL query to find the desired bean, starting right after WHERE clause
	 * @param array       $bindings array of values to be bound to parameters in query
	 * @param string|NULL $snippet  SQL snippet to include in query (for example: FOR UPDATE)
	 *
	 * @phpstan-param literal-string|null $sql
	 * @psalm-param   literal-string|null $sql
	 *
	 * @return OODBBean[]
	 */
	public static function find( $type, $sql = NULL, $bindings = array(), $snippet = NULL )
	{
		if ( $snippet !== NULL ) self::$writer->setSQLSelectSnippet( $snippet );
		return self::$finder->find( $type, $sql, $bindings );
	}

	/**
	 * Alias for find().
	 *
	 * @param string      $type     the type of bean you are looking for
	 * @param string|NULL $sql      SQL query to find the desired bean, starting right after WHERE clause
	 * @param array       $bindings array of values to be bound to parameters in query
	 *
	 * @return OODBBean[]
	 */
	public static function findAll( $type, $sql = NULL, $bindings = array() )
	{
		return self::$finder->find( $type, $sql, $bindings );
	}

	/**
	 * Like find() but also exports the beans as an array.
	 * This method will perform a find-operation. For every bean
	 * in the result collection this method will call the export() method.
	 * This method returns an array containing the array representations
	 * of every bean in the result set.
	 *
	 * @see Finder::find
	 *
	 * @param string      $type     type   the type of bean you are looking for
	 * @param string|NULL $sql      sql    SQL query to find the desired bean, starting right after WHERE clause
	 * @param array       $bindings values array of values to be bound to parameters in query
	 *
	 * @return array
	 */
	public static function findAndExport( $type, $sql = NULL, $bindings = array() )
	{
		return self::$finder->findAndExport( $type, $sql, $bindings );
	}

	/**
	 * Like R::find() but returns the first bean only.
	 *
	 * @param string      $type     the type of bean you are looking for
	 * @param string|NULL $sql      SQL query to find the desired bean, starting right after WHERE clause
	 * @param array       $bindings array of values to be bound to parameters in query
	 *
	 * @return OODBBean|NULL
	 */
	public static function findOne( $type, $sql = NULL, $bindings = array() )
	{
		return self::$finder->findOne( $type, $sql, $bindings );
	}

	/**
	 * @deprecated
	 *
	 * Like find() but returns the last bean of the result array.
	 * Opposite of Finder::findLast().
	 * If no beans are found, this method will return NULL.
	 *
	 * Please do not use this function, it is horribly ineffective.
	 * Instead use a reversed ORDER BY clause and a LIMIT 1 with R::findOne().
	 * This function should never be used and only remains for
	 * the sake of backward compatibility.
	 *
	 * @see Finder::find
	 *
	 * @param string      $type     the type of bean you are looking for
	 * @param string|NULL $sql      SQL query to find the desired bean, starting right after WHERE clause
	 * @param array       $bindings values array of values to be bound to parameters in query
	 *
	 * @return OODBBean|NULL
	 */
	public static function findLast( $type, $sql = NULL, $bindings = array() )
	{
		return self::$finder->findLast( $type, $sql, $bindings );
	}

	/**
	 * Finds a BeanCollection using the repository.
	 * A bean collection can be used to retrieve one bean at a time using
	 * cursors - this is useful for processing large datasets. A bean collection
	 * will not load all beans into memory all at once, just one at a time.
	 *
	 * @param  string      $type     the type of bean you are looking for
	 * @param  string|NULL $sql      SQL query to find the desired bean, starting right after WHERE clause
	 * @param  array       $bindings values array of values to be bound to parameters in query
	 *
	 * @return BeanCollection
	 */
	public static function findCollection( $type, $sql = NULL, $bindings = array() )
	{
		return self::$finder->findCollection( $type, $sql, $bindings );
	}

	/**
	 * Returns a hashmap with bean arrays keyed by type using an SQL
	 * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review'
	 * this method will return movie and review beans.
	 *
	 * Example:
	 *
	 * <code>
	 * $stuff = $finder->findMulti('movie,review', '
	 *          SELECT movie.*, review.* FROM movie
	 *          LEFT JOIN review ON review.movie_id = movie.id');
	 * </code>
	 *
	 * After this operation, $stuff will contain an entry 'movie' containing all
	 * movies and an entry named 'review' containing all reviews (all beans).
	 * You can also pass bindings.
	 *
	 * If you want to re-map your beans, so you can use $movie->ownReviewList without
	 * having RedBeanPHP executing an SQL query you can use the fourth parameter to
	 * define a selection of remapping closures.
	 *
	 * The remapping argument (optional) should contain an array of arrays.
	 * Each array in the remapping array should contain the following entries:
	 *
	 * <code>
	 * array(
	 * 	'a'       => TYPE A
	 *    'b'       => TYPE B
	 *    'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS
	 *    'do'      => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS
	 * )
	 * </code>
	 *
	 * Using this mechanism you can build your own 'preloader' with tiny function
	 * snippets (and those can be re-used and shared online of course).
	 *
	 * Example:
	 *
	 * <code>
	 * array(
	 * 	'a'       => 'movie'     //define A as movie
	 *    'b'       => 'review'    //define B as review
	 *    'matcher' => function( $a, $b ) {
	 *       return ( $b->movie_id == $a->id );  //Perform action if review.movie_id equals movie.id
	 *    }
	 *    'do'      => function( $a, $b ) {
	 *       $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie
	 *       $a->clearHistory();                 //optional, act 'as if these beans have been loaded through ownReviewList'.
	 *    }
	 * )
	 * </code>
	 *
	 * @note the SQL query provided IS NOT THE ONE used internally by this function,
	 * this function will pre-process the query to get all the data required to find the beans.
	 *
	 * @note if you use the 'book.*' notation make SURE you're
	 * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because
	 * it's actually an SQL-like template SLOT, not real SQL.
	 *
	 * @note instead of an SQL query you can pass a result array as well.
	 *
	 * @param string|string[]      $types         a list of types (either array or comma separated string)
	 * @param string|array[]|NULL  $sql           an SQL query or an array of prefetched records
	 * @param array                $bindings      optional, bindings for SQL query
	 * @param array[]              $remappings    optional, an array of remapping arrays
	 *
	 * @return array
	 */
	public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() )
	{
		return self::$finder->findMulti( $types, $sql, $bindings, $remappings );
	}

	/**
	 * Returns an array of beans. Pass a type and a series of ids and
	 * this method will bring you the corresponding beans.
	 *
	 * important note: Because this method loads beans using the load()
	 * function (but faster) it will return empty beans with ID 0 for
	 * every bean that could not be located. The resulting beans will have the
	 * passed IDs as their keys.
	 *
	 * @param string $type type of beans
	 * @param int[]  $ids  ids to load
	 *
	 * @return OODBBean[]
	 */
	public static function batch( $type, $ids )
	{
		return self::$redbean->batch( $type, $ids );
	}

	/**
	 * Alias for batch(). Batch method is older but since we added so-called *All
	 * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to
	 * improve the consistency of the Facade API and also add an alias for batch() called
	 * loadAll.
	 *
	 * @param string $type type of beans
	 * @param int[]  $ids  ids to load
	 *
	 * @return OODBBean[]
	 */
	public static function loadAll( $type, $ids )
	{
		return self::$redbean->batch( $type, $ids );
	}

	/**
	 * Convenience function to execute Queries directly.
	 * Executes SQL.
	 *
	 * @param string $sql       SQL query to execute
	 * @param array  $bindings  a list of values to be bound to query parameters
	 *
	 * @return integer
	 */
	public static function exec( $sql, $bindings = array() )
	{
		return intval( self::query( 'exec', $sql, $bindings ) );
	}

	/**
	 * Convenience function to fire an SQL query using the RedBeanPHP
	 * database adapter. This method allows you to directly query the
	 * database without having to obtain an database adapter instance first.
	 * Executes the specified SQL query together with the specified
	 * parameter bindings and returns all rows
	 * and all columns.
	 *
	 * @param string $sql      SQL query to execute
	 * @param array  $bindings a list of values to be bound to query parameters
	 *
	 * @return string[][]
	 */
	public static function getAll( $sql, $bindings = array() )
	{
		return self::query( 'get', $sql, $bindings );
	}

	/**
	 * Convenience function to fire an SQL query using the RedBeanPHP
	 * database adapter. This method allows you to directly query the
	 * database without having to obtain an database adapter instance first.
	 * Executes the specified SQL query together with the specified
	 * parameter bindings and returns a single cell.
	 *
	 * @param string $sql      SQL query to execute
	 * @param array  $bindings a list of values to be bound to query parameters
	 *
	 * @return string|NULL
	 */
	public static function getCell( $sql, $bindings = array() )
	{
		return self::query( 'getCell', $sql, $bindings );
	}

	/**
	 * Convenience function to fire an SQL query using the RedBeanPHP
	 * database adapter. This method allows you to directly query the
	 * database without having to obtain an database adapter instance first.
	 * Executes the specified SQL query together with the specified
	 * parameter bindings and returns a PDOCursor instance.
	 *
	 * @param string $sql      SQL query to execute
	 * @param array  $bindings a list of values to be bound to query parameters
	 *
	 * @return Cursor
	 */
	public static function getCursor( $sql, $bindings = array() )
	{
		return self::query( 'getCursor', $sql, $bindings );
	}

	/**
	 * Convenience function to fire an SQL query using the RedBeanPHP
	 * database adapter. This method allows you to directly query the
	 * database without having to obtain an database adapter instance first.
	 * Executes the specified SQL query together with the specified
	 * parameter bindings and returns a single row.
	 *
	 * @param string $sql      SQL query to execute
	 * @param array  $bindings a list of values to be bound to query parameters
	 *
	 * @return array|NULL
	 */
	public static function getRow( $sql, $bindings = array() )
	{
		return self::query( 'getRow', $sql, $bindings );
	}

	/**
	 * Convenience function to fire an SQL query using the RedBeanPHP
	 * database adapter. This method allows you to directly query the
	 * database without having to obtain an database adapter instance first.
	 * Executes the specified SQL query together with the specified
	 * parameter bindings and returns a single column.
	 *
	 * @param string $sql      SQL query to execute
	 * @param array  $bindings a list of values to be bound to query parameters
	 *
	 * @return string[]
	 */
	public static function getCol( $sql, $bindings = array() )
	{
		return self::query( 'getCol', $sql, $bindings );
	}

	/**
	 * Convenience function to execute Queries directly.
	 * Executes SQL.
	 * Results will be returned as an associative array. The first
	 * column in the select clause will be used for the keys in this array and
	 * the second column will be used for the values. If only one column is
	 * selected in the query, both key and value of the array will have the
	 * value of this field for each row.
	 *
	 * @param string $sql      SQL query to execute
	 * @param array  $bindings a list of values to be bound to query parameters
	 *
	 * @return string[]
	 */
	public static function getAssoc( $sql, $bindings = array() )
	{
		return self::query( 'getAssoc', $sql, $bindings );
	}

	/**
	 * Convenience function to fire an SQL query using the RedBeanPHP
	 * database adapter. This method allows you to directly query the
	 * database without having to obtain an database adapter instance first.
	 * Executes the specified SQL query together with the specified
	 * parameter bindings and returns an associative array.
	 * Results will be returned as an associative array indexed by the first
	 * column in the select.
	 *
	 * @param string $sql      SQL query to execute
	 * @param array  $bindings a list of values to be bound to query parameters
	 *
	 * @return array
	 */
	public static function getAssocRow( $sql, $bindings = array() )
	{
		return self::query( 'getAssocRow', $sql, $bindings );
	}

	/**
	 * Returns the insert ID for databases that support/require this
	 * functionality. Alias for R::getAdapter()->getInsertID().
	 *
	 * @return int
	 */
	public static function getInsertID()
	{
		return self::$adapter->getInsertID();
	}

	/**
	 * Makes a copy of a bean. This method makes a deep copy
	 * of the bean.The copy will have the following features.
	 * - All beans in own-lists will be duplicated as well
	 * - All references to shared beans will be copied but not the shared beans themselves
	 * - All references to parent objects (_id fields) will be copied but not the parents themselves
	 * In most cases this is the desired scenario for copying beans.
	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
	 * (i.e. one that already has been processed) the ID of the bean will be returned.
	 * This should not happen though.
	 *
	 * Note:
	 * This function does a reflectional database query so it may be slow.
	 *
	 * @deprecated
	 * This function is deprecated in favour of R::duplicate().
	 * This function has a confusing method signature, the R::duplicate() function
	 * only accepts two arguments: bean and filters.
	 *
	 * @param OODBBean   $bean    bean to be copied
	 * @param OODBBean[] $trail   for internal usage, pass array()
	 * @param boolean    $pid     for internal usage
	 * @param array      $filters white list filter with bean types to duplicate
	 *
	 * @return OODBBean
	 */
	public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() )
	{
		self::$duplicationManager->setFilters( $filters );
		return self::$duplicationManager->dup( $bean, $trail, $pid );
	}

	/**
	 * Makes a deep copy of a bean. This method makes a deep copy
	 * of the bean.The copy will have the following:
	 *
	 * * All beans in own-lists will be duplicated as well
	 * * All references to shared beans will be copied but not the shared beans themselves
	 * * All references to parent objects (_id fields) will be copied but not the parents themselves
	 *
	 * In most cases this is the desired scenario for copying beans.
	 * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
	 * (i.e. one that already has been processed) the ID of the bean will be returned.
	 * This should not happen though.
	 *
	 * Note:
	 * This function does a reflectional database query so it may be slow.
	 *
	 * Note:
	 * This is a simplified version of the deprecated R::dup() function.
	 *
	 * @param OODBBean $bean    bean to be copied
	 * @param array    $filters white list filter with bean types to duplicate
	 *
	 * @return OODBBean
	 */
	public static function duplicate( $bean, $filters = array() )
	{
		return self::dup( $bean, array(), FALSE, $filters );
	}

	/**
	 * Exports a collection of beans. Handy for XML/JSON exports with a
	 * Javascript framework like Dojo or ExtJS.
	 * What will be exported:
	 *
	 * * contents of the bean
	 * * all own bean lists (recursively)
	 * * all shared beans (not THEIR own lists)
	 *
	 * @param    OODBBean|OODBBean[] $beans   beans to be exported
	 * @param    boolean             $parents whether you want parent beans to be exported
	 * @param    array               $filters whitelist of types
	 * @param    boolean             $meta      export meta data as well
	 *
	 * @return array[]
	 */
	public static function exportAll( $beans, $parents = FALSE, $filters = array(), $meta = FALSE )
	{
		return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle, $meta );
	}

	/**
	 * Selects case style for export.
	 * This will determine the case style for the keys of exported beans (see exportAll).
	 * The following options are accepted:
	 *
	 * * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid )
	 * * 'camel'   Camel Case   (i.e. bookId isValid   )
	 * * 'dolphin' Dolphin Case (i.e. bookID isValid   ) Like CamelCase but ID is written all uppercase
	 *
	 * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different
	 * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id.
	 * Due to information loss this cannot be corrected. However if you might try
	 * DolphinCase for IDs it takes into account the exception concerning IDs.
	 *
	 * @param string $caseStyle case style identifier
	 *
	 * @return void
	 */
	public static function useExportCase( $caseStyle = 'default' )
	{
		if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' );
		self::$exportCaseStyle = $caseStyle;
	}

	/**
	 * Converts a series of rows to beans.
	 * This method converts a series of rows to beans.
	 * The type of the desired output beans can be specified in the
	 * first parameter. The second parameter is meant for the database
	 * result rows.
	 *
	 * Usage:
	 *
	 * <code>
	 * $rows = R::getAll( 'SELECT * FROM ...' )
	 * $beans = R::convertToBeans( $rows );
	 * </code>
	 *
	 * As of version 4.3.2 you can specify a meta-mask.
	 * Data from columns with names starting with the value specified in the mask
	 * will be transferred to the meta section of a bean (under data.bundle).
	 *
	 * <code>
	 * $rows = R::getAll( 'SELECT FROM... COUNT(*) AS extra_count ...' );
	 * $beans = R::convertToBeans( $rows, 'extra_' );
	 * $bean = reset( $beans );
	 * $data = $bean->getMeta( 'data.bundle' );
	 * $extra_count = $data['extra_count'];
	 * </code>
	 *
	 * New in 4.3.2: meta mask. The meta mask is a special mask to send
	 * data from raw result rows to the meta store of the bean. This is
	 * useful for bundling additional information with custom queries.
	 * Values of every column who's name starts with $mask will be
	 * transferred to the meta section of the bean under key 'data.bundle'.
	 *
	 * @param string            $type     type of beans to produce
	 * @param string[][]        $rows     must contain an array of array
	 * @param string|array|NULL $metamask meta mask to apply (optional)
	 *
	 * @return OODBBean[]
	 */
	public static function convertToBeans( $type, $rows, $metamask = NULL )
	{
		return self::$redbean->convertToBeans( $type, $rows, $metamask );
	}

	/**
	 * Just like convertToBeans, but for one bean.
	 *
	 * @param string            $type      type of bean to produce
	 * @param string[]          $row       one row from the database
	 * @param string|array|NULL $metamask  metamask (see convertToBeans)
	 *
	 * @return OODBBean|NULL
	 */
	public static function convertToBean( $type, $row, $metamask = NULL )
	{
		if ( !count( $row ) ) return NULL;
		$beans = self::$redbean->convertToBeans( $type, array( $row ), $metamask );
		$bean  = reset( $beans );
		return $bean;
	}

	/**
	 * Convenience function to 'find' beans from an SQL query.
	 * Used mostly to obtain a series of beans as well as
	 * pagination data (to paginate results) and optionally
	 * other data as well (that should not be considered part of
	 * a bean).
	 *
	 * Example:
	 *
	 *  $books = R::findFromSQL('book',"
	 *  SELECT *, count(*) OVER() AS total
	 *  FROM book
	 *  WHERE {$filter}
	 *  OFFSET {$from} LIMIT {$to} ", ['total']);
	 *
	 * This is the same as doing (example uses PostgreSQL dialect):
	 *
	 *  $rows = R::getAll("
	 *  SELECT *, count(*) OVER() AS total
	 *  FROM book
	 *  WHERE {$filter}
	 *  OFFSET {$from} LIMIT {$to}
	 *  ", $params);
	 *  $books = R::convertToBeans('book', $rows, ['total']);
	 *
	 * The additional data can be obtained using:
	 *
	 * $book->info('total');
	 *
	 * For further details see R::convertToBeans().
	 * If you set $autoExtract to TRUE and meta mask is an array,
	 * an array will be returned containing two nested arrays, the
	 * first of those nested arrays will contain the meta values
	 * you requested, the second array will contain the beans.
	 *
	 * @param string  $type        Type of bean to produce
	 * @param string  $sql         SQL query snippet to use
	 * @param array   $bindings    bindings for query (optional)
	 * @param string|array   $metamask    meta mask (optional, defaults to 'extra_')
	 * @param boolean $autoExtract TRUE to return meta mask values as first item of array
	 *
	 * @return array
	 */
	public static function findFromSQL( $type, $sql, $bindings = array(), $metamask = 'extra_', $autoExtract = false) {
		$rows = self::query( 'get', $sql, $bindings );
		$beans = array();
		if (count($rows)) $beans = self::$redbean->convertToBeans( $type, $rows, $metamask );
		if ($autoExtract && is_array($metamask)) {
			$values = array();
			$firstBean = NULL;
			if (count($beans)) $firstBean = reset($beans);
			foreach($metamask as $key) {
				$values[$key] = ($firstBean) ? $firstBean->info($key) : NULL;
			}
			return array( $values, $beans );
		}
		return $beans;
	}

	/**
	 * Tests whether a bean has been associated with one ore more
	 * of the listed tags. If the third parameter is TRUE this method
	 * will return TRUE only if all tags that have been specified are indeed
	 * associated with the given bean, otherwise FALSE.
	 * If the third parameter is FALSE this
	 * method will return TRUE if one of the tags matches, FALSE if none
	 * match.
	 *
	 * Tag list can be either an array with tag names or a comma separated list
	 * of tag names.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::hasTag( $blog, 'horror,movie', TRUE );
	 * </code>
	 *
	 * The example above returns TRUE if the $blog bean has been tagged
	 * as BOTH horror and movie. If the post has only been tagged as 'movie'
	 * or 'horror' this operation will return FALSE because the third parameter
	 * has been set to TRUE.
	 *
	 * @param  OODBBean        $bean bean to check for tags
	 * @param  string|string[] $tags list of tags
	 * @param  boolean         $all  whether they must all match or just some
	 *
	 * @return boolean
	 */
	public static function hasTag( $bean, $tags, $all = FALSE )
	{
		return self::$tagManager->hasTag( $bean, $tags, $all );
	}

	/**
	 * Removes all specified tags from the bean. The tags specified in
	 * the second parameter will no longer be associated with the bean.
	 *
	 * Tag list can be either an array with tag names or a comma separated list
	 * of tag names.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::untag( $blog, 'smart,interesting' );
	 * </code>
	 *
	 * In the example above, the $blog bean will no longer
	 * be associated with the tags 'smart' and 'interesting'.
	 *
	 * @param  OODBBean        $bean    tagged bean
	 * @param  string|string[] $tagList list of tags (names)
	 *
	 * @return void
	 */
	public static function untag( $bean, $tagList )
	{
		self::$tagManager->untag( $bean, $tagList );
	}

	/**
	 * Tags a bean or returns tags associated with a bean.
	 * If $tagList is NULL or omitted this method will return a
	 * comma separated list of tags associated with the bean provided.
	 * If $tagList is a comma separated list (string) of tags all tags will
	 * be associated with the bean.
	 * You may also pass an array instead of a string.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::tag( $meal, "TexMex,Mexican" );
	 * $tags = R::tag( $meal );
	 * </code>
	 *
	 * The first line in the example above will tag the $meal
	 * as 'TexMex' and 'Mexican Cuisine'. The second line will
	 * retrieve all tags attached to the meal object.
	 *
	 * @param OODBBean|SimpleModelInterface $bean    bean to tag
	 * @param string[]|NULL $tagList tags to attach to the specified bean
	 *
	 * @return string[]
	 */
	public static function tag( $bean, $tagList = NULL )
	{
		return self::$tagManager->tag( $bean, $tagList );
	}

	/**
	 * Adds tags to a bean.
	 * If $tagList is a comma separated list of tags all tags will
	 * be associated with the bean.
	 * You may also pass an array instead of a string.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::addTags( $blog, ["halloween"] );
	 * </code>
	 *
	 * The example adds the tag 'halloween' to the $blog
	 * bean.
	 *
	 * @param OODBBean|SimpleModelInterface        $bean    bean to tag
	 * @param string|string[] $tagList list of tags to add to bean
	 *
	 * @return void
	 */
	public static function addTags( $bean, $tagList )
	{
		self::$tagManager->addTags( $bean, $tagList );
	}

	/**
	 * Returns all beans that have been tagged with one or more
	 * of the specified tags.
	 *
	 * Tag list can be either an array with tag names or a comma separated list
	 * of tag names.
	 *
	 * Usage:
	 *
	 * <code>
	 * $watchList = R::tagged(
	 *   'movie',
	 *   'horror,gothic',
	 *   ' ORDER BY movie.title DESC LIMIT ?',
	 *   [ 10 ]
	 * );
	 * </code>
	 *
	 * The example uses R::tagged() to find all movies that have been
	 * tagged as 'horror' or 'gothic', order them by title and limit
	 * the number of movies to be returned to 10.
	 *
	 * @param string          $beanType type of bean you are looking for
	 * @param string|string[] $tagList  list of tags to match
	 * @param string          $sql      additional SQL (use only for pagination)
	 * @param array           $bindings bindings
	 *
	 * @return OODBBean[]
	 */
	public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
	{
		return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings );
	}

	/**
	 * Returns all beans that have been tagged with ALL of the tags given.
	 * This method works the same as R::tagged() except that this method only returns
	 * beans that have been tagged with all the specified labels.
	 *
	 * Tag list can be either an array with tag names or a comma separated list
	 * of tag names.
	 *
	 * Usage:
	 *
	 * <code>
	 * $watchList = R::taggedAll(
	 *    'movie',
	 *    [ 'gothic', 'short' ],
	 *    ' ORDER BY movie.id DESC LIMIT ? ',
	 *    [ 4 ]
	 * );
	 * </code>
	 *
	 * The example above returns at most 4 movies (due to the LIMIT clause in the SQL
	 * Query Snippet) that have been tagged as BOTH 'short' AND 'gothic'.
	 *
	 * @param string          $beanType type of bean you are looking for
	 * @param string|string[] $tagList  list of tags to match
	 * @param string          $sql      additional sql snippet
	 * @param array           $bindings bindings
	 *
	 * @return OODBBean[]
	 */
	public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
	{
		return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings );
	}

	/**
	 * Same as taggedAll() but counts beans only (does not return beans).
	 *
	 * @see R::taggedAll
	 *
	 * @param string          $beanType type of bean you are looking for
	 * @param string|string[] $tagList  list of tags to match
	 * @param string          $sql      additional sql snippet
	 * @param array           $bindings bindings
	 *
	 * @return integer
	 */
	public static function countTaggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
	{
		return self::$tagManager->countTaggedAll( $beanType, $tagList, $sql, $bindings );
	}

	/**
	 * Same as tagged() but counts beans only (does not return beans).
	 *
	 * @see R::tagged
	 *
	 * @param string          $beanType type of bean you are looking for
	 * @param string|string[] $tagList  list of tags to match
	 * @param string          $sql      additional sql snippet
	 * @param array           $bindings bindings
	 *
	 * @return integer
	 */
	public static function countTagged( $beanType, $tagList, $sql = '', $bindings = array() )
	{
		return self::$tagManager->countTagged( $beanType, $tagList, $sql, $bindings );
	}

	/**
	 * Wipes all beans of type $beanType.
	 *
	 * @param string $beanType type of bean you want to destroy entirely
	 *
	 * @return boolean
	 */
	public static function wipe( $beanType )
	{
		return Facade::$redbean->wipe( $beanType );
	}

	/**
	 * Counts the number of beans of type $type.
	 * This method accepts a second argument to modify the count-query.
	 * A third argument can be used to provide bindings for the SQL snippet.
	 *
	 * @param string $type     type of bean we are looking for
	 * @param string $addSQL   additional SQL snippet
	 * @param array  $bindings parameters to bind to SQL
	 *
	 * @return integer
	 */
	public static function count( $type, $addSQL = '', $bindings = array() )
	{
		return Facade::$redbean->count( $type, $addSQL, $bindings );
	}

	/**
	 * Configures the facade, want to have a new Writer? A new Object Database or a new
	 * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new
	 * toolbox.
	 *
	 * @param ToolBox $tb toolbox to configure facade with
	 *
	 * @return ToolBox
	 */
	public static function configureFacadeWithToolbox( ToolBox $tb )
	{
		$oldTools                 = self::$toolbox;
		self::$toolbox            = $tb;
		self::$writer             = self::$toolbox->getWriter();
		self::$adapter            = self::$toolbox->getDatabaseAdapter();
		self::$redbean            = self::$toolbox->getRedBean();
		self::$finder             = new Finder( self::$toolbox );
		self::$associationManager = new AssociationManager( self::$toolbox );
		self::$tree               = new Tree( self::$toolbox );
		self::$redbean->setAssociationManager( self::$associationManager );
		self::$labelMaker         = new LabelMaker( self::$toolbox );
		$helper                   = new SimpleModelHelper();
		$helper->attachEventListeners( self::$redbean );
		if (self::$redbean->getBeanHelper() === NULL) {
			self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper );
		}
		self::$duplicationManager = new DuplicationManager( self::$toolbox );
		self::$tagManager         = new TagManager( self::$toolbox );
		return $oldTools;
	}

	/**
	 * Facade Convenience method for adapter transaction system.
	 * Begins a transaction.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::begin();
	 * try {
	 *  $bean1 = R::dispense( 'bean' );
	 *  R::store( $bean1 );
	 *  $bean2 = R::dispense( 'bean' );
	 *  R::store( $bean2 );
	 *  R::commit();
	 * } catch( \Exception $e ) {
	 *  R::rollback();
	 * }
	 * </code>
	 *
	 * The example above illustrates how transactions in RedBeanPHP are used.
	 * In this example 2 beans are stored or nothing is stored at all.
	 * It's not possible for this piece of code to store only half of the beans.
	 * If an exception occurs, the transaction gets rolled back and the database
	 * will be left 'untouched'.
	 *
	 * In fluid mode transactions will be ignored and all queries will
	 * be executed as-is because database schema changes will automatically
	 * trigger the transaction system to commit everything in some database
	 * systems. If you use a database that can handle DDL changes you might wish
	 * to use setAllowFluidTransactions(TRUE). If you do this, the behavior of
	 * this function in fluid mode will depend on the database platform used.
	 *
	 * @return bool
	 */
	public static function begin()
	{
		if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
		self::$adapter->startTransaction();
		return TRUE;
	}

	/**
	 * Facade Convenience method for adapter transaction system.
	 * Commits a transaction.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::begin();
	 * try {
	 *  $bean1 = R::dispense( 'bean' );
	 *  R::store( $bean1 );
	 *  $bean2 = R::dispense( 'bean' );
	 *  R::store( $bean2 );
	 *  R::commit();
	 * } catch( \Exception $e ) {
	 *  R::rollback();
	 * }
	 * </code>
	 *
	 * The example above illustrates how transactions in RedBeanPHP are used.
	 * In this example 2 beans are stored or nothing is stored at all.
	 * It's not possible for this piece of code to store only half of the beans.
	 * If an exception occurs, the transaction gets rolled back and the database
	 * will be left 'untouched'.
	 *
	 * In fluid mode transactions will be ignored and all queries will
	 * be executed as-is because database schema changes will automatically
	 * trigger the transaction system to commit everything in some database
	 * systems. If you use a database that can handle DDL changes you might wish
	 * to use setAllowFluidTransactions(TRUE). If you do this, the behavior of
	 * this function in fluid mode will depend on the database platform used.
	 *
	 * @return bool
	 */
	public static function commit()
	{
		if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
		self::$adapter->commit();
		return TRUE;
	}

	/**
	 * Facade Convenience method for adapter transaction system.
	 * Rolls back a transaction.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::begin();
	 * try {
	 *  $bean1 = R::dispense( 'bean' );
	 *  R::store( $bean1 );
	 *  $bean2 = R::dispense( 'bean' );
	 *  R::store( $bean2 );
	 *  R::commit();
	 * } catch( \Exception $e ) {
	 *  R::rollback();
	 * }
	 * </code>
	 *
	 * The example above illustrates how transactions in RedBeanPHP are used.
	 * In this example 2 beans are stored or nothing is stored at all.
	 * It's not possible for this piece of code to store only half of the beans.
	 * If an exception occurs, the transaction gets rolled back and the database
	 * will be left 'untouched'.
	 *
	 * In fluid mode transactions will be ignored and all queries will
	 * be executed as-is because database schema changes will automatically
	 * trigger the transaction system to commit everything in some database
	 * systems. If you use a database that can handle DDL changes you might wish
	 * to use setAllowFluidTransactions(TRUE). If you do this, the behavior of
	 * this function in fluid mode will depend on the database platform used.
	 *
	 * @return bool
	 */
	public static function rollback()
	{
		if ( !self::$allowFluidTransactions && !self::$redbean->isFrozen() ) return FALSE;
		self::$adapter->rollback();
		return TRUE;
	}

	/**
	 * Returns a list of columns. Format of this array:
	 * array( fieldname => type )
	 * Note that this method only works in fluid mode because it might be
	 * quite heavy on production servers!
	 *
	 * @param  string $table name of the table (not type) you want to get columns of
	 *
	 * @return string[]
	 */
	public static function getColumns( $table )
	{
		return self::$writer->getColumns( $table );
	}

	/**
	 * Generates question mark slots for an array of values.
	 * Given an array and an optional template string this method
	 * will produce string containing parameter slots for use in
	 * an SQL query string.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::genSlots( array( 'a', 'b' ) );
	 * </code>
	 *
	 * The statement in the example will produce the string:
	 * '?,?'.
	 *
	 * Another example, using a template string:
	 *
	 * <code>
	 * R::genSlots( array('a', 'b'), ' IN( %s ) ' );
	 * </code>
	 *
	 * The statement in the example will produce the string:
	 * ' IN( ?,? ) '.
	 *
	 * @param array  $array    array to generate question mark slots for
	 * @param string|NULL $template template to use
	 *
	 * @return string
	 */
	public static function genSlots( $array, $template = NULL )
	{
		return ArrayTool::genSlots( $array, $template );
	}

	/**
	 * Convenience method to quickly attach parent beans.
	 * Although usually this can also be done with findMulti(), that
	 * approach can be a bit verbose sometimes. This convenience method
	 * uses a default yet overridable SQL snippet to perform the
	 * operation, leveraging the power of findMulti().
	 *
	 * Usage:
	 *
	 * <code>
	 * $users = R::find('user');
	 * $users = R::loadJoined( $users, 'country' );
	 * </code>
	 *
	 * This is an alternative for:
	 *
	 * <code>
	 * $all = R::findMulti('country',
	 *    R::genSlots( $users,
	 *       'SELECT country.* FROM country WHERE id IN ( %s )' ),
	 *    array_column( $users, 'country_id' ),
	 *    [Finder::onmap('country', $gebruikers)]
	 * );
	 * </code>
	 *
	 * @param OODBBean[]|TypedModel[]  $beans       a list of OODBBeans
	 * @param string      $type        a type string
	 * @param string      $sqlTemplate an SQL template string for the SELECT-query
	 *
	 * @return OODBBean[]|TypedModel[]
	 */
	public static function loadJoined( $beans, $type, $sqlTemplate = 'SELECT %s.* FROM %s WHERE id IN (%s)' )
	{
		if (!count($beans)) return array();
		$ids  = array();
		$key  = "{$type}_id";
		foreach( $beans as $bean ) $ids[] = $bean->{$key};
		$result = self::findMulti($type, self::genSlots( $beans,sprintf($sqlTemplate, $type, $type, '%s')), $ids, array( Finder::onmap($type, $beans) ) );
		$bean = reset($beans);
		return $result[ $bean->getMeta('type') ];
	}

	/**
	 * Flattens a multi dimensional bindings array for use with genSlots().
	 *
	 * Usage:
	 *
	 * <code>
	 * R::flat( array( 'a', array( 'b' ), 'c' ) );
	 * </code>
	 *
	 * produces an array like: [ 'a', 'b', 'c' ]
	 *
	 * @param array $array  array to flatten
	 * @param array $result result array parameter (for recursion)
	 *
	 * @return array
	 */
	public static function flat( $array, $result = array() )
	{
		return ArrayTool::flat( $array, $result );
	}

	/**
	 * Nukes the entire database.
	 * This will remove all schema structures from the database.
	 * Only works in fluid mode. Be careful with this method.
	 *
	 * @warning dangerous method, will remove all tables, columns etc.
	 *
	 * @return void
	 */
	public static function nuke()
	{
		return self::wipeAll( TRUE );
	}

	/**
	 * Truncates or drops all database tables/views.
	 * Empties the database. If the deleteTables flag is set to TRUE
	 * this function will also remove the database structures.
	 * The latter only works in fluid mode.
	 *
	 * @param boolean $alsoDeleteTables TRUE to clear entire database.
	 *
	 * @return void
	 */
	public static function wipeAll( $alsoDeleteTables = FALSE )
	{
		if ( $alsoDeleteTables ) {
			if ( !self::$redbean->isFrozen() ) {
				self::$writer->wipeAll();
			}
		} else {
			foreach ( self::$writer->getTables() as $table ) {
				self::wipe( $table );
			}
		}
	}

	/**
	 * Short hand function to store a set of beans at once, IDs will be
	 * returned as an array. For information please consult the R::store()
	 * function.
	 * A loop saver.
	 *
	 * If the second parameter is set to TRUE and
	 * Hybrid mode is allowed (default OFF for novice), then RedBeanPHP
	 * will automatically temporarily switch to fluid mode to attempt to store the
	 * bean in case of an SQLException.
	 *
	 * @param OODBBean[] $beans            list of beans to be stored
	 * @param boolean    $unfreezeIfNeeded retries in fluid mode in hybrid mode
	 *
	 * @return int[] ids
	 */
	public static function storeAll( $beans, $unfreezeIfNeeded = FALSE )
	{
		$ids = array();
		foreach ( $beans as $bean ) {
			$ids[] = self::store( $bean, $unfreezeIfNeeded );
		}
		return $ids;
	}

	/**
	 * Short hand function to trash a set of beans at once.
	 * For information please consult the R::trash() function.
	 * A loop saver.
	 *
	 * @param OODBBean[] $beans list of beans to be trashed
	 *
	 * @return int
	 */
	public static function trashAll( $beans )
	{
		$numberOfDeletion = 0;
		foreach ( $beans as $bean ) {
			$numberOfDeletion += self::trash( $bean );
		}
		return $numberOfDeletion;
	}

	/**
	 * Short hand function to trash a series of beans using
	 * only IDs. This function combines trashAll and batch loading
	 * in one call. Note that while this function accepts just
	 * bean IDs, the beans will still be loaded first. This is because
	 * the function still respects all the FUSE hooks that may have been
	 * associated with the domain logic associated with these beans.
	 * If you really want to delete just records from the database use
	 * a simple DELETE-FROM SQL query instead.
	 *
	 * @param string $type the bean type you wish to trash
	 * @param int[]  $ids  list of bean IDs
	 *
	 * @return void
	 */
	public static function trashBatch( $type, $ids )
	{
		self::trashAll( self::batch( $type, $ids ) );
	}

	/**
	 * Short hand function to find and trash beans.
	 * This function combines trashAll and find.
	 * Given a bean type, a query snippet and optionally some parameter
	 * bindings, this function will search for the beans described in the
	 * query and its parameters and then feed them to the trashAll function
	 * to be trashed.
	 *
	 * Note that while this function accepts just
	 * a bean type and query snippet, the beans will still be loaded first. This is because
	 * the function still respects all the FUSE hooks that may have been
	 * associated with the domain logic associated with these beans.
	 * If you really want to delete just records from the database use
	 * a simple DELETE-FROM SQL query instead.
	 *
	 * Returns the number of beans deleted.
	 *
	 * @param string      $type       bean type to look for in database
	 * @param string|NULL $sqlSnippet an SQL query snippet
	 * @param array       $bindings   SQL parameter bindings
	 *
	 * @return int
	 */
	public static function hunt( $type, $sqlSnippet = NULL, $bindings = array() )
	{
		$numberOfTrashedBeans = 0;
		$beans = self::findCollection( $type, $sqlSnippet, $bindings );
		while( $bean = $beans->next() ) {
			self::trash( $bean );
			$numberOfTrashedBeans++;
		}
		return $numberOfTrashedBeans;
	}

	/**
	 * Toggles Writer Cache.
	 * Turns the Writer Cache on or off. The Writer Cache is a simple
	 * query based caching system that may improve performance without the need
	 * for cache management. This caching system will cache non-modifying queries
	 * that are marked with special SQL comments. As soon as a non-marked query
	 * gets executed the cache will be flushed. Only non-modifying select queries
	 * have been marked therefore this mechanism is a rather safe way of caching, requiring
	 * no explicit flushes or reloads. Of course this does not apply if you intend to test
	 * or simulate concurrent querying.
	 *
	 * @param boolean $yesNo TRUE to enable cache, FALSE to disable cache
	 *
	 * @return void
	 */
	public static function useWriterCache( $yesNo )
	{
		self::getWriter()->setUseCache( $yesNo );
	}

	/**
	 * A label is a bean with only an id, type and name property.
	 * This function will dispense beans for all entries in the array. The
	 * values of the array will be assigned to the name property of each
	 * individual bean.
	 *
	 * @param string   $type   type of beans you would like to have
	 * @param string[] $labels list of labels, names for each bean
	 *
	 * @return OODBBean[]
	 */
	public static function dispenseLabels( $type, $labels )
	{
		return self::$labelMaker->dispenseLabels( $type, $labels );
	}

	/**
	 * Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs.
	 * Either returns a (newly created) bean representing the desired ENUM
	 * value or returns a list of all enums for the type.
	 *
	 * To obtain (and add if necessary) an ENUM value:
	 *
	 * <code>
	 * $tea->flavour = R::enum( 'flavour:apple' );
	 * </code>
	 *
	 * Returns a bean of type 'flavour' with  name = apple.
	 * This will add a bean with property name (set to APPLE) to the database
	 * if it does not exist yet.
	 *
	 * To obtain all flavours:
	 *
	 * <code>
	 * R::enum('flavour');
	 * </code>
	 *
	 * To get a list of all flavour names:
	 *
	 * <code>
	 * R::gatherLabels( R::enum( 'flavour' ) );
	 * </code>
	 *
	 * @param string $enum either type or type-value
	 *
	 * @return OODBBean|OODBBean[]
	 */
	public static function enum( $enum )
	{
		return self::$labelMaker->enum( $enum );
	}

	/**
	 * Gathers labels from beans. This function loops through the beans,
	 * collects the values of the name properties of each individual bean
	 * and stores the names in a new array. The array then gets sorted using the
	 * default sort function of PHP (sort).
	 *
	 * @param OODBBean[] $beans list of beans to loop
	 *
	 * @return string[]
	 */
	public static function gatherLabels( $beans )
	{
		return self::$labelMaker->gatherLabels( $beans );
	}

	/**
	 * Closes the database connection.
	 * While database connections are closed automatically at the end of the PHP script,
	 * closing database connections is generally recommended to improve performance.
	 * Closing a database connection will immediately return the resources to PHP.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::setup( ... );
	 * ... do stuff ...
	 * R::close();
	 * </code>
	 *
	 * @return void
	 */
	public static function close()
	{
		if ( isset( self::$adapter ) ) {
			self::$adapter->close();
		}
	}

	/**
	 * Simple convenience function, returns ISO date formatted representation
	 * of $time.
	 *
	 * @param int|NULL $time UNIX timestamp
	 *
	 * @return string
	 */
	public static function isoDate( $time = NULL )
	{
		if ( !$time ) {
			$time = time();
		}

		return @date( 'Y-m-d', $time );
	}

	/**
	 * Simple convenience function, returns ISO date time
	 * formatted representation
	 * of $time.
	 *
	 * @param int|NULL $time UNIX timestamp
	 *
	 * @return string
	 */
	public static function isoDateTime( $time = NULL )
	{
		if ( !$time ) $time = time();
		return @date( 'Y-m-d H:i:s', $time );
	}

	/**
	 * Sets the database adapter you want to use.
	 * The database adapter manages the connection to the database
	 * and abstracts away database driver specific interfaces.
	 *
	 * @param Adapter $adapter Database Adapter for facade to use
	 *
	 * @return void
	 */
	public static function setDatabaseAdapter( Adapter $adapter )
	{
		self::$adapter = $adapter;
	}

	/**
	 * Sets the Query Writer you want to use.
	 * The Query Writer writes and executes database queries using
	 * the database adapter. It turns RedBeanPHP 'commands' into
	 * database 'statements'.
	 *
	 * @param QueryWriter $writer Query Writer instance for facade to use
	 *
	 * @return void
	 */
	public static function setWriter( QueryWriter $writer )
	{
		self::$writer = $writer;
	}

	/**
	 * Sets the OODB you want to use.
	 * The RedBeanPHP Object oriented database is the main RedBeanPHP
	 * interface that allows you to store and retrieve RedBeanPHP
	 * objects (i.e. beans).
	 *
	 * @param OODB $redbean Object Database for facade to use
	 */
	public static function setRedBean( OODB $redbean )
	{
		self::$redbean = $redbean;
	}

	/**
	 * Optional accessor for neat code.
	 * Sets the database adapter you want to use.
	 *
	 * @return Adapter
	 */
	public static function getDatabaseAdapter()
	{
		return self::$adapter;
	}

	/**
	 * In case you use PDO (which is recommended and the default but not mandatory, hence
	 * the database adapter), you can use this method to obtain the PDO object directly.
	 * This is a convenience method, it will do the same as:
	 *
	 * <code>
	 * R::getDatabaseAdapter()->getDatabase()->getPDO();
	 * </code>
	 *
	 * If the PDO object could not be found, for whatever reason, this method
	 * will return NULL instead.
	 *
	 * @return NULL|\PDO
	 */
	public static function getPDO()
	{
		$databaseAdapter = self::getDatabaseAdapter();
		if ( is_null( $databaseAdapter ) ) return NULL;
		$database = $databaseAdapter->getDatabase();
		if ( is_null( $database ) ) return NULL;
		if ( !method_exists( $database, 'getPDO' ) ) return NULL;

		/** @var RPDO $database */
		return $database->getPDO();
	}

	/**
	 * Returns the current duplication manager instance.
	 *
	 * @return DuplicationManager
	 */
	public static function getDuplicationManager()
	{
		return self::$duplicationManager;
	}

	/**
	 * Optional accessor for neat code.
	 * Sets the database adapter you want to use.
	 *
	 * @return QueryWriter
	 */
	public static function getWriter()
	{
		return self::$writer;
	}

	/**
	 * Optional accessor for neat code.
	 * Sets the database adapter you want to use.
	 *
	 * @return OODB
	 */
	public static function getRedBean()
	{
		return self::$redbean;
	}

	/**
	 * Returns the toolbox currently used by the facade.
	 * To set the toolbox use R::setup() or R::configureFacadeWithToolbox().
	 * To create a toolbox use Setup::kickstart(). Or create a manual
	 * toolbox using the ToolBox class.
	 *
	 * @return ToolBox
	 */
	public static function getToolBox()
	{
		return self::$toolbox;
	}

	/**
	 * Mostly for internal use, but might be handy
	 * for some users.
	 * This returns all the components of the currently
	 * selected toolbox.
	 *
	 * Returns the components in the following order:
	 *
	 * # OODB instance (getRedBean())
	 * # Database Adapter
	 * # Query Writer
	 * # Toolbox itself
	 *
	 * @return array
	 */
	public static function getExtractedToolbox()
	{
		return array( self::$redbean, self::$adapter, self::$writer, self::$toolbox );
	}

	/**
	 * Facade method for AQueryWriter::renameAssociation()
	 *
	 * @param string|string[] $from
	 * @param string          $to
	 *
	 * @return void
	 */
	public static function renameAssociation( $from, $to = NULL )
	{
		AQueryWriter::renameAssociation( $from, $to );
	}

	/**
	 * Little helper method for Resty Bean Can server and others.
	 * Takes an array of beans and exports each bean.
	 * Unlike exportAll this method does not recurse into own lists
	 * and shared lists, the beans are exported as-is, only loaded lists
	 * are exported.
	 *
	 * @param OODBBean[] $beans beans
	 *
	 * @return array[]
	 */
	public static function beansToArray( $beans )
	{
		$list = array();
		foreach( $beans as $bean ) $list[] = $bean->export();
		return $list;
	}

	/**
	 * Sets the error mode for FUSE.
	 * What to do if a FUSE model method does not exist?
	 * You can set the following options:
	 *
	 * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
	 * * OODBBean::C_ERR_LOG, logs the incident using error_log
	 * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
	 * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
	 * * OODBBean::C_ERR_EXCEPTION, throws an exception
	 * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
	 * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
	 *
	 * <code>
	 * Custom handler method signature: handler( array (
	 * 	'message' => string
	 * 	'bean' => OODBBean
	 * 	'method' => string
	 * ) )
	 * </code>
	 *
	 * This method returns the old mode and handler as an array.
	 *
	 * @param integer       $mode mode, determines how to handle errors
	 * @param callable|NULL $func custom handler (if applicable)
	 *
	 * @return array
	 */
	public static function setErrorHandlingFUSE( $mode, $func = NULL )
	{
		return OODBBean::setErrorHandlingFUSE( $mode, $func );
	}

	/**
	 * Dumps bean data to array.
	 * Given a one or more beans this method will
	 * return an array containing first part of the string
	 * representation of each item in the array.
	 *
	 * Usage:
	 *
	 * <code>
	 * echo R::dump( $bean );
	 * </code>
	 *
	 * The example shows how to echo the result of a simple
	 * dump. This will print the string representation of the
	 * specified bean to the screen, limiting the output per bean
	 * to 35 characters to improve readability. Nested beans will
	 * also be dumped.
	 *
	 * @param OODBBean|OODBBean[] $data either a bean or an array of beans
	 *
	 * @return string|string[]
	 */
	public static function dump( $data )
	{
		return Dump::dump( $data );
	}

	/**
	 * Binds an SQL function to a column.
	 * This method can be used to setup a decode/encode scheme or
	 * perform UUID insertion. This method is especially useful for handling
	 * MySQL spatial columns, because they need to be processed first using
	 * the asText/GeomFromText functions.
	 *
	 * Example:
	 *
	 * <code>
	 * R::bindFunc( 'read', 'location.point', 'asText' );
	 * R::bindFunc( 'write', 'location.point', 'GeomFromText' );
	 * </code>
	 *
	 * Passing NULL as the function will reset (clear) the function
	 * for this column/mode.
	 *
	 * @param string $mode     mode for function: i.e. read or write
	 * @param string $field    field (table.column) to bind function to
	 * @param string $function SQL function to bind to specified column
	 * @param boolean $isTemplate TRUE if $function is an SQL string, FALSE for just a function name
	 *
	 * @return void
	 */
	public static function bindFunc( $mode, $field, $function, $isTemplate = FALSE )
	{
		self::$redbean->bindFunc( $mode, $field, $function, $isTemplate );
	}

	/**
	 * Sets global aliases.
	 * Registers a batch of aliases in one go. This works the same as
	 * fetchAs but explicitly. For instance if you register
	 * the alias 'cover' for 'page' a property containing a reference to a
	 * page bean called 'cover' will correctly return the page bean and not
	 * a (non-existent) cover bean.
	 *
	 * <code>
	 * R::aliases( array( 'cover' => 'page' ) );
	 * $book = R::dispense( 'book' );
	 * $page = R::dispense( 'page' );
	 * $book->cover = $page;
	 * R::store( $book );
	 * $book = $book->fresh();
	 * $cover = $book->cover;
	 * echo $cover->getMeta( 'type' ); //page
	 * </code>
	 *
	 * The format of the aliases registration array is:
	 *
	 * {alias} => {actual type}
	 *
	 * In the example above we use:
	 *
	 * cover => page
	 *
	 * From that point on, every bean reference to a cover
	 * will return a 'page' bean.
	 *
	 * @param string[] $list list of global aliases to use
	 *
	 * @return void
	 */
	public static function aliases( $list )
	{
		OODBBean::aliases( $list );
	}

	/**
	 * Tries to find a bean matching a certain type and
	 * criteria set. If no beans are found a new bean
	 * will be created, the criteria will be imported into this
	 * bean and the bean will be stored and returned.
	 * If multiple beans match the criteria only the first one
	 * will be returned.
	 *
	 * @param string $type type of bean to search for
	 * @param array  $like criteria set describing the bean to search for
	 * @param boolean &$hasBeenCreated set to TRUE if bean has been created
	 *
	 * @return OODBBean
	 */
	public static function findOrCreate( $type, $like = array(), $sql = '', &$hasBeenCreated = false )
	{
		return self::$finder->findOrCreate( $type, $like, $sql, $hasBeenCreated );
	}

	/**
	 * Tries to find beans matching the specified type and
	 * criteria set.
	 *
	 * If the optional additional SQL snippet is a condition, it will
	 * be glued to the rest of the query using the AND operator.
	 *
	 * @param string $type type of bean to search for
	 * @param array  $like optional criteria set describing the bean to search for
	 * @param string $sql  optional additional SQL for sorting
	 * @param array  $bindings bindings
	 *
	 * @return OODBBean[]
	 */
	public static function findLike( $type, $like = array(), $sql = '', $bindings = array() )
	{
		return self::$finder->findLike( $type, $like, $sql, $bindings );
	}

	/**
	 * Starts logging queries.
	 * Use this method to start logging SQL queries being
	 * executed by the adapter. Logging queries will not
	 * print them on the screen. Use R::getLogs() to
	 * retrieve the logs.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::startLogging();
	 * R::store( R::dispense( 'book' ) );
	 * R::find('book', 'id > ?',[0]);
	 * $logs = R::getLogs();
	 * $count = count( $logs );
	 * print_r( $logs );
	 * R::stopLogging();
	 * </code>
	 *
	 * In the example above we start a logging session during
	 * which we store an empty bean of type book. To inspect the
	 * logs we invoke R::getLogs() after stopping the logging.
	 *
	 * @note you cannot use R::debug and R::startLogging
	 * at the same time because R::debug is essentially a
	 * special kind of logging.
	 *
	 * @return void
	 */
	public static function startLogging()
	{
		self::debug( TRUE, RDefault::C_LOGGER_ARRAY );
	}

	/**
	 * Stops logging and flushes the logs,
	 * convenient method to stop logging of queries.
	 * Use this method to stop logging SQL queries being
	 * executed by the adapter. Logging queries will not
	 * print them on the screen. Use R::getLogs() to
	 * retrieve the logs.
	 *
	 * <code>
	 * R::startLogging();
	 * R::store( R::dispense( 'book' ) );
	 * R::find('book', 'id > ?',[0]);
	 * $logs = R::getLogs();
	 * $count = count( $logs );
	 * print_r( $logs );
	 * R::stopLogging();
	 * </code>
	 *
	 * In the example above we start a logging session during
	 * which we store an empty bean of type book. To inspect the
	 * logs we invoke R::getLogs() after stopping the logging.
	 *
	 * @note you cannot use R::debug and R::startLogging
	 * at the same time because R::debug is essentially a
	 * special kind of logging.
	 *
	 * @note by stopping the logging you also flush the logs.
	 * Therefore, only stop logging AFTER you have obtained the
	 * query logs using R::getLogs()
	 *
	 * @return void
	 */
	public static function stopLogging()
	{
		self::debug( FALSE );
	}

	/**
	 * Returns the log entries written after the startLogging.
	 *
	 * Use this method to obtain the query logs gathered
	 * by the logging mechanisms.
	 * Logging queries will not
	 * print them on the screen. Use R::getLogs() to
	 * retrieve the logs.
	 *
	 * <code>
	 * R::startLogging();
	 * R::store( R::dispense( 'book' ) );
	 * R::find('book', 'id > ?',[0]);
	 * $logs = R::getLogs();
	 * $count = count( $logs );
	 * print_r( $logs );
	 * R::stopLogging();
	 * </code>
	 *
	 * In the example above we start a logging session during
	 * which we store an empty bean of type book. To inspect the
	 * logs we invoke R::getLogs() after stopping the logging.
	 *
	 * The logs may look like:
	 *
	 * [1] => SELECT `book`.*  FROM `book`  WHERE id > ?  -- keep-cache
	 * [2] => array ( 0 => 0, )
	 * [3] => resultset: 1 rows
	 *
	 * Basically, element in the array is a log entry.
	 * Parameter bindings are  represented as nested arrays (see 2).
	 *
	 * @note you cannot use R::debug and R::startLogging
	 * at the same time because R::debug is essentially a
	 * special kind of logging.
	 *
	 * @note by stopping the logging you also flush the logs.
	 * Therefore, only stop logging AFTER you have obtained the
	 * query logs using R::getLogs()
	 *
	 * @return string[]
	 */
	public static function getLogs()
	{
		return self::getLogger()->getLogs();
	}

	/**
	 * Resets the query counter.
	 * The query counter can be used to monitor the number
	 * of database queries that have
	 * been processed according to the database driver. You can use this
	 * to monitor the number of queries required to render a page.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::resetQueryCount();
	 * echo R::getQueryCount() . ' queries processed.';
	 * </code>
	 *
	 * @return void
	 */
	public static function resetQueryCount()
	{
		self::$adapter->getDatabase()->resetCounter();
	}

	/**
	 * Returns the number of SQL queries processed.
	 * This method returns the number of database queries that have
	 * been processed according to the database driver. You can use this
	 * to monitor the number of queries required to render a page.
	 *
	 * Usage:
	 *
	 * <code>
	 * echo R::getQueryCount() . ' queries processed.';
	 * </code>
	 *
	 * @return integer
	 */
	public static function getQueryCount()
	{
		return self::$adapter->getDatabase()->getQueryCount();
	}

	/**
	 * Returns the current logger instance being used by the
	 * database object.
	 *
	 * @return Logger|NULL
	 */
	public static function getLogger()
	{
		return self::$adapter->getDatabase()->getLogger();
	}

	/**
	 * @deprecated
	 */
	public static function setAutoResolve( $automatic = TRUE ){}

	/**
	 * Toggles 'partial bean mode'. If this mode has been
	 * selected the repository will only update the fields of a bean that
	 * have been changed rather than the entire bean.
	 * Pass the value TRUE to select 'partial mode' for all beans.
	 * Pass the value FALSE to disable 'partial mode'.
	 * Pass an array of bean types if you wish to use partial mode only
	 * for some types.
	 * This method will return the previous value.
	 *
	 * @param boolean|string[] $yesNoBeans List of type names or 'all'
	 *
	 * @return boolean|string[]
	 */
	public static function usePartialBeans( $yesNoBeans )
	{
		return self::$redbean->getCurrentRepository()->usePartialBeans( $yesNoBeans );
	}

	/**
	 * Exposes the result of the specified SQL query as a CSV file.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::csv( 'SELECT
	 *                 `name`,
	 *                  population
	 *          FROM city
	 *          WHERE region = :region ',
	 *          array( ':region' => 'Denmark' ),
	 *          array( 'city', 'population' ),
	 *          '/tmp/cities.csv'
	 * );
	 * </code>
	 *
	 * The command above will select all cities in Denmark
	 * and create a CSV with columns 'city' and 'population' and
	 * populate the cells under these column headers with the
	 * names of the cities and the population numbers respectively.
	 *
	 * @param string  $sql      SQL query to expose result of
	 * @param array   $bindings parameter bindings
	 * @param array   $columns  column headers for CSV file
	 * @param string  $path     path to save CSV file to
	 * @param boolean $output   TRUE to output CSV directly using readfile
	 * @param array   $options  delimiter, quote and escape character respectively
	 *
	 * @return void
	 */
	public static function csv( $sql = '', $bindings = array(), $columns = NULL, $path = '/tmp/redexport_%s.csv', $output = TRUE )
	{
		$quickExport = new QuickExport( self::$toolbox );
		$quickExport->csv( $sql, $bindings, $columns, $path, $output );
	}

	/**
	 * MatchUp is a powerful productivity boosting method that can replace simple control
	 * scripts with a single RedBeanPHP command. Typically, matchUp() is used to
	 * replace login scripts, token generation scripts and password reset scripts.
	 * The MatchUp method takes a bean type, an SQL query snippet (starting at the WHERE clause),
	 * SQL bindings, a pair of task arrays and a bean reference.
	 *
	 * If the first 3 parameters match a bean, the first task list will be considered,
	 * otherwise the second one will be considered. On consideration, each task list,
	 * an array of keys and values will be executed. Every key in the task list should
	 * correspond to a bean property while every value can either be an expression to
	 * be evaluated or a closure (PHP 5.3+). After applying the task list to the bean
	 * it will be stored. If no bean has been found, a new bean will be dispensed.
	 *
	 * This method will return TRUE if the bean was found and FALSE if not AND
	 * there was a NOT-FOUND task list. If no bean was found AND there was also
	 * no second task list, NULL will be returned.
	 *
	 * To obtain the bean, pass a variable as the sixth parameter.
	 * The function will put the matching bean in the specified variable.
	 *
	 * @param string   $type         type of bean you're looking for
	 * @param string   $sql          SQL snippet (starting at the WHERE clause, omit WHERE-keyword)
	 * @param array    $bindings     array of parameter bindings for SQL snippet
	 * @param array    $onFoundDo    task list to be considered on finding the bean
	 * @param array    $onNotFoundDo task list to be considered on NOT finding the bean
	 * @param OODBBean &$bean        reference to obtain the found bean
	 *
	 * @return bool|NULL
	 */
	public static function matchUp( $type, $sql, $bindings = array(), $onFoundDo = NULL, $onNotFoundDo = NULL, &$bean = NULL 	) {
		$matchUp = new MatchUp( self::$toolbox );
		return $matchUp->matchUp( $type, $sql, $bindings, $onFoundDo, $onNotFoundDo, $bean );
	}

	/**
	 * @deprecated
	 *
	 * Returns an instance of the Look Helper class.
	 * The instance will be configured with the current toolbox.
	 *
	 * In previous versions of RedBeanPHP you had to use:
	 * R::getLook()->look() instead of R::look(). However to improve useability of the
	 * library the look() function can now directly be invoked from the facade.
	 *
	 * For more details regarding the Look functionality, please consult R::look().
	 * @see Facade::look
	 * @see Look::look
	 *
	 * @return Look
	 */
	public static function getLook()
	{
		return new Look( self::$toolbox );
	}

	/**
	 * Takes an full SQL query with optional bindings, a series of keys, a template
	 * and optionally a filter function and glue and assembles a view from all this.
	 * This is the fastest way from SQL to view. Typically this function is used to
	 * generate pulldown (select tag) menus with options queried from the database.
	 *
	 * Usage:
	 *
	 * <code>
	 * $htmlPulldown = R::look(
	 *   'SELECT * FROM color WHERE value != ? ORDER BY value ASC',
	 *   [ 'g' ],
	 *   [ 'value', 'name' ],
	 *   '<option value="%s">%s</option>',
	 *   'strtoupper',
	 *   "\n"
	 * );
	 * </code>
	 *
	 * The example above creates an HTML fragment like this:
	 *
	 * <option value="B">BLUE</option>
	 * <option value="R">RED</option>
	 *
	 * to pick a color from a palette. The HTML fragment gets constructed by
	 * an SQL query that selects all colors that do not have value 'g' - this
	 * excludes green. Next, the bean properties 'value' and 'name' are mapped to the
	 * HTML template string, note that the order here is important. The mapping and
	 * the HTML template string follow vsprintf-rules. All property values are then
	 * passed through the specified filter function 'strtoupper' which in this case
	 * is a native PHP function to convert strings to uppercase characters only.
	 * Finally the resulting HTML fragment strings are glued together using a
	 * newline character specified in the last parameter for readability.
	 *
	 * In previous versions of RedBeanPHP you had to use:
	 * R::getLook()->look() instead of R::look(). However to improve useability of the
	 * library the look() function can now directly be invoked from the facade.
	 *
	 * @param string   $sql      query to execute
	 * @param array    $bindings parameters to bind to slots mentioned in query or an empty array
	 * @param array    $keys     names in result collection to map to template
	 * @param string   $template HTML template to fill with values associated with keys, use printf notation (i.e. %s)
	 * @param callable $filter   function to pass values through (for translation for instance)
	 * @param string   $glue     optional glue to use when joining resulting strings
	 *
	 * @return string
	 */
	public static function look( $sql, $bindings = array(), $keys = array( 'selected', 'id', 'name' ), $template = '<option %s value="%s">%s</option>', $filter = 'trim', $glue = '' )
	{
		return self::getLook()->look( $sql, $bindings, $keys, $template, $filter, $glue );
	}

	/**
	 * Calculates a diff between two beans (or arrays of beans).
	 * The result of this method is an array describing the differences of the second bean compared to
	 * the first, where the first bean is taken as reference. The array is keyed by type/property, id and property name, where
	 * type/property is either the type (in case of the root bean) or the property of the parent bean where the type resides.
	 * The diffs are mainly intended for logging, you cannot apply these diffs as patches to other beans.
	 * However this functionality might be added in the future.
	 *
	 * The keys of the array can be formatted using the $format parameter.
	 * A key will be composed of a path (1st), id (2nd) and property (3rd).
	 * Using printf-style notation you can determine the exact format of the key.
	 * The default format will look like:
	 *
	 * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
	 *
	 * If you only want a simple diff of one bean and you don't care about ids,
	 * you might pass a format like: '%1$s.%3$s' which gives:
	 *
	 * 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
	 *
	 * The filter parameter can be used to set filters, it should be an array
	 * of property names that have to be skipped. By default this array is filled with
	 * two strings: 'created' and 'modified'.
	 *
	 * @param OODBBean|OODBBean[] $bean    reference beans
	 * @param OODBBean|OODBBean[] $other   beans to compare
	 * @param array          $filters names of properties of all beans to skip
	 * @param string         $pattern the format of the key, defaults to '%s.%s.%s'
	 *
	 * @return array
	 */
	public static function diff( $bean, $other, $filters = array( 'created', 'modified' ), $pattern = '%s.%s.%s' )
	{
		$diff = new Diff( self::$toolbox );
		return $diff->diff( $bean, $other, $filters, $pattern );
	}

	/**
	 * The gentleman's way to register a RedBeanPHP ToolBox instance
	 * with the facade. Stores the toolbox in the static toolbox
	 * registry of the facade class. This allows for a neat and
	 * explicit way to register a toolbox.
	 *
	 * @param string  $key     key to store toolbox instance under
	 * @param ToolBox $toolbox toolbox to register
	 *
	 * @return void
	 */
	public static function addToolBoxWithKey( $key, ToolBox $toolbox )
	{
		self::$toolboxes[$key] = $toolbox;
	}

	/**
	 * The gentleman's way to remove a RedBeanPHP ToolBox instance
	 * from the facade. Removes the toolbox identified by
	 * the specified key in the static toolbox
	 * registry of the facade class. This allows for a neat and
	 * explicit way to remove a toolbox.
	 * Returns TRUE if the specified toolbox was found and removed.
	 * Returns FALSE otherwise.
	 *
	 * @param string  $key     identifier of the toolbox to remove
	 *
	 * @return boolean
	 */
	public static function removeToolBoxByKey( $key )
	{
		if ( !array_key_exists( $key, self::$toolboxes ) ) {
			return FALSE;
		}
		unset( self::$toolboxes[$key] );
		return TRUE;
	}

	/**
	 * Returns the toolbox associated with the specified key.
	 *
	 * @param string  $key     key to store toolbox instance under
	 *
	 * @return ToolBox|NULL
	 */
	public static function getToolBoxByKey( $key )
	{
		if ( !array_key_exists( $key, self::$toolboxes ) ) {
			return NULL;
		}
		return self::$toolboxes[$key];
	}

	/**
	 * Toggles JSON column features.
	 * Invoking this method with boolean TRUE causes 2 JSON features to be enabled.
	 * Beans will automatically JSONify any array that's not in a list property and
	 * the Query Writer (if capable) will attempt to create a JSON column for strings that
	 * appear to contain JSON.
	 *
	 * Feature #1:
	 * AQueryWriter::useJSONColumns
	 *
	 * Toggles support for automatic generation of JSON columns.
	 * Using JSON columns means that strings containing JSON will
	 * cause the column to be created (not modified) as a JSON column.
	 * However it might also trigger exceptions if this means the DB attempts to
	 * convert a non-json column to a JSON column.
	 *
	 * Feature #2:
	 * OODBBean::convertArraysToJSON
	 *
	 * Toggles array to JSON conversion. If set to TRUE any array
	 * set to a bean property that's not a list will be turned into
	 * a JSON string. Used together with AQueryWriter::useJSONColumns this
	 * extends the data type support for JSON columns.
	 *
	 * So invoking this method is the same as:
	 *
	 * <code>
	 * AQueryWriter::useJSONColumns( $flag );
	 * OODBBean::convertArraysToJSON( $flag );
	 * </code>
	 *
	 * Unlike the methods above, that return the previous state, this
	 * method does not return anything (void).
	 *
	 * @param boolean $flag feature flag (either TRUE or FALSE)
	 *
	 * @return void
	 */
	public static function useJSONFeatures( $flag )
	{
		AQueryWriter::useJSONColumns( $flag );
		OODBBean::convertArraysToJSON( $flag );
	}

	/**
	 * Given a bean and an optional SQL snippet,
	 * this method will return the bean together with all
	 * child beans in a hierarchically structured
	 * bean table.
	 *
	 * @note that not all database support this functionality. You'll need
	 * at least MariaDB 10.2.2 or Postgres. This method does not include
	 * a warning mechanism in case your database does not support this
	 * functionality.
	 *
	 * @param OODBBean    $bean     bean to find children of
	 * @param string|NULL $sql      optional SQL snippet
	 * @param array       $bindings SQL snippet parameter bindings
	 *
	 * @return OODBBean[]
	 */
	public static function children( OODBBean $bean, $sql = NULL, $bindings = array() )
	{
		return self::$tree->children( $bean, $sql, $bindings );
	}

	/**
	 * Given a bean and an optional SQL snippet,
	 * this method will count all child beans in a hierarchically structured
	 * bean table.
	 *
	 * @note that not all database support this functionality. You'll need
	 * at least MariaDB 10.2.2 or Postgres. This method does not include
	 * a warning mechanism in case your database does not support this
	 * functionality.
	 *
	 * @note:
	 * You are allowed to use named parameter bindings as well as
	 * numeric parameter bindings (using the question mark notation).
	 * However, you can not mix. Also, if using named parameter bindings,
	 * parameter binding key ':slot0' is reserved for the ID of the bean
	 * and used in the query.
	 *
	 * @note:
	 * By default, if no select is given or select=TRUE this method will subtract 1 of
	 * the total count to omit the starting bean. If you provide your own select,
	 * this method assumes you take control of the resulting total yourself since
	 * it cannot 'predict' what or how you are trying to 'count'.
	 *
	 * @param OODBBean       $bean     bean to find children of
	 * @param string|NULL    $sql      optional SQL snippet
	 * @param array          $bindings SQL snippet parameter bindings
	 * @param string|boolean $select   select snippet to use (advanced, optional, see QueryWriter::queryRecursiveCommonTableExpression)
	 *
	 * @return int
	 */
	public static function countChildren( OODBBean $bean, $sql = NULL, $bindings = array(), $select = QueryWriter::C_CTE_SELECT_COUNT )
	{
		return self::$tree->countChildren( $bean, $sql, $bindings, $select );
	}

	/**
	 * Given a bean and an optional SQL snippet,
	 * this method will count all parent beans in a hierarchically structured
	 * bean table.
	 *
	 * @note that not all database support this functionality. You'll need
	 * at least MariaDB 10.2.2 or Postgres. This method does not include
	 * a warning mechanism in case your database does not support this
	 * functionality.
	 *
	 * @note:
	 * You are allowed to use named parameter bindings as well as
	 * numeric parameter bindings (using the question mark notation).
	 * However, you can not mix. Also, if using named parameter bindings,
	 * parameter binding key ':slot0' is reserved for the ID of the bean
	 * and used in the query.
	 *
	 * @note:
	 * By default, if no select is given or select=TRUE this method will subtract 1 of
	 * the total count to omit the starting bean. If you provide your own select,
	 * this method assumes you take control of the resulting total yourself since
	 * it cannot 'predict' what or how you are trying to 'count'.
	 *
	 * @param OODBBean       $bean     bean to find children of
	 * @param string|NULL    $sql      optional SQL snippet
	 * @param array          $bindings SQL snippet parameter bindings
	 * @param string|boolean $select   select snippet to use (advanced, optional, see QueryWriter::queryRecursiveCommonTableExpression)
	 *
	 * @return int
	 */
	public static function countParents( OODBBean $bean, $sql = NULL, $bindings = array(), $select = QueryWriter::C_CTE_SELECT_COUNT )
	{
		return self::$tree->countParents( $bean, $sql, $bindings, $select );
	}

	/**
	 * Given a bean and an optional SQL snippet,
	 * this method will return the bean along with all parent beans
	 * in a hierarchically structured bean table.
	 *
	 * @note that not all database support this functionality. You'll need
	 * at least MariaDB 10.2.2 or Postgres. This method does not include
	 * a warning mechanism in case your database does not support this
	 * functionality.
	 *
	 * @param OODBBean    $bean     bean to find parents of
	 * @param string|NULL $sql      optional SQL snippet
	 * @param array       $bindings SQL snippet parameter bindings
	 *
	 * @return OODBBean[]
	 */
	public static function parents( OODBBean $bean, $sql = NULL, $bindings = array() )
	{
		return self::$tree->parents( $bean, $sql, $bindings );
	}

	/**
	 * Toggles support for nuke().
	 * Can be used to turn off the nuke() feature for security reasons.
	 * Returns the old flag value.
	 *
	 * @param boolean $flag TRUE or FALSE
	 *
	 * @return boolean
	 */
	public static function noNuke( $yesNo ) {
		return AQueryWriter::forbidNuke( $yesNo );
	}

	/**
	 * Globally available service method for RedBeanPHP.
	 * Converts a snake cased string to a camel cased string.
	 * If the parameter is an array, the keys will be converted.
	 *
	 * @param string|array $snake snake_cased string to convert to camelCase
	 * @param boolean $dolphin exception for Ids - (bookId -> bookID)
	 *                         too complicated for the human mind, only dolphins can understand this
	 *
	 * @return string|array
	 */
	public static function camelfy( $snake, $dolphin = false )
	{
		if ( is_array( $snake ) ) {
			$newArray = array();
			foreach( $snake as $key => $value ) {
				$newKey = self::camelfy( $key, $dolphin );
				if ( is_array( $value ) ) {
					$value = self::camelfy( $value, $dolphin );
				}
				$newArray[ $newKey ] = $value;
			}
			return $newArray;
		}
		return AQueryWriter::snakeCamel( $snake, $dolphin );
	}

	/**
	 * Globally available service method for RedBeanPHP.
	 * Converts a camel cased string to a snake cased string.
	 * If the parameter is an array, the keys will be converted.
	 *
	 * @param string|array $camel camelCased string to convert to snake case
	 *
	 * @return string|array
	 */
	public static function uncamelfy( $camel )
	{
		if ( is_array( $camel ) ) {
			$newArray = array();
			foreach( $camel as $key => $value ) {
				$newKey = self::uncamelfy( $key );
				if ( is_array( $value ) ) {
					$value = self::uncamelfy( $value );
				}
				$newArray[ $newKey ] = $value;
			}
			return $newArray;
		}
		return AQueryWriter::camelsSnake( $camel );
	}

	/**
	 * Selects the feature set you want as specified by
	 * the label.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::useFeatureSet( 'novice/latest' );
	 * </code>
	 *
	 * @param string $label label
	 *
	 * @return void
	 */
	public static function useFeatureSet( $label ) {
		return Feature::feature($label);
	}

	/**
	 * Dynamically extends the facade with a plugin.
	 * Using this method you can register your plugin with the facade and then
	 * use the plugin by invoking the name specified plugin name as a method on
	 * the facade.
	 *
	 * Usage:
	 *
	 * <code>
	 * R::ext( 'makeTea', function() { ... }  );
	 * </code>
	 *
	 * Now you can use your makeTea plugin like this:
	 *
	 * <code>
	 * R::makeTea();
	 * </code>
	 *
	 * @param string   $pluginName name of the method to call the plugin
	 * @param callable $callable   a PHP callable
	 *
	 * @return void
	 */
	public static function ext( $pluginName, $callable )
	{
		if ( !preg_match( '#^[a-zA-Z_][a-zA-Z0-9_]*$#', $pluginName ) ) {
			throw new RedException( 'Plugin name may only contain alphanumeric characters and underscores and cannot start with a number.' );
		}
		self::$plugins[$pluginName] = $callable;
	}

	/**
	 * Call static for use with dynamic plugins. This magic method will
	 * intercept static calls and route them to the specified plugin.
	 *
	 * @param string $pluginName name of the plugin
	 * @param array  $params     list of arguments to pass to plugin method
	 *
	 * @return mixed
	 */
	public static function __callStatic( $pluginName, $params )
	{
		if ( !isset( self::$plugins[$pluginName] ) ) {
			if ( !preg_match( '#^[a-zA-Z_][a-zA-Z0-9_]*$#', $pluginName ) ) {
				throw new RedException( 'Plugin name may only contain alphanumeric characters and underscores and cannot start with a number.' );
			}
			throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' );
		}
		return call_user_func_array( self::$plugins[$pluginName], $params );
	}
}

