├── .gitignore ├── .travis.yml ├── README.markdown ├── RedBeanPHP ├── Adapter.php ├── Adapter │ └── DBAdapter.php ├── AssociationManager.php ├── BeanCollection.php ├── BeanHelper.php ├── BeanHelper │ ├── DynamicBeanHelper.php │ └── SimpleFacadeBeanHelper.php ├── Cursor.php ├── Cursor │ ├── NullCursor.php │ └── PDOCursor.php ├── Driver.php ├── Driver │ └── RPDO.php ├── DuplicationManager.php ├── Facade.php ├── Finder.php ├── Functions.php ├── LabelMaker.php ├── Logger.php ├── Logger │ ├── RDefault.php │ └── RDefault │ │ └── Debug.php ├── OODB.php ├── OODBBean.php ├── Observable.php ├── Observer.php ├── Plugin.php ├── Plugin │ ├── Pool.php │ ├── SQN.php │ ├── TinyQueryBuilder.php │ └── put_your_plugins_here.txt ├── QueryWriter.php ├── QueryWriter │ ├── AQueryWriter.php │ ├── CUBRID.php │ ├── Firebird.php │ ├── MySQL.php │ ├── PostgreSQL.php │ └── SQLiteT.php ├── R.php ├── RedException.php ├── RedException │ └── SQL.php ├── Repository.php ├── Repository │ ├── Fluid.php │ └── Frozen.php ├── SimpleModel.php ├── SimpleModelHelper.php ├── SimpleModelInterface.php ├── TagManager.php ├── ToolBox.php ├── TypedModel.php ├── Util │ ├── ArrayTool.php │ ├── Diff.php │ ├── DispenseHelper.php │ ├── Dump.php │ ├── Either.php │ ├── Feature.php │ ├── Look.php │ ├── MatchUp.php │ ├── MultiLoader.php │ ├── QuickExport.php │ ├── Transaction.php │ └── Tree.php ├── license.txt └── loader.php ├── composer.json ├── p533patch.php ├── replica2-win.php ├── replica2.php ├── run_all_tests.sh ├── run_single_test.sh ├── test-dist.ini └── testing ├── RedUNIT.php ├── RedUNIT ├── Base.php ├── Base │ ├── Aliasing.php │ ├── Arrays.php │ ├── Association.php │ ├── Batch.php │ ├── Bean.php │ ├── Boxing.php │ ├── Chill.php │ ├── Close.php │ ├── Concurrency.php │ ├── Copy.php │ ├── Count.php │ ├── Cross.php │ ├── Cursors.php │ ├── Database.php │ ├── Dispense.php │ ├── Dup.php │ ├── Either.php │ ├── Exceptions.php │ ├── Facade.php │ ├── Finding.php │ ├── Foreignkeys.php │ ├── Frozen.php │ ├── Fuse.php │ ├── Hybrid.php │ ├── Indexes.php │ ├── Issue259.php │ ├── Issue303.php │ ├── Issue408.php │ ├── Issue841.php │ ├── Issue90.php │ ├── Joins.php │ ├── Keywords.php │ ├── Largenum.php │ ├── Logging.php │ ├── Misc.php │ ├── Namedparams.php │ ├── Nuke.php │ ├── Observers.php │ ├── Partial.php │ ├── Performance.php │ ├── Prefixes.php │ ├── Productivity.php │ ├── PullRequest530.php │ ├── Quickexport.php │ ├── Relations.php │ ├── Tags.php │ ├── Threeway.php │ ├── Trash.php │ ├── Traverse.php │ ├── Typechecking.php │ ├── Update.php │ ├── Utf8.php │ ├── Via.php │ ├── With.php │ ├── Writecache.php │ └── Xnull.php ├── Blackhole.php ├── Blackhole │ ├── Debug.php │ ├── Export.php │ ├── Fusebox.php │ ├── Glue.php │ ├── Import.php │ ├── Labels.php │ ├── Meta.php │ ├── Misc.php │ ├── Plugins.php │ ├── Stub.php │ ├── Tainted.php │ ├── Toolbox.php │ └── Version.php ├── CUBRID.php ├── CUBRID │ ├── Setget.php │ └── Writer.php ├── Mysql.php ├── Mysql │ ├── Bigint.php │ ├── Double.php │ ├── Foreignkeys.php │ ├── Freeze.php │ ├── Issue411.php │ ├── Parambind.php │ ├── Preexist.php │ ├── Setget.php │ ├── Uuid.php │ └── Writer.php ├── Postgres.php ├── Postgres │ ├── Bigint.php │ ├── Foreignkeys.php │ ├── Parambind.php │ ├── Partial.php │ ├── Setget.php │ ├── Trees.php │ ├── Uuid.php │ └── Writer.php ├── Pretest.php ├── Sqlite.php └── Sqlite │ ├── Foreignkeys.php │ ├── Parambind.php │ ├── Rebuild.php │ ├── Setget.php │ └── Writer.php ├── cli ├── plugins │ └── myhooks.php ├── runperf.php ├── runtests.php ├── test_hook_example.php └── testcontainer │ └── put-rb-file-here.txt ├── config ├── test-dist.ini └── test-travis.ini ├── helpers ├── classes.php └── functions.php └── notes.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | *.pyc 10 | 11 | # Logs and databases # 12 | ###################### 13 | *.log 14 | 15 | # OS generated files # 16 | ###################### 17 | .DS_Store* 18 | ehthumbs.db 19 | Icon? 20 | Thumbs.db 21 | /.project 22 | /.settings/org.eclipse.php.core.prefs 23 | /.settings/org.eclipse.php.debug.core.Debug_Process_Preferences.prefs 24 | /rb.phar 25 | /rb.php 26 | /rb-mysql.php 27 | /rb-postgres.php 28 | /rb-sqlite.php 29 | build/ 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | services: 4 | - mysql 5 | - postgresql 6 | 7 | matrix: 8 | include: 9 | - php: 5.3 10 | dist: precise 11 | group: legacy 12 | - php: 5.4 13 | dist: trusty 14 | - php: 5.5 15 | dist: trusty 16 | - php: 5.6 17 | dist: trusty 18 | - php: 7.0 19 | dist: trusty 20 | - php: 7.1 21 | dist: trusty 22 | - php: 7.2 23 | dist: trusty 24 | - php: 7.3 25 | dist: trusty 26 | - php: 7.4 27 | dist: trusty 28 | - php: 8.0 29 | dist: xenial 30 | - php: 8.1.0 31 | dist: bionic 32 | - php: 8.2.0 33 | dist: focal 34 | - php: 8.3.0 35 | dist: bionic 36 | - php: 8.4.0 37 | dist: noble 38 | allow_failures: true 39 | 40 | env: 41 | global: 42 | - PGUSER=postgres 43 | - PGPORT=5432 44 | - PGHOST=localhost 45 | - XDEBUG_MODE=coverage 46 | 47 | before_install: 48 | - if [[ "$TRAVIS_DIST" == "focal" ]]; then sudo apt-get install libonig5 ;fi 49 | - if [[ "$TRAVIS_DIST" == "focal" ]]; then sudo service postgresql restart ;fi 50 | 51 | before_script: 52 | - touch /tmp/oodb.db 53 | - mysql -e 'create database oodb;' 54 | - psql template1 -c 'CREATE EXTENSION "uuid-ossp";' -U postgres 55 | - psql -c 'create database oodb;' -U postgres 56 | - php replica2.php onlyphp 57 | - cp rb.php testing/cli/testcontainer/rb.php 58 | - cd testing/cli 59 | 60 | 61 | script: php runtests.php 62 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | RedBeanPHP 5 2 | ============ 3 | 4 | [![Build Status](https://travis-ci.org/gabordemooij/redbean.svg?branch=master)](https://travis-ci.org/gabordemooij/redbean) 5 | 6 | RedBeanPHP is an easy to use ORM tool for PHP. 7 | 8 | * Automatically creates tables and columns as you go 9 | * No configuration, just fire and forget 10 | * No complicated package tools, no autoloaders, just ONE file 11 | 12 | Installation (recommended) 13 | --------------------------- 14 | 15 | Download RedBeanPHP from the website: 16 | 17 | https://redbeanphp.com/download 18 | 19 | Extract the archive and put it in your PHP project, voila! 20 | 21 | Optional: sha256sum and check signature. 22 | 23 | 24 | Installation via Composer (not recommended) 25 | ----------------------------------------- 26 | 27 | Just open your composer.json file and add the package name ```(e.g. "gabordemooij/redbean": "dev-master")``` in your require list. 28 | 29 | ```json 30 | { 31 | "require": { 32 | "gabordemooij/redbean": "dev-master" 33 | } 34 | } 35 | ``` 36 | 37 | **NOTE**: 38 | You will find many examples on the RedBean website make use of RedBean's `R` class. Because of namespaced autoloading in Composer, this class will be available as `\RedbeanPHP\R` instead of `R`. If you desire to use the much shorter `R` alias, you can add a `use` statement at the beginning of your code: 39 | 40 | ```php 41 | use \RedBeanPHP\R as R; 42 | ``` 43 | **NOTE:** 44 | It is important to note that when using RedBeanPHP with Composer, there are some extra precautions needed when working with [Models](https://redbeanphp.com/index.php?p=/models). Due to the namespace requirements of Composer, when creating Models we need to use the `SimpleModel` to extend, not `RedBean_SimpleModel`. Furthermore, we need to specify the namespace of the `SimpleModel`, so a full example of using a Model with RedBean with Composer is as follows: 45 | 46 | ```php 47 | use \RedBeanPHP\R; 48 | 49 | class User extends \RedBeanPHP\SimpleModel 50 | { 51 | ... 52 | } 53 | ``` 54 | Notice that we also need to add the `use \RedBeanPHP\R` statement so that we can use the `R::` shortcut within the Model. 55 | 56 | 57 | Quick Example 58 | ------------- 59 | 60 | How we store a book object with RedBeanPHP: 61 | ```php 62 | $book = R::dispense("book"); 63 | $book->author = "Santa Claus"; 64 | $book->title = "Secrets of Christmas"; 65 | $id = R::store( $book ); 66 | ``` 67 | 68 | Yep, it's that simple. 69 | 70 | 71 | More information 72 | ---------------- 73 | 74 | For more information about RedBeanPHP please consult 75 | the RedBeanPHP website: 76 | 77 | https://www.redbeanphp.com/ 78 | -------------------------------------------------------------------------------- /RedBeanPHP/BeanCollection.php: -------------------------------------------------------------------------------- 1 | type = $type; 66 | $this->cursor = $cursor; 67 | $this->repository = $repository; 68 | $this->mask = $mask; 69 | } 70 | 71 | /** 72 | * Returns the next bean in the collection. 73 | * If called the first time, this will return the first bean in the collection. 74 | * If there are no more beans left in the collection, this method 75 | * will return NULL. 76 | * 77 | * @return OODBBean|NULL 78 | */ 79 | public function next() 80 | { 81 | $row = $this->cursor->getNextItem(); 82 | if ( $row ) { 83 | $beans = $this->repository->convertToBeans( $this->type, array( $row ), $this->mask ); 84 | return reset( $beans ); 85 | } 86 | return NULL; 87 | } 88 | 89 | /** 90 | * Resets the collection from the start, like a fresh() on a bean. 91 | * 92 | * @return void 93 | */ 94 | public function reset() 95 | { 96 | $this->cursor->reset(); 97 | } 98 | 99 | /** 100 | * Closes the underlying cursor (needed for some databases). 101 | * 102 | * @return void 103 | */ 104 | public function close() 105 | { 106 | $this->cursor->close(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /RedBeanPHP/BeanHelper.php: -------------------------------------------------------------------------------- 1 | 41 | * R::addDatabase( ..., new DynamicBeanHelper('Prefix1_') ); 42 | * 43 | * 44 | * @param string $modelPrefix prefix 45 | */ 46 | public function __construct( $modelPrefix ) { 47 | $this->modelPrefix = $modelPrefix; 48 | } 49 | 50 | /** 51 | * @see BeanHelper::getModelForBean 52 | */ 53 | public function getModelForBean( OODBBean $bean ) 54 | { 55 | return $this->resolveModel( $this->modelPrefix, $bean->getMeta( 'type' ), $bean ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /RedBeanPHP/BeanHelper/SimpleFacadeBeanHelper.php: -------------------------------------------------------------------------------- 1 | getMeta( 'type' ); 78 | $prefix = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_'; 79 | 80 | return $this->resolveModel($prefix, $model, $bean); 81 | } 82 | 83 | /** 84 | * Resolves the model associated with the bean using the model name (type), 85 | * the prefix and the bean. 86 | * 87 | * @note 88 | * If REDBEAN_CLASS_AUTOLOAD is defined this will be passed to class_exist as 89 | * autoloading flag. 90 | * 91 | * @param string $prefix Prefix to use for resolution 92 | * @param string $model Type name 93 | * @param OODBBean $bean Bean to resolve model for 94 | * 95 | * @return SimpleModel|SimpleModelInterface|NULL 96 | */ 97 | protected function resolveModel($prefix, $model, $bean) { 98 | 99 | /* Determine autoloading preference */ 100 | $autoloadFlag = ( defined( 'REDBEAN_CLASS_AUTOLOAD' ) ? REDBEAN_CLASS_AUTOLOAD : TRUE ); 101 | 102 | if ( strpos( $model, '_' ) !== FALSE ) { 103 | $modelParts = explode( '_', $model ); 104 | $modelName = ''; 105 | foreach( $modelParts as $part ) { 106 | $modelName .= ucfirst( $part ); 107 | } 108 | $modelName = $prefix . $modelName; 109 | if ( !class_exists( $modelName ) ) { 110 | $modelName = $prefix . ucfirst( $model ); 111 | if ( !class_exists( $modelName, $autoloadFlag ) ) { 112 | return NULL; 113 | } 114 | } 115 | } else { 116 | $modelName = $prefix . ucfirst( $model ); 117 | if ( !class_exists( $modelName, $autoloadFlag ) ) { 118 | return NULL; 119 | } 120 | } 121 | $obj = self::factory( $modelName ); 122 | $obj->loadBean( $bean ); 123 | return $obj; 124 | } 125 | 126 | /** 127 | * @see BeanHelper::getExtractedToolbox 128 | */ 129 | public function getExtractedToolbox() 130 | { 131 | return Facade::getExtractedToolbox(); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /RedBeanPHP/Cursor.php: -------------------------------------------------------------------------------- 1 | res = $res; 47 | $this->fetchStyle = $fetchStyle; 48 | } 49 | 50 | /** 51 | * @see Cursor::getNextItem 52 | */ 53 | public function getNextItem() 54 | { 55 | return $this->res->fetch(); 56 | } 57 | 58 | /** 59 | * @see Cursor::reset 60 | */ 61 | public function reset() 62 | { 63 | $this->close(); 64 | $this->res->execute(); 65 | } 66 | 67 | /** 68 | * @see Cursor::close 69 | */ 70 | public function close() 71 | { 72 | $this->res->closeCursor(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /RedBeanPHP/Functions.php: -------------------------------------------------------------------------------- 1 | 23 | * R::find( 'paint', ' color_id = ? ', [ EID('color:yellow') ] ); 24 | * 25 | * 26 | * If a function called EID() already exists you'll have to write this 27 | * wrapper yourself ;) 28 | * 29 | * @param string $enumName enum code as you would pass to R::enum() 30 | * 31 | * @return mixed 32 | */ 33 | if (!function_exists('EID')) { 34 | 35 | function EID($enumName) 36 | { 37 | return \RedBeanPHP\Facade::enum( $enumName )->id; 38 | } 39 | 40 | } 41 | 42 | /** 43 | * Prints the result of R::dump() to the screen using 44 | * print_r. 45 | * 46 | * @param mixed $data data to dump 47 | * 48 | * @return void 49 | */ 50 | if ( !function_exists( 'dmp' ) ) { 51 | 52 | function dmp( $list ) 53 | { 54 | print_r( \RedBeanPHP\Facade::dump( $list ) ); 55 | } 56 | } 57 | 58 | /** 59 | * Function alias for R::genSlots(). 60 | */ 61 | if ( !function_exists( 'genslots' ) ) { 62 | 63 | function genslots( $slots, $tpl = NULL ) 64 | { 65 | return \RedBeanPHP\Facade::genSlots( $slots, $tpl ); 66 | } 67 | } 68 | 69 | /** 70 | * Function alias for R::flat(). 71 | */ 72 | if ( !function_exists( 'array_flatten' ) ) { 73 | 74 | function array_flatten( $array ) 75 | { 76 | return \RedBeanPHP\Facade::flat( $array ); 77 | } 78 | } 79 | 80 | /** 81 | * Function pstr() generates [ $value, \PDO::PARAM_STR ] 82 | * Ensures that your parameter is being treated as a string. 83 | * 84 | * Usage: 85 | * 86 | * 87 | * R::find('book', 'title = ?', [ pstr('1') ]); 88 | * 89 | */ 90 | if ( !function_exists( 'pstr' ) ) { 91 | 92 | function pstr( $value ) 93 | { 94 | return array( strval( $value ) , \PDO::PARAM_STR ); 95 | } 96 | } 97 | 98 | 99 | /** 100 | * Function pint() generates [ $value, \PDO::PARAM_INT ] 101 | * Ensures that your parameter is being treated as an integer. 102 | * 103 | * Usage: 104 | * 105 | * 106 | * R::find('book', ' pages > ? ', [ pint(2) ] ); 107 | * 108 | */ 109 | if ( !function_exists( 'pint' ) ) { 110 | 111 | function pint( $value ) 112 | { 113 | return array( intval( $value ) , \PDO::PARAM_INT ); 114 | } 115 | } 116 | 117 | /** 118 | * Function DBPrefix() is a simple function to allow you to 119 | * quickly set a different namespace for FUSE model resolution 120 | * per database connection. It works by creating a new DynamicBeanHelper 121 | * with the specified string as model prefix. 122 | * 123 | * Usage: 124 | * 125 | * 126 | * R::addDatabase( ..., DBPrefix( 'Prefix1_' ) ); 127 | * 128 | */ 129 | if ( !function_exists( 'DBPrefix' ) ) { 130 | 131 | function DBPrefix( $prefix = '\\Model' ) { 132 | return new \RedBeanPHP\BeanHelper\DynamicBeanHelper( $prefix ); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /RedBeanPHP/Logger.php: -------------------------------------------------------------------------------- 1 | mode === self::C_LOGGER_ECHO ) { 56 | echo $log; 57 | } else { 58 | $this->logs[] = $log; 59 | } 60 | } else { 61 | if ( $this->mode === self::C_LOGGER_ECHO ) { 62 | echo $argument; 63 | } else { 64 | $this->logs[] = $argument; 65 | } 66 | } 67 | 68 | if ( $this->mode === self::C_LOGGER_ECHO ) echo "
" . PHP_EOL; 69 | } 70 | } 71 | 72 | /** 73 | * Returns the internal log array. 74 | * The internal log array is where all log messages are stored. 75 | * 76 | * @return array 77 | */ 78 | public function getLogs() 79 | { 80 | return $this->logs; 81 | } 82 | 83 | /** 84 | * Clears the internal log array, removing all 85 | * previously stored entries. 86 | * 87 | * @return self 88 | */ 89 | public function clear() 90 | { 91 | $this->logs = array(); 92 | return $this; 93 | } 94 | 95 | /** 96 | * Selects a logging mode. 97 | * There are several options available. 98 | * 99 | * * C_LOGGER_ARRAY - log silently, stores entries in internal log array only 100 | * * C_LOGGER_ECHO - also forward log messages directly to STDOUT 101 | * 102 | * @param integer $mode mode of operation for logging object 103 | * 104 | * @return self 105 | */ 106 | public function setMode( $mode ) 107 | { 108 | if ($mode !== self::C_LOGGER_ARRAY && $mode !== self::C_LOGGER_ECHO ) { 109 | throw new RedException( 'Invalid mode selected for logger, use C_LOGGER_ARRAY or C_LOGGER_ECHO.' ); 110 | } 111 | $this->mode = $mode; 112 | return $this; 113 | } 114 | 115 | /** 116 | * Searches for all log entries in internal log array 117 | * for $needle and returns those entries. 118 | * This method will return an array containing all matches for your 119 | * search query. 120 | * 121 | * @param string $needle phrase to look for in internal log array 122 | * 123 | * @return array 124 | */ 125 | public function grep( $needle ) 126 | { 127 | $found = array(); 128 | foreach( $this->logs as $logEntry ) { 129 | if ( strpos( $logEntry, $needle ) !== FALSE ) $found[] = $logEntry; 130 | } 131 | return $found; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /RedBeanPHP/Observable.php: -------------------------------------------------------------------------------- 1 | observers[$eventname] ) ) { 42 | $this->observers[$eventname] = array(); 43 | } 44 | 45 | if ( in_array( $observer, $this->observers[$eventname] ) ) { 46 | return; 47 | } 48 | 49 | $this->observers[$eventname][] = $observer; 50 | } 51 | 52 | /** 53 | * Notifies listeners. 54 | * Sends the signal $eventname, the event identifier and a message object 55 | * to all observers that have been registered to receive notification for 56 | * this event. Part of the observer pattern implementation in RedBeanPHP. 57 | * 58 | * @param string $eventname event you want signal 59 | * @param mixed $info message object to send along 60 | * 61 | * @return void 62 | */ 63 | public function signal( $eventname, $info ) 64 | { 65 | if ( !isset( $this->observers[$eventname] ) ) { 66 | $this->observers[$eventname] = array(); 67 | } 68 | 69 | foreach ( $this->observers[$eventname] as $observer ) { 70 | $observer->onEvent( $eventname, $info ); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /RedBeanPHP/Observer.php: -------------------------------------------------------------------------------- 1 | 16 | * R::sqn('shop 19 | * 20 | * SQN assumes id fields follow the following conventions: 21 | * 22 | * Primary key: id 23 | * Foreign key: {table}_id 24 | * No table prefix. 25 | * 26 | * SQN can also generate additional aliases: 27 | * 28 | * 29 | * R::sqn( ..., 'area/x,y;place/x,y' ) - area_x area_y place_x place_y 30 | * 31 | * 32 | * @author Gabor de Mooij 33 | * @license BSD/GPLv2 34 | * 35 | * @copyright 36 | * copyright (c) G.J.G.T. (Gabor) de Mooij 37 | * This source file is subject to the BSD License that is bundled 38 | * with this source code in the file license.txt. 39 | */ 40 | R::ext('sqn', function( $query, $aliases = null, $q = '`' ) { 41 | $map = [ 42 | '|' => 'INNER JOIN', 43 | '||' => 'INNER JOIN', 44 | '>' => 'RIGHT JOIN', 45 | '>>' => 'RIGHT JOIN', 46 | '<' => 'LEFT JOIN', 47 | '<<' => 'LEFT JOIN', 48 | ]; 49 | $select = []; 50 | $from = ''; 51 | $joins = []; 52 | $prev = ''; 53 | $ents = preg_split( '/[^\w_]+/', $query ); 54 | $tokens = preg_split( '/[\w_]+/', $query ); 55 | array_pop($tokens); 56 | foreach( $ents as $i => $ent ) { 57 | $select[] = " {$q}{$ent}{$q}.* "; 58 | if (!$i) { 59 | $from = $ent; 60 | $prev = $ent; 61 | continue; 62 | } 63 | if ( $tokens[$i] == '<' || $tokens[$i] == '>' || $tokens[$i] == '|') { 64 | $join[] = " {$map[$tokens[$i]]} {$q}{$ent}{$q} ON {$q}{$ent}{$q}.{$prev}_id = {$q}{$prev}{$q}.id "; 65 | } 66 | if ( $tokens[$i] == '<<' || $tokens[$i] == '>>' || $tokens[$i] == '||') { 67 | $combi = [$prev, $ent]; 68 | sort( $combi ); 69 | $combi = implode( '_', $combi ); 70 | $select[] = " {$q}{$combi}{$q}.* "; 71 | $join[] = " {$map[$tokens[$i]]} {$q}{$combi}{$q} ON {$q}{$combi}{$q}.{$prev}_id = {$q}{$prev}{$q}.id "; 72 | $join[] = " {$map[$tokens[$i]]} {$q}{$ent}{$q} ON {$q}{$combi}{$q}.{$ent}_id = {$q}{$ent}{$q}.id "; 73 | } 74 | $prev = $ent; 75 | } 76 | if (!is_null($aliases)) { 77 | $aliases = explode(';', $aliases); 78 | foreach($aliases as $alias) { 79 | list($table, $cols) = explode('/', $alias); 80 | $cols = explode(',', $cols); 81 | foreach($cols as $col) { 82 | $select[] = " {$q}{$table}{$q}.{$q}{$col}{$q} AS {$q}{$table}_{$col}{$q} "; 83 | } 84 | } 85 | } 86 | $selectSQL = implode( ",\n", $select ); 87 | $joinSQL = implode( "\n", $join ); 88 | return "SELECT{$selectSQL}\n FROM {$q}{$from}{$q}\n{$joinSQL}"; 89 | }); 90 | 91 | -------------------------------------------------------------------------------- /RedBeanPHP/Plugin/TinyQueryBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * $sql = build_query([ 8 | * [ 'SELECT * FROM book'], 9 | * [$title ,'WHERE','title = ?'], 10 | * [$price ,'AND','price < ?'], 11 | * [$order ,'ORDER BY ? ASC'], 12 | * [$limit ,'LIMIT ?'] 13 | * ]); 14 | * 15 | * 16 | * Now, if we have a $title and a $price the query will be: 17 | * 'SELECT * FROM book WHERE title = ? AND price < ? ' 18 | * If we only have a $price and a $limit: 19 | * 'SELECT * FROM book WHERE price < ? LIMIT ?' 20 | * The Query Builder works very easy, it simply loops through the array, 21 | * each element is another array inside this main array, 22 | * let's call this inner array a 'piece'. 23 | * A piece can have one, two or three elements. 24 | * If it has one element, the element is simply concatenated to the final query. 25 | * If a piece has two elements, the second element will be 26 | * concatenated only if the first evaluates to TRUE. 27 | * Finally a piece having three elements works the same as a piece with two elements, 28 | * except that it will use the glue provided in the second element 29 | * to concat the value of the third element. The glue acts as a little tube of glue. 30 | * If there is still glue left in the tube (WHERE) it will preserve this 31 | * until it can be applied (so the first AND will be ignored in case of a WHERE condition). 32 | */ 33 | R::ext('buildQuery', function($pieces) { 34 | $sql = ''; 35 | $glue = NULL; 36 | foreach( $pieces as $piece ) { 37 | $n = count( $piece ); 38 | switch( $n ) { 39 | case 1: 40 | $sql .= " {$piece[0]} "; 41 | break; 42 | case 2: 43 | $glue = NULL; 44 | if (!is_null($piece[0])) $sql .= " {$piece[1]} "; 45 | break; 46 | case 3: 47 | $glue = ( is_null( $glue ) ) ? $piece[1] : $glue; 48 | if (!is_null($piece[0])) { 49 | $sql .= " {$glue} {$piece[2]} "; 50 | $glue = NULL; 51 | } 52 | break; 53 | } 54 | } 55 | return $sql; 56 | }); 57 | -------------------------------------------------------------------------------- /RedBeanPHP/Plugin/put_your_plugins_here.txt: -------------------------------------------------------------------------------- 1 | This folder has been reserved for your plugins 2 | 3 | -------------------------------------------------------------------------------- /RedBeanPHP/R.php: -------------------------------------------------------------------------------- 1 | driverDetails; 38 | } 39 | 40 | /** 41 | * @param array $driverDetails 42 | * 43 | * @return void 44 | */ 45 | public function setDriverDetails($driverDetails) 46 | { 47 | $this->driverDetails = $driverDetails; 48 | } 49 | 50 | /** 51 | * Returns an ANSI-92 compliant SQL state. 52 | * 53 | * @return string 54 | */ 55 | public function getSQLState() 56 | { 57 | return $this->sqlState; 58 | } 59 | 60 | /** 61 | * Returns the raw SQL STATE, possibly compliant with 62 | * ANSI SQL error codes - but this depends on database driver. 63 | * 64 | * @param string $sqlState SQL state error code 65 | * 66 | * @return void 67 | */ 68 | public function setSQLState( $sqlState ) 69 | { 70 | $this->sqlState = $sqlState; 71 | } 72 | 73 | /** 74 | * To String prints both code and SQL state. 75 | * 76 | * @return string 77 | */ 78 | public function __toString() 79 | { 80 | return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n". 81 | 'trace: ' . $this->getTraceAsString(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /RedBeanPHP/SimpleModel.php: -------------------------------------------------------------------------------- 1 | bean = $bean; 42 | } 43 | 44 | /** 45 | * Magic Getter to make the bean properties available from 46 | * the $this-scope. 47 | * 48 | * @note this method returns a value, not a reference! 49 | * To obtain a reference unbox the bean first! 50 | * 51 | * @param string $prop property to get 52 | * 53 | * @return mixed 54 | */ 55 | public function __get( $prop ) 56 | { 57 | return $this->bean->$prop; 58 | } 59 | 60 | /** 61 | * Magic Setter. 62 | * Sets the value directly as a bean property. 63 | * 64 | * @param string $prop property to set value of 65 | * @param mixed $value value to set 66 | * 67 | * @return void 68 | */ 69 | public function __set( $prop, $value ) 70 | { 71 | $this->bean->$prop = $value; 72 | } 73 | 74 | /** 75 | * Isset implementation. 76 | * Implements the isset function for array-like access. 77 | * 78 | * @param string $key key to check 79 | * 80 | * @return boolean 81 | */ 82 | public function __isset( $key ) 83 | { 84 | return isset( $this->bean->$key ); 85 | } 86 | 87 | /** 88 | * Box the bean using the current model. 89 | * This method wraps the current bean in this model. 90 | * This method can be reached using FUSE through a simple 91 | * OODBBean. The method returns a RedBeanPHP Simple Model. 92 | * This is useful if you would like to rely on PHP type hinting. 93 | * You can box your beans before passing them to functions or methods 94 | * with typed parameters. 95 | * 96 | * Note about beans vs models: 97 | * Use unbox to obtain the bean powering the model. If you want to use bean functionality, 98 | * you should -always- unbox first. While some functionality (like magic get/set) is 99 | * available in the model, this is just read-only. To use a model as a typical RedBean 100 | * OODBBean you should always unbox the model to a bean. Models are meant to 101 | * expose only domain logic added by the developer (business logic, no ORM logic). 102 | * 103 | * @return SimpleModel|SimpleModelInterface 104 | */ 105 | public function box() 106 | { 107 | return $this; 108 | } 109 | 110 | /** 111 | * Unbox the bean from the model. 112 | * This method returns the bean inside the model. 113 | * 114 | * Note about beans vs models: 115 | * Use unbox to obtain the bean powering the model. If you want to use bean functionality, 116 | * you should -always- unbox first. While some functionality (like magic get/set) is 117 | * available in the model, this is just read-only. To use a model as a typical RedBean 118 | * OODBBean you should always unbox the model to a bean. Models are meant to 119 | * expose only domain logic added by the developer (business logic, no ORM logic). 120 | * 121 | * @return OODBBean 122 | */ 123 | public function unbox() 124 | { 125 | return $this->bean; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /RedBeanPHP/SimpleModelHelper.php: -------------------------------------------------------------------------------- 1 | $eventName(); 40 | } 41 | 42 | /** 43 | * Attaches the FUSE event listeners. Now the Model Helper will listen for 44 | * CRUD events. If a CRUD event occurs it will send a signal to the model 45 | * that belongs to the CRUD bean and this model will take over control from 46 | * there. This method will attach the following event listeners to the observable: 47 | * 48 | * - 'update' (gets called by R::store, before the records gets inserted / updated) 49 | * - 'after_update' (gets called by R::store, after the records have been inserted / updated) 50 | * - 'open' (gets called by R::load, after the record has been retrieved) 51 | * - 'delete' (gets called by R::trash, before deletion of record) 52 | * - 'after_delete' (gets called by R::trash, after deletion) 53 | * - 'dispense' (gets called by R::dispense) 54 | * 55 | * For every event type, this method will register this helper as a listener. 56 | * The observable will notify the listener (this object) with the event ID and the 57 | * affected bean. This helper will then process the event (onEvent) by invoking 58 | * the event on the bean. If a bean offers a method with the same name as the 59 | * event ID, this method will be invoked. 60 | * 61 | * @param Observable $observable object to observe 62 | * 63 | * @return void 64 | */ 65 | public function attachEventListeners( Observable $observable ) 66 | { 67 | foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $eventID ) { 68 | $observable->addEventListener( $eventID, $this ); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /RedBeanPHP/SimpleModelInterface.php: -------------------------------------------------------------------------------- 1 | box(); 35 | } 36 | } -------------------------------------------------------------------------------- /RedBeanPHP/Util/ArrayTool.php: -------------------------------------------------------------------------------- 1 | 39 | * R::genSlots( array( 'a', 'b' ) ); 40 | * 41 | * 42 | * The statement in the example will produce the string: 43 | * '?,?'. 44 | * 45 | * Another example, using a template string: 46 | * 47 | * 48 | * R::genSlots( array('a', 'b'), ' IN( %s ) ' ); 49 | * 50 | * 51 | * The statement in the example will produce the string: 52 | * ' IN( ?,? ) '. 53 | * 54 | * @param array $array array to generate question mark slots for 55 | * @param string|NULL $template template to use 56 | * 57 | * @return string 58 | */ 59 | public static function genSlots( $array, $template = NULL ) 60 | { 61 | $str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : ''; 62 | return ( is_null( $template ) || $str === '' ) ? $str : sprintf( $template, $str ); 63 | } 64 | 65 | /** 66 | * Flattens a multi dimensional bindings array for use with genSlots(). 67 | * 68 | * Usage: 69 | * 70 | * 71 | * R::flat( array( 'a', array( 'b' ), 'c' ) ); 72 | * 73 | * 74 | * produces an array like: [ 'a', 'b', 'c' ] 75 | * 76 | * @param array $array array to flatten 77 | * @param array $result result array parameter (for recursion) 78 | * 79 | * @return array 80 | */ 81 | public static function flat( $array, $result = array() ) 82 | { 83 | foreach( $array as $value ) { 84 | if ( is_array( $value ) ) $result = self::flat( $value, $result ); 85 | else $result[] = $value; 86 | } 87 | return $result; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /RedBeanPHP/Util/Diff.php: -------------------------------------------------------------------------------- 1 | toolbox = $toolbox; 41 | } 42 | 43 | /** 44 | * Calculates a diff between two beans (or arrays of beans). 45 | * The result of this method is an array describing the differences of the second bean compared to 46 | * the first, where the first bean is taken as reference. The array is keyed by type/property, id and property name, where 47 | * type/property is either the type (in case of the root bean) or the property of the parent bean where the type resides. 48 | * The diffs are mainly intended for logging, you cannot apply these diffs as patches to other beans. 49 | * However this functionality might be added in the future. 50 | * 51 | * The keys of the array can be formatted using the $format parameter. 52 | * A key will be composed of a path (1st), id (2nd) and property (3rd). 53 | * Using printf-style notation you can determine the exact format of the key. 54 | * The default format will look like: 55 | * 56 | * 'book.1.title' => array( , ) 57 | * 58 | * If you only want a simple diff of one bean and you don't care about ids, 59 | * you might pass a format like: '%1$s.%3$s' which gives: 60 | * 61 | * 'book.1.title' => array( , ) 62 | * 63 | * The filter parameter can be used to set filters, it should be an array 64 | * of property names that have to be skipped. By default this array is filled with 65 | * two strings: 'created' and 'modified'. 66 | * 67 | * @param OODBBean|array $beans reference beans 68 | * @param OODBBean|array $others beans to compare 69 | * @param array $filters names of properties of all beans to skip 70 | * @param string $format the format of the key, defaults to '%s.%s.%s' 71 | * @param string|NULL $type type/property of bean to use for key generation 72 | * 73 | * @return array 74 | */ 75 | public function diff( $beans, $others, $filters = array( 'created', 'modified' ), $format = '%s.%s.%s', $type = NULL ) 76 | { 77 | $diff = array(); 78 | 79 | if ( !is_array( $beans ) ) $beans = array( $beans ); 80 | $beansI = array(); 81 | foreach ( $beans as $bean ) { 82 | if ( !( $bean instanceof OODBBean ) ) continue; 83 | $beansI[$bean->id] = $bean; 84 | } 85 | 86 | if ( !is_array( $others ) ) $others = array( $others ); 87 | $othersI = array(); 88 | foreach ( $others as $other ) { 89 | if ( !( $other instanceof OODBBean ) ) continue; 90 | $othersI[$other->id] = $other; 91 | } 92 | 93 | if ( count( $beansI ) == 0 || count( $othersI ) == 0 ) { 94 | return array(); 95 | } 96 | 97 | $type = $type != NULL ? $type : reset($beansI)->getMeta( 'type' ); 98 | 99 | foreach( $beansI as $id => $bean ) { 100 | if ( !isset( $othersI[$id] ) ) continue; 101 | $other = $othersI[$id]; 102 | foreach( $bean as $property => $value ) { 103 | if ( in_array( $property, $filters ) ) continue; 104 | $key = vsprintf( $format, array( $type, $bean->id, $property ) ); 105 | $compare = $other->{$property}; 106 | if ( !is_object( $value ) && !is_array( $value ) && $value != $compare ) { 107 | $diff[$key] = array( $value, $compare ); 108 | } else { 109 | $diff = array_merge( $diff, $this->diff( $value, $compare, $filters, $format, $key ) ); 110 | } 111 | } 112 | } 113 | 114 | return $diff; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /RedBeanPHP/Util/Dump.php: -------------------------------------------------------------------------------- 1 | 38 | * echo R::dump( $bean ); 39 | * 40 | * 41 | * The example shows how to echo the result of a simple 42 | * dump. This will print the string representation of the 43 | * specified bean to the screen, limiting the output per bean 44 | * to 35 characters to improve readability. Nested beans will 45 | * also be dumped. 46 | * 47 | * @param OODBBean|array $data either a bean or an array of beans 48 | * 49 | * @return array 50 | */ 51 | public static function dump( $data ) 52 | { 53 | $array = array(); 54 | if ( $data instanceof OODBBean ) { 55 | $str = strval( $data ); 56 | if (strlen($str) > 35) { 57 | $beanStr = substr( $str, 0, 35 ).'... '; 58 | } else { 59 | $beanStr = $str; 60 | } 61 | return $beanStr; 62 | } 63 | if ( is_array( $data ) ) { 64 | foreach( $data as $key => $item ) { 65 | $array[$key] = self::dump( $item ); 66 | } 67 | } 68 | return $array; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /RedBeanPHP/Util/Either.php: -------------------------------------------------------------------------------- 1 | 35 | * $author = $text 36 | * ->either() 37 | * ->page 38 | * ->book 39 | * ->autor 40 | * ->name 41 | * ->or('unknown'); 42 | * 43 | * 44 | * The Either-class lets you access bean properties without having to do 45 | * NULL-checks. The mechanism resembles the use of the ?? somewhat but 46 | * offers backward compatibility with older PHP versions. The mechanism also works 47 | * on arrays. 48 | * 49 | * 50 | * $budget = $company 51 | * ->either() 52 | * ->sharedProject 53 | * ->first() 54 | * ->budget 55 | * ->or(0); 56 | * 57 | */ 58 | public function __construct($result) { 59 | $this->result = $result; 60 | } 61 | 62 | /** 63 | * Extracts a value from the wrapped object and stores 64 | * it in the internal result object. If the desired 65 | * value cannot be found, the internal result object will be set 66 | * to NULL. Chainable. 67 | * 68 | * @param string $something name of the property you wish to extract the value of 69 | * 70 | * @return self 71 | */ 72 | public function __get($something) { 73 | if (is_object($this->result)) { 74 | $this->result = $this->result->{$something}; 75 | } else { 76 | $this->result = NULL; 77 | } 78 | return $this; 79 | } 80 | 81 | /** 82 | * Extracts the first element of the array in the internal result 83 | * object and stores it as the new value of the internal result object. 84 | * Chainable. 85 | * 86 | * @return self 87 | */ 88 | public function first() { 89 | if (is_array($this->result)) { 90 | reset($this->result); 91 | $key = key($this->result); 92 | if (isset($this->result[$key])) { 93 | $this->result = $this->result[$key]; 94 | } else { 95 | $this->result = NULL; 96 | } 97 | } 98 | return $this; 99 | } 100 | 101 | /** 102 | * Extracts the last element of the array in the internal result 103 | * object and stores it as the new value of the internal result object. 104 | * Chainable. 105 | * 106 | * @return self 107 | */ 108 | public function last() { 109 | if (is_array($this->result)) { 110 | end($this->result); 111 | $key = key($this->result); 112 | if (isset($this->result[$key])) { 113 | $this->result = $this->result[$key]; 114 | } else { 115 | $this->result = NULL; 116 | } 117 | } 118 | return $this; 119 | } 120 | 121 | /** 122 | * Extracts the specified element of the array in the internal result 123 | * object and stores it as the new value of the internal result object. 124 | * Chainable. 125 | * 126 | * @return self 127 | */ 128 | public function index( $key = 0 ) { 129 | if (is_array($this->result)) { 130 | if (isset($this->result[$key])) { 131 | $this->result = $this->result[$key]; 132 | } else { 133 | $this->result = NULL; 134 | } 135 | } 136 | return $this; 137 | } 138 | 139 | /** 140 | * Resolves the Either-instance to a final value, either the value 141 | * contained in the internal result object or the value specified 142 | * in the or() function. 143 | * 144 | * @param mixed $value value to resolve to if internal result equals NULL 145 | * 146 | * @return mixed 147 | */ 148 | public function _or( $value ) { 149 | $reference = (is_null($this->result)) ? $value : $this->result; 150 | return $reference; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /RedBeanPHP/Util/Feature.php: -------------------------------------------------------------------------------- 1 | 60 | * R::useFeatureSet( 'novice/latest' ); 61 | * 62 | * 63 | * @param string $label label 64 | * 65 | * @return void 66 | */ 67 | public static function feature( $label ) { 68 | switch( $label ) { 69 | case self::C_FEATURE_NOVICE_LATEST: 70 | case self::C_FEATURE_NOVICE_5_4: 71 | case self::C_FEATURE_NOVICE_5_5: 72 | OODBBean::useFluidCount( TRUE ); 73 | R::noNuke( TRUE ); 74 | R::setAllowHybridMode( FALSE ); 75 | R::useISNULLConditions( TRUE ); 76 | break; 77 | case self::C_FEATURE_LATEST: 78 | case self::C_FEATURE_5_4: 79 | case self::C_FEATURE_5_5: 80 | OODBBean::useFluidCount( TRUE ); 81 | R::noNuke( FALSE ); 82 | R::setAllowHybridMode( TRUE ); 83 | R::useISNULLConditions( TRUE ); 84 | break; 85 | case self::C_FEATURE_NOVICE_5_3: 86 | OODBBean::useFluidCount( TRUE ); 87 | R::noNuke( TRUE ); 88 | R::setAllowHybridMode( FALSE ); 89 | R::useISNULLConditions( FALSE ); 90 | break; 91 | case self::C_FEATURE_5_3: 92 | OODBBean::useFluidCount( TRUE ); 93 | R::noNuke( FALSE ); 94 | R::setAllowHybridMode( FALSE ); 95 | R::useISNULLConditions( FALSE ); 96 | break; 97 | case self::C_FEATURE_ORIGINAL: 98 | OODBBean::useFluidCount( TRUE ); 99 | R::noNuke( FALSE ); 100 | R::setAllowHybridMode( FALSE ); 101 | R::useISNULLConditions( FALSE ); 102 | break; 103 | default: 104 | throw new \Exception("Unknown feature set label."); 105 | break; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /RedBeanPHP/Util/Look.php: -------------------------------------------------------------------------------- 1 | toolbox = $toolbox; 41 | } 42 | 43 | /** 44 | * Takes an full SQL query with optional bindings, a series of keys, a template 45 | * and optionally a filter function and glue and assembles a view from all this. 46 | * This is the fastest way from SQL to view. Typically this function is used to 47 | * generate pulldown (select tag) menus with options queried from the database. 48 | * 49 | * Usage: 50 | * 51 | * 52 | * $htmlPulldown = R::look( 53 | * 'SELECT * FROM color WHERE value != ? ORDER BY value ASC', 54 | * [ 'g' ], 55 | * [ 'value', 'name' ], 56 | * '', 57 | * 'strtoupper', 58 | * "\n" 59 | * ); 60 | * 61 | * 62 | * The example above creates an HTML fragment like this: 63 | * 64 | * 65 | * 66 | * 67 | * to pick a color from a palette. The HTML fragment gets constructed by 68 | * an SQL query that selects all colors that do not have value 'g' - this 69 | * excludes green. Next, the bean properties 'value' and 'name' are mapped to the 70 | * HTML template string, note that the order here is important. The mapping and 71 | * the HTML template string follow vsprintf-rules. All property values are then 72 | * passed through the specified filter function 'strtoupper' which in this case 73 | * is a native PHP function to convert strings to uppercase characters only. 74 | * Finally the resulting HTML fragment strings are glued together using a 75 | * newline character specified in the last parameter for readability. 76 | * 77 | * In previous versions of RedBeanPHP you had to use: 78 | * R::getLook()->look() instead of R::look(). However to improve useability of the 79 | * library the look() function can now directly be invoked from the facade. 80 | * 81 | * @param string $sql query to execute 82 | * @param array $bindings parameters to bind to slots mentioned in query or an empty array 83 | * @param array $keys names in result collection to map to template 84 | * @param string $template HTML template to fill with values associated with keys, use printf notation (i.e. %s) 85 | * @param callable $filter function to pass values through (for translation for instance) 86 | * @param string $glue optional glue to use when joining resulting strings 87 | * 88 | * @return string 89 | */ 90 | public function look( $sql, $bindings = array(), $keys = array( 'selected', 'id', 'name' ), $template = '', $filter = 'trim', $glue = '' ) 91 | { 92 | $adapter = $this->toolbox->getDatabaseAdapter(); 93 | $lines = array(); 94 | $rows = $adapter->get( $sql, $bindings ); 95 | foreach( $rows as $row ) { 96 | $values = array(); 97 | foreach( $keys as $key ) { 98 | if (!empty($filter)) { 99 | $values[] = call_user_func_array( $filter, array( $row[$key] ) ); 100 | } else { 101 | $values[] = $row[$key]; 102 | } 103 | } 104 | $lines[] = vsprintf( $template, $values ); 105 | } 106 | $string = implode( $glue, $lines ); 107 | return $string; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /RedBeanPHP/Util/MatchUp.php: -------------------------------------------------------------------------------- 1 | toolbox = $toolbox; 44 | } 45 | 46 | /** 47 | * MatchUp is a powerful productivity boosting method that can replace simple control 48 | * scripts with a single RedBeanPHP command. Typically, matchUp() is used to 49 | * replace login scripts, token generation scripts and password reset scripts. 50 | * The MatchUp method takes a bean type, an SQL query snippet (starting at the WHERE clause), 51 | * SQL bindings, a pair of task arrays and a bean reference. 52 | * 53 | * If the first 3 parameters match a bean, the first task list will be considered, 54 | * otherwise the second one will be considered. On consideration, each task list, 55 | * an array of keys and values will be executed. Every key in the task list should 56 | * correspond to a bean property while every value can either be an expression to 57 | * be evaluated or a closure (PHP 5.3+). After applying the task list to the bean 58 | * it will be stored. If no bean has been found, a new bean will be dispensed. 59 | * 60 | * This method will return TRUE if the bean was found and FALSE if not AND 61 | * there was a NOT-FOUND task list. If no bean was found AND there was also 62 | * no second task list, NULL will be returned. 63 | * 64 | * To obtain the bean, pass a variable as the sixth parameter. 65 | * The function will put the matching bean in the specified variable. 66 | * 67 | * Usage (this example resets a password in one go): 68 | * 69 | * 70 | * $newpass = '1234'; 71 | * $didResetPass = R::matchUp( 72 | * 'account', ' token = ? AND tokentime > ? ', 73 | * [ $token, time()-100 ], 74 | * [ 'pass' => $newpass, 'token' => '' ], 75 | * NULL, 76 | * $account ); 77 | * 78 | * 79 | * @param string $type type of bean you're looking for 80 | * @param string $sql SQL snippet (starting at the WHERE clause, omit WHERE-keyword) 81 | * @param array $bindings array of parameter bindings for SQL snippet 82 | * @param array|NULL $onFoundDo task list to be considered on finding the bean 83 | * @param array|NULL $onNotFoundDo task list to be considered on NOT finding the bean 84 | * @param OODBBean|NULL &$bean reference to obtain the found bean 85 | * 86 | * @return bool|NULL 87 | */ 88 | public function matchUp( $type, $sql, $bindings = array(), $onFoundDo = NULL, $onNotFoundDo = NULL, &$bean = NULL ) 89 | { 90 | $finder = new Finder( $this->toolbox ); 91 | $oodb = $this->toolbox->getRedBean(); 92 | $bean = $finder->findOne( $type, $sql, $bindings ); 93 | if ( $bean && $onFoundDo ) { 94 | foreach( $onFoundDo as $property => $value ) { 95 | if ( function_exists('is_callable') && is_callable( $value ) ) { 96 | $bean[$property] = call_user_func_array( $value, array( $bean ) ); 97 | } else { 98 | $bean[$property] = $value; 99 | } 100 | } 101 | $oodb->store( $bean ); 102 | return TRUE; 103 | } 104 | if ( $onNotFoundDo ) { 105 | $bean = $oodb->dispense( $type ); 106 | foreach( $onNotFoundDo as $property => $value ) { 107 | if ( function_exists('is_callable') && is_callable( $value ) ) { 108 | $bean[$property] = call_user_func_array( $value, array( $bean ) ); 109 | } else { 110 | $bean[$property] = $value; 111 | } 112 | } 113 | $oodb->store( $bean ); 114 | return FALSE; 115 | } 116 | return NULL; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /RedBeanPHP/Util/MultiLoader.php: -------------------------------------------------------------------------------- 1 | 41 | * list( $author, $bio ) = R::loadMulti( 'author, bio', $id ); 42 | * 43 | * 44 | * @param OODB $oodb OODB object 45 | * @param string|array $types the set of types to load at once 46 | * @param mixed $id the common ID 47 | * 48 | * @return OODBBean[] 49 | */ 50 | public static function load( OODB $oodb, $types, $id ) 51 | { 52 | if ( is_string( $types ) ) $types = explode( ',', $types ); 53 | if ( !is_array( $types ) ) return array(); 54 | foreach ( $types as $k => $typeItem ) { 55 | $types[$k] = $oodb->load( $typeItem, $id ); 56 | } 57 | return $types; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /RedBeanPHP/Util/QuickExport.php: -------------------------------------------------------------------------------- 1 | toolbox = $toolbox; 45 | } 46 | 47 | /** 48 | * Makes csv() testable. 49 | * 50 | * @param string $name 51 | * @param mixed $arg1 52 | * @param boolean $arg2 53 | * 54 | * @return mixed 55 | */ 56 | public static function operation( $name, $arg1, $arg2 = TRUE ) { 57 | $out = ''; 58 | switch( $name ) { 59 | case 'test': 60 | self::$test = (boolean) $arg1; 61 | break; 62 | case 'header': 63 | $out = ( self::$test ) ? $arg1 : header( $arg1, $arg2 ); 64 | break; 65 | case 'readfile': 66 | $out = ( self::$test ) ? file_get_contents( $arg1 ) : readfile( $arg1 ); 67 | break; 68 | case 'exit': 69 | $out = ( self::$test ) ? 'exit' : exit(); 70 | break; 71 | } 72 | return $out; 73 | } 74 | 75 | /** 76 | * Exposes the result of the specified SQL query as a CSV file. 77 | * 78 | * Usage: 79 | * 80 | * 81 | * R::csv( 'SELECT 82 | * `name`, 83 | * population 84 | * FROM city 85 | * WHERE region = :region ', 86 | * array( ':region' => 'Denmark' ), 87 | * array( 'city', 'population' ), 88 | * '/tmp/cities.csv' 89 | * ); 90 | * 91 | * 92 | * The command above will select all cities in Denmark 93 | * and create a CSV with columns 'city' and 'population' and 94 | * populate the cells under these column headers with the 95 | * names of the cities and the population numbers respectively. 96 | * 97 | * @param string $sql SQL query to expose result of 98 | * @param array $bindings parameter bindings 99 | * @param array $columns column headers for CSV file 100 | * @param string $path path to save CSV file to 101 | * @param boolean $output TRUE to output CSV directly using readfile 102 | * @param array $options delimiter, quote and escape character respectively 103 | * 104 | * @return string|NULL 105 | */ 106 | public function csv( $sql = '', $bindings = array(), $columns = NULL, $path = '/tmp/redexport_%s.csv', $output = TRUE, $options = array(',','"','\\') ) 107 | { 108 | list( $delimiter, $enclosure, $escapeChar ) = $options; 109 | $path = sprintf( $path, date('Ymd_his') ); 110 | $handle = fopen( $path, 'w' ); 111 | if ($columns) if (PHP_VERSION_ID>=505040) fputcsv($handle, $columns, $delimiter, $enclosure, $escapeChar ); else fputcsv($handle, $columns, $delimiter, $enclosure ); 112 | $cursor = $this->toolbox->getDatabaseAdapter()->getCursor( $sql, $bindings ); 113 | while( $row = $cursor->getNextItem() ) { 114 | if (PHP_VERSION_ID>=505040) fputcsv($handle, $row, $delimiter, $enclosure, $escapeChar ); else fputcsv($handle, $row, $delimiter, $enclosure ); 115 | } 116 | fclose($handle); 117 | if ( $output ) { 118 | $file = basename($path); 119 | $out = self::operation('header',"Pragma: public"); 120 | $out .= self::operation('header',"Expires: 0"); 121 | $out .= self::operation('header',"Cache-Control: must-revalidate, post-check=0, pre-check=0"); 122 | $out .= self::operation('header',"Cache-Control: private", FALSE ); 123 | $out .= self::operation('header',"Content-Type: text/csv"); 124 | $out .= self::operation('header',"Content-Disposition: attachment; filename={$file}" ); 125 | $out .= self::operation('header',"Content-Transfer-Encoding: binary"); 126 | $out .= self::operation('readfile',$path ); 127 | @unlink( $path ); 128 | self::operation('exit', FALSE); 129 | return $out; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /RedBeanPHP/Util/Transaction.php: -------------------------------------------------------------------------------- 1 | 43 | * $from = 1; 44 | * $to = 2; 45 | * $amount = 300; 46 | * 47 | * R::transaction(function() use($from, $to, $amount) 48 | * { 49 | * $accountFrom = R::load('account', $from); 50 | * $accountTo = R::load('account', $to); 51 | * $accountFrom->money -= $amount; 52 | * $accountTo->money += $amount; 53 | * R::store($accountFrom); 54 | * R::store($accountTo); 55 | * }); 56 | * 57 | * 58 | * @param Adapter $adapter Database Adapter providing transaction mechanisms. 59 | * @param callable $callback Closure (or other callable) with the transaction logic 60 | * 61 | * @return mixed 62 | */ 63 | public static function transaction( Adapter $adapter, $callback ) 64 | { 65 | if ( !is_callable( $callback ) ) { 66 | throw new RedException( 'R::transaction needs a valid callback.' ); 67 | } 68 | 69 | static $depth = 0; 70 | $result = null; 71 | try { 72 | if ( $depth == 0 ) { 73 | $adapter->startTransaction(); 74 | } 75 | $depth++; 76 | $result = call_user_func( $callback ); //maintain 5.2 compatibility 77 | $depth--; 78 | if ( $depth == 0 ) { 79 | $adapter->commit(); 80 | } 81 | } catch ( \Exception $exception ) { 82 | $depth--; 83 | if ( $depth == 0 ) { 84 | $adapter->rollback(); 85 | } 86 | throw $exception; 87 | } 88 | return $result; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /RedBeanPHP/loader.php: -------------------------------------------------------------------------------- 1 | =5.3.4" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "RedBeanPHP\\" : "RedBeanPHP" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /p533patch.php: -------------------------------------------------------------------------------- 1 | 0) { 12 | echo 'Applied patch for PHP < 5.3.3'; 13 | echo PHP_EOL; 14 | exit; 15 | } else { 16 | echo 'Somthing went wrong.'; 17 | echo PHP_EOL; 18 | exit; 19 | } 20 | -------------------------------------------------------------------------------- /replica2-win.php: -------------------------------------------------------------------------------- 1 | buildFromDirectory('./build'); 28 | echo "Done.\n"; 29 | 30 | echo "Adding stub... "; 31 | $phar->setStub($phar->createDefaultStub("loader.php")); 32 | echo "Done.\n"; 33 | 34 | echo "Your PHAR file has been generated.\n"; 35 | -------------------------------------------------------------------------------- /replica2.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | $code, 'rb-mysql.php' => $codeMySQL, 'rb-postgres.php' => $codePostgres, 'rb-sqlite.php' => $codeSQLite ); 112 | foreach( $files as $file => $content ) { 113 | echo 'Okay, seems we have all the code.. now writing file: ', $file ,PHP_EOL; 114 | $b = file_put_contents($file, $content); 115 | echo 'Written: ',$b,' bytes.',PHP_EOL; 116 | if ($b > 0) { 117 | echo 'Done!' ,PHP_EOL; 118 | } else { 119 | echo 'Hm, something seems to have gone wrong... ',PHP_EOL; 120 | } 121 | } 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /run_all_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | php replica2.php 3 | cp rb.php testing/cli/testcontainer/rb.php 4 | cd testing 5 | cd cli 6 | php runtests.php 7 | -------------------------------------------------------------------------------- /run_single_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z "$1" ] 3 | then 4 | echo "Please enter the name of a test suite, example: Blackhole/Version" 5 | exit 6 | fi 7 | php replica2.php 8 | cp rb.php testing/cli/testcontainer/rb.php 9 | cd testing 10 | cd cli 11 | php runtests.php $1 12 | -------------------------------------------------------------------------------- /test-dist.ini: -------------------------------------------------------------------------------- 1 | ; Test suite database config 2 | ; Rename this file to test.ini if you are done 3 | 4 | [mysql] 5 | host = "localhost" 6 | schema = "oodb" 7 | user = "root" 8 | pass = "password" 9 | 10 | [pgsql] 11 | host = "localhost" 12 | schema = "oodb" 13 | user = "postgres" 14 | pass = "password" 15 | 16 | [sqlite] 17 | file = "/tmp/database.db" 18 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base.php: -------------------------------------------------------------------------------- 1 | box(); 35 | R::trash( $bean ); 36 | pass(); 37 | $bean = R::dispense( 'boxedbean' ); 38 | $bean->sharedBoxbean = R::dispense( 'boxedbean' )->box(); 39 | R::store( $bean ); 40 | pass(); 41 | $bean = R::dispense( 'boxedbean' ); 42 | $bean->ownBoxedbean = R::dispense( 'boxedbean' )->box(); 43 | R::store( $bean ); 44 | pass(); 45 | $bean = R::dispense( 'boxedbean' ); 46 | $bean->other = R::dispense( 'boxedbean' )->box(); 47 | R::store( $bean ); 48 | pass(); 49 | $bean = R::dispense( 'boxedbean' ); 50 | $bean->title = 'MyBean'; 51 | $box = $bean->box(); 52 | asrt( ( $box instanceof \Model_Boxedbean ), TRUE ); 53 | R::store( $box ); 54 | } 55 | 56 | /** 57 | * Test fix for issue #512 - thanks for reporting Bernhard H. 58 | * OODBBean::__toString() implementation only works with C_ERR_IGNORE 59 | * 60 | * @return void 61 | */ 62 | public function testToStringIssue512() 63 | { 64 | R::setErrorHandlingFUSE( \RedBeanPHP\OODBBean::C_ERR_FATAL ); 65 | $boxedBean = R::dispense( 'boxedbean' ); 66 | $str = (string) $boxedBean; 67 | asrt( $str, '{"id":0}' ); //no fatal error 68 | R::setErrorHandlingFUSE( FALSE ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Chill.php: -------------------------------------------------------------------------------- 1 | col1 = '1'; 35 | $bean->col2 = '2'; 36 | R::store( $bean ); 37 | asrt( count( R::getWriter()->getColumns( 'bean' ) ), 3 ); 38 | $bean->col3 = '3'; 39 | R::store( $bean ); 40 | asrt( count( R::getWriter()->getColumns( 'bean' ) ), 4 ); 41 | R::freeze( array( 'umbrella' ) ); 42 | $bean->col4 = '4'; 43 | R::store( $bean ); 44 | asrt( count( R::getWriter()->getColumns( 'bean' ) ), 5 ); 45 | R::freeze( array( 'bean' ) ); 46 | $bean->col5 = '5'; 47 | try { 48 | R::store( $bean ); 49 | fail(); 50 | } catch (\Exception $e ) { 51 | pass(); 52 | } 53 | asrt( count( R::getWriter()->getColumns( 'bean' ) ), 5 ); 54 | R::freeze( array() ); 55 | $bean->col5 = '5'; 56 | R::store( $bean ); 57 | asrt( count( R::getWriter()->getColumns( 'bean' ) ), 6 ); 58 | } 59 | 60 | /** 61 | * Test whether we cannot add unique constraints on chilled tables, 62 | * otherwise you cannot avoid this from happening when adding beans to the 63 | * shared list :) -- this is almost a theoretical issue however we want it 64 | * to work according to specifications! 65 | * 66 | * @return void 67 | */ 68 | public function testDontAddUniqueConstraintForChilledBeanTypes() 69 | { 70 | R::nuke(); 71 | $person = R::dispense( 'person' ); 72 | $role = R::dispense( 'role' ); 73 | $person->sharedRole[] = $role; 74 | R::store( $person ); 75 | $person->sharedRole[] = R::dispense( 'role' ); 76 | R::store( $person ); 77 | $bean = R::getRedBean()->dispense('person_role'); 78 | $bean->personId = $person->id; 79 | $bean->roleId = $role->id; 80 | try { 81 | R::store( $bean ); 82 | fail(); 83 | } catch(\Exception $e) { 84 | pass(); 85 | } 86 | asrt(R::count('person_role'), 2); 87 | R::nuke(); 88 | $link = R::getRedBean()->dispense('person_role'); 89 | $person = R::dispense( 'person' ); 90 | $role = R::dispense( 'role' ); 91 | $link->person = $person; 92 | $link->role = $role; 93 | R::store( $link ); 94 | R::freeze(array('person_role')); 95 | $person->sharedRole[] = R::dispense( 'role' ); 96 | R::store( $person ); 97 | $bean = R::getRedBean()->dispense('person_role'); 98 | $bean->personId = $person->id; 99 | $bean->roleId = $role->id; 100 | try { 101 | R::store( $bean ); 102 | pass(); 103 | } catch(\Exception $e) { 104 | fail(); 105 | } 106 | asrt(R::count('person_role'), 3); 107 | R::freeze( array() ); //set freeze to FALSE and clear CHILL LIST! 108 | } 109 | 110 | /** 111 | * Test whether we can set and reset the chill list and check the contents 112 | * of the chill list. 113 | * 114 | * @return void 115 | */ 116 | public function testChillTest() 117 | { 118 | R::freeze( array( 'beer' ) ); 119 | $oodb = R::getRedBean(); 120 | asrt( $oodb->isChilled( 'beer' ), TRUE ); 121 | asrt( $oodb->isChilled( 'wine' ), FALSE ); 122 | R::freeze( FALSE ); 123 | $oodb = R::getRedBean(); 124 | asrt( $oodb->isChilled( 'beer' ), TRUE ); 125 | asrt( $oodb->isChilled( 'wine' ), FALSE ); 126 | R::freeze( TRUE ); 127 | $oodb = R::getRedBean(); 128 | asrt( $oodb->isChilled( 'beer' ), TRUE ); 129 | asrt( $oodb->isChilled( 'wine' ), FALSE ); 130 | R::freeze( array() ); 131 | $oodb = R::getRedBean(); 132 | asrt( $oodb->isChilled( 'beer' ), FALSE ); 133 | asrt( $oodb->isChilled( 'wine' ), FALSE ); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Close.php: -------------------------------------------------------------------------------- 1 | getMessage(), 'Unknown feature set label.' ); 47 | } 48 | try { 49 | R::nuke(); 50 | fail(); 51 | } catch( \Exception $e ) { 52 | asrt( $e->getMessage(), 'The nuke() command has been disabled using noNuke() or R::feature(novice/...).' ); 53 | } 54 | R::useFeatureSet('latest'); 55 | //Close 56 | R::getDatabaseAdapter()->setOption( 'setInitQuery', NULL ); 57 | asrt( R::getDatabaseAdapter()->getDatabase()->isConnected(), TRUE ); 58 | R::close(); 59 | asrt( R::getDatabaseAdapter()->getDatabase()->isConnected(), FALSE ); 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Either.php: -------------------------------------------------------------------------------- 1 | 'book', 35 | 'title' => 'Socratic Questions', 36 | 'ownPageList' => array( 37 | array( 38 | '_type'=> 'page', 39 | 'ownParagraphList' => array( 40 | array( 41 | '_type'=>'paragraph', 42 | 'text' => 'What is a Bean?', 43 | ) 44 | ) 45 | ) 46 | ) 47 | ))); 48 | $book = R::load('book', $id); 49 | asrt($book->either()->title->_or('nothing'),'Socratic Questions'); 50 | $pageId = R::findOne('page')->id; 51 | asrt($book->either()->ownPageList->index($pageId)->id->_or(0), $pageId); 52 | asrt($book->either()->ownPageList->index($pageId+1)->ownTextList->index(1)->_or(0), 0); 53 | $textId = $book->either()->ownPageList->first()->ownParagraphList->last()->id->_or(0); 54 | asrt($textId > 0, TRUE); 55 | $text = R::load('paragraph', $textId); 56 | asrt($text->either()->page->book->title->_or('nothing'), 'Socratic Questions'); 57 | asrt($book->either()->ownPageList->last()->id->_or(-1),$pageId); 58 | asrt($book->either()->ownPageList->id->_or(-1),-1); 59 | asrt($book->either()->ownPageList->first()->ownQuoteList->first()->_or('?'),'?'); 60 | } 61 | } -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Frozen.php: -------------------------------------------------------------------------------- 1 | xownPageList[] = R::dispense( 'page' ); 41 | $book->sharedTagList[] = R::dispense( 'tag' ); 42 | R::store( $book ); 43 | $book = $book->fresh(); 44 | R::freeze( TRUE ); 45 | $book->xownPageList = array(); 46 | R::store( $book ); 47 | $book = $book->fresh(); 48 | asrt( R::count('page'), 0 ); 49 | $book->xownPageList[] = R::dispense( 'page' ); 50 | R::store( $book ); 51 | $book = $book->fresh(); 52 | asrt( R::count('page'), 1 ); 53 | $book->xownPageList; 54 | $book->sharedTagList; 55 | R::trash( $book ); 56 | asrt( R::count('book'), 0 ); 57 | asrt( R::count('page'), 0 ); 58 | asrt( R::count('tag'), 1 ); 59 | asrt( R::count('book_tag'), 0 ); 60 | R::freeze( FALSE ); 61 | } 62 | 63 | /** 64 | * Tests whether invalid list checks are 65 | * operational in frozen mode. 66 | * 67 | * @return void 68 | */ 69 | public function testInvalidList() 70 | { 71 | R::nuke(); 72 | $book = R::dispense( 'book' ); 73 | $book->xownPageList[] = R::dispense( 'page' ); 74 | $book->sharedTagList[] = R::dispense( 'tag' ); 75 | R::store( $book ); 76 | R::freeze( TRUE ); 77 | $book = R::dispense( 'book' ); 78 | $book->xownPageList[] = 'nonsense'; 79 | try { 80 | R::store( $book ); 81 | fail(); 82 | } catch( \Exception $e ) { 83 | pass(); 84 | } 85 | R::freeze( FALSE ); 86 | } 87 | 88 | /** 89 | * Tests whether loading non-existant beans 90 | * returns the same results in frozen mode. 91 | * 92 | * @return 93 | */ 94 | public function testLoadNonExistant() 95 | { 96 | R::nuke(); 97 | R::store( R::dispense( 'bean' ) ); 98 | R::freeze( TRUE ); 99 | $bean = R::load( 'bean', 123 ); 100 | R::freeze( FALSE ); 101 | asrt( ( $bean instanceof OODBBean ), TRUE ); 102 | asrt( $bean->id, 0 ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Indexes.php: -------------------------------------------------------------------------------- 1 | ownPageList[] = $page; 37 | R::store( $book ); 38 | $indexes = getIndexes( 'page' ); 39 | asrt( in_array( 'index_foreignkey_page_book', $indexes ), TRUE ); 40 | } 41 | 42 | /** 43 | * Tests indexes on parent beans. 44 | * 45 | * @return void 46 | */ 47 | public function testIndexCreationParentBean() 48 | { 49 | R::nuke(); 50 | $book = R::dispense( 'book' ); 51 | $page = R::dispense( 'page' ); 52 | $page->book = $book; 53 | R::store( $page ); 54 | $indexes = getIndexes( 'page' ); 55 | asrt( in_array( 'index_foreignkey_page_book', $indexes ), TRUE ); 56 | } 57 | 58 | /** 59 | * Tests indexes on link tables. 60 | * 61 | * @return void 62 | */ 63 | public function testIndexCreationMany2Many() 64 | { 65 | R::nuke(); 66 | $book = R::dispense( 'book' ); 67 | $category = R::dispense( 'category' ); 68 | $book->sharedCategoryList[] = $category; 69 | R::store( $book ); 70 | $indexes = getIndexes( 'book_category' ); 71 | asrt( in_array( 'index_foreignkey_book_category_book', $indexes ), TRUE ); 72 | asrt( in_array( 'index_foreignkey_book_category_category', $indexes ), TRUE ); 73 | R::nuke(); 74 | R::nuke(); 75 | $book = R::dispense( 'book' ); 76 | $category = R::dispense( 'category' ); 77 | $category->sharedBookList[] = $book; 78 | R::store( $category ); 79 | $indexes = getIndexes( 'book_category' ); 80 | asrt( in_array( 'index_foreignkey_book_category_book', $indexes ), TRUE ); 81 | asrt( in_array( 'index_foreignkey_book_category_category', $indexes ), TRUE ); 82 | } 83 | 84 | /** 85 | * Tests indexes on aliases. 86 | * 87 | * @return void 88 | */ 89 | public function testIndexCreationAlias() 90 | { 91 | R::nuke(); 92 | $book = R::dispense( 'book' ); 93 | $author = R::dispense( 'author' ); 94 | $book->coAuthor = $author; 95 | R::store( $book ); 96 | $indexes = getIndexes( 'book' ); 97 | asrt( in_array( 'index_foreignkey_book_co_author', $indexes ), TRUE ); 98 | R::nuke(); 99 | $project = R::dispense( 'project' ); 100 | $person = R::dispense( 'person' ); 101 | $person->alias( 'teacher' )->ownProject[] = $project; 102 | $person2 = R::dispense( 'person' ); 103 | $person2->alias( 'student' )->ownProject[] = $project; 104 | R::store( $person ); 105 | $indexes = getIndexes( 'project' ); 106 | asrt( in_array( 'index_foreignkey_project_teacher', $indexes ), TRUE ); 107 | R::store( $person2 ); 108 | $indexes = getIndexes( 'project' ); 109 | asrt( in_array( 'index_foreignkey_project_student', $indexes ), TRUE ); 110 | } 111 | 112 | /** 113 | * Tests index fails. 114 | * 115 | * @return void 116 | */ 117 | public function testIndexCreationFail() 118 | { 119 | R::nuke(); 120 | $book = R::dispense( 'book' ); 121 | $book->author_id = '999'; 122 | R::store( $book ); 123 | $indexes = getIndexes( 'book' ); 124 | //should just work fine 125 | asrt( in_array( 'index_foreignkey_book_author', $indexes ), TRUE ); 126 | //these should just pass, no indexes but no errors as well 127 | R::getWriter()->addIndex( 'book', 'bla', 'nonexist' ); 128 | pass(); 129 | R::getWriter()->addIndex( 'book', '@#$', 'nonexist' ); 130 | pass(); 131 | R::getWriter()->addIndex( 'nonexist', 'bla', 'nonexist' ); 132 | pass(); 133 | $indexesAfter = getIndexes( 'book' ); 134 | asrt( count( $indexesAfter ), count( $indexes ) ); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Issue259.php: -------------------------------------------------------------------------------- 1 | desc = 'I am mother'; 37 | R::store( $mother ); 38 | $child = R::dispense( 'child' ); 39 | $child->mother = $mother; 40 | $child->desc = 'I am child'; 41 | $id = R::store( $child ); 42 | R::findOne( 'child', ' id = ?', array( $id ) ); 43 | R::find( 'child', ' id = ? ', array( $id ) ); 44 | R::load( 'child', $id ); 45 | } 46 | } 47 | /** 48 | * Mock Model. 49 | */ 50 | class Model_Mother extends SimpleModel 51 | { 52 | public function open() 53 | { 54 | $bean = $this->bean; 55 | asrt( $this->bean->desc, 'I am mother' ); 56 | } 57 | } 58 | /** 59 | * Mock Model. 60 | */ 61 | class Model_Child extends SimpleModel 62 | { 63 | public function open() 64 | { 65 | $this->bean->mother; 66 | asrt( $this->bean->desc, 'I am child' ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Issue303.php: -------------------------------------------------------------------------------- 1 | setAttr( 'invalid.property', 'value' ) ); 38 | fail(); 39 | } catch (RedException $e ) { 40 | asrt( $e->getMessage(), 'Invalid Bean property: property invalid.property' ); 41 | } 42 | try { 43 | R::store( R::dispense( 'invalidbean' )->setAttr( 'property', array() ) ); 44 | fail(); 45 | } catch (RedException $e ) { 46 | asrt( $e->getMessage(), 'Invalid Bean value: property property' ); 47 | } 48 | try { 49 | R::store( R::dispense( 'invalidbean' )->setAttr( 'property', new \stdClass ) ); 50 | fail(); 51 | } catch (RedException $e ) { 52 | asrt( $e->getMessage(), 'Invalid Bean value: property property' ); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Issue408.php: -------------------------------------------------------------------------------- 1 | post = array( 38 | 'first', 39 | 'second' 40 | ); 41 | R::store( $feed ); 42 | $rows = R::getAll('SELECT * FROM feed'); 43 | asrt( $rows[0]['post'], '["first","second"]' ); 44 | $feed = $feed->fresh(); 45 | asrt( is_array( $feed->post ), TRUE ); 46 | asrt( $feed->post[0], 'first' ); 47 | asrt( $feed->post[1], 'second' ); 48 | R::store( $feed ); 49 | $rows = R::getAll('SELECT * FROM feed'); 50 | asrt( $rows[0]['post'], '["first","second"]' ); 51 | $feed = R::load( 'feed', $feed->id ); 52 | $feed->post[] = 'third'; 53 | R::store( $feed ); 54 | $rows = R::getAll('SELECT * FROM feed'); 55 | asrt( $rows[0]['post'], '["first","second","third"]' ); 56 | $feed = $feed->fresh(); 57 | asrt( is_array( $feed->post ), TRUE ); 58 | asrt( $feed->post[0], 'first' ); 59 | asrt( $feed->post[1], 'second' ); 60 | asrt( $feed->post[2], 'third' ); 61 | //now the catch: can we use export? 62 | //PHP Fatal error: Call to a member function export() on a non-object 63 | $feeds = R::exportAll( R::find( 'feed' ) ); 64 | asrt( is_array( $feeds ), TRUE ); 65 | $feed = reset( $feeds ); 66 | asrt( $feed['post'][0], 'first' ); 67 | asrt( $feed['post'][1], 'second' ); 68 | asrt( $feed['post'][2], 'third' ); 69 | //can we also dup()? 70 | $feedOne = R::findOne( 'feed' ); 71 | R::store( R::dup( $feedOne ) ); 72 | asrt( R::count( 'feed' ), 2 ); 73 | //can we delete? 74 | R::trash( $feedOne ); 75 | asrt( R::count( 'feed' ), 1 ); 76 | $feedTwo = R::findOne( 'feed' ); 77 | $feed = $feedTwo->export(); 78 | asrt( $feed['post'][0], 'first' ); 79 | asrt( $feed['post'][1], 'second' ); 80 | asrt( $feed['post'][2], 'third' ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Issue841.php: -------------------------------------------------------------------------------- 1 | name = 'TAG_'.$i; 37 | R::store($tag); 38 | } 39 | $record = R::dispense('record'); 40 | $record->point = rand(-100,-1); 41 | $record->sharedTagList[] = R::load('tag',2); 42 | R::store($record); 43 | asrt(count($record->sharedTagList),1); 44 | $record = R::load('record',1); 45 | $record->sharedTagList = array(); 46 | R::store($record); 47 | $record = R::load('record',1); 48 | asrt(count($record->sharedTagList),0); 49 | R::bindFunc( 'read', 'record.point', null ); 50 | R::bindFunc( 'write', 'record.point', null ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Issue90.php: -------------------------------------------------------------------------------- 1 | name = 'a'; 35 | $f = R::dispense( 'bottle' ); 36 | $s->ownBottle[] = $f; 37 | R::store( $s ); 38 | $s2 = R::dispense( 'box' ); 39 | $s2->name = 'a'; 40 | R::store( $s2 ); 41 | R::trash( $s2 ); 42 | pass(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Keywords.php: -------------------------------------------------------------------------------- 1 | $k = $k; 59 | $id = R::store( $bean ); 60 | $bean = R::load( $k, $id ); 61 | $bean2 = R::dispense( 'other' ); 62 | $bean2->name = $k; 63 | $bean->bean = $bean2; 64 | $bean->ownBean[] = $bean2; 65 | $bean->sharedBean[] = $bean2; 66 | $id = R::store( $bean ); 67 | R::trash( $bean ); 68 | pass(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Largenum.php: -------------------------------------------------------------------------------- 1 | name = 'big number'; 35 | R::store( $number ); 36 | //This should not cause an error... (some people use LIMIT 0, HUGE to simulate OFFSET on MYSQL). 37 | $beans = R::findAll( 'number', ' LIMIT ? ', array( PHP_INT_MAX ) ); 38 | asrt( is_array( $beans ), TRUE ); 39 | asrt( count( $beans ), 1 ); 40 | pass(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Namedparams.php: -------------------------------------------------------------------------------- 1 | title = 'book'; 38 | $book->sharedPage[] = $page; 39 | R::store($book); 40 | //should not give error like: Uncaught [HY093] - SQLSTATE[HY093]: Invalid parameter number: mixed named and positional parameters 41 | $books = $page->withCondition(' title = :title ', array( ':title' => 'book' ) )->sharedBook; 42 | asrt( count( $books ), 1 ); 43 | //should not give error... 44 | $books = $page->withCondition( ' title = :title ', array( ':title' => 'book' ) )->sharedBook; 45 | asrt( count( $books ), 1 ); 46 | R::nuke(); 47 | $book = R::dispense( 'book' ); 48 | $page = R::dispense( 'page' ); 49 | $book->title = 'book'; 50 | $book->comment = 'comment'; 51 | $page->title = 'page'; 52 | $book->ownPage[] = $page; 53 | R::store( $book ); 54 | //should also not give error.. 55 | $count = $book->countOwn( 'page' ); 56 | asrt( $count, 1 ); 57 | $book = $book->fresh(); 58 | //should also not give error.. 59 | $count = $book->withCondition( ' title = ? ', array( 'page' ) )->countOwn( 'page' ); 60 | asrt( $count, 1 ); 61 | $book = $book->fresh(); 62 | //should also not give error.. 63 | $count = $book->withCondition( ' title = :title ', array( ':title' => 'page' ) )->countOwn( 'page' ); 64 | asrt( $count, 1 ); 65 | $book = $book->fresh(); 66 | $pages = $book->withCondition( ' title = :title ', array( ':title' => 'page' ) )->ownPage; 67 | asrt( count( $pages ), 1 ); 68 | //test with duplicate slots... 69 | $page = reset( $pages ); 70 | $page2 = R::dispense( 'page' ); 71 | $page2->ownPage[] = $page; 72 | R::store( $page2 ); 73 | $page2 = $page2->fresh(); 74 | $pages = $page2->withCondition( ' title = :title ', array( ':title' => 'page' ) )->ownPage; 75 | asrt( count( $pages ), 1 ); 76 | //test with find() 77 | $books = R::getRedBean()->find( 'book', 78 | array( 79 | 'title' => array('book')), 80 | ' AND title = :title ', array(':title'=>'book')); 81 | asrt( count( $books ), 1 ); 82 | $books = R::getRedBean()->find( 'book', 83 | array( 84 | 'title' => array('book', 'book2'), 85 | 'comment' => array('comment', 'comment2')), 86 | ' AND title = :title ', array(':title'=>'book')); 87 | asrt( count( $books ), 1 ); 88 | //just check numeric works as well... 89 | $books = R::getRedBean()->find( 'book', 90 | array( 91 | 'title' => array('book', 'book2'), 92 | 'comment' => array('comment', 'comment2')), 93 | ' AND title = ? ', array('book')); 94 | asrt( count( $books ), 1 ); 95 | //just extra check to verify glue works 96 | $books = R::getRedBean()->find( 'book', 97 | array( 98 | 'title' => array('book', 'book2'), 99 | 'comment' => array('comment', 'comment2')), 100 | ' ORDER BY id '); 101 | asrt( count( $books ), 1 ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Nuke.php: -------------------------------------------------------------------------------- 1 | getTables() ), 1 ); 58 | R::nuke(); 59 | asrt( count( R::getWriter()->getTables() ), 0 ); 60 | $bean = R::dispense( 'bean' ); 61 | R::store( $bean ); 62 | asrt( count( R::getWriter()->getTables() ), 1 ); 63 | R::freeze(); 64 | R::nuke(); 65 | // No effect 66 | asrt( count( R::getWriter()->getTables() ), 1 ); 67 | R::freeze( FALSE ); 68 | } 69 | 70 | /** 71 | * Test noNuke(). 72 | * 73 | * @return void 74 | */ 75 | public function testNoNuke() { 76 | $bean = R::dispense( 'bean' ); 77 | R::store( $bean ); 78 | asrt( count( R::getWriter()->getTables() ), 1 ); 79 | R::noNuke( TRUE ); 80 | try { 81 | R::nuke(); 82 | fail(); 83 | } catch( \Exception $e ) { 84 | pass(); 85 | } 86 | asrt( count( R::getWriter()->getTables() ), 1 ); 87 | R::noNuke( FALSE ); 88 | R::nuke(); 89 | asrt( count( R::getWriter()->getTables() ), 0 ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Observers.php: -------------------------------------------------------------------------------- 1 | getDatabaseAdapter(); 36 | $writer = $toolbox->getWriter(); 37 | $redbean = $toolbox->getRedBean(); 38 | asrt( ( $adapter instanceof DBAdapter ), TRUE ); 39 | asrt( ( $writer instanceof QueryWriter ), TRUE ); 40 | asrt( ( $redbean instanceof OODB ), TRUE ); 41 | $observable = new \ObservableMock(); 42 | $observer = new \ObserverMock(); 43 | $observable->addEventListener( "event1", $observer ); 44 | $observable->addEventListener( "event3", $observer ); 45 | $observable->test( "event1", "testsignal1" ); 46 | asrt( $observer->event, "event1" ); 47 | asrt( $observer->info, "testsignal1" ); 48 | $observable->test( "event2", "testsignal2" ); 49 | asrt( $observer->event, "event1" ); 50 | asrt( $observer->info, "testsignal1" ); 51 | $observable->test( "event3", "testsignal3" ); 52 | asrt( $observer->event, "event3" ); 53 | asrt( $observer->info, "testsignal3" ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Performance.php: -------------------------------------------------------------------------------- 1 | title = 'book'; 37 | $pages = R::dispense( 'page', 10 ); 38 | foreach( $pages as $page ) { 39 | $page->content = 'lorem ipsum'; 40 | $page->title = 'data'; 41 | $page->sequence = 'data'; 42 | $page->order = 'data'; 43 | $page->columns = 'data'; 44 | $page->paragraphs = 'data'; 45 | $page->paragraphs1 = 'data'; 46 | $page->paragraphs2 = 'data'; 47 | $page->paragraphs3 = 'data'; 48 | $page->paragraphs4 = 'data'; 49 | } 50 | $book->xownPageList = $pages; 51 | $tags = R::dispense( 'tag', 6 ); 52 | foreach( $tags as $tag ) { 53 | $tag->label = 'tag'; 54 | } 55 | $book->sharedTagList = $tags; 56 | R::store( $book ); 57 | } 58 | 59 | /** 60 | * CRUD performance. 61 | * 62 | * @return void 63 | */ 64 | public function crud() 65 | { 66 | R::freeze( TRUE ); 67 | 68 | $book = R::dispense( 'book' ); 69 | $book->title = 'Book'; 70 | $page = R::dispense('page'); 71 | $page->content = 'Content'; 72 | $page->title = 'data'; 73 | $page->sequence = 'data'; 74 | $page->order = 'data'; 75 | $page->columns = 'data'; 76 | $page->paragraphs = 'data'; 77 | $page->paragraphs1 = 'data'; 78 | $page->paragraphs2 = 'data'; 79 | $page->paragraphs3 = 'data'; 80 | $page->paragraphs4 = 'data'; 81 | $tag = R::dispense('tag'); 82 | $tag->label = 'Tag '; 83 | $book->noLoad()->ownPage[] = $page; 84 | $book->noLoad()->sharedTag[] = $tag; 85 | R::store( $book ); 86 | $book = $book->fresh(); 87 | $book->ownPage; 88 | $book->sharedTag; 89 | R::trash( $book ); 90 | 91 | } 92 | 93 | /** 94 | * CRUD performance Array Access. 95 | * 96 | * @return void 97 | */ 98 | public function crudaa() 99 | { 100 | R::freeze( TRUE ); 101 | 102 | $book = R::dispense( 'book' ); 103 | $book['title'] = 'Book'; 104 | $page = R::dispense('page'); 105 | $page['content'] = 'Content'; 106 | $page['title'] = 'data'; 107 | $page['sequence'] = 'data'; 108 | $page['order'] = 'data'; 109 | $page['columns'] = 'data'; 110 | $page['paragraphs'] = 'data'; 111 | $page['paragraphs1'] = 'data'; 112 | $page['paragraphs2'] = 'data'; 113 | $page['paragraphs3'] = 'data'; 114 | $page['paragraphs4'] = 'data'; 115 | $tag = R::dispense('tag'); 116 | $tag['label'] = 'Tag '; 117 | $book->ownPage[] = $page; 118 | $book->noLoad()->sharedTag[] = $tag; 119 | R::store( $book ); 120 | $book = $book->fresh(); 121 | $book->ownPage; 122 | $book->sharedTag; 123 | R::trash( $book ); 124 | 125 | } 126 | } -------------------------------------------------------------------------------- /testing/RedUNIT/Base/PullRequest530.php: -------------------------------------------------------------------------------- 1 | if ($bean->$linkField != $id) $bean->$linkField = $id; 33 | * 34 | * @return void 35 | */ 36 | public function testPullRequest530() 37 | { 38 | testpack( 'Testing Pull Request #530 - OODBBean __set() checks if $property is a field link' ); 39 | R::freeze( FALSE ); 40 | $linkedObjects = R::dispense('linked', 2); 41 | R::storeAll($linkedObjects); 42 | $tester = R::dispense('parent'); 43 | $tester->linked = $linkedObjects[0]; 44 | R::store($tester); 45 | $tester = R::findOne('parent'); 46 | asrt($tester->linked->id, $linkedObjects[0]->id); 47 | $tester->linked_id = $linkedObjects[1]->id; 48 | R::store($tester); 49 | asrt($tester->linked->id, $linkedObjects[1]->id); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Quickexport.php: -------------------------------------------------------------------------------- 1 | 'bean', 'a' => 1, 'b' => 2, 'c' => 3 ) ) ); 37 | $path = '/tmp/redbeantest.txt'; 38 | R::csv( 'SELECT a,b,c FROM bean', array(), array( 'A', 'B', 'C' ), $path, FALSE ); 39 | $csv = file_get_contents( $path ); 40 | $expected = "A,B,C\n1,2,3"; 41 | asrt( strpos($csv, $expected) !== FALSE, TRUE ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Threeway.php: -------------------------------------------------------------------------------- 1 | sharedRole[] = $role; 40 | R::store( $person ); 41 | $person->link( 'person_role', array( 42 | 'unit' => R::dispense('unit') 43 | ))->role = $role; 44 | //Can we add a duplicate role now? - No because we started with a simple N-M table 45 | //and unique constraint has been applied accordingly, manually change database. 46 | asrt( R::count( 'person_role' ), 1 ); 47 | R::nuke(); 48 | $person = R::dispense( 'person' ); 49 | $role = R::dispense( 'role' ); 50 | $person->via('participant')->sharedRole[] = $role; 51 | R::store( $person ); 52 | $person->link( 'participant', array( 53 | 'unit' => R::dispense('unit') 54 | ))->role = $role; 55 | //Can we add a duplicate role now? - No because we started with a simple N-M table 56 | //and unique constraint has been applied accordingly, manually change database. 57 | asrt( R::count( 'participant' ), 1 ); 58 | R::nuke(); 59 | $participant = R::dispense( 'participant' ); 60 | $person = R::dispense( 'person' ); 61 | $role = R::dispense( 'role' ); 62 | $unit = R::dispense( 'unit' ); 63 | $participant->person = $person; 64 | $participant->role = $role; 65 | $participant->unit = $unit; 66 | R::store( $participant ); 67 | $person->link( 'participant', array( 68 | 'unit' => R::dispense('unit') 69 | ))->role = $role; 70 | R::store( $person ); 71 | //Can we add a duplicate role now? 72 | asrt( R::count( 'participant' ), 2 ); 73 | AQueryWriter::clearRenames(); 74 | } 75 | 76 | /** 77 | * Test whether a duplicate bean in the list isnt saved. 78 | * This was an issue with Postgres while testing the threeway tables. 79 | * Postgres returned the ID as a string while other drivers returned 80 | * a numeric value causing different outcome in array_diff when 81 | * calculating the shared additions. 82 | * 83 | * @return void 84 | */ 85 | public function testIssueWithDriverReturnID() 86 | { 87 | AQueryWriter::clearRenames(); 88 | R::nuke(); 89 | $book = R::dispense( 'book' ); 90 | $page = R::dispense( 'page' ); 91 | $book->sharedPageList[] = $page; 92 | R::store( $book ); 93 | asrt( R::count( 'page' ), 1 ); 94 | $book = $book->fresh(); 95 | $book->sharedPageList[] = $page; 96 | R::store( $book ); 97 | //don't save the duplicate bean! 98 | asrt( R::count( 'page' ), 1 ); 99 | $book = $book->fresh(); 100 | $page->item = 2; //even if we change a property ? 101 | $book->sharedPageList[] = $page; 102 | R::store( $book ); 103 | foreach( $book->sharedPageList as $listItem) { 104 | asrt( is_string( $listItem->id ), TRUE ); 105 | } 106 | //same test but for own-list 107 | R::nuke(); 108 | $book = R::dispense( 'book' ); 109 | $page = R::dispense( 'page' ); 110 | $book->ownPageList[] = $page; 111 | R::store( $book ); 112 | asrt( R::count( 'page' ), 1 ); 113 | $book = $book->fresh(); 114 | $book->ownPageList[] = $page; 115 | R::store( $book ); 116 | //don't save the duplicate bean! 117 | asrt( R::count( 'page' ), 1 ); 118 | $book = $book->fresh(); 119 | $book->ownPageList[] = $page; 120 | $page->item = 3; 121 | R::store( $book ); 122 | //don't save the duplicate bean! 123 | asrt( R::count( 'page' ), 1 ); 124 | foreach( $book->ownPageList as $listItem) { 125 | asrt( is_string( $listItem->id ), TRUE ); 126 | } 127 | AQueryWriter::clearRenames(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Trash.php: -------------------------------------------------------------------------------- 1 | 'book', 36 | 'pages'=>3 37 | ) 38 | )); 39 | asrt( R::count('book'), 1 ); 40 | $n = R::trash(R::findOne('book')); 41 | asrt( $n, 1 ); 42 | asrt( R::count('book'), 0 ); 43 | list($books) = R::dispenseAll('book*10'); 44 | R::storeAll( $books ); 45 | asrt( R::count('book'), 10 ); 46 | $n = R::trashAll( $books ); 47 | asrt( R::count('book'), 0 ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /testing/RedUNIT/Base/Utf8.php: -------------------------------------------------------------------------------- 1 | byte = $byte; 35 | OODBBean::setEnforceUTF8encoding( TRUE ); 36 | $str = strval( $bean ); 37 | OODBBean::setEnforceUTF8encoding( FALSE ); 38 | pass(); 39 | } 40 | 41 | /** 42 | * Test UTF8 handling. 43 | * 44 | * @return void 45 | */ 46 | public function testUTF8() 47 | { 48 | //skip if < 5.3 49 | if (version_compare(PHP_VERSION, '5.4', '<')) return pass(); 50 | $str = '𠜎ὃ𠻗𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜'; 51 | $bean = R::dispense( 'bean' ); 52 | $bean->bla = $str; 53 | R::store( $bean ); 54 | $bean = R::load( 'bean', $bean->id ); 55 | asrt( $bean->bla, $str ); 56 | pass(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /testing/RedUNIT/Blackhole.php: -------------------------------------------------------------------------------- 1 | taste() ); 38 | asrt( 'tomato', $soup->flavour ); 39 | } 40 | 41 | /** 42 | * Test unboxing 43 | * 44 | * @param OODBBean $bean 45 | */ 46 | private function giveMeBean( OODBBean $bean ) 47 | { 48 | asrt( ( $bean instanceof OODBBean ), TRUE ); 49 | asrt( 'A bit too salty', $bean->taste() ); 50 | asrt( 'tomato', $bean->flavour ); 51 | } 52 | 53 | /** 54 | * Test boxing. 55 | * 56 | * @return void 57 | */ 58 | public function testBasicBox() 59 | { 60 | $soup = R::dispense( 'soup' ); 61 | $soup->flavour = 'tomato'; 62 | $this->giveMeSoup( $soup->box() ); 63 | $this->giveMeBean( $soup->box()->unbox() ); 64 | $this->giveMeBean( $soup ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /testing/RedUNIT/Blackhole/Glue.php: -------------------------------------------------------------------------------- 1 | glueSQLCondition( ' name = ? '), ' WHERE name = ? ' ); 47 | asrt( $writer->glueSQLCondition( ' value1 > ? OR value < ? '), ' WHERE value1 > ? OR value < ? ' ); 48 | //Does it recognize NON-WHERE conditions? - usual suspects 49 | asrt( $writer->glueSQLCondition( ' ORDER BY name '), ' ORDER BY name ' ); 50 | asrt( $writer->glueSQLCondition( ' LIMIT 10 '), ' LIMIT 10 ' ); 51 | asrt( $writer->glueSQLCondition( ' OFFSET 20 '), ' OFFSET 20 ' ); 52 | //highly doubtful but who knows... - I think nobody will ever use this in a query snippet. 53 | asrt( $writer->glueSQLCondition( ' GROUP BY grp '), ' GROUP BY grp ' ); 54 | asrt( $writer->glueSQLCondition( ' HAVING x = ? '), ' HAVING x = ? ' ); 55 | //can we replace WHERE with AND ? 56 | asrt( $writer->glueSQLCondition( ' AND name = ? ', QueryWriter::C_GLUE_WHERE ), ' WHERE name = ? ' ); 57 | //can we glue with AND instead of WHERE ? 58 | asrt( $writer->glueSQLCondition( ' value1 > ? OR value < ? ', QueryWriter::C_GLUE_AND ), ' AND value1 > ? OR value < ? ' ); 59 | //non-cases 60 | asrt( $writer->glueSQLCondition( ' GROUP BY grp ', QueryWriter::C_GLUE_WHERE ), ' GROUP BY grp ' ); 61 | asrt( $writer->glueSQLCondition( ' GROUP BY grp ', QueryWriter::C_GLUE_AND ), ' GROUP BY grp ' ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /testing/RedUNIT/Blackhole/Labels.php: -------------------------------------------------------------------------------- 1 | setMeta( "this.is.a.custom.metaproperty", "yes" ); 40 | asrt( $bean->getMeta( "this.is.a.custom.metaproperty" ), "yes" ); 41 | asrt( $bean->getMeta( "nonexistant" ), NULL ); 42 | asrt( $bean->getMeta( "nonexistant", "abc" ), "abc" ); 43 | asrt( $bean->getMeta( "nonexistant.nested" ), NULL ); 44 | asrt( $bean->getMeta( "nonexistant,nested", "abc" ), "abc" ); 45 | $bean->setMeta( "test.two", "second" ); 46 | asrt( $bean->getMeta( "test.two" ), "second" ); 47 | $bean->setMeta( "another.little.property", "yes" ); 48 | asrt( $bean->getMeta( "another.little.property" ), "yes" ); 49 | asrt( $bean->getMeta( "test.two" ), "second" ); 50 | // Copy Metadata 51 | $bean = new OODBBean; 52 | $bean->setMeta( "meta.meta", "123" ); 53 | $bean2 = new OODBBean; 54 | asrt( $bean2->getMeta( "meta.meta" ), NULL ); 55 | $bean2->copyMetaFrom( $bean ); 56 | asrt( $bean2->getMeta( "meta.meta" ), "123" ); 57 | } 58 | 59 | /** 60 | * Meta properties should not be saved. 61 | * 62 | * @return void 63 | */ 64 | public function testMetaPersist() 65 | { 66 | $bean = R::dispense( 'bean' ); 67 | $bean->property = 'test'; 68 | $bean->setMeta( 'meta', 'hello' ); 69 | R::store( $bean ); 70 | asrt( $bean->getMeta( 'meta' ), 'hello' ); 71 | $bean = $bean->fresh(); 72 | asrt( $bean->getMeta( 'meta' ), NULL ); 73 | } 74 | 75 | /** 76 | * You cant access meta data using the array accessors. 77 | * 78 | * @return void 79 | */ 80 | public function testNoArrayMetaAccess() 81 | { 82 | $bean = R::dispense( 'bean' ); 83 | $bean->setMeta( 'greet', 'hello' ); 84 | asrt( isset( $bean['greet'] ), FALSE ); 85 | asrt( isset( $bean['__info']['greet'] ), FALSE ); 86 | asrt( isset( $bean['__info'] ), FALSE ); 87 | asrt( isset( $bean['meta'] ), FALSE ); 88 | asrt( count( $bean ), 1 ); 89 | } 90 | 91 | /** 92 | * Test meta masks. 93 | * 94 | * @return void 95 | */ 96 | public function testMetaMask() 97 | { 98 | $rows = array( 99 | array('id'=>1, 'name'=>'a', '__meta_rows'=>2, '__meta_columns'=>4), 100 | array('id'=>2, 'name'=>'b', '__meta_rows'=>2, '__meta_columns'=>4) 101 | ); 102 | $books = R::convertToBeans( 'book', $rows, '__meta' ); 103 | $book = reset($books); 104 | $data = $book->getMeta('data.bundle'); 105 | asrt( $data['__meta_rows'], 2 ); 106 | asrt( $data['__meta_columns'], 4 ); 107 | $books = R::convertToBeans( 'book', $rows, array( '__meta_rows', '__meta_columns' ) ); 108 | $book = reset($books); 109 | $data = $book->getMeta('data.bundle'); 110 | asrt( $data['__meta_rows'], 2 ); 111 | asrt( $data['__meta_columns'], 4 ); 112 | $books = R::convertToBeans( 'book', $rows, array( '__meta_rows' ) ); 113 | $book = reset($books); 114 | $data = $book->getMeta('data.bundle'); 115 | asrt( $data['__meta_rows'], 2 ); 116 | asrt( isset($data['__meta_columns']), FALSE ); 117 | $books = R::convertToBeans( 'book', $rows, array( '__meta_rows', TRUE ) ); 118 | $book = reset($books); 119 | $data = $book->getMeta('data.bundle'); 120 | asrt( isset($data['__meta_rows']), FALSE ); 121 | asrt( isset($data['__meta_columns']), FALSE ); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /testing/RedUNIT/Blackhole/Plugins.php: -------------------------------------------------------------------------------- 1 | getMessage(), 'Plugin name may only contain alphanumeric characters and underscores and cannot start with a number.' ); 57 | } 58 | try { 59 | R::__callStatic( '---', function() {} ); 60 | fail(); 61 | } catch ( RedException $e ) { 62 | asrt( $e->getMessage(), 'Plugin name may only contain alphanumeric characters and underscores and cannot start with a number.' ); 63 | } 64 | try { 65 | R::invalidMethod(); 66 | fail(); 67 | } catch ( RedException $e ) { 68 | asrt( $e->getMessage(), 'Plugin \'invalidMethod\' does not exist, add this plugin using: R::ext(\'invalidMethod\')' ); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /testing/RedUNIT/Blackhole/Tainted.php: -------------------------------------------------------------------------------- 1 | hasListChanged( 'ownPage' ), FALSE ); 36 | $book->ownPage[] = $page; 37 | asrt( $book->hasListChanged( 'ownPage' ), TRUE ); 38 | R::store( $book ); 39 | $book = $book->fresh(); 40 | asrt( $book->hasListChanged( 'ownPage' ), FALSE ); 41 | $page = R::dispense( 'page' ); 42 | $book->ownPageList[] = $page; 43 | asrt( $book->hasListChanged( 'ownPage' ), TRUE ); 44 | R::store( $book ); 45 | $book = $book->fresh(); 46 | asrt( $book->hasListChanged( 'ownPage' ), FALSE ); 47 | asrt( count( $book->ownPageList ), 2 ); 48 | array_pop( $book->ownPageList ); 49 | asrt( count( $book->ownPageList ), 1 ); 50 | asrt( $book->hasListChanged( 'ownPage' ), TRUE ); 51 | array_pop( $book->ownPageList ); 52 | asrt( count( $book->ownPageList ), 0 ); 53 | asrt( $book->hasListChanged( 'ownPage' ), TRUE ); 54 | $book = $book->fresh(); 55 | asrt( $book->hasListChanged( 'ownPage' ), FALSE ); 56 | asrt( count( $book->ownPageList ), 2 ); 57 | $otherPage = R::dispense( 'page' ); 58 | array_pop( $book->ownPageList ); 59 | $book->ownPageList[] = $otherPage; 60 | asrt( count( $book->ownPageList ), 2 ); 61 | asrt( $book->hasListChanged( 'ownPage' ), TRUE ); 62 | $book = $book->fresh(); 63 | $firstPage = reset( $book->ownPageList ); 64 | $firstPage->content = 'abc'; 65 | asrt( $book->hasListChanged( 'ownPage' ), FALSE ); 66 | $book = $book->fresh(); 67 | asrt( $book->hasListChanged( 'ownPage' ), FALSE ); 68 | $lastPage = end( $book->ownPageList ); 69 | $lastPage->ownText[] = R::dispense( 'text' ); 70 | asrt( $book->hasListChanged( 'ownPage' ), FALSE ); 71 | } 72 | 73 | /** 74 | * Tests whether we can clear the history of a bean. 75 | * 76 | * @return void 77 | */ 78 | public function testClearHist() 79 | { 80 | R::nuke(); 81 | $book = R::dispense( 'book' ); 82 | asrt( $book->hasChanged( 'title' ), FALSE ); 83 | $book->title = 'book'; 84 | asrt( $book->hasChanged( 'title' ), TRUE ); 85 | R::store( $book ); 86 | asrt( $book->hasChanged( 'title' ), TRUE ); 87 | $book->clearHistory(); 88 | asrt( $book->hasChanged( 'title' ), FALSE ); 89 | } 90 | 91 | /** 92 | * Test tainted. 93 | * 94 | * @return void 95 | */ 96 | public function testTainted() 97 | { 98 | testpack( 'Original Tainted Tests' ); 99 | $redbean = R::getRedBean(); 100 | $spoon = $redbean->dispense( "spoon" ); 101 | asrt( $spoon->getMeta( "tainted" ), TRUE ); 102 | $spoon->dirty = "yes"; 103 | asrt( $spoon->getMeta( "tainted" ), TRUE ); 104 | testpack( 'Tainted List test' ); 105 | $note = R::dispense( 'note' ); 106 | $note->text = 'abc'; 107 | $note->ownNote[] = R::dispense( 'note' )->setAttr( 'text', 'def' ); 108 | $id = R::store( $note ); 109 | $note = R::load( 'note', $id ); 110 | asrt( $note->isTainted(), FALSE ); 111 | // Shouldn't affect tainted 112 | $note->text; 113 | asrt( $note->isTainted(), FALSE ); 114 | $note->ownNote; 115 | asrt( $note->isTainted(), TRUE ); 116 | testpack( 'Tainted Test Old Value' ); 117 | $text = $note->old( 'text' ); 118 | asrt( $text, 'abc' ); 119 | asrt( $note->hasChanged( 'text' ), FALSE ); 120 | $note->text = 'xxx'; 121 | asrt( $note->hasChanged( 'text' ), TRUE ); 122 | $text = $note->old( 'text' ); 123 | asrt( $text, 'abc' ); 124 | testpack( 'Tainted Non-exist' ); 125 | asrt( $note->hasChanged( 'text2' ), FALSE ); 126 | testpack( 'Misc Tainted Tests' ); 127 | $bean = R::dispense( 'bean' ); 128 | $bean->hasChanged( 'prop' ); 129 | $bean->old( 'prop' ); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /testing/RedUNIT/Blackhole/Version.php: -------------------------------------------------------------------------------- 1 | getFeatureFlags(), '1,0,0,0,0' ); 86 | R::useFeatureSet('5.3'); 87 | asrt( $this->getFeatureFlags(), '1,0,0,0,0' ); 88 | R::useFeatureSet('novice/5.3'); 89 | asrt( $this->getFeatureFlags(), '1,1,0,0,0' ); 90 | R::useFeatureSet('5.4'); 91 | asrt( $this->getFeatureFlags(), '1,0,0,1,1' ); 92 | R::useFeatureSet('latest'); 93 | asrt( $this->getFeatureFlags(), '1,0,0,1,1' ); 94 | R::useFeatureSet('novice/5.4'); 95 | asrt( $this->getFeatureFlags(), '1,1,0,0,1' ); 96 | R::useFeatureSet('5.5'); 97 | asrt( $this->getFeatureFlags(), '1,0,0,1,1' ); 98 | R::useFeatureSet('novice/5.5'); 99 | asrt( $this->getFeatureFlags(), '1,1,0,0,1' ); 100 | R::useFeatureSet('novice/latest'); 101 | asrt( $this->getFeatureFlags(), '1,1,0,0,1' ); 102 | R::useFeatureSet('original'); 103 | asrt( $this->getFeatureFlags(), '1,0,0,0,0' ); 104 | } 105 | 106 | /** 107 | * Test whether an invalid feature set label will 108 | * cause an exception. 109 | * 110 | * @return void 111 | */ 112 | public function testInvalidFeatureLabel() 113 | { 114 | try { 115 | R::useFeatureSet('Invalid'); 116 | fail(); 117 | } catch( \Exception $e ) { 118 | pass(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /testing/RedUNIT/CUBRID.php: -------------------------------------------------------------------------------- 1 | getDatabaseAdapter(); 34 | $writer = $toolbox->getWriter(); 35 | $redbean = $toolbox->getRedBean(); 36 | $pdo = $adapter->getDatabase(); 37 | $writer->createTable( "testtable" ); 38 | $writer->addColumn( "testtable", "special", CUBRID::C_DATATYPE_SPECIAL_DATE ); 39 | $cols = $writer->getColumns( "testtable" ); 40 | asrt( $writer->code( $cols['special'], TRUE ), CUBRID::C_DATATYPE_SPECIAL_DATE ); 41 | asrt( $writer->code( $cols['special'], FALSE ), CUBRID::C_DATATYPE_SPECIFIED ); 42 | $writer->addColumn( "testtable", "special2", CUBRID::C_DATATYPE_SPECIAL_DATETIME ); 43 | $cols = $writer->getColumns( "testtable" ); 44 | asrt( $writer->code( $cols['special2'], TRUE ), CUBRID::C_DATATYPE_SPECIAL_DATETIME ); 45 | asrt( $writer->code( $cols['special'], FALSE ), CUBRID::C_DATATYPE_SPECIFIED ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /testing/RedUNIT/Mysql.php: -------------------------------------------------------------------------------- 1 | id, $book1ID ); 111 | asrt( $book1->title, 'book 1' ); 112 | $book2 = R::load( 'book', $book2ID ); 113 | asrt( $book2->id, $book2ID ); 114 | asrt( $book2->title, 'book 2' ); 115 | asrt( count( $book1->ownPage ), 2 ); 116 | asrt( count( $book1->fresh()->with( 'LIMIT 1' )->ownPage ), 1 ); 117 | asrt( count( $book1->fresh()->withCondition( ' title = ? ', array('page 2 of book 1'))->ownPage ), 1 ); 118 | asrt( count($book2->ownPage), 1 ); 119 | asrt( $book2->fresh()->countOwn( 'page' ), 1 ); 120 | $page1 = R::load( 'page', $page1ID ); 121 | asrt( count( $page1->sharedPage ), 0 ); 122 | asrt( $page1->fetchAs( 'book' )->magazine->id, $book2ID ); 123 | $page2 = R::load( 'page', $page2ID ); 124 | asrt( count($page2->sharedPage), 1 ); 125 | asrt( $page2->fresh()->countShared( 'page' ), 1 ); 126 | $page3 = R::findOne( 'page', ' title = ? ', array( 'page 1 of book 2' ) ); 127 | asrt( $page3->id, $page3ID ); 128 | asrt( $page3->book->id, $book2ID ); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /testing/RedUNIT/Mysql/Double.php: -------------------------------------------------------------------------------- 1 | getDatabaseAdapter(); 34 | $writer = $toolbox->getWriter(); 35 | $redbean = $toolbox->getRedBean(); 36 | $pdo = $adapter->getDatabase(); 37 | $largeDouble = 999999888889999922211111; //8.88889999922211e+17; 38 | $page = $redbean->dispense( "page" ); 39 | $page->weight = $largeDouble; 40 | $id = $redbean->store( $page ); 41 | $cols = $writer->getColumns( 'page' ); 42 | asrt( $cols['weight'], 'double' ); 43 | $page = $redbean->load( 'page', $id ); 44 | $page->name = 'dont change the numbers!'; 45 | $redbean->store( $page ); 46 | $page = $redbean->load( 'page', $id ); 47 | $cols = $writer->getColumns( 'page' ); 48 | asrt( $cols['weight'], 'double' ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /testing/RedUNIT/Mysql/Freeze.php: -------------------------------------------------------------------------------- 1 | getDatabaseAdapter(); 38 | $writer = $toolbox->getWriter(); 39 | $redbean = $toolbox->getRedBean(); 40 | $pdo = $adapter->getDatabase(); 41 | $a = new AssociationManager( $toolbox ); 42 | $post = $redbean->dispense( 'post' ); 43 | $post->title = 'title'; 44 | $redbean->store( $post ); 45 | $page = $redbean->dispense( 'page' ); 46 | $page->name = 'title'; 47 | $redbean->store( $page ); 48 | $page = $redbean->dispense( "page" ); 49 | $page->name = "John's page"; 50 | $idpage = $redbean->store( $page ); 51 | $page2 = $redbean->dispense( "page" ); 52 | $page2->name = "John's second page"; 53 | $idpage2 = $redbean->store( $page2 ); 54 | $a->associate( $page, $page2 ); 55 | $redbean->freeze( TRUE ); 56 | $page = $redbean->dispense( "page" ); 57 | $page->sections = 10; 58 | $page->name = "half a page"; 59 | try { 60 | $id = $redbean->store( $page ); 61 | fail(); 62 | } catch ( SQL $e ) { 63 | pass(); 64 | } 65 | $post = $redbean->dispense( "post" ); 66 | $post->title = "existing table"; 67 | try { 68 | $id = $redbean->store( $post ); 69 | pass(); 70 | } catch ( SQL $e ) { 71 | fail(); 72 | } 73 | asrt( in_array( "name", array_keys( $writer->getColumns( "page" ) ) ), TRUE ); 74 | asrt( in_array( "sections", array_keys( $writer->getColumns( "page" ) ) ), FALSE ); 75 | $newtype = $redbean->dispense( "newtype" ); 76 | $newtype->property = 1; 77 | try { 78 | $id = $redbean->store( $newtype ); 79 | fail(); 80 | } catch ( SQL $e ) { 81 | pass(); 82 | } 83 | $logger = R::debug( TRUE, 1 ); 84 | // Now log and make sure no 'describe SQL' happens 85 | $page = $redbean->dispense( "page" ); 86 | $page->name = "just another page that has been frozen..."; 87 | $id = $redbean->store( $page ); 88 | $page = $redbean->load( "page", $id ); 89 | $page->name = "just a frozen page..."; 90 | $redbean->store( $page ); 91 | $page2 = $redbean->dispense( "page" ); 92 | $page2->name = "an associated frozen page"; 93 | $a->associate( $page, $page2 ); 94 | $a->related( $page, "page" ); 95 | $a->unassociate( $page, $page2 ); 96 | $a->clearRelations( $page, "page" ); 97 | $items = $redbean->find( "page", array(), array( "1" ) ); 98 | $redbean->trash( $page ); 99 | $redbean->freeze( FALSE ); 100 | asrt( count( $logger->grep( "SELECT" ) ) > 0, TRUE ); 101 | asrt( count( $logger->grep( "describe" ) ) < 1, TRUE ); 102 | asrt( is_array( $logger->getLogs() ), TRUE ); 103 | R::debug( FALSE ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /testing/RedUNIT/Mysql/Issue411.php: -------------------------------------------------------------------------------- 1 | text = 'abcd'; 39 | R::store( $book ); 40 | $columns = R::inspect( 'book' ); 41 | asrt( isset( $columns['text'] ), TRUE ); 42 | asrt( $columns['text'], 'varchar(191)' ); 43 | $book = $book->fresh(); 44 | $book->text = str_repeat( 'x', 190 ); 45 | R::store( $book ); 46 | $columns = R::inspect( 'book' ); 47 | asrt( isset( $columns['text'] ), TRUE ); 48 | asrt( $columns['text'], 'varchar(191)' ); 49 | $book = $book->fresh(); 50 | $book->text = str_repeat( 'x', 191 ); 51 | R::store( $book ); 52 | $columns = R::inspect( 'book' ); 53 | asrt( isset( $columns['text'] ), TRUE ); 54 | asrt( $columns['text'], 'varchar(191)' ); 55 | $book = $book->fresh(); 56 | $book->text = str_repeat( 'x', 192 ); 57 | R::store( $book ); 58 | $columns = R::inspect( 'book' ); 59 | asrt( isset( $columns['text'] ), TRUE ); 60 | asrt( $columns['text'], 'varchar(255)' ); 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /testing/RedUNIT/Mysql/Parambind.php: -------------------------------------------------------------------------------- 1 | getDatabaseAdapter(); 36 | $writer = $toolbox->getWriter(); 37 | $redbean = $toolbox->getRedBean(); 38 | $pdo = $adapter->getDatabase(); 39 | R::getDatabaseAdapter()->getDatabase()->setUseStringOnlyBinding( TRUE ); 40 | try { 41 | R::getAll( "select * from job limit ? ", array( 1 ) ); 42 | fail(); 43 | } catch (\Exception $e ) { 44 | pass(); 45 | } 46 | try { 47 | R::getAll( "select * from job limit :l ", array( ":l" => 1 ) ); 48 | fail(); 49 | } catch (\Exception $e ) { 50 | pass(); 51 | } 52 | try { 53 | R::exec( "select * from job limit ? ", array( 1 ) ); 54 | fail(); 55 | } catch (\Exception $e ) { 56 | pass(); 57 | } 58 | try { 59 | R::exec( "select * from job limit :l ", array( ":l" => 1 ) ); 60 | fail(); 61 | } catch (\Exception $e ) { 62 | pass(); 63 | } 64 | R::getDatabaseAdapter()->getDatabase()->setUseStringOnlyBinding( FALSE ); 65 | try { 66 | R::getAll( "select * from job limit ? ", array( 1 ) ); 67 | pass(); 68 | } catch (\Exception $e ) { 69 | fail(); 70 | } 71 | try { 72 | R::getAll( "select * from job limit :l ", array( ":l" => 1 ) ); 73 | pass(); 74 | } catch (\Exception $e ) { 75 | fail(); 76 | } 77 | try { 78 | R::exec( "select * from job limit ? ", array( 1 ) ); 79 | pass(); 80 | } catch (\Exception $e ) { 81 | fail(); 82 | } 83 | try { 84 | R::exec( "select * from job limit :l ", array( ":l" => 1 ) ); 85 | pass(); 86 | } catch (\Exception $e ) { 87 | fail(); 88 | } 89 | testpack( "Test findOrDispense" ); 90 | $person = R::findOrDispense( "person", " job = ? ", array( "developer" ) ); 91 | asrt( ( count( $person ) > 0 ), TRUE ); 92 | $person = R::findOrDispense( "person", " job = ? ", array( "musician" ) ); 93 | asrt( ( count( $person ) > 0 ), TRUE ); 94 | $musician = array_pop( $person ); 95 | asrt( intval( $musician->id ), 0 ); 96 | try { 97 | $adapter->exec( "an invalid query" ); 98 | fail(); 99 | } catch ( SQL $e ) { 100 | pass(); 101 | } 102 | asrt( (int) $adapter->getCell( "SELECT 123" ), 123 ); 103 | asrt( (int) $adapter->getCell( "SELECT ?", array( "987" ) ), 987 ); 104 | asrt( (int) $adapter->getCell( "SELECT ?+?", array( "987", "2" ) ), 989 ); 105 | asrt( (int) $adapter->getCell( "SELECT :numberOne+:numberTwo", array( 106 | ":numberOne" => 42, ":numberTwo" => 50 ) ), 92 ); 107 | $pair = $adapter->getAssoc( "SELECT 'thekey','thevalue' " ); 108 | asrt( is_array( $pair ), TRUE ); 109 | asrt( count( $pair ), 1 ); 110 | asrt( isset( $pair["thekey"] ), TRUE ); 111 | asrt( $pair["thekey"], "thevalue" ); 112 | testpack( 'Test whether we can properly bind and receive NULL values' ); 113 | asrt( $adapter->getCell( 'SELECT :nil ', array( ':nil' => 'NULL' ) ), 'NULL' ); 114 | asrt( $adapter->getCell( 'SELECT :nil ', array( ':nil' => NULL ) ), NULL ); 115 | asrt( $adapter->getCell( 'SELECT ? ', array( 'NULL' ) ), 'NULL' ); 116 | asrt( $adapter->getCell( 'SELECT ? ', array( NULL ) ), NULL ); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /testing/RedUNIT/Mysql/Preexist.php: -------------------------------------------------------------------------------- 1 | getDatabaseAdapter(); 35 | $writer = $toolbox->getWriter(); 36 | $redbean = $toolbox->getRedBean(); 37 | $pdo = $adapter->getDatabase(); 38 | $a = new AssociationManager( $toolbox ); 39 | $page = $redbean->dispense( "page" ); 40 | $page->name = "John's page"; 41 | $idpage = $redbean->store( $page ); 42 | $page2 = $redbean->dispense( "page" ); 43 | $page2->name = "John's second page"; 44 | $idpage2 = $redbean->store( $page2 ); 45 | $a->associate( $page, $page2 ); 46 | $adapter->exec( "ALTER TABLE " . $writer->esc( 'page' ) . " 47 | CHANGE " . $writer->esc( 'name' ) . " " . $writer->esc( 'name' ) . " 48 | VARCHAR( 254 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL " ); 49 | $page = $redbean->dispense( "page" ); 50 | $page->name = "Just Another Page In a Table"; 51 | $cols = $writer->getColumns( "page" ); 52 | asrt( $cols["name"], "varchar(254)" ); 53 | $redbean->store( $page ); 54 | pass(); // No crash? 55 | $cols = $writer->getColumns( "page" ); 56 | asrt( $cols["name"], "varchar(254)" ); //must still be same 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /testing/RedUNIT/Postgres.php: -------------------------------------------------------------------------------- 1 | id, $book1ID ); 101 | asrt( $book1->title, 'book 1' ); 102 | $book2 = R::load( 'book', $book2ID ); 103 | asrt( $book2->id, $book2ID ); 104 | asrt( $book2->title, 'book 2' ); 105 | asrt( count( $book1->ownPage ), 2 ); 106 | asrt( count( $book1->fresh()->with( 'LIMIT 1' )->ownPage ), 1 ); 107 | asrt( count( $book1->fresh()->withCondition( ' title = ? ', array('page 2 of book 1'))->ownPage ), 1 ); 108 | asrt( count($book2->ownPage), 1 ); 109 | asrt( $book2->fresh()->countOwn( 'page' ), 1 ); 110 | $page1 = R::load( 'page', $page1ID ); 111 | asrt( count( $page1->sharedPage ), 0 ); 112 | asrt( $page1->fetchAs( 'book' )->magazine->id, $book2ID ); 113 | $page2 = R::load( 'page', $page2ID ); 114 | asrt( count($page2->sharedPage), 1 ); 115 | asrt( $page2->fresh()->countShared( 'page' ), 1 ); 116 | $page3 = R::findOne( 'page', ' title = ? ', array( 'page 1 of book 2' ) ); 117 | asrt( $page3->id, $page3ID ); 118 | asrt( $page3->book->id, $book2ID ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /testing/RedUNIT/Postgres/Parambind.php: -------------------------------------------------------------------------------- 1 | name = "abc"; 36 | $page->number = 2; 37 | R::store( $page ); 38 | R::exec( "insert into page (name) values(:name) ", array( ":name" => "my name" ) ); 39 | R::exec( "insert into page (number) values(:one) ", array( ":one" => 1 ) ); 40 | R::exec( "insert into page (number) values(:one) ", array( ":one" => "1" ) ); 41 | R::exec( "insert into page (number) values(:one) ", array( ":one" => "1234" ) ); 42 | R::exec( "insert into page (number) values(:one) ", array( ":one" => "-21" ) ); 43 | pass(); 44 | testpack( 'Test whether we can properly bind and receive NULL values' ); 45 | $adapter = R::getDatabaseAdapter(); 46 | asrt( $adapter->getCell( 'SELECT TEXT( :nil ) ', array( ':nil' => 'NULL' ) ), 'NULL' ); 47 | asrt( $adapter->getCell( 'SELECT TEXT( :nil ) ', array( ':nil' => NULL ) ), NULL ); 48 | asrt( $adapter->getCell( 'SELECT TEXT( ? ) ', array( 'NULL' ) ), 'NULL' ); 49 | asrt( $adapter->getCell( 'SELECT TEXT( ? ) ', array( NULL ) ), NULL ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /testing/RedUNIT/Pretest.php: -------------------------------------------------------------------------------- 1 | getDatabaseAdapter(); 35 | $writer = $toolbox->getWriter(); 36 | $redbean = $toolbox->getRedBean(); 37 | $pdo = $adapter->getDatabase(); 38 | asrt( (int) $adapter->getCell( "SELECT 123" ), 123 ); 39 | asrt( (int) $adapter->getCell( "SELECT ?", array( "987" ) ), 987 ); 40 | asrt( (int) $adapter->getCell( "SELECT ?+?", array( "987", "2" ) ), 989 ); 41 | asrt( (int) $adapter->getCell( 42 | "SELECT :numberOne+:numberTwo", 43 | array( 44 | ":numberOne" => 42, 45 | ":numberTwo" => 50 ) 46 | ), 47 | 92 48 | ); 49 | $pair = $adapter->getAssoc( "SELECT 'thekey','thevalue' " ); 50 | asrt( is_array( $pair ), TRUE ); 51 | asrt( count( $pair ), 1 ); 52 | asrt( isset( $pair["thekey"] ), TRUE ); 53 | asrt( $pair["thekey"], "thevalue" ); 54 | testpack( 'Test whether we can properly bind and receive NULL values' ); 55 | asrt( $adapter->getCell( 'SELECT :nil ', array( ':nil' => 'NULL' ) ), 'NULL' ); 56 | asrt( $adapter->getCell( 'SELECT :nil ', array( ':nil' => NULL ) ), NULL ); 57 | asrt( $adapter->getCell( 'SELECT ? ', array( 'NULL' ) ), 'NULL' ); 58 | asrt( $adapter->getCell( 'SELECT ? ', array( NULL ) ), NULL ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /testing/RedUNIT/Sqlite/Rebuild.php: -------------------------------------------------------------------------------- 1 | getDatabaseAdapter(); 37 | $writer = $toolbox->getWriter(); 38 | $redbean = $toolbox->getRedBean(); 39 | $pdo = $adapter->getDatabase(); 40 | $book = R::dispense( 'book' ); 41 | $page = R::dispense( 'page' ); 42 | $book->xownPage[] = $page; 43 | $id = R::store( $book ); 44 | $book = R::load( 'book', $id ); 45 | asrt( count( $book->xownPage ), 1 ); 46 | asrt( (int) R::getCell( 'SELECT COUNT(*) FROM page' ), 1 ); 47 | R::trash( $book ); 48 | asrt( (int) R::getCell( 'SELECT COUNT(*) FROM page' ), 0 ); 49 | $book = R::dispense( 'book' ); 50 | $page = R::dispense( 'page' ); 51 | $book->xownPage[] = $page; 52 | $id = R::store( $book ); 53 | $book = R::load( 'book', $id ); 54 | asrt( count( $book->xownPage ), 1 ); 55 | asrt( (int) R::getCell( 'SELECT COUNT(*) FROM page' ), 1 ); 56 | $book->added = 2; 57 | R::store( $book ); 58 | $book->added = 'added'; 59 | R::store( $book ); 60 | R::trash( $book ); 61 | asrt( (int) R::getCell( 'SELECT COUNT(*) FROM page' ), 0 ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /testing/RedUNIT/Sqlite/Setget.php: -------------------------------------------------------------------------------- 1 | setTimeZone( new \DateTimeZone( 'Europe/Amsterdam' ) ); 46 | $dt->setDate( 1981, 5, 1 ); 47 | $dt->setTime( 3, 13, 13 ); 48 | asrt( setget( $dt ), '1981-05-01 03:13:13' ); 49 | $bean = R::dispense( 'bean' ); 50 | $bean->dt = $dt; 51 | } 52 | 53 | /** 54 | * Test numbers. 55 | * 56 | * @return void 57 | */ 58 | public function testNumbers() 59 | { 60 | asrt( setget( "-1" ), "-1" ); 61 | asrt( setget( -1 ), "-1" ); 62 | asrt( setget( "-0.25" ), "-0.25" ); 63 | asrt( setget( -0.25 ), "-0.25" ); 64 | asrt( setget( "1.0" ), "1" ); 65 | asrt( setget( 1.0 ), "1" ); 66 | asrt( setget( "0.12345678" ), "0.12345678" ); 67 | asrt( setget( 0.12345678 ), "0.12345678" ); 68 | asrt( setget( "-0.12345678" ), "-0.12345678" ); 69 | asrt( setget( -0.12345678 ), "-0.12345678" ); 70 | asrt( setget( "2147483647" ), "2147483647" ); 71 | asrt( setget( 2147483647 ), "2147483647" ); 72 | asrt( setget( -2147483647 ), "-2147483647" ); 73 | asrt( setget( "-2147483647" ), "-2147483647" ); 74 | asrt( setget( "2147483648" ), "2147483648" ); 75 | asrt( setget( "-2147483648" ), "-2147483648" ); 76 | $x = setget( "199936710040730" ); 77 | asrt( $x === "199936710040730" || $x === "1.9993671004073E+14", TRUE ); 78 | $x = setget( "-199936710040730" ); 79 | asrt( $x === "-199936710040730" || $x === "-1.9993671004073E+14", TRUE ); 80 | //asrt( setget( "199936710040730" ), "199936710040730" ); 81 | //asrt( setget( "-199936710040730" ), "-199936710040730" ); 82 | } 83 | 84 | /** 85 | * Test dates. 86 | * 87 | * @return void 88 | */ 89 | public function testDates() 90 | { 91 | asrt( setget( "2010-10-11" ), "2010-10-11" ); 92 | asrt( setget( "2010-10-11 12:10" ), "2010-10-11 12:10" ); 93 | asrt( setget( "2010-10-11 12:10:11" ), "2010-10-11 12:10:11" ); 94 | asrt( setget( "x2010-10-11 12:10:11" ), "x2010-10-11 12:10:11" ); 95 | } 96 | 97 | /** 98 | * Test strings. 99 | * 100 | * @return void 101 | */ 102 | public function testStrings() 103 | { 104 | asrt( setget( "a" ), "a" ); 105 | asrt( setget( "." ), "." ); 106 | asrt( setget( "\"" ), "\"" ); 107 | asrt( setget( "just some text" ), "just some text" ); 108 | } 109 | 110 | /** 111 | * Test booleans. 112 | * 113 | * @return void 114 | */ 115 | public function testBool() 116 | { 117 | asrt( setget( TRUE ), "1" ); 118 | asrt( setget( FALSE ), "0" ); 119 | asrt( setget( "TRUE" ), "TRUE" ); 120 | asrt( setget( "FALSE" ), "FALSE" ); 121 | } 122 | 123 | /** 124 | * Test NULL. 125 | * 126 | * @return void 127 | */ 128 | public function testNull() 129 | { 130 | asrt( setget( "NULL" ), "NULL" ); 131 | asrt( setget( "NULL" ), "NULL" ); 132 | asrt( setget( NULL ), NULL ); 133 | asrt( ( setget( 0 ) == 0 ), TRUE ); 134 | asrt( ( setget( 1 ) == 1 ), TRUE ); 135 | asrt( ( setget( TRUE ) == TRUE ), TRUE ); 136 | asrt( ( setget( FALSE ) == FALSE ), TRUE ); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /testing/cli/plugins/myhooks.php: -------------------------------------------------------------------------------- 1 | 'localhost', 17 | 'schema' => 'cubase', 18 | 'user' => 'dba', 19 | 'pass' => '' 20 | ); 21 | 22 | $colorMap[ 'CUBRID' ] = '0;35'; 23 | 24 | $dsn = "cubrid:host={$ini['CUBRID']['host']};port=33000;dbname={$ini['CUBRID']['schema']}"; 25 | R::addDatabase( 'CUBRID', $dsn, $ini['CUBRID']['user'], $ini['CUBRID']['pass'], FALSE ); 26 | R::selectDatabase( 'CUBRID' ); 27 | R::exec( 'AUTOCOMMIT IS ON' ); 28 | -------------------------------------------------------------------------------- /testing/cli/runperf.php: -------------------------------------------------------------------------------- 1 | getTargetDrivers(); 52 | 53 | foreach ( $drivers as $driver ) { 54 | 55 | if ( !isset( $ini[$driver] ) ) continue; 56 | if ( !isset( $_SERVER['argv'][1])) die('Missing parameter. Usage: php runperf.php '); 57 | 58 | $method = $_SERVER['argv'][1]; 59 | 60 | if ($method === 'setup') { 61 | 62 | echo 'Setup...'.PHP_EOL; 63 | 64 | $test->$method(); 65 | 66 | echo 'READY'.PHP_EOL; 67 | 68 | } else { 69 | 70 | $times = 100; 71 | if (isset($_SERVER['argv'][2])) { 72 | $times = (int) $_SERVER['argv'][2]; 73 | } 74 | 75 | echo "Performing test: $method with driver $driver ".PHP_EOL; 76 | 77 | for ($j=0; $j<$times; $j++) { 78 | 79 | $t1 = microtime( TRUE ); 80 | 81 | $test->$method(); 82 | 83 | $t2 = microtime( TRUE ); 84 | 85 | $d[] = ($t2 - $t1); 86 | 87 | } 88 | 89 | $s = array_sum($d); 90 | $a = ($s / $times); 91 | $mx = max($d); 92 | $mn = min($d); 93 | 94 | echo PHP_EOL."AVG: $a, MAX: $mx, MIN: $mn, TOTAL: $s, TIMES: $times ".PHP_EOL; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /testing/cli/test_hook_example.php: -------------------------------------------------------------------------------- 1 | add 1 additional path which will be searched for unit tests 11 | * $extraTestsFromHook -> array with additional test packs (will be executed if you pass 'all' argument to CLI runner) 12 | * 13 | * 14 | */ -------------------------------------------------------------------------------- /testing/cli/testcontainer/put-rb-file-here.txt: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Put the rb.php file in this directory 4 | 5 | */ 6 | -------------------------------------------------------------------------------- /testing/config/test-dist.ini: -------------------------------------------------------------------------------- 1 | ; Test suite database config 2 | ; Rename this file to test.ini if you are done 3 | 4 | [mysql] 5 | host = "localhost" 6 | schema = "" 7 | user = "" 8 | pass = "" 9 | 10 | [pgsql] 11 | host = "localhost" 12 | schema = "" 13 | user = "" 14 | pass = "" 15 | 16 | [sqlite] 17 | file = "/tmp/database.db" 18 | -------------------------------------------------------------------------------- /testing/config/test-travis.ini: -------------------------------------------------------------------------------- 1 | ; Test suite database config 2 | ; Rename this file to test.ini if you are done 3 | 4 | [mysql] 5 | host = "localhost" 6 | schema = "oodb" 7 | user = "root" 8 | pass = "" 9 | 10 | [pgsql] 11 | host = "localhost" 12 | schema = "oodb" 13 | user = "postgres" 14 | pass = "" 15 | 16 | [sqlite] 17 | file = "/tmp/oodb.db" 18 | 19 | -------------------------------------------------------------------------------- /testing/notes.txt: -------------------------------------------------------------------------------- 1 | 2 | Test notes 3 | ========== 4 | 5 | * PostgreSQL tests perform a text-to-money cast test, make sure lc_monetary is set to en_US 6 | in your postgres.conf 7 | * PostgreSQL tests need the ossp extension, to enable this, install the postgres-contrib package and run SQL: CREATE EXTENSION "uuid-ossp"; 8 | * XDebug is NOT required but recommended 9 | --------------------------------------------------------------------------------